Merge pull request 'bigTypeShii' (#20) from bigTypeShii into dev
Reviewed-on: #20
This commit is contained in:
commit
d58d45c2bf
@ -92,6 +92,14 @@ class AbonemmentService {
|
||||
const normalizedRecipientEmail = this.normalizeEmail(recipientEmail);
|
||||
const forSelf = isForSelf !== false && !normalizedRecipientEmail;
|
||||
|
||||
if (typeof signingCity !== 'string' || signingCity.trim() === '') {
|
||||
throw new Error('signingCity is required');
|
||||
}
|
||||
|
||||
if (typeof signatureDataUrl !== 'string' || signatureDataUrl.trim() === '') {
|
||||
throw new Error('signatureDataUrl is required');
|
||||
}
|
||||
|
||||
if (!forSelf && !normalizedRecipientEmail) {
|
||||
throw new Error('recipient_email is required when subscription is for another person');
|
||||
}
|
||||
@ -349,6 +357,14 @@ class AbonemmentService {
|
||||
const normalizedRecipientEmail = this.normalizeEmail(recipientEmail);
|
||||
console.log('[SUBSCRIBE] Normalized recipient email:', normalizedRecipientEmail); // NEW
|
||||
|
||||
if (typeof signingCity !== 'string' || signingCity.trim() === '') {
|
||||
throw new Error('signingCity is required');
|
||||
}
|
||||
|
||||
if (typeof signatureDataUrl !== 'string' || signatureDataUrl.trim() === '') {
|
||||
throw new Error('signatureDataUrl is required');
|
||||
}
|
||||
|
||||
if (coffeeId === undefined || coffeeId === null) throw new Error('coffeeId is required');
|
||||
|
||||
const hasRecipientFields = recipientName || normalizedRecipientEmail || recipientNotes;
|
||||
|
||||
@ -12,6 +12,7 @@ const fs = require('fs/promises');
|
||||
const path = require('path');
|
||||
|
||||
const CompanySettingsRepository = require('../../repositories/settings/CompanySettingsRepository');
|
||||
const CoffeeShippingFeeService = require('../subscriptions/CoffeeShippingFeeService');
|
||||
|
||||
class InvoiceService {
|
||||
constructor() {
|
||||
@ -58,6 +59,54 @@ class InvoiceService {
|
||||
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) {
|
||||
const safePieceCount = pieceCount === 120 ? 120 : 60;
|
||||
try {
|
||||
@ -598,26 +647,7 @@ class InvoiceService {
|
||||
// NEW: resolve invoice vat_rate (standard) from buyer country
|
||||
const vat_rate = await this.resolveVatRateForCountry(addr.country);
|
||||
|
||||
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) : 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 items = await this._buildInvoiceItems({ abonement, vatRate: vat_rate, lang });
|
||||
|
||||
const context = {
|
||||
source: 'abonement',
|
||||
@ -677,6 +707,9 @@ class InvoiceService {
|
||||
logger.error('InvoiceService.issueForAbonement:invoice_email_error', {
|
||||
invoiceId: invoice?.id,
|
||||
message: mailError?.message,
|
||||
stack: mailError?.stack,
|
||||
brevoStatus: mailError?.statusCode ?? mailError?.response?.status ?? null,
|
||||
brevoData: mailError?.body ?? mailError?.response?.data ?? mailError?.response?.text ?? null,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user