Merge pull request 'feat: enhance subscription handling with uid and tax mode support' (#29) from refactor/reverseChargeTemplate into dev
Reviewed-on: #29
This commit is contained in:
commit
2d9d8214ef
@ -45,6 +45,9 @@ module.exports = {
|
|||||||
invoiceCity: req.body.invoiceCity,
|
invoiceCity: req.body.invoiceCity,
|
||||||
invoicePhone: req.body.invoicePhone,
|
invoicePhone: req.body.invoicePhone,
|
||||||
invoiceEmail: req.body.invoiceEmail,
|
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,
|
contractNumber: req.body.contractNumber || req.body.contract_number,
|
||||||
signingCity: req.body.signingCity || req.body.signing_city || '',
|
signingCity: req.body.signingCity || req.body.signing_city || '',
|
||||||
signatureDataUrl: req.body.signatureDataUrl || req.body.signature_data_url,
|
signatureDataUrl: req.body.signatureDataUrl || req.body.signature_data_url,
|
||||||
|
|||||||
@ -11,6 +11,10 @@ const CAPSULES_PER_PACK = 10;
|
|||||||
const MIN_ABO_PACKS = 6;
|
const MIN_ABO_PACKS = 6;
|
||||||
const MAX_ABO_PACKS = 10000;
|
const MAX_ABO_PACKS = 10000;
|
||||||
|
|
||||||
|
function normalizeUid(value) {
|
||||||
|
return String(value || '').trim().toUpperCase().replace(/[^A-Z0-9]/g, '');
|
||||||
|
}
|
||||||
|
|
||||||
function getPackCountError(totalPacks) {
|
function getPackCountError(totalPacks) {
|
||||||
if (totalPacks < MIN_ABO_PACKS) {
|
if (totalPacks < MIN_ABO_PACKS) {
|
||||||
return `Order must contain at least ${MIN_ABO_PACKS} packs (${MIN_ABO_PACKS * CAPSULES_PER_PACK} capsules).`;
|
return `Order must contain at least ${MIN_ABO_PACKS} packs (${MIN_ABO_PACKS * CAPSULES_PER_PACK} capsules).`;
|
||||||
@ -85,6 +89,9 @@ class AbonemmentService {
|
|||||||
invoiceCity,
|
invoiceCity,
|
||||||
invoicePhone,
|
invoicePhone,
|
||||||
invoiceEmail,
|
invoiceEmail,
|
||||||
|
uidNumber,
|
||||||
|
atuNumber,
|
||||||
|
taxMode,
|
||||||
contractNumber,
|
contractNumber,
|
||||||
signingCity,
|
signingCity,
|
||||||
signatureDataUrl,
|
signatureDataUrl,
|
||||||
@ -155,6 +162,7 @@ class AbonemmentService {
|
|||||||
|
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const nextBilling = this.addInterval(now, billingInterval || 'month', intervalCount || 1);
|
const nextBilling = this.addInterval(now, billingInterval || 'month', intervalCount || 1);
|
||||||
|
const normalizedInvoiceUid = normalizeUid(uidNumber || atuNumber || '');
|
||||||
|
|
||||||
const effectiveRecipientName = recipientName || `${firstName || ''} ${lastName || ''}`.trim() || null;
|
const effectiveRecipientName = recipientName || `${firstName || ''} ${lastName || ''}`.trim() || null;
|
||||||
const effectiveEmail = forSelf ? normalizedEmail : normalizedRecipientEmail;
|
const effectiveEmail = forSelf ? normalizedEmail : normalizedRecipientEmail;
|
||||||
@ -174,6 +182,8 @@ class AbonemmentService {
|
|||||||
total_packs: totalPacks,
|
total_packs: totalPacks,
|
||||||
is_for_self: forSelf,
|
is_for_self: forSelf,
|
||||||
recipient_name: forSelf ? null : effectiveRecipientName,
|
recipient_name: forSelf ? null : effectiveRecipientName,
|
||||||
|
tax_mode_hint: taxMode || null,
|
||||||
|
invoice_uid_number: normalizedInvoiceUid || null,
|
||||||
},
|
},
|
||||||
pack_breakdown: breakdown,
|
pack_breakdown: breakdown,
|
||||||
first_name: forSelf ? firstName : (effectiveRecipientName || firstName),
|
first_name: forSelf ? firstName : (effectiveRecipientName || firstName),
|
||||||
@ -270,7 +280,9 @@ class AbonemmentService {
|
|||||||
snapshot.next_billing_at,
|
snapshot.next_billing_at,
|
||||||
{
|
{
|
||||||
actorUserId: actorUser?.id || null,
|
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:', {
|
console.log('[SUBSCRIBE ORDER] Issued invoice:', {
|
||||||
|
|||||||
@ -772,7 +772,7 @@ class InvoiceService {
|
|||||||
return this._loadInvoiceUserProfile(userId);
|
return this._loadInvoiceUserProfile(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
async resolveTaxDecisionForSubscription({ buyerCountry, invoiceOwnerUserId, invoiceOwnerProfile = null }) {
|
async resolveTaxDecisionForSubscription({ buyerCountry, invoiceOwnerUserId, invoiceOwnerProfile = null, uidOverride = null }) {
|
||||||
const uow = new UnitOfWork();
|
const uow = new UnitOfWork();
|
||||||
await uow.start();
|
await uow.start();
|
||||||
|
|
||||||
@ -791,7 +791,7 @@ class InvoiceService {
|
|||||||
await uow.commit();
|
await uow.commit();
|
||||||
|
|
||||||
const companyProfile = invoiceOwnerProfile || await this._loadCompanyTaxProfile(invoiceOwnerUserId);
|
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 normalizedUid = this._normalizeUid(uidCandidate);
|
||||||
const hasValidUid = this._isLikelyValidUid(normalizedUid);
|
const hasValidUid = this._isLikelyValidUid(normalizedUid);
|
||||||
const countryCode = String(country?.country_code || '').toUpperCase();
|
const countryCode = String(country?.country_code || '').toUpperCase();
|
||||||
@ -822,7 +822,7 @@ class InvoiceService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Issue invoice for a subscription period, with items from pack_breakdown
|
// 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:', {
|
console.log('[INVOICE ISSUE] Inputs:', {
|
||||||
abonement_id: abonement?.id,
|
abonement_id: abonement?.id,
|
||||||
abonement_user_id: abonement?.user_id,
|
abonement_user_id: abonement?.user_id,
|
||||||
@ -854,9 +854,20 @@ class InvoiceService {
|
|||||||
buyerCountry: addr.country,
|
buyerCountry: addr.country,
|
||||||
invoiceOwnerUserId: userIdForInvoice,
|
invoiceOwnerUserId: userIdForInvoice,
|
||||||
invoiceOwnerProfile: invoiceUserProfile,
|
invoiceOwnerProfile: invoiceUserProfile,
|
||||||
|
uidOverride,
|
||||||
});
|
});
|
||||||
const vat_rate = taxDecision?.vatRate ?? null;
|
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 items = await this._buildInvoiceItems({ abonement, vatRate: vat_rate, lang });
|
||||||
|
|
||||||
const context = {
|
const context = {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user