diff --git a/controller/abonemments/AbonemmentController.js b/controller/abonemments/AbonemmentController.js index 7969ed0..98d58ec 100644 --- a/controller/abonemments/AbonemmentController.js +++ b/controller/abonemments/AbonemmentController.js @@ -45,6 +45,9 @@ module.exports = { invoiceCity: req.body.invoiceCity, invoicePhone: req.body.invoicePhone, invoiceEmail: req.body.invoiceEmail, + uidNumber: req.body.uidNumber || req.body.uid_number, + atuNumber: req.body.atuNumber || req.body.atu_number, + taxMode: req.body.taxMode || req.body.tax_mode, contractNumber: req.body.contractNumber || req.body.contract_number, signingCity: req.body.signingCity || req.body.signing_city || '', signatureDataUrl: req.body.signatureDataUrl || req.body.signature_data_url, diff --git a/services/abonemments/AbonemmentService.js b/services/abonemments/AbonemmentService.js index c2e520c..a49fe98 100644 --- a/services/abonemments/AbonemmentService.js +++ b/services/abonemments/AbonemmentService.js @@ -11,6 +11,10 @@ const CAPSULES_PER_PACK = 10; const MIN_ABO_PACKS = 6; const MAX_ABO_PACKS = 10000; +function normalizeUid(value) { + return String(value || '').trim().toUpperCase().replace(/[^A-Z0-9]/g, ''); +} + function getPackCountError(totalPacks) { if (totalPacks < MIN_ABO_PACKS) { return `Order must contain at least ${MIN_ABO_PACKS} packs (${MIN_ABO_PACKS * CAPSULES_PER_PACK} capsules).`; @@ -85,6 +89,9 @@ class AbonemmentService { invoiceCity, invoicePhone, invoiceEmail, + uidNumber, + atuNumber, + taxMode, contractNumber, signingCity, signatureDataUrl, @@ -155,6 +162,7 @@ class AbonemmentService { const now = new Date(); const nextBilling = this.addInterval(now, billingInterval || 'month', intervalCount || 1); + const normalizedInvoiceUid = normalizeUid(uidNumber || atuNumber || ''); const effectiveRecipientName = recipientName || `${firstName || ''} ${lastName || ''}`.trim() || null; const effectiveEmail = forSelf ? normalizedEmail : normalizedRecipientEmail; @@ -174,6 +182,8 @@ class AbonemmentService { total_packs: totalPacks, is_for_self: forSelf, recipient_name: forSelf ? null : effectiveRecipientName, + tax_mode_hint: taxMode || null, + invoice_uid_number: normalizedInvoiceUid || null, }, pack_breakdown: breakdown, first_name: forSelf ? firstName : (effectiveRecipientName || firstName), @@ -270,7 +280,9 @@ class AbonemmentService { snapshot.next_billing_at, { actorUserId: actorUser?.id || null, - lang: actorUser?.lang || actorUser?.language || 'en' + lang: actorUser?.lang || actorUser?.language || 'en', + uidOverride: normalizedInvoiceUid || null, + taxModeHint: taxMode || null, } ); console.log('[SUBSCRIBE ORDER] Issued invoice:', { diff --git a/services/invoice/InvoiceService.js b/services/invoice/InvoiceService.js index d52158e..ba7a11e 100644 --- a/services/invoice/InvoiceService.js +++ b/services/invoice/InvoiceService.js @@ -772,7 +772,7 @@ class InvoiceService { return this._loadInvoiceUserProfile(userId); } - async resolveTaxDecisionForSubscription({ buyerCountry, invoiceOwnerUserId, invoiceOwnerProfile = null }) { + async resolveTaxDecisionForSubscription({ buyerCountry, invoiceOwnerUserId, invoiceOwnerProfile = null, uidOverride = null }) { const uow = new UnitOfWork(); await uow.start(); @@ -791,7 +791,7 @@ class InvoiceService { await uow.commit(); const companyProfile = invoiceOwnerProfile || await this._loadCompanyTaxProfile(invoiceOwnerUserId); - const uidCandidate = companyProfile?.atu_number || companyProfile?.registration_number || ''; + const uidCandidate = uidOverride || companyProfile?.atu_number || companyProfile?.registration_number || ''; const normalizedUid = this._normalizeUid(uidCandidate); const hasValidUid = this._isLikelyValidUid(normalizedUid); const countryCode = String(country?.country_code || '').toUpperCase(); @@ -822,7 +822,7 @@ class InvoiceService { } // Issue invoice for a subscription period, with items from pack_breakdown - async issueForAbonement(abonement, periodStart, periodEnd, { actorUserId, lang = 'en' } = {}) { + async issueForAbonement(abonement, periodStart, periodEnd, { actorUserId, lang = 'en', uidOverride = null, taxModeHint = null } = {}) { console.log('[INVOICE ISSUE] Inputs:', { abonement_id: abonement?.id, abonement_user_id: abonement?.user_id, @@ -854,9 +854,20 @@ class InvoiceService { buyerCountry: addr.country, invoiceOwnerUserId: userIdForInvoice, invoiceOwnerProfile: invoiceUserProfile, + uidOverride, }); const vat_rate = taxDecision?.vatRate ?? null; + logger.info('InvoiceService.issueForAbonement:tax_decision', { + userId: userIdForInvoice, + buyerCountry: addr.country || null, + profileUserType: invoiceUserProfile?.user_type || null, + providedUidOverride: uidOverride || null, + taxModeHint: taxModeHint || null, + taxMode: taxDecision?.isReverseCharge ? 'reverse_charge' : 'standard', + resolvedUid: taxDecision?.uid || null, + }); + const items = await this._buildInvoiceItems({ abonement, vatRate: vat_rate, lang }); const context = {