feat: add shipping fee resolution to invoice items and refactor item building logic

This commit is contained in:
seaznCode 2026-03-20 16:27:37 +01:00
parent bd21801c41
commit b87b999455

View File

@ -12,6 +12,7 @@ const fs = require('fs/promises');
const path = require('path'); const path = require('path');
const CompanySettingsRepository = require('../../repositories/settings/CompanySettingsRepository'); const CompanySettingsRepository = require('../../repositories/settings/CompanySettingsRepository');
const CoffeeShippingFeeService = require('../subscriptions/CoffeeShippingFeeService');
class InvoiceService { class InvoiceService {
constructor() { constructor() {
@ -58,6 +59,54 @@ class InvoiceService {
return null; return null;
} }
async _resolveShippingFeeItem({ abonement, vatRate, lang }) {
const pieceCount = this._resolvePieceCountForQr(abonement);
if (!pieceCount) return null;
const shippingFee = await CoffeeShippingFeeService.get(pieceCount);
const unitPrice = Number(shippingFee?.price || 0);
if (!(unitPrice > 0)) return null;
return {
product_id: null,
sku: `SHIPPING-${pieceCount}`,
description: lang === 'de' ? `Versandkosten (${pieceCount} Stk.)` : `Shipping fee (${pieceCount} pcs)` ,
quantity: 1,
unit_price: unitPrice,
tax_rate: vatRate,
};
}
async _buildInvoiceItems({ abonement, vatRate, lang }) {
const breakdown = Array.isArray(abonement?.pack_breakdown) ? abonement.pack_breakdown : [];
const items = breakdown.length
? breakdown.map((b) => ({
product_id: Number(b.coffee_table_id) || null,
sku: `COFFEE-${b.coffee_table_id || 'N/A'}`,
description: b.coffee_title || `Coffee subscription: ${b.coffee_table_id}`,
quantity: Number(b.packs || 1),
unit_price: Number(b.price_per_pack || 0),
tax_rate: b.tax_rate != null ? Number(b.tax_rate) : vatRate,
}))
: [
{
product_id: null,
sku: 'SUBSCRIPTION',
description: `Subscription ${abonement?.pack_group || ''}`,
quantity: 1,
unit_price: Number(abonement?.price || 0),
tax_rate: vatRate,
},
];
const shippingItem = await this._resolveShippingFeeItem({ abonement, vatRate, lang });
if (shippingItem) {
items.push(shippingItem);
}
return items;
}
async _getCompanySettingsQrDataUri(pieceCount) { async _getCompanySettingsQrDataUri(pieceCount) {
const safePieceCount = pieceCount === 120 ? 120 : 60; const safePieceCount = pieceCount === 120 ? 120 : 60;
try { try {
@ -598,26 +647,7 @@ class InvoiceService {
// NEW: resolve invoice vat_rate (standard) from buyer country // NEW: resolve invoice vat_rate (standard) from buyer country
const vat_rate = await this.resolveVatRateForCountry(addr.country); const vat_rate = await this.resolveVatRateForCountry(addr.country);
const breakdown = Array.isArray(abonement.pack_breakdown) ? abonement.pack_breakdown : []; const items = await this._buildInvoiceItems({ abonement, vatRate: vat_rate, lang });
const items = breakdown.length
? breakdown.map((b) => ({
product_id: Number(b.coffee_table_id) || null,
sku: `COFFEE-${b.coffee_table_id || 'N/A'}`,
description: b.coffee_title || `Coffee subscription: ${b.coffee_table_id}`,
quantity: Number(b.packs || 1),
unit_price: Number(b.price_per_pack || 0),
tax_rate: b.tax_rate != null ? Number(b.tax_rate) : vat_rate, // CHANGED: default to invoice vat_rate
}))
: [
{
product_id: null,
sku: 'SUBSCRIPTION',
description: `Subscription ${abonement.pack_group || ''}`,
quantity: 1,
unit_price: Number(abonement.price || 0),
tax_rate: vat_rate, // CHANGED
},
];
const context = { const context = {
source: 'abonement', source: 'abonement',
@ -677,6 +707,9 @@ class InvoiceService {
logger.error('InvoiceService.issueForAbonement:invoice_email_error', { logger.error('InvoiceService.issueForAbonement:invoice_email_error', {
invoiceId: invoice?.id, invoiceId: invoice?.id,
message: mailError?.message, message: mailError?.message,
stack: mailError?.stack,
brevoStatus: mailError?.statusCode ?? mailError?.response?.status ?? null,
brevoData: mailError?.body ?? mailError?.response?.data ?? mailError?.response?.text ?? null,
}); });
} }