From e8002d184f1dd65c334316bf3eeb73af303d6a64 Mon Sep 17 00:00:00 2001 From: Luca Haid Date: Mon, 26 Jan 2026 15:44:35 +0100 Subject: [PATCH] fxied vat calculation --- Layout/default/ManualInvoice/PDF_MAIN.php | 16 +-- .../ManualInvoice/ManualInvoiceController.php | 123 ++++++++++-------- .../ManualInvoice/ManualInvoiceModel.php | 9 +- .../ManualInvoicepositionModel.php | 6 +- .../WarehouseArticleController.php | 2 +- .../WarehouseArticleModel.php | 2 +- ...le_rename_revenueaccount_to_vatgroupid.php | 33 +++++ ...126130000_manualinvoice_column_cleanup.php | 58 +++++++++ .../js/pages/ManualInvoice/ManualInvoice.js | 83 ++++++------ .../WarehouseArticle/WarehouseArticle.js | 16 +-- 10 files changed, 224 insertions(+), 124 deletions(-) create mode 100644 db/migrations/20260126120000_warehousearticle_rename_revenueaccount_to_vatgroupid.php create mode 100644 db/migrations/20260126130000_manualinvoice_column_cleanup.php diff --git a/Layout/default/ManualInvoice/PDF_MAIN.php b/Layout/default/ManualInvoice/PDF_MAIN.php index 49ef1d468..3e6523035 100644 --- a/Layout/default/ManualInvoice/PDF_MAIN.php +++ b/Layout/default/ManualInvoice/PDF_MAIN.php @@ -17,7 +17,7 @@ foreach($invoice->positions as $p) { } } -$gesamtrabatt = $invoice->gesamtrabatt ?? 0; +$total_discount = $invoice->total_discount ?? 0; $subtotal = 0; foreach($invoice->positions as $p) { $subtotal += $p->price_total ?? 0; @@ -127,8 +127,8 @@ $this->setReturnValue(['filename' => $invoice->invoice_number . ".pdf"]);

Ihre Xinon vom invoice_date)?>

- einleitender_text ?? ''): ?> -

einleitender_text))?>

+ introductory_text ?? ''): ?> +

introductory_text))?>

@@ -166,7 +166,7 @@ $this->setReturnValue(['filename' => $invoice->invoice_number . ".pdf"]); "> - - + + - + diff --git a/application/ManualInvoice/ManualInvoiceController.php b/application/ManualInvoice/ManualInvoiceController.php index e55a94a8c..b29cca986 100644 --- a/application/ManualInvoice/ManualInvoiceController.php +++ b/application/ManualInvoice/ManualInvoiceController.php @@ -105,8 +105,8 @@ class ManualInvoiceController extends TTCrud "{{ billingAccount }}" => $invoice->fibu_account_number ?? '', "{{ invoiceNumber }}" => $invoice->invoice_number ?? "VORSCHAU", "{{ invoiceDate }}" => date("d.m.Y", $invoice->invoice_date ?? time()), - "{{ leistungszeitraumHtml }}" => ($invoice->leistungszeitraum ?? '') ? "" : "", - "{{ externeReferenzHtml }}" => ($invoice->externe_referenz ?? '') ? "" : "", + "{{ leistungszeitraumHtml }}" => ($invoice->performance_period ?? '') ? "" : "", + "{{ externeReferenzHtml }}" => ($invoice->external_reference ?? '') ? "" : "", "{{ vatHtml }}" => ($invoice->uid ?? '') ? "" : "", "{{ qrCodeHtml }}" => ($invoice->total_gross ?? 0) >= 0 ? '' @@ -393,11 +393,32 @@ class ManualInvoiceController extends TTCrud $data['status'] = 'erstellt'; $data['fibu_payment_skonto'] = $data['fibu_payment_skonto'] ?? 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_gross'] = $data['total_gross'] ?? 0; $data['lock'] = 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['edit_by'] = $me->id; $data['create'] = time(); @@ -476,31 +497,40 @@ class ManualInvoiceController extends TTCrud $me = new User(); $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) { - // Skip empty positions - if (empty($position['product_name']) || ($position['amount'] ?? 0) == 0) continue; + $articleName = $position['warehousearticle_name'] ?? $position['product_name'] ?? ''; + if (empty($articleName) || ($position['amount'] ?? 0) == 0) continue; - // Map _group to position_group - $groupName = $position['_group'] ?? null; + $amount = floatval($position['amount']); + $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 - $filteredPosition = array_intersect_key($position, array_flip($allowedFields)); - - ManualInvoicepositionModel::create(array_merge([ + ManualInvoicepositionModel::create([ 'manualinvoice_id' => $invoiceId, - 'position_group' => $groupName, - 'unit' => 'Stk.', - 'discount' => 0, + 'position_group' => $position['_group'] ?? null, + 'matchcode' => $position['matchcode'] ?? null, + '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, 'edit_by' => $me->id, 'create' => time(), 'edit' => time() - ], $filteredPosition)); + ]); } $this->tempPositions = []; } @@ -510,17 +540,13 @@ class ManualInvoiceController extends TTCrud $positions = ManualInvoicepositionModel::search(['manualinvoice_id' => $invoiceId]); $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; foreach ($positions as $pos) { $positionNet = $pos->price_total; - $positionAfterDiscount = $positionNet * (1 - $gesamtrabatt / 100); + $positionAfterDiscount = $positionNet * (1 - $totalDiscount / 100); $grossTotal += $positionAfterDiscount * (1 + $pos->vatrate / 100); } @@ -582,11 +608,11 @@ class ManualInvoiceController extends TTCrud 'id' => $pos->id, 'manualinvoice_id' => $pos->manualinvoice_id, '_group' => $pos->position_group ?? '', - 'billing_id' => $pos->billing_id, - 'contract_id' => $pos->contract_id, 'matchcode' => $pos->matchcode, - 'product_id' => $pos->product_id, - 'product_name' => $pos->product_name, + 'warehousearticle_id' => $pos->warehousearticle_id, + 'warehousearticle_name' => $pos->warehousearticle_name, + 'product_id' => $pos->warehousearticle_id, + 'product_name' => $pos->warehousearticle_name, 'product_info' => $pos->product_info, 'amount' => $pos->amount, 'unit' => $pos->unit ?? 'Stk.', @@ -632,19 +658,20 @@ class ManualInvoiceController extends TTCrud foreach ($existingCredits as $credit) { 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); } } $availablePositions = []; foreach ($positions as $pos) { - $key = $pos->product_id . '_' . $pos->matchcode; + $key = $pos->warehousearticle_id . '_' . $pos->matchcode; $availableAmount = $pos->amount - ($creditedAmounts[$key] ?? 0); if ($availableAmount > 0) { $availablePositions[] = [ 'id' => $pos->id, - 'product_name' => $pos->product_name, + 'warehousearticle_name' => $pos->warehousearticle_name, + 'product_name' => $pos->warehousearticle_name, 'product_info' => $pos->product_info, 'original_amount' => $pos->amount, 'credited_amount' => $creditedAmounts[$key] ?? 0, @@ -652,7 +679,8 @@ class ManualInvoiceController extends TTCrud 'unit' => $pos->unit ?? 'Stk.', 'price' => $pos->price, 'vatrate' => $pos->vatrate, - 'product_id' => $pos->product_id, + 'warehousearticle_id' => $pos->warehousearticle_id, + 'product_id' => $pos->warehousearticle_id, 'matchcode' => $pos->matchcode, 'fibu_cost_account' => $pos->fibu_cost_account, 'fibu_taxcode' => $pos->fibu_taxcode @@ -692,10 +720,10 @@ class ManualInvoiceController extends TTCrud $invoiceData = [ 'invoice_number' => ManualInvoiceModel::getNextInvoiceNumber(), 'invoice_date' => time(), - 'leistungszeitraum' => $originalInvoice->leistungszeitraum ?? null, - 'einleitender_text' => 'Gutschrift zur Rechnung ' . $originalInvoice->invoice_number, - 'externe_referenz' => $originalInvoice->externe_referenz ?? null, - 'gesamtrabatt' => 0, + 'performance_period' => $originalInvoice->performance_period ?? null, + 'introductory_text' => 'Gutschrift zur Rechnung ' . $originalInvoice->invoice_number, + 'external_reference' => $originalInvoice->external_reference ?? null, + 'total_discount' => 0, 'owner_id' => $originalInvoice->owner_id, 'billingaddress_id' => $originalInvoice->billingaddress_id, 'customer_number' => $originalInvoice->customer_number, @@ -721,7 +749,6 @@ class ManualInvoiceController extends TTCrud 'email' => $originalInvoice->email, 'uid' => $originalInvoice->uid, 'billing_type' => $originalInvoice->billing_type, - 'billing_delivery' => $originalInvoice->billing_delivery, 'bank_account_bank' => $originalInvoice->bank_account_bank, 'bank_account_owner' => $originalInvoice->bank_account_owner, 'bank_account_iban' => $originalInvoice->bank_account_iban, @@ -749,8 +776,8 @@ class ManualInvoiceController extends TTCrud ManualInvoicepositionModel::create([ 'manualinvoice_id' => $creditInvoiceId, 'position_group' => null, - 'product_id' => $pos['product_id'], - 'product_name' => $pos['product_name'], + 'warehousearticle_id' => $pos['warehousearticle_id'] ?? $pos['product_id'] ?? 0, + 'warehousearticle_name' => $pos['warehousearticle_name'] ?? $pos['product_name'] ?? '', 'product_info' => $pos['product_info'] ?? '', 'amount' => -abs($pos['amount']), 'unit' => $pos['unit'] ?? 'Stk.', @@ -762,8 +789,6 @@ class ManualInvoiceController extends TTCrud 'matchcode' => $pos['matchcode'] ?? null, 'fibu_cost_account' => $pos['fibu_cost_account'] ?? null, 'fibu_taxcode' => $pos['fibu_taxcode'] ?? null, - 'contract_id' => 0, - 'billing_id' => null, 'create_by' => $me->id, 'edit_by' => $me->id, 'create' => time(), @@ -813,12 +838,7 @@ class ManualInvoiceController extends TTCrud return; } - // Map revenueAccount to 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 + $vatgroupId = $article->vatgroup_id; $vatrate = VatrateModel::getFirst(['vatgroup_id' => $vatgroupId, 'area' => $vatarea]); if (!$vatrate) { @@ -826,7 +846,6 @@ class ManualInvoiceController extends TTCrud return; } - // Parse prices from cheapestSellPrice JSON $prices = []; if (!empty($article->cheapestSellPrice)) { $pricesData = json_decode($article->cheapestSellPrice, true); @@ -842,7 +861,7 @@ class ManualInvoiceController extends TTCrud 'title' => $article->title, 'articleNumber' => $article->articleNumber, 'description' => $article->description, - 'revenueAccount' => $article->revenueAccount, + 'vatgroup_id' => $article->vatgroup_id, 'unit' => $article->unit ], 'prices' => $prices, diff --git a/application/ManualInvoice/ManualInvoiceModel.php b/application/ManualInvoice/ManualInvoiceModel.php index 7c6d82560..e92102a85 100644 --- a/application/ManualInvoice/ManualInvoiceModel.php +++ b/application/ManualInvoice/ManualInvoiceModel.php @@ -4,10 +4,10 @@ class ManualInvoiceModel extends TTCrudBaseModel { public int $id; public ?string $invoice_number; public int $invoice_date; - public ?string $leistungszeitraum; - public ?string $einleitender_text; - public ?string $externe_referenz; - public float $gesamtrabatt; + public ?string $performance_period; + public ?string $introductory_text; + public ?string $external_reference; + public float $total_discount; public int $owner_id; public int $billingaddress_id; public int $customer_number; @@ -33,7 +33,6 @@ class ManualInvoiceModel extends TTCrudBaseModel { public ?string $email; public ?string $uid; public string $billing_type; - public string $billing_delivery; public ?string $bank_account_bank; public ?string $bank_account_owner; public ?string $bank_account_iban; diff --git a/application/ManualInvoiceposition/ManualInvoicepositionModel.php b/application/ManualInvoiceposition/ManualInvoicepositionModel.php index ddaca0479..1a3d71227 100644 --- a/application/ManualInvoiceposition/ManualInvoicepositionModel.php +++ b/application/ManualInvoiceposition/ManualInvoicepositionModel.php @@ -4,11 +4,9 @@ class ManualInvoicepositionModel extends TTCrudBaseModel { public int $id; public ?int $manualinvoice_id; public ?string $position_group; - public ?int $billing_id; - public int $contract_id; public ?string $matchcode; - public int $product_id; - public string $product_name; + public int $warehousearticle_id; + public string $warehousearticle_name; public ?string $product_info; public float $amount; public string $unit; diff --git a/application/WarehouseArticle/WarehouseArticleController.php b/application/WarehouseArticle/WarehouseArticleController.php index aa09bdc00..d7edbaded 100644 --- a/application/WarehouseArticle/WarehouseArticleController.php +++ b/application/WarehouseArticle/WarehouseArticleController.php @@ -13,7 +13,7 @@ class WarehouseArticleController extends TTCrud { ['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' => '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' => 'cheapestSellPrice', 'text' => 'Verkauf', 'modal' => false, 'table' => ['class' => 'text-center', 'suffix' => ' €']], ['key' => 'warningAmount', 'text' => 'Warnmenge', 'required' => true,'modal' => ['type' => 'number'], 'table' => false], diff --git a/application/WarehouseArticle/WarehouseArticleModel.php b/application/WarehouseArticle/WarehouseArticleModel.php index 245e3bb64..a142c81fd 100644 --- a/application/WarehouseArticle/WarehouseArticleModel.php +++ b/application/WarehouseArticle/WarehouseArticleModel.php @@ -17,7 +17,7 @@ class WarehouseArticleModel extends TTCrudBaseModel { public ?int $isEndOfLife; public string $unit; public ?int $isSerialDocumentation; - public int $revenueAccount; + public int $vatgroup_id; public int $create; public int $createBy; } \ No newline at end of file diff --git a/db/migrations/20260126120000_warehousearticle_rename_revenueaccount_to_vatgroupid.php b/db/migrations/20260126120000_warehousearticle_rename_revenueaccount_to_vatgroupid.php new file mode 100644 index 000000000..0ab388300 --- /dev/null +++ b/db/migrations/20260126120000_warehousearticle_rename_revenueaccount_to_vatgroupid.php @@ -0,0 +1,33 @@ +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"); + } + } +} diff --git a/db/migrations/20260126130000_manualinvoice_column_cleanup.php b/db/migrations/20260126130000_manualinvoice_column_cleanup.php new file mode 100644 index 000000000..967850e86 --- /dev/null +++ b/db/migrations/20260126130000_manualinvoice_column_cleanup.php @@ -0,0 +1,58 @@ +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(); + } + } +} diff --git a/public/js/pages/ManualInvoice/ManualInvoice.js b/public/js/pages/ManualInvoice/ManualInvoice.js index ec1b87315..2967d5712 100644 --- a/public/js/pages/ManualInvoice/ManualInvoice.js +++ b/public/js/pages/ManualInvoice/ManualInvoice.js @@ -52,27 +52,21 @@ Vue.component('manual-invoice', { }, async handleSave(invoiceData) { try { - const positions = invoiceData.positions.map(p => { - const amount = parseFloat(p.amount) || 0; - const price = parseFloat(p.price) || 0; - const discount = parseFloat(p.discount) || 0; - const vatrate = parseFloat(p.vatrate) || 0; - const priceAfterDiscount = amount * price * (1 - discount / 100); - return { - ...p, amount, price, discount, vatrate, - unit: p.unit || 'Stk.', - price_total: priceAfterDiscount, - price_gross: priceAfterDiscount * (1 + vatrate / 100), - product_id: p.product_id || 0, - contract_id: p.contract_id || 0, - billing_id: p.billing_id || null, - matchcode: p.matchcode || null, - fibu_cost_account: p.fibu_cost_account || null, - fibu_cost_account_legacy: p.fibu_cost_account_legacy || null, - fibu_taxcode: p.fibu_taxcode || null, - options: p.options || null - }; - }); + const positions = invoiceData.positions.map(p => ({ + ...p, + amount: parseFloat(p.amount) || 0, + price: parseFloat(p.price) || 0, + discount: parseFloat(p.discount) || 0, + vatrate: parseFloat(p.vatrate) || 0, + unit: p.unit || 'Stk.', + warehousearticle_id: p.warehousearticle_id || p.product_id || 0, + warehousearticle_name: p.warehousearticle_name || p.product_name || '', + matchcode: p.matchcode || null, + fibu_cost_account: p.fibu_cost_account || null, + fibu_cost_account_legacy: p.fibu_cost_account_legacy || null, + fibu_taxcode: p.fibu_taxcode || null, + options: p.options || null + })); const payload = { ...invoiceData, @@ -82,11 +76,13 @@ Vue.component('manual-invoice', { customer_number: invoiceData.customer_number || 0, country: invoiceData.country || 'Österreich', billing_type: invoiceData.billing_type || 'invoice', - billing_delivery: 'email', fibu_payment_due: 14, fibu_account_number: invoiceData.fibu_account_number || 0, - vatgroup_id: 1, - gesamtrabatt: parseFloat(invoiceData.gesamtrabatt) || 0 + vatgroup_id: invoiceData.vatgroup_id || 1, + 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; @@ -174,15 +170,15 @@ Vue.component('manual-invoice-modal', { - - - + + + - + @@ -215,8 +211,8 @@ Vue.component('manual-invoice-modal', { 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, company: '', firstname: '', lastname: '', street: '', zip: '', city: '', country: 'Österreich', - uid: '', email: '', billing_type: 'invoice', tax_text: '', - leistungszeitraum: '', einleitender_text: '', externe_referenz: '', gesamtrabatt: 0, + uid: '', email: '', billing_type: 'invoice', tax_text: '', vatgroup_id: 1, + performance_period: '', introductory_text: '', external_reference: '', total_discount: 0, positions: [], total: 0, total_gross: 0 }, positionsConfig: { @@ -228,7 +224,7 @@ Vue.component('manual-invoice-modal', { customFieldReference: 'WarehouseArticle', emitDisplayValue: true }, - product_name: { type: 'input', label: 'Bezeichnung' }, + warehousearticle_name: { type: 'input', label: 'Bezeichnung' }, product_info: { type: 'input', label: 'Zusatzinfo' }, amount: { type: 'input', label: 'Menge', inputType: 'number' }, price_type: { @@ -240,7 +236,7 @@ Vue.component('manual-invoice-modal', { discount: { type: 'input', label: 'Rabatt (%)', inputType: 'number' }, }, 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.price == null) { window.notify('error', 'Preis ist erforderlich.'); return false; } return true; @@ -261,18 +257,15 @@ Vue.component('manual-invoice-modal', { subtotal += lineTotal; }); - // Apply gesamtrabatt - const gesamtrabatt = parseFloat(this.invoiceData.gesamtrabatt) || 0; - const net = subtotal * (1 - gesamtrabatt / 100); - - // Calculate VAT + const totalDiscount = parseFloat(this.invoiceData.total_discount) || 0; + const net = subtotal * (1 - totalDiscount / 100); let vat = {}, gross = 0; (this.invoiceData.positions || []).forEach(p => { const amount = parseFloat(p.amount) || 0; const price = parseFloat(p.price) || 0; const discount = parseFloat(p.discount) || 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); vat[r] = (vat[r] || 0) + lineVat; gross += lineNet + lineVat; @@ -400,7 +393,7 @@ Vue.component('manual-invoice-modal', { if (data.success && this.$refs.positionsManager) { const pm = this.$refs.positionsManager; 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, 'unit', data.article.unit || 'Stk.'); } @@ -455,7 +448,7 @@ Vue.component('manual-invoice-modal', { this.pdfLoading = true; try { 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 => { const amount = parseFloat(p.amount) || 0; const price = parseFloat(p.price) || 0; @@ -524,17 +517,17 @@ Vue.component('manual-invoice-modal', { } // 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 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) if (shippingNoteData.positions && Array.isArray(shippingNoteData.positions)) { const newPositions = shippingNoteData.positions.map(position => ({ - product_name: position.product_name || '', + warehousearticle_name: position.warehousearticle_name || position.product_name || '', product_info: position.product_info || '', amount: parseFloat(position.amount) || 0, unit: position.unit || 'Stk.', @@ -583,7 +576,7 @@ Vue.component('gutschrift-modal', { - + @@ -624,7 +617,7 @@ Vue.component('gutschrift-modal', { .map((p, i) => ({ p, i })).filter(({ i }) => this.selectedPositions[i]) .map(({ p, 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; }).filter(Boolean); diff --git a/public/js/pages/WarehouseArticle/WarehouseArticle.js b/public/js/pages/WarehouseArticle/WarehouseArticle.js index b8ad34990..046c6ef87 100644 --- a/public/js/pages/WarehouseArticle/WarehouseArticle.js +++ b/public/js/pages/WarehouseArticle/WarehouseArticle.js @@ -437,8 +437,8 @@ Vue.component('warehouse-article-modal', {
@@ -538,7 +538,7 @@ Vue.component('warehouse-article-modal', { category_id: null, articleNumber: '', unit: 'Stk.', - revenueAccount: 0, + vatgroup_id: 2, warningAmount: 0, criticalAmount: 0, isSerialDocumentation: false, @@ -566,10 +566,10 @@ Vue.component('warehouse-article-modal', { { value: 'km', text: 'km' } ]; }, - revenueAccountOptions() { + vatgroupOptions() { return [ - { value: 0, text: 'Dienstleistungen' }, - { value: 1, text: 'Handelswaren' } + { value: 2, text: 'Dienstleistungen' }, + { value: 3, text: 'Handelswaren' } ]; }, isValid() { @@ -607,7 +607,7 @@ Vue.component('warehouse-article-modal', { category_id: data.category_id, articleNumber: data.articleNumber || '', unit: data.unit || 'Stk.', - revenueAccount: data.revenueAccount || 0, + vatgroup_id: data.vatgroup_id || 2, warningAmount: data.warningAmount || 0, criticalAmount: data.criticalAmount || 0, isSerialDocumentation: !!data.isSerialDocumentation, @@ -637,7 +637,7 @@ Vue.component('warehouse-article-modal', { category_id: null, articleNumber: '', unit: 'Stk.', - revenueAccount: 0, + vatgroup_id: 2, warningAmount: 0, criticalAmount: 0, isSerialDocumentation: false,
- product_name ?? '')?> + warehousearticle_name ?? '')?> product_info) && $p->product_info): ?>
product_info)?>
@@ -186,17 +186,17 @@ $this->setReturnValue(['filename' => $invoice->invoice_number . ".pdf"]); endforeach; endforeach; ?> - 0): ?> + 0): ?>
Zwischensumme:
Gesamtrabatt %:-Gesamtrabatt %:-
Gesamt Netto:
Leistungszeitraum:" . htmlspecialchars($invoice->leistungszeitraum) . "
Externe Referenz:" . htmlspecialchars($invoice->externe_referenz) . "
Leistungszeitraum:" . htmlspecialchars($invoice->performance_period) . "
Externe Referenz:" . htmlspecialchars($invoice->external_reference) . "
Ihre UID:" . $invoice->uid . "
QR-Codetotal_gross ?? 0, 2)) . '" style="display: block; height: 100%; max-height: 3.5cm; width: auto;">
{{ pos.product_name }}
{{ pos.product_info }}
{{ pos.warehousearticle_name }}
{{ pos.product_info }}
{{ pos.original_amount }}{{ pos.credited_amount }} {{ pos.available_amount }}