Verifactu
This commit is contained in:
parent
89e22de2bd
commit
4c4afe2b3a
@ -1,7 +1,9 @@
|
|||||||
import { Collection, Result, ResultCollection } from "@repo/rdx-utils";
|
import { Collection, Result, ResultCollection } from "@repo/rdx-utils";
|
||||||
import { Model } from "sequelize";
|
import type { Model } from "sequelize";
|
||||||
import { MapperParamsType } from "../../../domain";
|
|
||||||
import { ISequelizeDomainMapper } from "./sequelize-mapper.interface";
|
import type { MapperParamsType } from "../../../domain";
|
||||||
|
|
||||||
|
import type { ISequelizeDomainMapper } from "./sequelize-mapper.interface";
|
||||||
|
|
||||||
export abstract class SequelizeDomainMapper<TModel extends Model, TModelAttributes, TEntity>
|
export abstract class SequelizeDomainMapper<TModel extends Model, TModelAttributes, TEntity>
|
||||||
implements ISequelizeDomainMapper<TModel, TModelAttributes, TEntity>
|
implements ISequelizeDomainMapper<TModel, TModelAttributes, TEntity>
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { DomainMapperWithBulk, IQueryMapperWithBulk } from "../../../domain";
|
import type { DomainMapperWithBulk, IQueryMapperWithBulk } from "../../../domain";
|
||||||
|
|
||||||
export interface ISequelizeDomainMapper<TModel, TModelAttributes, TEntity>
|
export interface ISequelizeDomainMapper<TModel, TModelAttributes, TEntity>
|
||||||
extends DomainMapperWithBulk<TModel | TModelAttributes, TEntity> {}
|
extends DomainMapperWithBulk<TModel | TModelAttributes, TEntity> {}
|
||||||
|
|||||||
@ -55,6 +55,7 @@ export class IssueProformaUseCase {
|
|||||||
proformaId,
|
proformaId,
|
||||||
transaction
|
transaction
|
||||||
);
|
);
|
||||||
|
|
||||||
if (proformaResult.isFailure) return Result.fail(proformaResult.error);
|
if (proformaResult.isFailure) return Result.fail(proformaResult.error);
|
||||||
const proforma = proformaResult.data;
|
const proforma = proformaResult.data;
|
||||||
|
|
||||||
@ -64,6 +65,7 @@ export class IssueProformaUseCase {
|
|||||||
proforma.series,
|
proforma.series,
|
||||||
transaction
|
transaction
|
||||||
);
|
);
|
||||||
|
|
||||||
if (nextNumberResult.isFailure) return Result.fail(nextNumberResult.error);
|
if (nextNumberResult.isFailure) return Result.fail(nextNumberResult.error);
|
||||||
|
|
||||||
/** 4. Crear factura definitiva (dominio) */
|
/** 4. Crear factura definitiva (dominio) */
|
||||||
@ -71,6 +73,7 @@ export class IssueProformaUseCase {
|
|||||||
issueNumber: nextNumberResult.data,
|
issueNumber: nextNumberResult.data,
|
||||||
issueDate: UtcDate.today(),
|
issueDate: UtcDate.today(),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (issuedInvoiceOrError.isFailure) return Result.fail(issuedInvoiceOrError.error);
|
if (issuedInvoiceOrError.isFailure) return Result.fail(issuedInvoiceOrError.error);
|
||||||
|
|
||||||
/** 5. Guardar la nueva factura */
|
/** 5. Guardar la nueva factura */
|
||||||
|
|||||||
@ -0,0 +1,284 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="es">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.2.19/tailwind.css"
|
||||||
|
referrerpolicy="no-referrer" />
|
||||||
|
<title>Factura F26200</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: Tahoma, sans-serif;
|
||||||
|
margin: 40px;
|
||||||
|
color: #333;
|
||||||
|
font-size: 11pt;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
font-family: Tahoma, sans-serif;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
margin-bottom: 0;
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.accent-color {
|
||||||
|
background-color: #F08119;
|
||||||
|
}
|
||||||
|
|
||||||
|
.company-info,
|
||||||
|
.invoice-meta {
|
||||||
|
width: 48%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invoice-meta {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 20px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact {
|
||||||
|
font-size: 14px;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin-top: 0px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
table th,
|
||||||
|
table td {
|
||||||
|
border-top: 0px solid;
|
||||||
|
border-left: 1px solid #000;
|
||||||
|
border-right: 1px solid #000;
|
||||||
|
border-bottom: 0px solid;
|
||||||
|
padding: 3px 10px;
|
||||||
|
text-align: left;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
table th {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border-top: 1px solid #000;
|
||||||
|
border-bottom: 1px solid #000;
|
||||||
|
text-align: center;
|
||||||
|
background-color: #e7e0df;
|
||||||
|
color: #ff0014;
|
||||||
|
}
|
||||||
|
|
||||||
|
.totals {
|
||||||
|
margin-top: 20px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.totals td {
|
||||||
|
padding: 5px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.totals td.label {
|
||||||
|
text-align: right;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
margin-top: 40px;
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.highlight {
|
||||||
|
background-color: #eef;
|
||||||
|
}
|
||||||
|
|
||||||
|
.accent-color {
|
||||||
|
background-color: #F08119;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-box {
|
||||||
|
border: 2px solid black;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 3px 3px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
* {
|
||||||
|
-webkit-print-color-adjust: exact;
|
||||||
|
}
|
||||||
|
|
||||||
|
thead {
|
||||||
|
display: table-header-group;
|
||||||
|
}
|
||||||
|
|
||||||
|
tfoot {
|
||||||
|
display: table-footer-group;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<header>
|
||||||
|
<aside class="flex items-start mb-4 w-full">
|
||||||
|
<!-- Bloque IZQUIERDO: imagen arriba + texto abajo, alineado a la izquierda -->
|
||||||
|
<div class="w-[70%] flex flex-col items-start text-left">
|
||||||
|
<img src="https://rodax-software.com/images/logo_acana.jpg" alt="Logo Acana" class="block h-24 w-auto mb-1" />
|
||||||
|
<div class="p-3 not-italic text-xs leading-tight" style="font-size: 8pt;">
|
||||||
|
<p>Aliso Design S.L. B86913910</p>
|
||||||
|
<p>C/ La Fundición, 27. Pol. Santa Ana</p>
|
||||||
|
<p>Rivas Vaciamadrid 28522 Madrid</p>
|
||||||
|
<p>Telf: 91 301 65 57 / 91 301 65 58</p>
|
||||||
|
<p><a href="mailto:info@acanainteriorismo.com"
|
||||||
|
class="hover:underline">info@acanainteriorismo.com</a> - <a
|
||||||
|
href="https://www.acanainteriorismo.com" target="_blank" rel="noopener"
|
||||||
|
class="hover:underline">www.acanainteriorismo.com</a></p>
|
||||||
|
</div>
|
||||||
|
<div class="flex w-full">
|
||||||
|
<div class="info-box" style="border: 2px solid black; border-radius: 12px; padding: 10px 20px;">
|
||||||
|
<p>Factura nº:<strong> {{series}}{{invoice_number}}</strong></p>
|
||||||
|
<p><span>Fecha:<strong> {{invoice_date}}</strong></p>
|
||||||
|
<p>Página <span class="pageNumber"></span> de <span class="totalPages"></span></p>
|
||||||
|
</div>
|
||||||
|
<div class="p-3 ml-9">
|
||||||
|
<h2 class="font-semibold uppercase mb-1">{{recipient.name}}</h2>
|
||||||
|
<p>{{recipient.tin}}</p>
|
||||||
|
<p>{{recipient.street}}</p>
|
||||||
|
<p>{{recipient.postal_code}} {{recipient.city}} {{recipient.province}}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Bloque DERECHO: logo2 arriba y texto DEBAJO -->
|
||||||
|
<div class="ml-auto flex flex-col items-end text-right">
|
||||||
|
<img src="https://rodax-software.com/images/factura_acana.jpg" alt="Factura"
|
||||||
|
class="block h-14 w-auto md:h-8 mb-1" />
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main id="main">
|
||||||
|
<section id="details" class="border-b border-black ">
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Tu tabla -->
|
||||||
|
<table class="table-header">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="py-2">Concepto</th>
|
||||||
|
<th class="py-2">Ud.</th>
|
||||||
|
<th class="py-2">Imp.</th>
|
||||||
|
<th class="py-2"> </th>
|
||||||
|
<th class="py-2">Imp. total</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
{{#each items}}
|
||||||
|
<tr>
|
||||||
|
<td>{{description}}</td>
|
||||||
|
<td class="text-right">{{#if quantity}}{{quantity}}{{else}} {{/if}}</td>
|
||||||
|
<td class="text-right">{{#if unit_amount}}{{unit_amount}}{{else}} {{/if}}</td>
|
||||||
|
<td class="text-right">{{#if discount_percentage}}{{discount_percentage}}{{else}} {{/if}}</td>
|
||||||
|
<td class="text-right">{{#if taxable_amount}}{{taxable_amount}}{{else}} {{/if}}</td>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{/each}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="resume" class="flex items-center justify-between pb-4 mb-4">
|
||||||
|
|
||||||
|
<div class="grow relative pt-10 self-start">
|
||||||
|
{{#if payment_method}}
|
||||||
|
<div class="">
|
||||||
|
<p class=" text-sm"><strong>Forma de pago:</strong> {{payment_method}}</p>
|
||||||
|
</div>
|
||||||
|
{{else}}
|
||||||
|
<!-- Empty payment method-->
|
||||||
|
{{/if}}
|
||||||
|
{{#if notes}}
|
||||||
|
<div class="pt-4">
|
||||||
|
<p class="text-sm"><strong>Notas:</strong> {{notes}} </p>
|
||||||
|
</div>
|
||||||
|
{{else}}
|
||||||
|
<!-- Empty notes-->
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="relative pt-10 grow">
|
||||||
|
<table class=" table-header min-w-full bg-transparent">
|
||||||
|
<tbody>
|
||||||
|
{{#if discount_percentage}}
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<td class="px-4 text-right">Importe neto</td>
|
||||||
|
<td class="w-5"> </td>
|
||||||
|
<td class="px-4 text-right">{{subtotal_amount}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<td class="px-4 text-right">Descuento {{discount_percentage}}</td>
|
||||||
|
<td class="w-5"> </td>
|
||||||
|
<td class="px-4 text-right">{{discount_amount.value}}</td>
|
||||||
|
</tr>
|
||||||
|
{{else}}
|
||||||
|
<!-- dto 0-->
|
||||||
|
{{/if}}
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<td class="px-4 text-right">Base imponible</td>
|
||||||
|
<td class="w-5"> </td>
|
||||||
|
<td class="px-4 text-right">{{taxable_amount}}</td>
|
||||||
|
</tr>
|
||||||
|
{{#each taxes}}
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<td class="px-4 text-right">{{tax_name}}</td>
|
||||||
|
<td class="w-5"> </td>
|
||||||
|
<td class="px-4 text-right">{{taxes_amount}}</td>
|
||||||
|
</tr>
|
||||||
|
{{/each}}
|
||||||
|
<tr class="">
|
||||||
|
<td></td>
|
||||||
|
<td class="px-4 text-right accent-color">
|
||||||
|
Total factura
|
||||||
|
</td>
|
||||||
|
<td class="w-5"> </td>
|
||||||
|
<td class="px-4 text-right accent-color">
|
||||||
|
{{total_amount}}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
|
||||||
|
<footer id="footer" class="mt-4">
|
||||||
|
<aside>
|
||||||
|
<p class="text-center">Insc. en el Reg. Merc. de Madrid, Tomo 31.839, Libro 0, Folio 191, Sección 8, Hoja M-572991
|
||||||
|
CIF: B86913910</p>
|
||||||
|
<p class="text-left" style="font-size: 6pt;">Información en protección de datos<br />De conformidad con lo
|
||||||
|
dispuesto en el RGPD y LOPDGDD,
|
||||||
|
informamos que los datos personales serán tratados por
|
||||||
|
ALISO DESIGN S.L para cumplir con la obligación tributaria de emitir facturas. Podrá solicitar más información,
|
||||||
|
y ejercer sus derechos escribiendo a info@acanainteriorismo.com o mediante correo postal a la dirección CALLE LA
|
||||||
|
FUNDICION 27 POL. IND. SANTA ANA (28522) RIVAS-VACIAMADRID, MADRID. Para el ejercicio de sus derechos, en caso
|
||||||
|
de que sea necesario, se le solicitará documento que acredite su identidad. Si siente vulnerados sus derechos
|
||||||
|
puede presentar una reclamación ante la AEPD, en su web: www.aepd.es.</p>
|
||||||
|
</aside>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@ -7,6 +7,8 @@ export interface VerifactuRecordProps {
|
|||||||
estado: VerifactuRecordEstado;
|
estado: VerifactuRecordEstado;
|
||||||
url: Maybe<URLAddress>;
|
url: Maybe<URLAddress>;
|
||||||
qrCode: Maybe<string>;
|
qrCode: Maybe<string>;
|
||||||
|
uuid: Maybe<string>;
|
||||||
|
operacion: Maybe<string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class VerifactuRecord extends DomainEntity<VerifactuRecordProps> {
|
export class VerifactuRecord extends DomainEntity<VerifactuRecordProps> {
|
||||||
@ -32,6 +34,14 @@ export class VerifactuRecord extends DomainEntity<VerifactuRecordProps> {
|
|||||||
return this.props.qrCode;
|
return this.props.qrCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get uuid(): Maybe<string> {
|
||||||
|
return this.props.uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
get operacion(): Maybe<string> {
|
||||||
|
return this.props.operacion;
|
||||||
|
}
|
||||||
|
|
||||||
getProps(): VerifactuRecordProps {
|
getProps(): VerifactuRecordProps {
|
||||||
return this.props;
|
return this.props;
|
||||||
}
|
}
|
||||||
@ -45,6 +55,8 @@ export class VerifactuRecord extends DomainEntity<VerifactuRecordProps> {
|
|||||||
status: this.estado.toString(),
|
status: this.estado.toString(),
|
||||||
url: toEmptyString(this.url, (value) => value.toString()),
|
url: toEmptyString(this.url, (value) => value.toString()),
|
||||||
qr_code: toEmptyString(this.qrCode, (value) => value.toString()),
|
qr_code: toEmptyString(this.qrCode, (value) => value.toString()),
|
||||||
|
uuid: toEmptyString(this.uuid, (value) => value.toString()),
|
||||||
|
operacion: toEmptyString(this.operacion, (value) => value.toString()),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +1,18 @@
|
|||||||
import type { UtcDate } from "@repo/rdx-ddd";
|
import { UniqueID, type UtcDate } from "@repo/rdx-ddd";
|
||||||
import { Maybe, Result } from "@repo/rdx-utils";
|
import { Maybe, Result } from "@repo/rdx-utils";
|
||||||
|
|
||||||
import { CustomerInvoice } from "../aggregates";
|
import { CustomerInvoice } from "../aggregates";
|
||||||
|
import { VerifactuRecord } from "../entities";
|
||||||
import { EntityIsNotProformaError, ProformaCannotBeConvertedToInvoiceError } from "../errors";
|
import { EntityIsNotProformaError, ProformaCannotBeConvertedToInvoiceError } from "../errors";
|
||||||
import {
|
import {
|
||||||
CustomerInvoiceIsProformaSpecification,
|
CustomerInvoiceIsProformaSpecification,
|
||||||
ProformaCanTranstionToIssuedSpecification,
|
ProformaCanTranstionToIssuedSpecification,
|
||||||
} from "../specs";
|
} from "../specs";
|
||||||
import { type CustomerInvoiceNumber, CustomerInvoiceStatus } from "../value-objects";
|
import {
|
||||||
|
type CustomerInvoiceNumber,
|
||||||
|
CustomerInvoiceStatus,
|
||||||
|
VerifactuRecordEstado,
|
||||||
|
} from "../value-objects";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Servicio de dominio que encapsula la lógica de emisión de factura definitiva desde una proforma.
|
* Servicio de dominio que encapsula la lógica de emisión de factura definitiva desde una proforma.
|
||||||
@ -43,6 +48,23 @@ export class IssueCustomerInvoiceDomainService {
|
|||||||
return Result.fail(new ProformaCannotBeConvertedToInvoiceError(proforma.id.toString()));
|
return Result.fail(new ProformaCannotBeConvertedToInvoiceError(proforma.id.toString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const verifactuRecordOrError = VerifactuRecord.create(
|
||||||
|
{
|
||||||
|
estado: VerifactuRecordEstado.createPendiente(),
|
||||||
|
qrCode: Maybe.none(),
|
||||||
|
url: Maybe.none(),
|
||||||
|
uuid: Maybe.none(),
|
||||||
|
operacion: Maybe.none(),
|
||||||
|
},
|
||||||
|
UniqueID.generateNewID()
|
||||||
|
);
|
||||||
|
|
||||||
|
if (verifactuRecordOrError.isFailure) {
|
||||||
|
return Result.fail(new ProformaCannotBeConvertedToInvoiceError(proforma.id.toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
const verifactuRecord = verifactuRecordOrError.data;
|
||||||
|
|
||||||
/** 3. Generar la nueva factura definitiva (inmutable) */
|
/** 3. Generar la nueva factura definitiva (inmutable) */
|
||||||
const proformaProps = proforma.getProps();
|
const proformaProps = proforma.getProps();
|
||||||
const newInvoiceOrError = CustomerInvoice.create({
|
const newInvoiceOrError = CustomerInvoice.create({
|
||||||
@ -53,6 +75,7 @@ export class IssueCustomerInvoiceDomainService {
|
|||||||
invoiceNumber: issueNumber,
|
invoiceNumber: issueNumber,
|
||||||
invoiceDate: issueDate,
|
invoiceDate: issueDate,
|
||||||
description: proformaProps.description.isNone() ? Maybe.some(".") : proformaProps.description,
|
description: proformaProps.description.isNone() ? Maybe.some(".") : proformaProps.description,
|
||||||
|
verifactu: Maybe.some(verifactuRecord),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (newInvoiceOrError.isFailure) {
|
if (newInvoiceOrError.isFailure) {
|
||||||
|
|||||||
@ -1,25 +1,34 @@
|
|||||||
import { ISequelizeDomainMapper, MapperParamsType, SequelizeDomainMapper } from "@erp/core/api";
|
|
||||||
import {
|
import {
|
||||||
|
type ISequelizeDomainMapper,
|
||||||
|
type MapperParamsType,
|
||||||
|
SequelizeDomainMapper,
|
||||||
|
} from "@erp/core/api";
|
||||||
|
import {
|
||||||
|
UniqueID,
|
||||||
|
ValidationErrorCollection,
|
||||||
|
type ValidationErrorDetail,
|
||||||
extractOrPushError,
|
extractOrPushError,
|
||||||
maybeFromNullableVO,
|
maybeFromNullableVO,
|
||||||
toNullable,
|
toNullable,
|
||||||
UniqueID,
|
|
||||||
ValidationErrorCollection,
|
|
||||||
ValidationErrorDetail,
|
|
||||||
} from "@repo/rdx-ddd";
|
} from "@repo/rdx-ddd";
|
||||||
import { Result } from "@repo/rdx-utils";
|
import { Result } from "@repo/rdx-utils";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CustomerInvoice,
|
type CustomerInvoice,
|
||||||
CustomerInvoiceItem,
|
CustomerInvoiceItem,
|
||||||
CustomerInvoiceItemDescription,
|
CustomerInvoiceItemDescription,
|
||||||
CustomerInvoiceItemProps,
|
type CustomerInvoiceItemProps,
|
||||||
CustomerInvoiceProps,
|
type CustomerInvoiceProps,
|
||||||
ItemAmount,
|
ItemAmount,
|
||||||
ItemDiscount,
|
ItemDiscount,
|
||||||
ItemQuantity,
|
ItemQuantity,
|
||||||
ItemTaxes,
|
ItemTaxes,
|
||||||
} from "../../../domain";
|
} from "../../../domain";
|
||||||
import { CustomerInvoiceItemCreationAttributes, CustomerInvoiceItemModel } from "../../sequelize";
|
import type {
|
||||||
|
CustomerInvoiceItemCreationAttributes,
|
||||||
|
CustomerInvoiceItemModel,
|
||||||
|
} from "../../sequelize";
|
||||||
|
|
||||||
import { ItemTaxesDomainMapper } from "./item-taxes.mapper";
|
import { ItemTaxesDomainMapper } from "./item-taxes.mapper";
|
||||||
|
|
||||||
export interface ICustomerInvoiceItemDomainMapper
|
export interface ICustomerInvoiceItemDomainMapper
|
||||||
|
|||||||
@ -1,31 +1,38 @@
|
|||||||
import { ISequelizeDomainMapper, MapperParamsType, SequelizeDomainMapper } from "@erp/core/api";
|
import {
|
||||||
|
type ISequelizeDomainMapper,
|
||||||
|
type MapperParamsType,
|
||||||
|
SequelizeDomainMapper,
|
||||||
|
} from "@erp/core/api";
|
||||||
import {
|
import {
|
||||||
CurrencyCode,
|
CurrencyCode,
|
||||||
extractOrPushError,
|
|
||||||
LanguageCode,
|
LanguageCode,
|
||||||
maybeFromNullableVO,
|
|
||||||
Percentage,
|
Percentage,
|
||||||
TextValue,
|
TextValue,
|
||||||
toNullable,
|
|
||||||
UniqueID,
|
UniqueID,
|
||||||
UtcDate,
|
UtcDate,
|
||||||
ValidationErrorCollection,
|
ValidationErrorCollection,
|
||||||
ValidationErrorDetail,
|
type ValidationErrorDetail,
|
||||||
|
extractOrPushError,
|
||||||
|
maybeFromNullableVO,
|
||||||
|
toNullable,
|
||||||
} from "@repo/rdx-ddd";
|
} from "@repo/rdx-ddd";
|
||||||
import { Collection, isNullishOrEmpty, Maybe, Result } from "@repo/rdx-utils";
|
import { Collection, Maybe, Result, isNullishOrEmpty } from "@repo/rdx-utils";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CustomerInvoice,
|
CustomerInvoice,
|
||||||
CustomerInvoiceItems,
|
CustomerInvoiceItems,
|
||||||
CustomerInvoiceNumber,
|
CustomerInvoiceNumber,
|
||||||
CustomerInvoiceProps,
|
type CustomerInvoiceProps,
|
||||||
CustomerInvoiceSerie,
|
CustomerInvoiceSerie,
|
||||||
CustomerInvoiceStatus,
|
CustomerInvoiceStatus,
|
||||||
InvoicePaymentMethod,
|
InvoicePaymentMethod,
|
||||||
} from "../../../domain";
|
} from "../../../domain";
|
||||||
import { CustomerInvoiceCreationAttributes, CustomerInvoiceModel } from "../../sequelize";
|
import type { CustomerInvoiceCreationAttributes, CustomerInvoiceModel } from "../../sequelize";
|
||||||
|
|
||||||
import { CustomerInvoiceItemDomainMapper } from "./customer-invoice-item.mapper";
|
import { CustomerInvoiceItemDomainMapper } from "./customer-invoice-item.mapper";
|
||||||
import { InvoiceRecipientDomainMapper } from "./invoice-recipient.mapper";
|
import { InvoiceRecipientDomainMapper } from "./invoice-recipient.mapper";
|
||||||
import { TaxesDomainMapper } from "./invoice-taxes.mapper";
|
import { TaxesDomainMapper } from "./invoice-taxes.mapper";
|
||||||
|
import { CustomerInvoiceVerifactuDomainMapper } from "./invoice-verifactu.mapper";
|
||||||
|
|
||||||
export interface ICustomerInvoiceDomainMapper
|
export interface ICustomerInvoiceDomainMapper
|
||||||
extends ISequelizeDomainMapper<
|
extends ISequelizeDomainMapper<
|
||||||
@ -45,6 +52,7 @@ export class CustomerInvoiceDomainMapper
|
|||||||
private _itemsMapper: CustomerInvoiceItemDomainMapper;
|
private _itemsMapper: CustomerInvoiceItemDomainMapper;
|
||||||
private _recipientMapper: InvoiceRecipientDomainMapper;
|
private _recipientMapper: InvoiceRecipientDomainMapper;
|
||||||
private _taxesMapper: TaxesDomainMapper;
|
private _taxesMapper: TaxesDomainMapper;
|
||||||
|
private _verifactuMapper: CustomerInvoiceVerifactuDomainMapper;
|
||||||
|
|
||||||
constructor(params: MapperParamsType) {
|
constructor(params: MapperParamsType) {
|
||||||
super();
|
super();
|
||||||
@ -52,6 +60,7 @@ export class CustomerInvoiceDomainMapper
|
|||||||
this._itemsMapper = new CustomerInvoiceItemDomainMapper(params); // Instanciar el mapper de items
|
this._itemsMapper = new CustomerInvoiceItemDomainMapper(params); // Instanciar el mapper de items
|
||||||
this._recipientMapper = new InvoiceRecipientDomainMapper();
|
this._recipientMapper = new InvoiceRecipientDomainMapper();
|
||||||
this._taxesMapper = new TaxesDomainMapper(params);
|
this._taxesMapper = new TaxesDomainMapper(params);
|
||||||
|
this._verifactuMapper = new CustomerInvoiceVerifactuDomainMapper();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _mapAttributesToDomain(source: CustomerInvoiceModel, params?: MapperParamsType) {
|
private _mapAttributesToDomain(source: CustomerInvoiceModel, params?: MapperParamsType) {
|
||||||
@ -219,7 +228,22 @@ export class CustomerInvoiceDomainMapper
|
|||||||
});
|
});
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
// 3) Items (colección)
|
// 3) Verifactu (snapshot en la factura o include)
|
||||||
|
const verifactuResult = this._verifactuMapper.mapToDomain(source.verifactu, {
|
||||||
|
errors,
|
||||||
|
attributes,
|
||||||
|
...params,
|
||||||
|
});
|
||||||
|
|
||||||
|
/*if (verifactuResult.isFailure) {
|
||||||
|
errors.push({
|
||||||
|
path: "verifactu",
|
||||||
|
|
||||||
|
message: verifactuResult.error.message,
|
||||||
|
});
|
||||||
|
}*/
|
||||||
|
|
||||||
|
// 4) Items (colección)
|
||||||
const itemsResults = this._itemsMapper.mapToDomainCollection(
|
const itemsResults = this._itemsMapper.mapToDomainCollection(
|
||||||
source.items,
|
source.items,
|
||||||
source.items.length,
|
source.items.length,
|
||||||
@ -249,6 +273,7 @@ export class CustomerInvoiceDomainMapper
|
|||||||
|
|
||||||
// 6) Construcción del agregado (Dominio)
|
// 6) Construcción del agregado (Dominio)
|
||||||
|
|
||||||
|
const verifactu = verifactuResult.data;
|
||||||
const recipient = recipientResult.data;
|
const recipient = recipientResult.data;
|
||||||
|
|
||||||
const items = CustomerInvoiceItems.create({
|
const items = CustomerInvoiceItems.create({
|
||||||
@ -283,6 +308,7 @@ export class CustomerInvoiceDomainMapper
|
|||||||
paymentMethod: attributes.paymentMethod!,
|
paymentMethod: attributes.paymentMethod!,
|
||||||
|
|
||||||
items,
|
items,
|
||||||
|
verifactu,
|
||||||
};
|
};
|
||||||
|
|
||||||
const createResult = CustomerInvoice.create(invoiceProps, attributes.invoiceId);
|
const createResult = CustomerInvoice.create(invoiceProps, attributes.invoiceId);
|
||||||
@ -342,7 +368,14 @@ export class CustomerInvoiceDomainMapper
|
|||||||
...params,
|
...params,
|
||||||
});
|
});
|
||||||
|
|
||||||
// 4) Si hubo errores de mapeo, devolvemos colección de validación
|
// 4) Verifactu
|
||||||
|
const verifactuResult = this._verifactuMapper.mapToPersistence(source.verifactu, {
|
||||||
|
errors,
|
||||||
|
parent: source,
|
||||||
|
...params,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 5) Si hubo errores de mapeo, devolvemos colección de validación
|
||||||
if (errors.length > 0) {
|
if (errors.length > 0) {
|
||||||
return Result.fail(
|
return Result.fail(
|
||||||
new ValidationErrorCollection("Customer invoice mapping to persistence failed", errors)
|
new ValidationErrorCollection("Customer invoice mapping to persistence failed", errors)
|
||||||
@ -351,6 +384,7 @@ export class CustomerInvoiceDomainMapper
|
|||||||
|
|
||||||
const items = itemsResult.data;
|
const items = itemsResult.data;
|
||||||
const taxes = taxesResult.data;
|
const taxes = taxesResult.data;
|
||||||
|
const verifactu = verifactuResult.data;
|
||||||
|
|
||||||
const allAmounts = source.getAllAmounts(); // Da los totales ya calculados
|
const allAmounts = source.getAllAmounts(); // Da los totales ya calculados
|
||||||
|
|
||||||
@ -404,6 +438,7 @@ export class CustomerInvoiceDomainMapper
|
|||||||
|
|
||||||
taxes,
|
taxes,
|
||||||
items,
|
items,
|
||||||
|
verifactu,
|
||||||
};
|
};
|
||||||
|
|
||||||
return Result.ok<CustomerInvoiceCreationAttributes>(
|
return Result.ok<CustomerInvoiceCreationAttributes>(
|
||||||
|
|||||||
@ -1,21 +1,22 @@
|
|||||||
import { MapperParamsType } from "@erp/core/api";
|
import type { MapperParamsType } from "@erp/core/api";
|
||||||
import {
|
import {
|
||||||
City,
|
City,
|
||||||
Country,
|
Country,
|
||||||
extractOrPushError,
|
|
||||||
maybeFromNullableVO,
|
|
||||||
Name,
|
Name,
|
||||||
PostalCode,
|
PostalCode,
|
||||||
Province,
|
Province,
|
||||||
Street,
|
Street,
|
||||||
TINNumber,
|
TINNumber,
|
||||||
toNullable,
|
|
||||||
ValidationErrorCollection,
|
ValidationErrorCollection,
|
||||||
ValidationErrorDetail,
|
type ValidationErrorDetail,
|
||||||
|
extractOrPushError,
|
||||||
|
maybeFromNullableVO,
|
||||||
|
toNullable,
|
||||||
} from "@repo/rdx-ddd";
|
} from "@repo/rdx-ddd";
|
||||||
import { Maybe, Result } from "@repo/rdx-utils";
|
import { Maybe, Result } from "@repo/rdx-utils";
|
||||||
import { CustomerInvoice, CustomerInvoiceProps, InvoiceRecipient } from "../../../domain";
|
|
||||||
import { CustomerInvoiceModel } from "../../sequelize";
|
import { type CustomerInvoice, type CustomerInvoiceProps, InvoiceRecipient } from "../../../domain";
|
||||||
|
import type { CustomerInvoiceModel } from "../../sequelize";
|
||||||
|
|
||||||
export class InvoiceRecipientDomainMapper {
|
export class InvoiceRecipientDomainMapper {
|
||||||
public mapToDomain(
|
public mapToDomain(
|
||||||
@ -133,7 +134,7 @@ export class InvoiceRecipientDomainMapper {
|
|||||||
const { isProforma, hasRecipient } = parent;
|
const { isProforma, hasRecipient } = parent;
|
||||||
|
|
||||||
// Validación: facturas emitidas deben tener destinatario.
|
// Validación: facturas emitidas deben tener destinatario.
|
||||||
if (!isProforma && !hasRecipient) {
|
if (!(isProforma || hasRecipient)) {
|
||||||
errors.push({
|
errors.push({
|
||||||
path: "recipient",
|
path: "recipient",
|
||||||
message: "[CustomerInvoiceDomainMapper] Issued customer invoice without recipient data",
|
message: "[CustomerInvoiceDomainMapper] Issued customer invoice without recipient data",
|
||||||
|
|||||||
@ -0,0 +1,142 @@
|
|||||||
|
import type { MapperParamsType } from "@erp/core/api";
|
||||||
|
import { type ISequelizeDomainMapper, SequelizeDomainMapper } from "@erp/core/api";
|
||||||
|
import {
|
||||||
|
URLAddress,
|
||||||
|
UniqueID,
|
||||||
|
ValidationErrorCollection,
|
||||||
|
type ValidationErrorDetail,
|
||||||
|
extractOrPushError,
|
||||||
|
maybeFromNullableVO,
|
||||||
|
toNullable,
|
||||||
|
} from "@repo/rdx-ddd";
|
||||||
|
import { Maybe, Result } from "@repo/rdx-utils";
|
||||||
|
|
||||||
|
import {
|
||||||
|
type CustomerInvoice,
|
||||||
|
type CustomerInvoiceProps,
|
||||||
|
VerifactuRecord,
|
||||||
|
VerifactuRecordEstado,
|
||||||
|
} from "../../../domain";
|
||||||
|
import type { VerifactuRecordCreationAttributes, VerifactuRecordModel } from "../../sequelize";
|
||||||
|
|
||||||
|
export interface ICustomerInvoiceVerifactuDomainMapper
|
||||||
|
extends ISequelizeDomainMapper<
|
||||||
|
VerifactuRecordModel,
|
||||||
|
VerifactuRecordCreationAttributes,
|
||||||
|
Maybe<VerifactuRecord>
|
||||||
|
> {}
|
||||||
|
|
||||||
|
export class CustomerInvoiceVerifactuDomainMapper
|
||||||
|
extends SequelizeDomainMapper<
|
||||||
|
VerifactuRecordModel,
|
||||||
|
VerifactuRecordCreationAttributes,
|
||||||
|
Maybe<VerifactuRecord>
|
||||||
|
>
|
||||||
|
implements ICustomerInvoiceVerifactuDomainMapper
|
||||||
|
{
|
||||||
|
public mapToDomain(
|
||||||
|
source: VerifactuRecordModel,
|
||||||
|
params?: MapperParamsType
|
||||||
|
): Result<Maybe<VerifactuRecord>, Error> {
|
||||||
|
const { errors, attributes } = params as {
|
||||||
|
errors: ValidationErrorDetail[];
|
||||||
|
attributes: Partial<CustomerInvoiceProps>;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!source) {
|
||||||
|
return Result.ok(Maybe.none());
|
||||||
|
}
|
||||||
|
|
||||||
|
const recordId = extractOrPushError(UniqueID.create(source.id), "id", errors);
|
||||||
|
const estado = extractOrPushError(
|
||||||
|
VerifactuRecordEstado.create(source.estado),
|
||||||
|
"estado",
|
||||||
|
errors
|
||||||
|
);
|
||||||
|
|
||||||
|
const qr = extractOrPushError(
|
||||||
|
maybeFromNullableVO(source.qr, (value) => Result.ok(String(value))),
|
||||||
|
"qr",
|
||||||
|
errors
|
||||||
|
);
|
||||||
|
|
||||||
|
const url = extractOrPushError(
|
||||||
|
maybeFromNullableVO(source.url, (value) => URLAddress.create(value)),
|
||||||
|
"url",
|
||||||
|
errors
|
||||||
|
);
|
||||||
|
|
||||||
|
const uuid = extractOrPushError(
|
||||||
|
maybeFromNullableVO(source.uuid, (value) => Result.ok(String(value))),
|
||||||
|
"uuid",
|
||||||
|
errors
|
||||||
|
);
|
||||||
|
|
||||||
|
const operacion = extractOrPushError(
|
||||||
|
maybeFromNullableVO(source.operacion, (value) => Result.ok(String(value))),
|
||||||
|
"operacion",
|
||||||
|
errors
|
||||||
|
);
|
||||||
|
|
||||||
|
if (errors.length > 0) {
|
||||||
|
return Result.fail(
|
||||||
|
new ValidationErrorCollection("Verifactu record mapping failed [mapToDTO]", errors)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const createResult = VerifactuRecord.create(
|
||||||
|
{
|
||||||
|
estado: estado!,
|
||||||
|
qrCode: qr!,
|
||||||
|
url: url!,
|
||||||
|
uuid: uuid!,
|
||||||
|
operacion: operacion!,
|
||||||
|
},
|
||||||
|
recordId!
|
||||||
|
);
|
||||||
|
|
||||||
|
if (createResult.isFailure) {
|
||||||
|
return Result.fail(
|
||||||
|
new ValidationErrorCollection("Invoice verifactu entity creation failed", [
|
||||||
|
{ path: "verifactu", message: createResult.error.message },
|
||||||
|
])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.ok(Maybe.some(createResult.data));
|
||||||
|
}
|
||||||
|
|
||||||
|
mapToPersistence(
|
||||||
|
source: Maybe<VerifactuRecord>,
|
||||||
|
params?: MapperParamsType
|
||||||
|
): Result<VerifactuRecordCreationAttributes, Error> {
|
||||||
|
const { errors, parent } = params as {
|
||||||
|
parent: CustomerInvoice;
|
||||||
|
errors: ValidationErrorDetail[];
|
||||||
|
};
|
||||||
|
|
||||||
|
if (source.isNone()) {
|
||||||
|
return Result.ok({
|
||||||
|
id: UniqueID.generateNewID().toPrimitive(),
|
||||||
|
invoice_id: parent.id.toPrimitive(),
|
||||||
|
estado: VerifactuRecordEstado.createPendiente().toPrimitive(),
|
||||||
|
qr: null,
|
||||||
|
url: null,
|
||||||
|
uuid: null,
|
||||||
|
operacion: null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const verifactu = source.unwrap();
|
||||||
|
|
||||||
|
return Result.ok({
|
||||||
|
id: verifactu.id.toPrimitive(),
|
||||||
|
invoice_id: parent.id.toPrimitive(),
|
||||||
|
estado: verifactu.estado.toPrimitive(),
|
||||||
|
qr: toNullable(verifactu.qrCode, (v) => v),
|
||||||
|
url: toNullable(verifactu.url, (v) => v.toPrimitive()),
|
||||||
|
uuid: toNullable(verifactu.uuid, (v) => v),
|
||||||
|
operacion: toNullable(verifactu.operacion, (v) => v),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -46,6 +46,18 @@ export class VerifactuRecordListMapper
|
|||||||
errors
|
errors
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const uuid = extractOrPushError(
|
||||||
|
maybeFromNullableVO(raw.uuid, (value) => Result.ok(String(value))),
|
||||||
|
"uuid",
|
||||||
|
errors
|
||||||
|
);
|
||||||
|
|
||||||
|
const operacion = extractOrPushError(
|
||||||
|
maybeFromNullableVO(raw.operacion, (value) => Result.ok(String(value))),
|
||||||
|
"operacion",
|
||||||
|
errors
|
||||||
|
);
|
||||||
|
|
||||||
if (errors.length > 0) {
|
if (errors.length > 0) {
|
||||||
return Result.fail(
|
return Result.fail(
|
||||||
new ValidationErrorCollection("Verifactu record mapping failed [mapToDTO]", errors)
|
new ValidationErrorCollection("Verifactu record mapping failed [mapToDTO]", errors)
|
||||||
@ -57,6 +69,8 @@ export class VerifactuRecordListMapper
|
|||||||
estado: estado!,
|
estado: estado!,
|
||||||
qrCode: qr!,
|
qrCode: qr!,
|
||||||
url: url!,
|
url: url!,
|
||||||
|
uuid: uuid!,
|
||||||
|
operacion: operacion!,
|
||||||
},
|
},
|
||||||
recordId!
|
recordId!
|
||||||
);
|
);
|
||||||
|
|||||||
@ -17,14 +17,18 @@ import type {
|
|||||||
CustomerInvoiceTaxCreationAttributes,
|
CustomerInvoiceTaxCreationAttributes,
|
||||||
CustomerInvoiceTaxModel,
|
CustomerInvoiceTaxModel,
|
||||||
} from "./customer-invoice-tax.model";
|
} from "./customer-invoice-tax.model";
|
||||||
import type { VerifactuRecordModel } from "./verifactu-record.model";
|
import type {
|
||||||
|
VerifactuRecordCreationAttributes,
|
||||||
|
VerifactuRecordModel,
|
||||||
|
} from "./verifactu-record.model";
|
||||||
|
|
||||||
export type CustomerInvoiceCreationAttributes = InferCreationAttributes<
|
export type CustomerInvoiceCreationAttributes = InferCreationAttributes<
|
||||||
CustomerInvoiceModel,
|
CustomerInvoiceModel,
|
||||||
{ omit: "items" | "taxes" | "current_customer" }
|
{ omit: "items" | "taxes" | "current_customer" | "verifactu" }
|
||||||
> & {
|
> & {
|
||||||
items?: CustomerInvoiceItemCreationAttributes[];
|
items?: CustomerInvoiceItemCreationAttributes[];
|
||||||
taxes?: CustomerInvoiceTaxCreationAttributes[];
|
taxes?: CustomerInvoiceTaxCreationAttributes[];
|
||||||
|
verifactu?: VerifactuRecordCreationAttributes;
|
||||||
};
|
};
|
||||||
|
|
||||||
export class CustomerInvoiceModel extends Model<
|
export class CustomerInvoiceModel extends Model<
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
|
type CreationOptional,
|
||||||
DataTypes,
|
DataTypes,
|
||||||
type InferAttributes,
|
type InferAttributes,
|
||||||
type InferCreationAttributes,
|
type InferCreationAttributes,
|
||||||
@ -6,7 +7,10 @@ import {
|
|||||||
type Sequelize,
|
type Sequelize,
|
||||||
} from "sequelize";
|
} from "sequelize";
|
||||||
|
|
||||||
export type VerifactuRecordCreationAttributes = InferCreationAttributes<VerifactuRecordModel>;
|
export type VerifactuRecordCreationAttributes = InferCreationAttributes<
|
||||||
|
VerifactuRecordModel,
|
||||||
|
{ omit: "url" | "qr" | "uuid" | "operacion" }
|
||||||
|
>;
|
||||||
|
|
||||||
export class VerifactuRecordModel extends Model<
|
export class VerifactuRecordModel extends Model<
|
||||||
InferAttributes<VerifactuRecordModel>,
|
InferAttributes<VerifactuRecordModel>,
|
||||||
@ -14,13 +18,12 @@ export class VerifactuRecordModel extends Model<
|
|||||||
> {
|
> {
|
||||||
declare id: string;
|
declare id: string;
|
||||||
declare invoice_id: string;
|
declare invoice_id: string;
|
||||||
|
|
||||||
declare estado: string;
|
declare estado: string;
|
||||||
declare url: string;
|
|
||||||
declare qr: Blob;
|
|
||||||
|
|
||||||
declare uuid: string;
|
declare url: CreationOptional<string>;
|
||||||
declare operacion: string;
|
declare qr: CreationOptional<Blob>;
|
||||||
|
declare uuid: CreationOptional<string>;
|
||||||
|
declare operacion: CreationOptional<string>;
|
||||||
|
|
||||||
static associate(database: Sequelize) {
|
static associate(database: Sequelize) {
|
||||||
const models = database.models;
|
const models = database.models;
|
||||||
@ -64,29 +67,31 @@ export default (database: Sequelize) => {
|
|||||||
|
|
||||||
estado: {
|
estado: {
|
||||||
type: new DataTypes.TEXT(),
|
type: new DataTypes.TEXT(),
|
||||||
allowNull: true,
|
allowNull: false,
|
||||||
defaultValue: null,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
url: {
|
url: {
|
||||||
type: new DataTypes.TEXT(),
|
type: new DataTypes.TEXT(),
|
||||||
allowNull: false,
|
allowNull: false,
|
||||||
|
defaultValue: "",
|
||||||
},
|
},
|
||||||
|
|
||||||
qr: {
|
qr: {
|
||||||
type: new DataTypes.BLOB(),
|
type: new DataTypes.BLOB(),
|
||||||
allowNull: false,
|
allowNull: false,
|
||||||
|
defaultValue: "",
|
||||||
},
|
},
|
||||||
|
|
||||||
uuid: {
|
uuid: {
|
||||||
type: DataTypes.STRING,
|
type: DataTypes.STRING,
|
||||||
allowNull: false,
|
allowNull: false,
|
||||||
|
defaultValue: "",
|
||||||
},
|
},
|
||||||
|
|
||||||
operacion: {
|
operacion: {
|
||||||
type: DataTypes.STRING,
|
type: DataTypes.STRING,
|
||||||
allowNull: true,
|
allowNull: false,
|
||||||
defaultValue: null,
|
defaultValue: "",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@ -28,11 +28,11 @@
|
|||||||
"proformas": {
|
"proformas": {
|
||||||
"status": {
|
"status": {
|
||||||
"all": "Todas",
|
"all": "Todas",
|
||||||
"draft": "Borradores",
|
"draft": "Borrador",
|
||||||
"sent": "Enviadas",
|
"sent": "Enviada",
|
||||||
"approved": "Aprovadas",
|
"approved": "Aprobada",
|
||||||
"rejected": "Rechazadas",
|
"rejected": "Rechazada",
|
||||||
"issued": "Emitidas"
|
"issued": "Emitida"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"issued_invoices": {
|
"issued_invoices": {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user