feat(web): copiar de / a entre documentos de ventas y compras
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
cafe4bc035
commit
29e80181fe
8 files changed
+383
-6
No files matched your search
@@ -40,9 +40,9 @@ describe('DocumentFormPage', () => {
|
||||
await new Promise((r) => setTimeout(r));
|
||||
fixture.detectChanges();
|
||||
};
|
||||
const buttonText = () => [...el().querySelectorAll('.actions .btn')].map((b) => b.textContent?.trim());
|
||||
const buttonText = () => [...el().querySelectorAll('.actions .start .btn')].map((b) => b.textContent?.trim());
|
||||
|
||||
async function open(docEntry: string | null, doc?: Record<string, unknown>) {
|
||||
async function open(docEntry: string | null, doc?: Record<string, unknown>, copyFrom?: unknown, afterCreate?: () => void) {
|
||||
const params = docEntry ? { docEntry } : {};
|
||||
const route = {
|
||||
snapshot: { data: { screen, title: 'Órdenes de venta' }, paramMap: convertToParamMap(params) },
|
||||
@@ -53,11 +53,14 @@ describe('DocumentFormPage', () => {
|
||||
providers: [provideRouter([]), provideHttpClient(), provideHttpClientTesting(), { provide: ActivatedRoute, useValue: route }],
|
||||
}).compileComponents();
|
||||
ctrl = TestBed.inject(HttpTestingController);
|
||||
navigate = vi.spyOn(TestBed.inject(Router), 'navigate').mockResolvedValue(true);
|
||||
const router = TestBed.inject(Router);
|
||||
navigate = vi.spyOn(router, 'navigate').mockResolvedValue(true);
|
||||
if (copyFrom) vi.spyOn(router, 'currentNavigation').mockReturnValue({ extras: { state: { copyFrom } } } as never);
|
||||
fixture = TestBed.createComponent(DocumentFormPage);
|
||||
await tick();
|
||||
ctrl.expectOne('/api/sl/SalesTaxCodes?$select=Code,Rate&$top=500').flush({ value: [{ Code: 'IVA', Rate: 19 }] });
|
||||
if (docEntry) ctrl.expectOne(`/api/sl/Orders/${docEntry}`).flush(doc ?? {});
|
||||
afterCreate?.();
|
||||
await tick();
|
||||
}
|
||||
|
||||
@@ -218,4 +221,97 @@ describe('DocumentFormPage', () => {
|
||||
expect(navigate).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('copiar a', () => {
|
||||
const items = () => [...el().querySelectorAll<HTMLButtonElement>('sbo-menu-button')].map((m) => m.textContent?.trim());
|
||||
|
||||
it('un documento abierto y guardado ofrece Copiar a con entrega y factura', async () => {
|
||||
await open('41', order());
|
||||
expect(items().join(' ')).toContain('Copiar a');
|
||||
const ids = page().copyToItems().map((i: { id: string; disabled: boolean }) => [i.id, i.disabled]);
|
||||
expect(ids).toEqual([['/ventas/entrega', false], ['/ventas/facturas', false]]);
|
||||
});
|
||||
|
||||
it('abre el documento nuevo de destino llevando el origen', async () => {
|
||||
await open('41', order());
|
||||
const nav = vi.spyOn(TestBed.inject(Router), 'navigateByUrl').mockResolvedValue(true);
|
||||
page().copyTo('/ventas/entrega');
|
||||
expect(nav).toHaveBeenCalledWith('/ventas/entrega/nuevo', { state: { copyFrom: { path: '/ventas/ordenes', docEntry: 41 } } });
|
||||
});
|
||||
|
||||
it('con cambios sin guardar o cerrado no se puede copiar', async () => {
|
||||
await open('41', order());
|
||||
page().setHeader('Comments', 'x');
|
||||
await tick();
|
||||
expect(page().copyToItems().every((i: { disabled: boolean }) => i.disabled)).toBe(true);
|
||||
TestBed.resetTestingModule();
|
||||
await open('41', order({ DocumentStatus: 'bost_Close' }));
|
||||
expect(page().copyToItems().every((i: { disabled: boolean }) => i.disabled)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('copiar de / a: documento nuevo con origen', () => {
|
||||
const source = {
|
||||
DocEntry: 7, DocNum: 1007, CardCode: 'C1', CardName: 'ACME', NumAtCard: 'REF', DiscountPercent: 0,
|
||||
DocumentLines: [
|
||||
{ LineNum: 0, ItemCode: 'A1', ItemDescription: 'Pieza', Quantity: 4, RemainingOpenQuantity: 3, WarehouseCode: '01', Price: 100, DiscountPercent: 0, TaxCode: 'IVA', LineStatus: 'bost_Open' },
|
||||
{ LineNum: 1, ItemCode: 'B2', Quantity: 1, RemainingOpenQuantity: 0, LineStatus: 'bost_Close' },
|
||||
],
|
||||
};
|
||||
|
||||
it('al llegar con un origen lo carga: socio, líneas abiertas y referencia al origen', async () => {
|
||||
await open(null, undefined, { path: '/ventas/ordenes', docEntry: 7 }, () => undefined);
|
||||
ctrl.expectOne('/api/sl/Orders/7').flush(source);
|
||||
await tick();
|
||||
expect(page().header().CardCode).toBe('C1');
|
||||
expect(page().partnerName()).toBe('ACME');
|
||||
expect(page().lines()).toHaveLength(1);
|
||||
expect(page().lines()[0]).toMatchObject({ ItemCode: 'A1', Quantity: 3, BaseType: 17, BaseEntry: 7, BaseLine: 0 });
|
||||
expect(el().querySelector('.status')?.textContent).toContain('Copiado de órdenes de venta 1007');
|
||||
expect(el().querySelector('.status')?.textContent).toContain('1 línea(s) cerradas');
|
||||
});
|
||||
|
||||
it('al crear envía las referencias al origen en las líneas', async () => {
|
||||
await open(null, undefined, { path: '/ventas/ordenes', docEntry: 7 });
|
||||
ctrl.expectOne('/api/sl/Orders/7').flush(source);
|
||||
await tick();
|
||||
(el().querySelector('.actions .btn.main') as HTMLElement).click();
|
||||
await tick();
|
||||
const req = ctrl.expectOne((r) => r.method === 'POST');
|
||||
expect(req.request.body.DocumentLines).toEqual([
|
||||
{ ItemCode: 'A1', ItemDescription: 'Pieza', Quantity: 3, WarehouseCode: '01', Price: 100, DiscountPercent: 0, TaxCode: 'IVA', BaseType: 17, BaseEntry: 7, BaseLine: 0 },
|
||||
]);
|
||||
req.flush({ DocEntry: 99, DocNum: 1099 });
|
||||
});
|
||||
|
||||
it('un origen sin líneas abiertas no copia nada y avisa', async () => {
|
||||
await open(null, undefined, { path: '/ventas/ordenes', docEntry: 7 });
|
||||
ctrl.expectOne('/api/sl/Orders/7').flush({ ...source, DocumentLines: [source.DocumentLines[1]] });
|
||||
await tick();
|
||||
expect(page().lines()).toHaveLength(0);
|
||||
expect(el().querySelector('.status')?.textContent).toContain('no tiene líneas abiertas');
|
||||
});
|
||||
|
||||
it('Copiar de: no mezcla socios distintos', async () => {
|
||||
await open(null);
|
||||
page().setHeader('CardCode', 'C9');
|
||||
await tick();
|
||||
page().copyListFor.set('/ventas/ordenes');
|
||||
const done = page().copyPicked([{ DocEntry: 7 }]);
|
||||
await Promise.resolve();
|
||||
ctrl.expectOne('/api/sl/Orders/7').flush(source);
|
||||
await done;
|
||||
await tick();
|
||||
expect(page().lines()).toHaveLength(0);
|
||||
expect(el().querySelector('.status')?.textContent).toContain('es de C1');
|
||||
});
|
||||
|
||||
it('Copiar de: ofrece solo los orígenes del flujo y lista los abiertos del socio', async () => {
|
||||
await open(null);
|
||||
expect(page().copyFromItems().map((i: { id: string }) => i.id)).toEqual(['/ventas/ofertas']);
|
||||
expect(page().copyListFilter()).toBe("DocumentStatus eq 'bost_Open' and Cancelled eq 'tNO'");
|
||||
page().setHeader('CardCode', "C'1");
|
||||
expect(page().copyListFilter()).toContain("CardCode eq 'C''1'");
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user