dev #30

Merged
Seazn merged 2 commits from dev into main 2026-05-21 19:20:13 +00:00
3 changed files with 30 additions and 4 deletions
Showing only changes of commit cf7d7d8f5a - Show all commits

View File

@ -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,

View File

@ -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:', {

View File

@ -768,7 +768,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();
@ -787,7 +787,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();
@ -818,7 +818,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,
@ -850,9 +850,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 = {