fxied vat calculation
This commit is contained in:
@@ -17,7 +17,7 @@ foreach($invoice->positions as $p) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$gesamtrabatt = $invoice->gesamtrabatt ?? 0;
|
$total_discount = $invoice->total_discount ?? 0;
|
||||||
$subtotal = 0;
|
$subtotal = 0;
|
||||||
foreach($invoice->positions as $p) {
|
foreach($invoice->positions as $p) {
|
||||||
$subtotal += $p->price_total ?? 0;
|
$subtotal += $p->price_total ?? 0;
|
||||||
@@ -127,8 +127,8 @@ $this->setReturnValue(['filename' => $invoice->invoice_number . ".pdf"]);
|
|||||||
|
|
||||||
<h2 style="text-align: center;color: #005384">Ihre Xinon <?=($is_credit) ? "Gutschrift" : "Rechnung"?> vom <?=date("d.m.Y",$invoice->invoice_date)?></h2>
|
<h2 style="text-align: center;color: #005384">Ihre Xinon <?=($is_credit) ? "Gutschrift" : "Rechnung"?> vom <?=date("d.m.Y",$invoice->invoice_date)?></h2>
|
||||||
|
|
||||||
<?php if($invoice->einleitender_text ?? ''): ?>
|
<?php if($invoice->introductory_text ?? ''): ?>
|
||||||
<p style="margin-top: 10pt; margin-bottom: 20pt; text-align: center; font-weight: bold;"><?=nl2br(htmlspecialchars($invoice->einleitender_text))?></p>
|
<p style="margin-top: 10pt; margin-bottom: 20pt; text-align: center; font-weight: bold;"><?=nl2br(htmlspecialchars($invoice->introductory_text))?></p>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<table style="border-collapse: collapse; width: 100%;" id="invoiceTable">
|
<table style="border-collapse: collapse; width: 100%;" id="invoiceTable">
|
||||||
@@ -166,7 +166,7 @@ $this->setReturnValue(['filename' => $invoice->invoice_number . ".pdf"]);
|
|||||||
|
|
||||||
<tr class="position <?=($i%2 == 0) ? "even" : "uneven" ?>">
|
<tr class="position <?=($i%2 == 0) ? "even" : "uneven" ?>">
|
||||||
<td style="padding-left: 4pt; vertical-align: top;">
|
<td style="padding-left: 4pt; vertical-align: top;">
|
||||||
<?=htmlspecialchars($p->product_name ?? '')?>
|
<?=htmlspecialchars($p->warehousearticle_name ?? '')?>
|
||||||
<?php if(isset($p->product_info) && $p->product_info): ?>
|
<?php if(isset($p->product_info) && $p->product_info): ?>
|
||||||
<div style="padding-left: 12pt; font-size: 10px; color: #666;"><?=htmlspecialchars($p->product_info)?></div>
|
<div style="padding-left: 12pt; font-size: 10px; color: #666;"><?=htmlspecialchars($p->product_info)?></div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
@@ -186,17 +186,17 @@ $this->setReturnValue(['filename' => $invoice->invoice_number . ".pdf"]);
|
|||||||
endforeach;
|
endforeach;
|
||||||
endforeach;
|
endforeach;
|
||||||
?>
|
?>
|
||||||
<?php if($gesamtrabatt > 0): ?>
|
<?php if($total_discount > 0): ?>
|
||||||
<tr style="background-color: #ebebeb; border-top: 2px solid black;">
|
<tr style="background-color: #ebebeb; border-top: 2px solid black;">
|
||||||
<td colspan="<?=$hasDiscount ? '5' : '4'?>" style="padding: 4px 0;">Zwischensumme:</td>
|
<td colspan="<?=$hasDiscount ? '5' : '4'?>" style="padding: 4px 0;">Zwischensumme:</td>
|
||||||
<td colspan="2" style="text-align: right; padding-right: 4pt;"><?=number_format($subtotal, 2, ",","."). " €"?></td>
|
<td colspan="2" style="text-align: right; padding-right: 4pt;"><?=number_format($subtotal, 2, ",","."). " €"?></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr style="background-color: #ebebeb; border-bottom: 1px solid #ccc;">
|
<tr style="background-color: #ebebeb; border-bottom: 1px solid #ccc;">
|
||||||
<td colspan="<?=$hasDiscount ? '5' : '4'?>" style="padding: 4px 0;">Gesamtrabatt <?=number_format($gesamtrabatt, 2, ",", ".")?>%:</td>
|
<td colspan="<?=$hasDiscount ? '5' : '4'?>" style="padding: 4px 0;">Gesamtrabatt <?=number_format($total_discount, 2, ",", ".")?>%:</td>
|
||||||
<td colspan="2" style="text-align: right; padding-right: 4pt; color: #d32f2f;">-<?=number_format($subtotal * ($gesamtrabatt / 100), 2, ",","."). " €"?></td>
|
<td colspan="2" style="text-align: right; padding-right: 4pt; color: #d32f2f;">-<?=number_format($subtotal * ($total_discount / 100), 2, ",","."). " €"?></td>
|
||||||
</tr>
|
</tr>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<tr style="font-weight: bold; background-color: #ebebeb; border-bottom: 1px solid black;<?=($gesamtrabatt > 0) ? '' : 'border-top: 2px solid black;'?>">
|
<tr style="font-weight: bold; background-color: #ebebeb; border-bottom: 1px solid black;<?=($total_discount > 0) ? '' : 'border-top: 2px solid black;'?>">
|
||||||
<td colspan="<?=$hasDiscount ? '5' : '4'?>" style="padding: 4px 0;">Gesamt Netto:</td>
|
<td colspan="<?=$hasDiscount ? '5' : '4'?>" style="padding: 4px 0;">Gesamt Netto:</td>
|
||||||
<td colspan="2" style="text-align: right; padding-right: 4pt;"><?=number_format($net_total, 2, ",","."). " €"?></td>
|
<td colspan="2" style="text-align: right; padding-right: 4pt;"><?=number_format($net_total, 2, ",","."). " €"?></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@@ -105,8 +105,8 @@ class ManualInvoiceController extends TTCrud
|
|||||||
"{{ billingAccount }}" => $invoice->fibu_account_number ?? '',
|
"{{ billingAccount }}" => $invoice->fibu_account_number ?? '',
|
||||||
"{{ invoiceNumber }}" => $invoice->invoice_number ?? "VORSCHAU",
|
"{{ invoiceNumber }}" => $invoice->invoice_number ?? "VORSCHAU",
|
||||||
"{{ invoiceDate }}" => date("d.m.Y", $invoice->invoice_date ?? time()),
|
"{{ invoiceDate }}" => date("d.m.Y", $invoice->invoice_date ?? time()),
|
||||||
"{{ leistungszeitraumHtml }}" => ($invoice->leistungszeitraum ?? '') ? "<tr><td>Leistungszeitraum:</td><td>" . htmlspecialchars($invoice->leistungszeitraum) . "</td></tr>" : "",
|
"{{ leistungszeitraumHtml }}" => ($invoice->performance_period ?? '') ? "<tr><td>Leistungszeitraum:</td><td>" . htmlspecialchars($invoice->performance_period) . "</td></tr>" : "",
|
||||||
"{{ externeReferenzHtml }}" => ($invoice->externe_referenz ?? '') ? "<tr><td>Externe Referenz:</td><td>" . htmlspecialchars($invoice->externe_referenz) . "</td></tr>" : "",
|
"{{ externeReferenzHtml }}" => ($invoice->external_reference ?? '') ? "<tr><td>Externe Referenz:</td><td>" . htmlspecialchars($invoice->external_reference) . "</td></tr>" : "",
|
||||||
"{{ vatHtml }}" => ($invoice->uid ?? '') ? "<tr><td>Ihre UID:</td><td>" . $invoice->uid . "</td></tr>" : "",
|
"{{ vatHtml }}" => ($invoice->uid ?? '') ? "<tr><td>Ihre UID:</td><td>" . $invoice->uid . "</td></tr>" : "",
|
||||||
"{{ qrCodeHtml }}" => ($invoice->total_gross ?? 0) >= 0
|
"{{ qrCodeHtml }}" => ($invoice->total_gross ?? 0) >= 0
|
||||||
? '<td style="vertical-align: top; padding-right: 10px;"><img alt="QR-Code" src="' . $this->generateSepaQRCode($invoice->invoice_number ?? "VORSCHAU", round($invoice->total_gross ?? 0, 2)) . '" style="display: block; height: 100%; max-height: 3.5cm; width: auto;"></td>'
|
? '<td style="vertical-align: top; padding-right: 10px;"><img alt="QR-Code" src="' . $this->generateSepaQRCode($invoice->invoice_number ?? "VORSCHAU", round($invoice->total_gross ?? 0, 2)) . '" style="display: block; height: 100%; max-height: 3.5cm; width: auto;"></td>'
|
||||||
@@ -393,11 +393,32 @@ class ManualInvoiceController extends TTCrud
|
|||||||
$data['status'] = 'erstellt';
|
$data['status'] = 'erstellt';
|
||||||
$data['fibu_payment_skonto'] = $data['fibu_payment_skonto'] ?? 0;
|
$data['fibu_payment_skonto'] = $data['fibu_payment_skonto'] ?? 0;
|
||||||
$data['fibu_payment_skonto_rate'] = $data['fibu_payment_skonto_rate'] ?? 0;
|
$data['fibu_payment_skonto_rate'] = $data['fibu_payment_skonto_rate'] ?? 0;
|
||||||
$data['gesamtrabatt'] = $data['gesamtrabatt'] ?? 0;
|
|
||||||
|
$data['total_discount'] = $data['total_discount'] ?? $data['gesamtrabatt'] ?? 0;
|
||||||
|
$data['performance_period'] = $data['performance_period'] ?? $data['leistungszeitraum'] ?? null;
|
||||||
|
$data['introductory_text'] = $data['introductory_text'] ?? $data['einleitender_text'] ?? null;
|
||||||
|
$data['external_reference'] = $data['external_reference'] ?? $data['externe_referenz'] ?? null;
|
||||||
|
unset($data['gesamtrabatt'], $data['leistungszeitraum'], $data['einleitender_text'], $data['externe_referenz'], $data['billing_delivery']);
|
||||||
|
|
||||||
$data['total'] = $data['total'] ?? 0;
|
$data['total'] = $data['total'] ?? 0;
|
||||||
$data['total_gross'] = $data['total_gross'] ?? 0;
|
$data['total_gross'] = $data['total_gross'] ?? 0;
|
||||||
$data['lock'] = 0;
|
$data['lock'] = 0;
|
||||||
$data['exported'] = 0;
|
$data['exported'] = 0;
|
||||||
|
|
||||||
|
if (($data['billing_type'] ?? '') === 'sepa' && ($data['billingaddress_id'] ?? null)) {
|
||||||
|
$address = new Address($data['billingaddress_id']);
|
||||||
|
if ($address->id) {
|
||||||
|
$data['bank_account_bank'] = $address->bank_account_bank;
|
||||||
|
$data['bank_account_owner'] = $address->bank_account_owner;
|
||||||
|
$data['bank_account_iban'] = str_replace(' ', '', $address->bank_account_iban ?? '');
|
||||||
|
$data['bank_account_bic'] = str_replace(' ', '', $address->bank_account_bic ?? '');
|
||||||
|
if ($address->sepa_date) {
|
||||||
|
$data['sepa_date'] = date('Y-m-d', $address->sepa_date);
|
||||||
|
}
|
||||||
|
$data['sepa_id'] = 'R' . ($data['fibu_account_number'] ?? '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$data['create_by'] = $me->id;
|
$data['create_by'] = $me->id;
|
||||||
$data['edit_by'] = $me->id;
|
$data['edit_by'] = $me->id;
|
||||||
$data['create'] = time();
|
$data['create'] = time();
|
||||||
@@ -476,31 +497,40 @@ class ManualInvoiceController extends TTCrud
|
|||||||
$me = new User();
|
$me = new User();
|
||||||
$me->loadMe();
|
$me->loadMe();
|
||||||
|
|
||||||
// Fields that exist in ManualInvoicepositionModel
|
|
||||||
$allowedFields = ['billing_id', 'contract_id', 'matchcode', 'product_id', 'product_name', 'product_info',
|
|
||||||
'amount', 'unit', 'price', 'discount', 'price_total', 'price_gross', 'vatrate',
|
|
||||||
'fibu_cost_account', 'fibu_cost_account_legacy', 'fibu_taxcode', 'options'];
|
|
||||||
|
|
||||||
foreach ($this->tempPositions as $position) {
|
foreach ($this->tempPositions as $position) {
|
||||||
// Skip empty positions
|
$articleName = $position['warehousearticle_name'] ?? $position['product_name'] ?? '';
|
||||||
if (empty($position['product_name']) || ($position['amount'] ?? 0) == 0) continue;
|
if (empty($articleName) || ($position['amount'] ?? 0) == 0) continue;
|
||||||
|
|
||||||
// Map _group to position_group
|
$amount = floatval($position['amount']);
|
||||||
$groupName = $position['_group'] ?? null;
|
$price = floatval($position['price']);
|
||||||
|
$discount = floatval($position['discount'] ?? 0);
|
||||||
|
$vatrate = floatval($position['vatrate'] ?? 0);
|
||||||
|
$priceAfterDiscount = $amount * $price * (1 - $discount / 100);
|
||||||
|
$priceGross = $priceAfterDiscount * (1 + $vatrate / 100);
|
||||||
|
|
||||||
// Filter to only allowed fields
|
ManualInvoicepositionModel::create([
|
||||||
$filteredPosition = array_intersect_key($position, array_flip($allowedFields));
|
|
||||||
|
|
||||||
ManualInvoicepositionModel::create(array_merge([
|
|
||||||
'manualinvoice_id' => $invoiceId,
|
'manualinvoice_id' => $invoiceId,
|
||||||
'position_group' => $groupName,
|
'position_group' => $position['_group'] ?? null,
|
||||||
'unit' => 'Stk.',
|
'matchcode' => $position['matchcode'] ?? null,
|
||||||
'discount' => 0,
|
'warehousearticle_id' => $position['warehousearticle_id'] ?? $position['product_id'] ?? 0,
|
||||||
|
'warehousearticle_name' => $articleName,
|
||||||
|
'product_info' => $position['product_info'] ?? '',
|
||||||
|
'amount' => $amount,
|
||||||
|
'unit' => $position['unit'] ?? 'Stk.',
|
||||||
|
'price' => $price,
|
||||||
|
'discount' => $discount,
|
||||||
|
'price_total' => $priceAfterDiscount,
|
||||||
|
'price_gross' => $priceGross,
|
||||||
|
'vatrate' => $vatrate,
|
||||||
|
'fibu_cost_account' => $position['fibu_cost_account'] ?? null,
|
||||||
|
'fibu_cost_account_legacy' => $position['fibu_cost_account_legacy'] ?? null,
|
||||||
|
'fibu_taxcode' => $position['fibu_taxcode'] ?? null,
|
||||||
|
'options' => $position['options'] ?? null,
|
||||||
'create_by' => $me->id,
|
'create_by' => $me->id,
|
||||||
'edit_by' => $me->id,
|
'edit_by' => $me->id,
|
||||||
'create' => time(),
|
'create' => time(),
|
||||||
'edit' => time()
|
'edit' => time()
|
||||||
], $filteredPosition));
|
]);
|
||||||
}
|
}
|
||||||
$this->tempPositions = [];
|
$this->tempPositions = [];
|
||||||
}
|
}
|
||||||
@@ -510,17 +540,13 @@ class ManualInvoiceController extends TTCrud
|
|||||||
|
|
||||||
$positions = ManualInvoicepositionModel::search(['manualinvoice_id' => $invoiceId]);
|
$positions = ManualInvoicepositionModel::search(['manualinvoice_id' => $invoiceId]);
|
||||||
$subtotal = array_sum(array_column($positions, 'price_total'));
|
$subtotal = array_sum(array_column($positions, 'price_total'));
|
||||||
|
$totalDiscount = $invoice->total_discount ?? 0;
|
||||||
|
$netTotal = $subtotal * (1 - $totalDiscount / 100);
|
||||||
|
|
||||||
// Apply gesamtrabatt (total discount) if exists
|
|
||||||
$gesamtrabatt = $invoice->gesamtrabatt ?? 0;
|
|
||||||
$discountAmount = $subtotal * ($gesamtrabatt / 100);
|
|
||||||
$netTotal = $subtotal - $discountAmount;
|
|
||||||
|
|
||||||
// Calculate gross total with VAT applied after discount
|
|
||||||
$grossTotal = 0;
|
$grossTotal = 0;
|
||||||
foreach ($positions as $pos) {
|
foreach ($positions as $pos) {
|
||||||
$positionNet = $pos->price_total;
|
$positionNet = $pos->price_total;
|
||||||
$positionAfterDiscount = $positionNet * (1 - $gesamtrabatt / 100);
|
$positionAfterDiscount = $positionNet * (1 - $totalDiscount / 100);
|
||||||
$grossTotal += $positionAfterDiscount * (1 + $pos->vatrate / 100);
|
$grossTotal += $positionAfterDiscount * (1 + $pos->vatrate / 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -582,11 +608,11 @@ class ManualInvoiceController extends TTCrud
|
|||||||
'id' => $pos->id,
|
'id' => $pos->id,
|
||||||
'manualinvoice_id' => $pos->manualinvoice_id,
|
'manualinvoice_id' => $pos->manualinvoice_id,
|
||||||
'_group' => $pos->position_group ?? '',
|
'_group' => $pos->position_group ?? '',
|
||||||
'billing_id' => $pos->billing_id,
|
|
||||||
'contract_id' => $pos->contract_id,
|
|
||||||
'matchcode' => $pos->matchcode,
|
'matchcode' => $pos->matchcode,
|
||||||
'product_id' => $pos->product_id,
|
'warehousearticle_id' => $pos->warehousearticle_id,
|
||||||
'product_name' => $pos->product_name,
|
'warehousearticle_name' => $pos->warehousearticle_name,
|
||||||
|
'product_id' => $pos->warehousearticle_id,
|
||||||
|
'product_name' => $pos->warehousearticle_name,
|
||||||
'product_info' => $pos->product_info,
|
'product_info' => $pos->product_info,
|
||||||
'amount' => $pos->amount,
|
'amount' => $pos->amount,
|
||||||
'unit' => $pos->unit ?? 'Stk.',
|
'unit' => $pos->unit ?? 'Stk.',
|
||||||
@@ -632,19 +658,20 @@ class ManualInvoiceController extends TTCrud
|
|||||||
|
|
||||||
foreach ($existingCredits as $credit) {
|
foreach ($existingCredits as $credit) {
|
||||||
foreach ($credit->getProperty('positions') as $creditPos) {
|
foreach ($credit->getProperty('positions') as $creditPos) {
|
||||||
$key = $creditPos->product_id . '_' . $creditPos->matchcode;
|
$key = $creditPos->warehousearticle_id . '_' . $creditPos->matchcode;
|
||||||
$creditedAmounts[$key] = ($creditedAmounts[$key] ?? 0) + abs($creditPos->amount);
|
$creditedAmounts[$key] = ($creditedAmounts[$key] ?? 0) + abs($creditPos->amount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$availablePositions = [];
|
$availablePositions = [];
|
||||||
foreach ($positions as $pos) {
|
foreach ($positions as $pos) {
|
||||||
$key = $pos->product_id . '_' . $pos->matchcode;
|
$key = $pos->warehousearticle_id . '_' . $pos->matchcode;
|
||||||
$availableAmount = $pos->amount - ($creditedAmounts[$key] ?? 0);
|
$availableAmount = $pos->amount - ($creditedAmounts[$key] ?? 0);
|
||||||
if ($availableAmount > 0) {
|
if ($availableAmount > 0) {
|
||||||
$availablePositions[] = [
|
$availablePositions[] = [
|
||||||
'id' => $pos->id,
|
'id' => $pos->id,
|
||||||
'product_name' => $pos->product_name,
|
'warehousearticle_name' => $pos->warehousearticle_name,
|
||||||
|
'product_name' => $pos->warehousearticle_name,
|
||||||
'product_info' => $pos->product_info,
|
'product_info' => $pos->product_info,
|
||||||
'original_amount' => $pos->amount,
|
'original_amount' => $pos->amount,
|
||||||
'credited_amount' => $creditedAmounts[$key] ?? 0,
|
'credited_amount' => $creditedAmounts[$key] ?? 0,
|
||||||
@@ -652,7 +679,8 @@ class ManualInvoiceController extends TTCrud
|
|||||||
'unit' => $pos->unit ?? 'Stk.',
|
'unit' => $pos->unit ?? 'Stk.',
|
||||||
'price' => $pos->price,
|
'price' => $pos->price,
|
||||||
'vatrate' => $pos->vatrate,
|
'vatrate' => $pos->vatrate,
|
||||||
'product_id' => $pos->product_id,
|
'warehousearticle_id' => $pos->warehousearticle_id,
|
||||||
|
'product_id' => $pos->warehousearticle_id,
|
||||||
'matchcode' => $pos->matchcode,
|
'matchcode' => $pos->matchcode,
|
||||||
'fibu_cost_account' => $pos->fibu_cost_account,
|
'fibu_cost_account' => $pos->fibu_cost_account,
|
||||||
'fibu_taxcode' => $pos->fibu_taxcode
|
'fibu_taxcode' => $pos->fibu_taxcode
|
||||||
@@ -692,10 +720,10 @@ class ManualInvoiceController extends TTCrud
|
|||||||
$invoiceData = [
|
$invoiceData = [
|
||||||
'invoice_number' => ManualInvoiceModel::getNextInvoiceNumber(),
|
'invoice_number' => ManualInvoiceModel::getNextInvoiceNumber(),
|
||||||
'invoice_date' => time(),
|
'invoice_date' => time(),
|
||||||
'leistungszeitraum' => $originalInvoice->leistungszeitraum ?? null,
|
'performance_period' => $originalInvoice->performance_period ?? null,
|
||||||
'einleitender_text' => 'Gutschrift zur Rechnung ' . $originalInvoice->invoice_number,
|
'introductory_text' => 'Gutschrift zur Rechnung ' . $originalInvoice->invoice_number,
|
||||||
'externe_referenz' => $originalInvoice->externe_referenz ?? null,
|
'external_reference' => $originalInvoice->external_reference ?? null,
|
||||||
'gesamtrabatt' => 0,
|
'total_discount' => 0,
|
||||||
'owner_id' => $originalInvoice->owner_id,
|
'owner_id' => $originalInvoice->owner_id,
|
||||||
'billingaddress_id' => $originalInvoice->billingaddress_id,
|
'billingaddress_id' => $originalInvoice->billingaddress_id,
|
||||||
'customer_number' => $originalInvoice->customer_number,
|
'customer_number' => $originalInvoice->customer_number,
|
||||||
@@ -721,7 +749,6 @@ class ManualInvoiceController extends TTCrud
|
|||||||
'email' => $originalInvoice->email,
|
'email' => $originalInvoice->email,
|
||||||
'uid' => $originalInvoice->uid,
|
'uid' => $originalInvoice->uid,
|
||||||
'billing_type' => $originalInvoice->billing_type,
|
'billing_type' => $originalInvoice->billing_type,
|
||||||
'billing_delivery' => $originalInvoice->billing_delivery,
|
|
||||||
'bank_account_bank' => $originalInvoice->bank_account_bank,
|
'bank_account_bank' => $originalInvoice->bank_account_bank,
|
||||||
'bank_account_owner' => $originalInvoice->bank_account_owner,
|
'bank_account_owner' => $originalInvoice->bank_account_owner,
|
||||||
'bank_account_iban' => $originalInvoice->bank_account_iban,
|
'bank_account_iban' => $originalInvoice->bank_account_iban,
|
||||||
@@ -749,8 +776,8 @@ class ManualInvoiceController extends TTCrud
|
|||||||
ManualInvoicepositionModel::create([
|
ManualInvoicepositionModel::create([
|
||||||
'manualinvoice_id' => $creditInvoiceId,
|
'manualinvoice_id' => $creditInvoiceId,
|
||||||
'position_group' => null,
|
'position_group' => null,
|
||||||
'product_id' => $pos['product_id'],
|
'warehousearticle_id' => $pos['warehousearticle_id'] ?? $pos['product_id'] ?? 0,
|
||||||
'product_name' => $pos['product_name'],
|
'warehousearticle_name' => $pos['warehousearticle_name'] ?? $pos['product_name'] ?? '',
|
||||||
'product_info' => $pos['product_info'] ?? '',
|
'product_info' => $pos['product_info'] ?? '',
|
||||||
'amount' => -abs($pos['amount']),
|
'amount' => -abs($pos['amount']),
|
||||||
'unit' => $pos['unit'] ?? 'Stk.',
|
'unit' => $pos['unit'] ?? 'Stk.',
|
||||||
@@ -762,8 +789,6 @@ class ManualInvoiceController extends TTCrud
|
|||||||
'matchcode' => $pos['matchcode'] ?? null,
|
'matchcode' => $pos['matchcode'] ?? null,
|
||||||
'fibu_cost_account' => $pos['fibu_cost_account'] ?? null,
|
'fibu_cost_account' => $pos['fibu_cost_account'] ?? null,
|
||||||
'fibu_taxcode' => $pos['fibu_taxcode'] ?? null,
|
'fibu_taxcode' => $pos['fibu_taxcode'] ?? null,
|
||||||
'contract_id' => 0,
|
|
||||||
'billing_id' => null,
|
|
||||||
'create_by' => $me->id,
|
'create_by' => $me->id,
|
||||||
'edit_by' => $me->id,
|
'edit_by' => $me->id,
|
||||||
'create' => time(),
|
'create' => time(),
|
||||||
@@ -813,12 +838,7 @@ class ManualInvoiceController extends TTCrud
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map revenueAccount to vatgroup_id
|
$vatgroupId = $article->vatgroup_id;
|
||||||
// revenueAccount 0 = Dienstleistungen = vatgroup_id 2
|
|
||||||
// revenueAccount 1 = Handelswaren = vatgroup_id 3
|
|
||||||
$vatgroupId = $article->revenueAccount == 0 ? 2 : 3;
|
|
||||||
|
|
||||||
// Get vatrate for this vatgroup and area
|
|
||||||
$vatrate = VatrateModel::getFirst(['vatgroup_id' => $vatgroupId, 'area' => $vatarea]);
|
$vatrate = VatrateModel::getFirst(['vatgroup_id' => $vatgroupId, 'area' => $vatarea]);
|
||||||
|
|
||||||
if (!$vatrate) {
|
if (!$vatrate) {
|
||||||
@@ -826,7 +846,6 @@ class ManualInvoiceController extends TTCrud
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse prices from cheapestSellPrice JSON
|
|
||||||
$prices = [];
|
$prices = [];
|
||||||
if (!empty($article->cheapestSellPrice)) {
|
if (!empty($article->cheapestSellPrice)) {
|
||||||
$pricesData = json_decode($article->cheapestSellPrice, true);
|
$pricesData = json_decode($article->cheapestSellPrice, true);
|
||||||
@@ -842,7 +861,7 @@ class ManualInvoiceController extends TTCrud
|
|||||||
'title' => $article->title,
|
'title' => $article->title,
|
||||||
'articleNumber' => $article->articleNumber,
|
'articleNumber' => $article->articleNumber,
|
||||||
'description' => $article->description,
|
'description' => $article->description,
|
||||||
'revenueAccount' => $article->revenueAccount,
|
'vatgroup_id' => $article->vatgroup_id,
|
||||||
'unit' => $article->unit
|
'unit' => $article->unit
|
||||||
],
|
],
|
||||||
'prices' => $prices,
|
'prices' => $prices,
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ class ManualInvoiceModel extends TTCrudBaseModel {
|
|||||||
public int $id;
|
public int $id;
|
||||||
public ?string $invoice_number;
|
public ?string $invoice_number;
|
||||||
public int $invoice_date;
|
public int $invoice_date;
|
||||||
public ?string $leistungszeitraum;
|
public ?string $performance_period;
|
||||||
public ?string $einleitender_text;
|
public ?string $introductory_text;
|
||||||
public ?string $externe_referenz;
|
public ?string $external_reference;
|
||||||
public float $gesamtrabatt;
|
public float $total_discount;
|
||||||
public int $owner_id;
|
public int $owner_id;
|
||||||
public int $billingaddress_id;
|
public int $billingaddress_id;
|
||||||
public int $customer_number;
|
public int $customer_number;
|
||||||
@@ -33,7 +33,6 @@ class ManualInvoiceModel extends TTCrudBaseModel {
|
|||||||
public ?string $email;
|
public ?string $email;
|
||||||
public ?string $uid;
|
public ?string $uid;
|
||||||
public string $billing_type;
|
public string $billing_type;
|
||||||
public string $billing_delivery;
|
|
||||||
public ?string $bank_account_bank;
|
public ?string $bank_account_bank;
|
||||||
public ?string $bank_account_owner;
|
public ?string $bank_account_owner;
|
||||||
public ?string $bank_account_iban;
|
public ?string $bank_account_iban;
|
||||||
|
|||||||
@@ -4,11 +4,9 @@ class ManualInvoicepositionModel extends TTCrudBaseModel {
|
|||||||
public int $id;
|
public int $id;
|
||||||
public ?int $manualinvoice_id;
|
public ?int $manualinvoice_id;
|
||||||
public ?string $position_group;
|
public ?string $position_group;
|
||||||
public ?int $billing_id;
|
|
||||||
public int $contract_id;
|
|
||||||
public ?string $matchcode;
|
public ?string $matchcode;
|
||||||
public int $product_id;
|
public int $warehousearticle_id;
|
||||||
public string $product_name;
|
public string $warehousearticle_name;
|
||||||
public ?string $product_info;
|
public ?string $product_info;
|
||||||
public float $amount;
|
public float $amount;
|
||||||
public string $unit;
|
public string $unit;
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ class WarehouseArticleController extends TTCrud {
|
|||||||
['key' => 'description', 'text' => 'Beschreibung', 'required' => true,'modal' => ['type' => 'textarea'], 'table' => ['sortable' => false]],
|
['key' => 'description', 'text' => 'Beschreibung', 'required' => true,'modal' => ['type' => 'textarea'], 'table' => ['sortable' => false]],
|
||||||
['key' => 'category_id', 'text' => 'Kategorie', 'required' => true, 'modal' => ['type' => 'select', 'items' => []], 'table' => ['filter' => 'select']],
|
['key' => 'category_id', 'text' => 'Kategorie', 'required' => true, 'modal' => ['type' => 'select', 'items' => []], 'table' => ['filter' => 'select']],
|
||||||
['key' => 'unit', 'text' => 'Einheit', 'required' => true,'modal' => ['type' => 'select', 'items' => [['value' => 'Stk.', 'text' => 'Stk.'], ['value' => 'Pau.', 'text' => 'Pau.'], ['value' => 'm.', 'text' => 'm.'], ['value' => 'Std.', 'text' => 'Std.'], ['value' => 'km', 'text' => 'km']]], 'table' => ['filter' => 'select', 'filterOptions' => [['value' => 'Stk.', 'text' => 'Stk.'], ['value' => 'Pau.', 'text' => 'Pau.'], ['value' => 'm.', 'text' => 'm.'], ['value' => 'Std.', 'text' => 'Std.'], ['value' => 'km', 'text' => 'km']]]],
|
['key' => 'unit', 'text' => 'Einheit', 'required' => true,'modal' => ['type' => 'select', 'items' => [['value' => 'Stk.', 'text' => 'Stk.'], ['value' => 'Pau.', 'text' => 'Pau.'], ['value' => 'm.', 'text' => 'm.'], ['value' => 'Std.', 'text' => 'Std.'], ['value' => 'km', 'text' => 'km']]], 'table' => ['filter' => 'select', 'filterOptions' => [['value' => 'Stk.', 'text' => 'Stk.'], ['value' => 'Pau.', 'text' => 'Pau.'], ['value' => 'm.', 'text' => 'm.'], ['value' => 'Std.', 'text' => 'Std.'], ['value' => 'km', 'text' => 'km']]]],
|
||||||
['key' => 'revenueAccount', 'text' => 'Erlöskonto', 'required' => true,'modal' => ['type' => 'select', 'items' => [['value' => 0, 'text' => 'Dienstleistungen'], ['value' => 1, 'text' => 'Handelswaren']]], 'table' => false],
|
['key' => 'vatgroup_id', 'text' => 'Erlöskonto', 'required' => true,'modal' => ['type' => 'select', 'items' => [['value' => 2, 'text' => 'Dienstleistungen'], ['value' => 3, 'text' => 'Handelswaren']]], 'table' => false],
|
||||||
['key' => 'cheapestPurchasePrice', 'text' => 'Einkauf', 'modal' => false, 'table' => ['class' => 'text-center', 'suffix' => ' €']],
|
['key' => 'cheapestPurchasePrice', 'text' => 'Einkauf', 'modal' => false, 'table' => ['class' => 'text-center', 'suffix' => ' €']],
|
||||||
['key' => 'cheapestSellPrice', 'text' => 'Verkauf', 'modal' => false, 'table' => ['class' => 'text-center', 'suffix' => ' €']],
|
['key' => 'cheapestSellPrice', 'text' => 'Verkauf', 'modal' => false, 'table' => ['class' => 'text-center', 'suffix' => ' €']],
|
||||||
['key' => 'warningAmount', 'text' => 'Warnmenge', 'required' => true,'modal' => ['type' => 'number'], 'table' => false],
|
['key' => 'warningAmount', 'text' => 'Warnmenge', 'required' => true,'modal' => ['type' => 'number'], 'table' => false],
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ class WarehouseArticleModel extends TTCrudBaseModel {
|
|||||||
public ?int $isEndOfLife;
|
public ?int $isEndOfLife;
|
||||||
public string $unit;
|
public string $unit;
|
||||||
public ?int $isSerialDocumentation;
|
public ?int $isSerialDocumentation;
|
||||||
public int $revenueAccount;
|
public int $vatgroup_id;
|
||||||
public int $create;
|
public int $create;
|
||||||
public int $createBy;
|
public int $createBy;
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use Phinx\Migration\AbstractMigration;
|
||||||
|
|
||||||
|
final class WarehousearticleRenameRevenueaccountToVatgroupid extends AbstractMigration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
if($this->getEnvironment() == "thetool") {
|
||||||
|
$this->execute("ALTER TABLE warehousearticle CHANGE revenueAccount vatgroup_id INT(11) NOT NULL DEFAULT 2");
|
||||||
|
|
||||||
|
$this->execute("UPDATE warehousearticle SET vatgroup_id = CASE
|
||||||
|
WHEN vatgroup_id = 0 THEN 2
|
||||||
|
WHEN vatgroup_id = 1 THEN 3
|
||||||
|
ELSE vatgroup_id
|
||||||
|
END");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
if($this->getEnvironment() == "thetool") {
|
||||||
|
$this->execute("UPDATE warehousearticle SET vatgroup_id = CASE
|
||||||
|
WHEN vatgroup_id = 2 THEN 0
|
||||||
|
WHEN vatgroup_id = 3 THEN 1
|
||||||
|
ELSE vatgroup_id
|
||||||
|
END");
|
||||||
|
|
||||||
|
$this->execute("ALTER TABLE warehousearticle CHANGE vatgroup_id revenueAccount INT(11) NOT NULL DEFAULT 0");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use Phinx\Migration\AbstractMigration;
|
||||||
|
|
||||||
|
final class ManualinvoiceColumnCleanup extends AbstractMigration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
if($this->getEnvironment() == "thetool") {
|
||||||
|
$position = $this->table("ManualInvoiceposition");
|
||||||
|
if ($position->hasColumn("billing_id")) {
|
||||||
|
$position->removeColumn("billing_id")->save();
|
||||||
|
}
|
||||||
|
if ($position->hasColumn("contract_id")) {
|
||||||
|
$position->removeColumn("contract_id")->save();
|
||||||
|
}
|
||||||
|
// ManualInvoiceposition: Rename columns
|
||||||
|
$this->execute("ALTER TABLE manualinvoiceposition CHANGE product_id warehousearticle_id INT(11) NOT NULL");
|
||||||
|
$this->execute("ALTER TABLE manualinvoiceposition CHANGE product_name warehousearticle_name VARCHAR(255) NOT NULL");
|
||||||
|
|
||||||
|
$invoice = $this->table("ManualInvoice");
|
||||||
|
if ($invoice->hasColumn("billing_delivery")) {
|
||||||
|
$invoice->removeColumn("billing_delivery")->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->execute("ALTER TABLE manualinvoice CHANGE leistungszeitraum performance_period VARCHAR(255) NULL");
|
||||||
|
$this->execute("ALTER TABLE manualinvoice CHANGE einleitender_text introductory_text TEXT NULL");
|
||||||
|
$this->execute("ALTER TABLE manualinvoice CHANGE externe_referenz external_reference VARCHAR(255) NULL");
|
||||||
|
$this->execute("ALTER TABLE manualinvoice CHANGE gesamtrabatt total_discount DECIMAL(6,2) NOT NULL DEFAULT 0.00");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
if($this->getEnvironment() == "thetool") {
|
||||||
|
$this->execute("ALTER TABLE manualinvoice CHANGE performance_period leistungszeitraum VARCHAR(255) NULL");
|
||||||
|
$this->execute("ALTER TABLE manualinvoice CHANGE introductory_text einleitender_text TEXT NULL");
|
||||||
|
$this->execute("ALTER TABLE manualinvoice CHANGE external_reference externe_referenz VARCHAR(255) NULL");
|
||||||
|
$this->execute("ALTER TABLE manualinvoice CHANGE total_discount gesamtrabatt DECIMAL(6,2) NOT NULL DEFAULT 0.00");
|
||||||
|
|
||||||
|
$invoice = $this->table("ManualInvoice");
|
||||||
|
$invoice->addColumn("billing_delivery", "enum", [
|
||||||
|
"null" => false,
|
||||||
|
"values" => ["email", "paper"],
|
||||||
|
"after" => "billing_type"
|
||||||
|
])->save();
|
||||||
|
|
||||||
|
$this->execute("ALTER TABLE manualinvoiceposition CHANGE warehousearticle_id product_id INT(11) NOT NULL");
|
||||||
|
$this->execute("ALTER TABLE manualinvoiceposition CHANGE warehousearticle_name product_name VARCHAR(255) NOT NULL");
|
||||||
|
|
||||||
|
$position = $this->table("ManualInvoiceposition");
|
||||||
|
$position->addColumn("billing_id", "integer", ["null" => true, "after" => "position_group"]);
|
||||||
|
$position->addColumn("contract_id", "integer", ["null" => false, "after" => "billing_id"]);
|
||||||
|
$position->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -52,27 +52,21 @@ Vue.component('manual-invoice', {
|
|||||||
},
|
},
|
||||||
async handleSave(invoiceData) {
|
async handleSave(invoiceData) {
|
||||||
try {
|
try {
|
||||||
const positions = invoiceData.positions.map(p => {
|
const positions = invoiceData.positions.map(p => ({
|
||||||
const amount = parseFloat(p.amount) || 0;
|
...p,
|
||||||
const price = parseFloat(p.price) || 0;
|
amount: parseFloat(p.amount) || 0,
|
||||||
const discount = parseFloat(p.discount) || 0;
|
price: parseFloat(p.price) || 0,
|
||||||
const vatrate = parseFloat(p.vatrate) || 0;
|
discount: parseFloat(p.discount) || 0,
|
||||||
const priceAfterDiscount = amount * price * (1 - discount / 100);
|
vatrate: parseFloat(p.vatrate) || 0,
|
||||||
return {
|
|
||||||
...p, amount, price, discount, vatrate,
|
|
||||||
unit: p.unit || 'Stk.',
|
unit: p.unit || 'Stk.',
|
||||||
price_total: priceAfterDiscount,
|
warehousearticle_id: p.warehousearticle_id || p.product_id || 0,
|
||||||
price_gross: priceAfterDiscount * (1 + vatrate / 100),
|
warehousearticle_name: p.warehousearticle_name || p.product_name || '',
|
||||||
product_id: p.product_id || 0,
|
|
||||||
contract_id: p.contract_id || 0,
|
|
||||||
billing_id: p.billing_id || null,
|
|
||||||
matchcode: p.matchcode || null,
|
matchcode: p.matchcode || null,
|
||||||
fibu_cost_account: p.fibu_cost_account || null,
|
fibu_cost_account: p.fibu_cost_account || null,
|
||||||
fibu_cost_account_legacy: p.fibu_cost_account_legacy || null,
|
fibu_cost_account_legacy: p.fibu_cost_account_legacy || null,
|
||||||
fibu_taxcode: p.fibu_taxcode || null,
|
fibu_taxcode: p.fibu_taxcode || null,
|
||||||
options: p.options || null
|
options: p.options || null
|
||||||
};
|
}));
|
||||||
});
|
|
||||||
|
|
||||||
const payload = {
|
const payload = {
|
||||||
...invoiceData,
|
...invoiceData,
|
||||||
@@ -82,11 +76,13 @@ Vue.component('manual-invoice', {
|
|||||||
customer_number: invoiceData.customer_number || 0,
|
customer_number: invoiceData.customer_number || 0,
|
||||||
country: invoiceData.country || 'Österreich',
|
country: invoiceData.country || 'Österreich',
|
||||||
billing_type: invoiceData.billing_type || 'invoice',
|
billing_type: invoiceData.billing_type || 'invoice',
|
||||||
billing_delivery: 'email',
|
|
||||||
fibu_payment_due: 14,
|
fibu_payment_due: 14,
|
||||||
fibu_account_number: invoiceData.fibu_account_number || 0,
|
fibu_account_number: invoiceData.fibu_account_number || 0,
|
||||||
vatgroup_id: 1,
|
vatgroup_id: invoiceData.vatgroup_id || 1,
|
||||||
gesamtrabatt: parseFloat(invoiceData.gesamtrabatt) || 0
|
performance_period: invoiceData.performance_period || invoiceData.leistungszeitraum || '',
|
||||||
|
introductory_text: invoiceData.introductory_text || invoiceData.einleitender_text || '',
|
||||||
|
external_reference: invoiceData.external_reference || invoiceData.externe_referenz || '',
|
||||||
|
total_discount: parseFloat(invoiceData.total_discount || invoiceData.gesamtrabatt) || 0
|
||||||
};
|
};
|
||||||
|
|
||||||
const url = invoiceData.id ? window.TT_CONFIG.UPDATE_URL : window.TT_CONFIG.CREATE_URL;
|
const url = invoiceData.id ? window.TT_CONFIG.UPDATE_URL : window.TT_CONFIG.CREATE_URL;
|
||||||
@@ -174,15 +170,15 @@ Vue.component('manual-invoice-modal', {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<tt-input label="Leistungszeitraum" v-model="invoiceData.leistungszeitraum" sm row placeholder="z.B. 01.01.2025 - 31.01.2025"/>
|
<tt-input label="Leistungszeitraum" v-model="invoiceData.performance_period" sm row placeholder="z.B. 01.01.2025 - 31.01.2025"/>
|
||||||
<tt-input label="Externe Referenz" v-model="invoiceData.externe_referenz" sm row placeholder="z.B. Auftragsnummer, Bestellnummer"/>
|
<tt-input label="Externe Referenz" v-model="invoiceData.external_reference" sm row placeholder="z.B. Auftragsnummer, Bestellnummer"/>
|
||||||
<tt-textarea label="Einleitender Text" v-model="invoiceData.einleitender_text" rows="3" sm row/>
|
<tt-textarea label="Einleitender Text" v-model="invoiceData.introductory_text" rows="3" sm row/>
|
||||||
</tt-card>
|
</tt-card>
|
||||||
<tt-card><template v-slot:header><h5><i class="fas fa-list-ol mr-2"></i>Positionen</h5></template>
|
<tt-card><template v-slot:header><h5><i class="fas fa-list-ol mr-2"></i>Positionen</h5></template>
|
||||||
<tt-positions-manager group-mode ref="positionsManager" v-model="invoiceData.positions" :config="positionsConfig" @updateField-article_id="onArticleSelected" @updateField-price_type="onPriceTypeChanged" />
|
<tt-positions-manager group-mode ref="positionsManager" v-model="invoiceData.positions" :config="positionsConfig" @updateField-article_id="onArticleSelected" @updateField-price_type="onPriceTypeChanged" />
|
||||||
</tt-card>
|
</tt-card>
|
||||||
<tt-card><template v-slot:header><h5><i class="fas fa-paragraph mr-2"></i>Rabatt</h5></template>
|
<tt-card><template v-slot:header><h5><i class="fas fa-paragraph mr-2"></i>Rabatt</h5></template>
|
||||||
<tt-input label="Gesamtrabatt (%)" v-model.number="invoiceData.gesamtrabatt" sm row type="number" placeholder="0"/>
|
<tt-input label="Gesamtrabatt (%)" v-model.number="invoiceData.total_discount" sm row type="number" placeholder="0"/>
|
||||||
</tt-card>
|
</tt-card>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -215,8 +211,8 @@ Vue.component('manual-invoice-modal', {
|
|||||||
id: null, invoice_number: null, invoice_date: moment().format('YYYY-MM-DD'),
|
id: null, invoice_number: null, invoice_date: moment().format('YYYY-MM-DD'),
|
||||||
billingaddress_id: null, owner_id: null, customer_number: 0, fibu_account_number: 0,
|
billingaddress_id: null, owner_id: null, customer_number: 0, fibu_account_number: 0,
|
||||||
company: '', firstname: '', lastname: '', street: '', zip: '', city: '', country: 'Österreich',
|
company: '', firstname: '', lastname: '', street: '', zip: '', city: '', country: 'Österreich',
|
||||||
uid: '', email: '', billing_type: 'invoice', tax_text: '',
|
uid: '', email: '', billing_type: 'invoice', tax_text: '', vatgroup_id: 1,
|
||||||
leistungszeitraum: '', einleitender_text: '', externe_referenz: '', gesamtrabatt: 0,
|
performance_period: '', introductory_text: '', external_reference: '', total_discount: 0,
|
||||||
positions: [], total: 0, total_gross: 0
|
positions: [], total: 0, total_gross: 0
|
||||||
},
|
},
|
||||||
positionsConfig: {
|
positionsConfig: {
|
||||||
@@ -228,7 +224,7 @@ Vue.component('manual-invoice-modal', {
|
|||||||
customFieldReference: 'WarehouseArticle',
|
customFieldReference: 'WarehouseArticle',
|
||||||
emitDisplayValue: true
|
emitDisplayValue: true
|
||||||
},
|
},
|
||||||
product_name: { type: 'input', label: 'Bezeichnung' },
|
warehousearticle_name: { type: 'input', label: 'Bezeichnung' },
|
||||||
product_info: { type: 'input', label: 'Zusatzinfo' },
|
product_info: { type: 'input', label: 'Zusatzinfo' },
|
||||||
amount: { type: 'input', label: 'Menge', inputType: 'number' },
|
amount: { type: 'input', label: 'Menge', inputType: 'number' },
|
||||||
price_type: {
|
price_type: {
|
||||||
@@ -240,7 +236,7 @@ Vue.component('manual-invoice-modal', {
|
|||||||
discount: { type: 'input', label: 'Rabatt (%)', inputType: 'number' },
|
discount: { type: 'input', label: 'Rabatt (%)', inputType: 'number' },
|
||||||
},
|
},
|
||||||
validateForm: (d) => {
|
validateForm: (d) => {
|
||||||
if (!d.product_name) { window.notify('error', 'Bezeichnung ist erforderlich.'); return false; }
|
if (!d.warehousearticle_name) { window.notify('error', 'Bezeichnung ist erforderlich.'); return false; }
|
||||||
if (d.amount == null || d.amount === '') { window.notify('error', 'Menge ist erforderlich.'); return false; }
|
if (d.amount == null || d.amount === '') { window.notify('error', 'Menge ist erforderlich.'); return false; }
|
||||||
if (d.price == null) { window.notify('error', 'Preis ist erforderlich.'); return false; }
|
if (d.price == null) { window.notify('error', 'Preis ist erforderlich.'); return false; }
|
||||||
return true;
|
return true;
|
||||||
@@ -261,18 +257,15 @@ Vue.component('manual-invoice-modal', {
|
|||||||
subtotal += lineTotal;
|
subtotal += lineTotal;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Apply gesamtrabatt
|
const totalDiscount = parseFloat(this.invoiceData.total_discount) || 0;
|
||||||
const gesamtrabatt = parseFloat(this.invoiceData.gesamtrabatt) || 0;
|
const net = subtotal * (1 - totalDiscount / 100);
|
||||||
const net = subtotal * (1 - gesamtrabatt / 100);
|
|
||||||
|
|
||||||
// Calculate VAT
|
|
||||||
let vat = {}, gross = 0;
|
let vat = {}, gross = 0;
|
||||||
(this.invoiceData.positions || []).forEach(p => {
|
(this.invoiceData.positions || []).forEach(p => {
|
||||||
const amount = parseFloat(p.amount) || 0;
|
const amount = parseFloat(p.amount) || 0;
|
||||||
const price = parseFloat(p.price) || 0;
|
const price = parseFloat(p.price) || 0;
|
||||||
const discount = parseFloat(p.discount) || 0;
|
const discount = parseFloat(p.discount) || 0;
|
||||||
const r = parseInt(p.vatrate) || 0;
|
const r = parseInt(p.vatrate) || 0;
|
||||||
const lineNet = amount * price * (1 - discount / 100) * (1 - gesamtrabatt / 100);
|
const lineNet = amount * price * (1 - discount / 100) * (1 - totalDiscount / 100);
|
||||||
const lineVat = lineNet * (r / 100);
|
const lineVat = lineNet * (r / 100);
|
||||||
vat[r] = (vat[r] || 0) + lineVat;
|
vat[r] = (vat[r] || 0) + lineVat;
|
||||||
gross += lineNet + lineVat;
|
gross += lineNet + lineVat;
|
||||||
@@ -400,7 +393,7 @@ Vue.component('manual-invoice-modal', {
|
|||||||
if (data.success && this.$refs.positionsManager) {
|
if (data.success && this.$refs.positionsManager) {
|
||||||
const pm = this.$refs.positionsManager;
|
const pm = this.$refs.positionsManager;
|
||||||
if (data.article) {
|
if (data.article) {
|
||||||
pm.$set(pm.formData, 'product_name', data.article.articleNumber + ' | ' + data.article.title);
|
pm.$set(pm.formData, 'warehousearticle_name', data.article.articleNumber + ' | ' + data.article.title);
|
||||||
pm.$set(pm.formData, 'product_info', data.article.description || '');
|
pm.$set(pm.formData, 'product_info', data.article.description || '');
|
||||||
pm.$set(pm.formData, 'unit', data.article.unit || 'Stk.');
|
pm.$set(pm.formData, 'unit', data.article.unit || 'Stk.');
|
||||||
}
|
}
|
||||||
@@ -455,7 +448,7 @@ Vue.component('manual-invoice-modal', {
|
|||||||
this.pdfLoading = true;
|
this.pdfLoading = true;
|
||||||
try {
|
try {
|
||||||
const positions = this.invoiceData.positions
|
const positions = this.invoiceData.positions
|
||||||
.filter(p => p.product_name && (parseFloat(p.amount) || 0) !== 0) // Filter out empty positions (allow negative for Gutschrift)
|
.filter(p => p.warehousearticle_name && (parseFloat(p.amount) || 0) !== 0)
|
||||||
.map(p => {
|
.map(p => {
|
||||||
const amount = parseFloat(p.amount) || 0;
|
const amount = parseFloat(p.amount) || 0;
|
||||||
const price = parseFloat(p.price) || 0;
|
const price = parseFloat(p.price) || 0;
|
||||||
@@ -524,17 +517,17 @@ Vue.component('manual-invoice-modal', {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Pre-fill external reference with shipping note reference
|
// Pre-fill external reference with shipping note reference
|
||||||
this.invoiceData.externe_referenz = `Lieferschein #${shippingNoteData.shippingNoteId}`;
|
this.invoiceData.external_reference = `Lieferschein #${shippingNoteData.shippingNoteId}`;
|
||||||
|
|
||||||
// Add introductory text if shipping note has notes
|
// Add introductory text if shipping note has notes
|
||||||
if (shippingNoteData.note) {
|
if (shippingNoteData.note) {
|
||||||
this.invoiceData.einleitender_text = shippingNoteData.note;
|
this.invoiceData.introductory_text = shippingNoteData.note;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add all positions (batch operation to avoid triggering watcher for each item)
|
// Add all positions (batch operation to avoid triggering watcher for each item)
|
||||||
if (shippingNoteData.positions && Array.isArray(shippingNoteData.positions)) {
|
if (shippingNoteData.positions && Array.isArray(shippingNoteData.positions)) {
|
||||||
const newPositions = shippingNoteData.positions.map(position => ({
|
const newPositions = shippingNoteData.positions.map(position => ({
|
||||||
product_name: position.product_name || '',
|
warehousearticle_name: position.warehousearticle_name || position.product_name || '',
|
||||||
product_info: position.product_info || '',
|
product_info: position.product_info || '',
|
||||||
amount: parseFloat(position.amount) || 0,
|
amount: parseFloat(position.amount) || 0,
|
||||||
unit: position.unit || 'Stk.',
|
unit: position.unit || 'Stk.',
|
||||||
@@ -583,7 +576,7 @@ Vue.component('gutschrift-modal', {
|
|||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="(pos, index) in invoice.positions" :key="index">
|
<tr v-for="(pos, index) in invoice.positions" :key="index">
|
||||||
<td class="text-center"><input type="checkbox" v-model="selectedPositions[index]"></td>
|
<td class="text-center"><input type="checkbox" v-model="selectedPositions[index]"></td>
|
||||||
<td><strong>{{ pos.product_name }}</strong><div v-if="pos.product_info" class="text-muted small">{{ pos.product_info }}</div></td>
|
<td><strong>{{ pos.warehousearticle_name }}</strong><div v-if="pos.product_info" class="text-muted small">{{ pos.product_info }}</div></td>
|
||||||
<td class="text-right">{{ pos.original_amount }}</td><td class="text-right">{{ pos.credited_amount }}</td>
|
<td class="text-right">{{ pos.original_amount }}</td><td class="text-right">{{ pos.credited_amount }}</td>
|
||||||
<td class="text-right">{{ pos.available_amount }}</td>
|
<td class="text-right">{{ pos.available_amount }}</td>
|
||||||
<td><input type="number" class="form-control form-control-sm" v-model.number="creditAmounts[index]" :max="pos.available_amount" :disabled="!selectedPositions[index]" step="0.001" min="0.001"></td>
|
<td><input type="number" class="form-control form-control-sm" v-model.number="creditAmounts[index]" :max="pos.available_amount" :disabled="!selectedPositions[index]" step="0.001" min="0.001"></td>
|
||||||
@@ -624,7 +617,7 @@ Vue.component('gutschrift-modal', {
|
|||||||
.map((p, i) => ({ p, i })).filter(({ i }) => this.selectedPositions[i])
|
.map((p, i) => ({ p, i })).filter(({ i }) => this.selectedPositions[i])
|
||||||
.map(({ p, i }) => {
|
.map(({ p, i }) => {
|
||||||
const amt = this.creditAmounts[i];
|
const amt = this.creditAmounts[i];
|
||||||
if (amt > p.available_amount) throw new Error(`Menge zu hoch: ${p.product_name}`);
|
if (amt > p.available_amount) throw new Error(`Menge zu hoch: ${p.warehousearticle_name}`);
|
||||||
return amt > 0 ? { ...p, amount: amt } : null;
|
return amt > 0 ? { ...p, amount: amt } : null;
|
||||||
}).filter(Boolean);
|
}).filter(Boolean);
|
||||||
|
|
||||||
|
|||||||
@@ -437,8 +437,8 @@ Vue.component('warehouse-article-modal', {
|
|||||||
<div class="col-md-2" :class="{ 'wa-field-disabled': !isEditMode && !formData.category_id }">
|
<div class="col-md-2" :class="{ 'wa-field-disabled': !isEditMode && !formData.category_id }">
|
||||||
<tt-select
|
<tt-select
|
||||||
label="Erlöskonto"
|
label="Erlöskonto"
|
||||||
v-model="formData.revenueAccount"
|
v-model="formData.vatgroup_id"
|
||||||
:options="revenueAccountOptions"
|
:options="vatgroupOptions"
|
||||||
required
|
required
|
||||||
sm/>
|
sm/>
|
||||||
</div>
|
</div>
|
||||||
@@ -538,7 +538,7 @@ Vue.component('warehouse-article-modal', {
|
|||||||
category_id: null,
|
category_id: null,
|
||||||
articleNumber: '',
|
articleNumber: '',
|
||||||
unit: 'Stk.',
|
unit: 'Stk.',
|
||||||
revenueAccount: 0,
|
vatgroup_id: 2,
|
||||||
warningAmount: 0,
|
warningAmount: 0,
|
||||||
criticalAmount: 0,
|
criticalAmount: 0,
|
||||||
isSerialDocumentation: false,
|
isSerialDocumentation: false,
|
||||||
@@ -566,10 +566,10 @@ Vue.component('warehouse-article-modal', {
|
|||||||
{ value: 'km', text: 'km' }
|
{ value: 'km', text: 'km' }
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
revenueAccountOptions() {
|
vatgroupOptions() {
|
||||||
return [
|
return [
|
||||||
{ value: 0, text: 'Dienstleistungen' },
|
{ value: 2, text: 'Dienstleistungen' },
|
||||||
{ value: 1, text: 'Handelswaren' }
|
{ value: 3, text: 'Handelswaren' }
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
isValid() {
|
isValid() {
|
||||||
@@ -607,7 +607,7 @@ Vue.component('warehouse-article-modal', {
|
|||||||
category_id: data.category_id,
|
category_id: data.category_id,
|
||||||
articleNumber: data.articleNumber || '',
|
articleNumber: data.articleNumber || '',
|
||||||
unit: data.unit || 'Stk.',
|
unit: data.unit || 'Stk.',
|
||||||
revenueAccount: data.revenueAccount || 0,
|
vatgroup_id: data.vatgroup_id || 2,
|
||||||
warningAmount: data.warningAmount || 0,
|
warningAmount: data.warningAmount || 0,
|
||||||
criticalAmount: data.criticalAmount || 0,
|
criticalAmount: data.criticalAmount || 0,
|
||||||
isSerialDocumentation: !!data.isSerialDocumentation,
|
isSerialDocumentation: !!data.isSerialDocumentation,
|
||||||
@@ -637,7 +637,7 @@ Vue.component('warehouse-article-modal', {
|
|||||||
category_id: null,
|
category_id: null,
|
||||||
articleNumber: '',
|
articleNumber: '',
|
||||||
unit: 'Stk.',
|
unit: 'Stk.',
|
||||||
revenueAccount: 0,
|
vatgroup_id: 2,
|
||||||
warningAmount: 0,
|
warningAmount: 0,
|
||||||
criticalAmount: 0,
|
criticalAmount: 0,
|
||||||
isSerialDocumentation: false,
|
isSerialDocumentation: false,
|
||||||
|
|||||||
Reference in New Issue
Block a user