feat(checkout): update checkout process to handle multiple direct items

This commit is contained in:
2026-08-12 14:46:15 -03:00
parent 9f8e42c30b
commit f7a8b42892
7 changed files with 85 additions and 60 deletions

View File

@@ -0,0 +1,41 @@
import { provideHttpClient } from '@angular/common/http';
import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing';
import { TestBed } from '@angular/core/testing';
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
import { environment } from '../../../environments/environment';
import { CheckoutService } from './checkout.service';
describe('CheckoutService', () => {
let service: CheckoutService;
let httpMock: HttpTestingController;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [CheckoutService, provideHttpClient(), provideHttpClientTesting()],
});
service = TestBed.inject(CheckoutService);
httpMock = TestBed.inject(HttpTestingController);
});
afterEach(() => httpMock.verify());
it('starts one direct checkout with all selected variants', async () => {
const payload = {
direct_items: [
{ catalog_item_id: 10, variant_id: 101, cantidad: 1 },
{ catalog_item_id: 10, variant_id: 102, cantidad: 1 },
],
};
const purchasePromise = service.startCheckout('desfile', payload);
const request = httpMock.expectOne(`${environment.url}tenants/desfile/compras/start-checkout`);
expect(request.request.method).toBe('POST');
expect(request.request.body).toEqual(payload);
request.flush({ data: { id: 55 } });
await expect(purchasePromise).resolves.toMatchObject({ id: 55 });
});
});

View File

@@ -21,9 +21,6 @@ export type StartCheckoutPayload =
| {
cart_id: number;
}
| {
direct_item: DirectCheckoutItem;
}
| {
direct_items: DirectCheckoutItem[];
};
@@ -186,10 +183,7 @@ export class CheckoutService extends BaseApiService {
return purchase;
}
async completePurchase(
tenantCode: string,
purchaseId: number,
): Promise<PurchaseStatusResponse> {
async completePurchase(tenantCode: string, purchaseId: number): Promise<PurchaseStatusResponse> {
const response = await firstValueFrom(
this.http.post<{ data?: PurchaseStatusResponse } | PurchaseStatusResponse>(
`${environment.url}tenants/${tenantCode}/compras/${purchaseId}/complete`,
@@ -227,10 +221,7 @@ export class CheckoutService extends BaseApiService {
return { status: purchase.status ?? null };
}
async cancelPurchase(
tenantCode: string,
purchaseId: number,
): Promise<PurchaseStatusResponse> {
async cancelPurchase(tenantCode: string, purchaseId: number): Promise<PurchaseStatusResponse> {
const response = await firstValueFrom(
this.http.post<{ data?: PurchaseStatusResponse } | PurchaseStatusResponse>(
`${environment.url}tenants/${tenantCode}/compras/${purchaseId}/cancel`,
@@ -254,9 +245,7 @@ export class CheckoutService extends BaseApiService {
if (status) {
url += `?status=${status}`;
}
const response = await firstValueFrom(
this.http.get<{ data: PurchaseSummaryResponse[] }>(url),
);
const response = await firstValueFrom(this.http.get<{ data: PurchaseSummaryResponse[] }>(url));
if (!response) {
throw new Error('Error al obtener las compras.');
}

View File

@@ -127,11 +127,13 @@ export class CategoryItemsPageComponent {
this.creatingDirectPurchase.set(true);
try {
const purchase = await this.injector.get(CheckoutService).startCheckout(tenant.codigo, {
direct_item: {
catalog_item_id: event.product.id,
variant_id: event.variant ?? null,
cantidad: event.quantity,
},
direct_items: [
{
catalog_item_id: event.product.id,
variant_id: event.variant ?? null,
cantidad: event.quantity,
},
],
});
await this.router.navigate(['/checkout'], { queryParams: { purchase: purchase.id } });
} catch (error) {

View File

@@ -480,11 +480,13 @@ describe('ProductDetailPageComponent', () => {
await Promise.resolve();
expect(checkoutServiceStub.startCheckout).toHaveBeenCalledWith('tenant-test', {
direct_item: {
catalog_item_id: 1,
variant_id: null,
cantidad: 1,
},
direct_items: [
{
catalog_item_id: 1,
variant_id: null,
cantidad: 1,
},
],
});
expect(routerStub.navigate).toHaveBeenCalledWith(['/checkout'], {
queryParams: { purchase: 44 },

View File

@@ -317,11 +317,13 @@ export class ProductDetailPageComponent implements OnInit, OnDestroy {
this.creatingDirectPurchase.set(true);
try {
const purchase = await this.checkoutService.startCheckout(tenant.codigo, {
direct_item: {
catalog_item_id: currentProduct.id,
variant_id: variant?.id ?? null,
cantidad: this.quantity(),
},
direct_items: [
{
catalog_item_id: currentProduct.id,
variant_id: variant?.id ?? null,
cantidad: this.quantity(),
},
],
});
await this.router.navigate(['/checkout'], {

View File

@@ -159,11 +159,13 @@ export class SearchPageComponent {
this.creatingDirectPurchase.set(true);
try {
const purchase = await this.injector.get(CheckoutService).startCheckout(tenant.codigo, {
direct_item: {
catalog_item_id: event.product.id,
variant_id: event.variant ?? null,
cantidad: event.quantity,
},
direct_items: [
{
catalog_item_id: event.product.id,
variant_id: event.variant ?? null,
cantidad: event.quantity,
},
],
});
await this.router.navigate(['/checkout'], { queryParams: { purchase: purchase.id } });
} catch (error) {

View File

@@ -180,21 +180,23 @@ export class StoreHomePageComponent implements OnInit, OnDestroy {
try {
const checkoutService = this.injector.get(CheckoutService);
const variantIds = event.variantIds ?? [];
const purchase =
variantIds.length > 1
? await this.startTicketCheckout(
checkoutService,
tenant.codigo,
event.product.id,
variantIds,
)
: await checkoutService.startCheckout(tenant.codigo, {
direct_item: {
const directItems =
variantIds.length > 0
? variantIds.map((variantId) => ({
catalog_item_id: event.product.id,
variant_id: variantId,
cantidad: 1,
}))
: [
{
catalog_item_id: event.product.id,
variant_id: event.variant ?? null,
cantidad: event.quantity,
},
});
];
const purchase = await checkoutService.startCheckout(tenant.codigo, {
direct_items: directItems,
});
await this.router.navigate(['/checkout'], { queryParams: { purchase: purchase.id } });
} catch (error) {
console.error('Failed to create direct purchase:', error);
@@ -204,21 +206,6 @@ export class StoreHomePageComponent implements OnInit, OnDestroy {
}
}
private async startTicketCheckout(
checkoutService: CheckoutService,
tenantCode: string,
catalogItemId: number,
variantIds: number[],
) {
return checkoutService.startCheckout(tenantCode, {
direct_items: variantIds.map((variantId) => ({
catalog_item_id: catalogItemId,
variant_id: variantId,
cantidad: 1,
})),
});
}
protected onAddToCart(event: ProductListCartEvent): void {
this.cartService.addItem(event.product.id, event.variant ?? null, event.quantity).subscribe({
next: (response) => {