feat: tarjetas de crédito en pagos recibidos (catálogo CreditCards, tabla en el formulario, validación y envío)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
GeorgeandClaude Sonnet 5.5 committed 2026-10-16 14:26:42 -05:00
1 parent a77995536b
commit c9d1a714c9
8 files changed
+91 -3

No files matched your search

@@ -64,6 +64,8 @@ export interface PaymentDraft {
journalRemarks: string;
means: Means;
checks: Row[];
/** Tarjetas de crédito (solo pagos recibidos). */
cards?: Row[];
docs: OpenDoc[];
accounts: Row[];
}
@@ -113,6 +115,7 @@ export interface PaymentTotals {
cash: number;
transfer: number;
checks: number;
cards: number;
/** Suma de los medios de pago. */
means: number;
/** Lo que sobra de los medios sobre lo aplicado: queda a cuenta del socio. */
@@ -121,13 +124,14 @@ export interface PaymentTotals {
accounts: number;
}
export function paymentTotals(d: Pick<PaymentDraft, 'means' | 'checks' | 'docs' | 'accounts'>): PaymentTotals {
export function paymentTotals(d: Pick<PaymentDraft, 'means' | 'checks' | 'docs' | 'accounts'> & { cards?: Row[] }): PaymentTotals {
const invoices = round2(d.docs.filter((x) => x.kind === 'invoice').reduce((s, x) => s + num(x.applied), 0));
const credits = round2(d.docs.filter((x) => x.kind === 'credit').reduce((s, x) => s + num(x.applied), 0));
const cash = round2(num(d.means.cashSum));
const transfer = round2(num(d.means.transferSum));
const checks = round2(d.checks.reduce((s, c) => s + num(c['CheckSum']), 0));
const means = round2(cash + transfer + checks);
const cards = round2((d.cards ?? []).reduce((s, c) => s + num(c['CreditSum']), 0));
const means = round2(cash + transfer + checks + cards);
const applied = round2(invoices - credits);
return {
invoices,
@@ -136,6 +140,7 @@ export function paymentTotals(d: Pick<PaymentDraft, 'means' | 'checks' | 'docs'
cash,
transfer,
checks,
cards,
means,
onAccount: round2(Math.max(0, means - applied)),
accounts: round2(d.accounts.reduce((s, a) => s + num(a['SumPaid']), 0)),
@@ -169,6 +174,15 @@ export function validatePayment(draft: PaymentDraft): string[] {
if (!has(c['DueDate'])) errors.push(`Cheque ${i + 1}: indique el vencimiento.`);
});
(draft.cards ?? []).forEach((c, i) => {
if (!has(c['CreditSum']) && !has(c['CreditCard']) && !has(c['CreditCardNumber'])) return; // fila vacía
if (num(c['CreditSum']) <= 0) errors.push(`Tarjeta ${i + 1}: indique el importe.`);
if (!has(c['CreditCard'])) errors.push(`Tarjeta ${i + 1}: indique la tarjeta.`);
if (!/^\d{4}$/.test(String(c['CreditCardNumber'] ?? '').trim())) errors.push(`Tarjeta ${i + 1}: indique los 4 últimos dígitos.`);
if (!has(c['CardValidUntil'])) errors.push(`Tarjeta ${i + 1}: indique la fecha de validez.`);
if (!has(c['VoucherNum'])) errors.push(`Tarjeta ${i + 1}: indique el número de comprobante.`);
});
if (toAccounts) {
const rows = draft.accounts.filter((a) => has(a['AccountCode']) || has(a['SumPaid']));
if (!rows.length) errors.push('Agregue al menos una cuenta contable.');
@@ -226,6 +240,20 @@ export function paymentPayload(screen: PaymentScreen, d: PaymentDraft): Row {
);
}
const cards = (d.cards ?? []).filter((c) => num(c['CreditSum']) > 0);
if (cards.length) {
body['PaymentCreditCards'] = cards.map((c) =>
clean({
CreditCard: c['CreditCard'],
CreditCardNumber: String(c['CreditCardNumber']).trim(),
CardValidUntil: c['CardValidUntil'],
VoucherNum: c['VoucherNum'],
CreditSum: round2(num(c['CreditSum'])),
CreditCur: screen.localCurrency,
}),
);
}
if (d.docType === 'rAccount') {
body['PaymentAccounts'] = d.accounts.filter((a) => num(a['SumPaid']) > 0).map((a) => clean({ AccountCode: a['AccountCode'], SumPaid: round2(num(a['SumPaid'])), Decription: a['Decription'] }));
} else {