feat(cart): add cart editing toggle based on tenant settings and update related components

This commit is contained in:
2026-08-18 16:32:20 -03:00
parent 61789d9238
commit a8e73af806
7 changed files with 67 additions and 7 deletions

View File

@@ -29,6 +29,7 @@
[subtotal]="cartSubtotal()" [subtotal]="cartSubtotal()"
[discount]="cartDiscount()" [discount]="cartDiscount()"
[total]="cartTotal()" [total]="cartTotal()"
[readonly]="!cartEditingEnabled()"
[backgroundColor]="'#ffffff'" [backgroundColor]="'#ffffff'"
(closed)="isCartOpen.set(false)" (closed)="isCartOpen.set(false)"
> >

View File

@@ -645,4 +645,35 @@ describe('StoreLayoutComponent', () => {
expect(cartItem.componentInstance.quantityDisabled()).toBe(false); expect(cartItem.componentInstance.quantityDisabled()).toBe(false);
expect(buyButton).toBeDefined(); expect(buyButton).toBeDefined();
}); });
it('hides quantity selectors when the tenant disables cart editing', () => {
tenantState.set({ ...tenant, cart_editing_enabled: false });
cartState.set({
id: 1,
tenant_codigo: tenant.codigo,
status: 'active',
subtotal: '100.00',
items: [
{
id: 1,
cantidad: 1,
precio_unitario: '100.00',
catalog_item_id: 1,
variant_id: null,
nombre: 'Producto',
imagen: null,
variant: null,
},
],
});
const fixture = TestBed.createComponent(StoreLayoutComponent);
fixture.detectChanges();
(fixture.componentInstance as any).isCartOpen.set(true);
fixture.detectChanges();
const cartItem = fixture.debugElement.query(By.css('app-cart-item'));
expect(cartItem.componentInstance.readonly()).toBe(true);
expect(cartItem.nativeElement.querySelector('app-quantity-selector')).toBeNull();
});
}); });

View File

@@ -36,6 +36,9 @@ export class StoreLayoutComponent implements OnInit {
protected readonly isCartOpen = signal(false); protected readonly isCartOpen = signal(false);
protected readonly isCreatingPurchase = signal(false); protected readonly isCreatingPurchase = signal(false);
protected readonly displayCart = computed(() => this.tenant()?.display_cart ?? true); protected readonly displayCart = computed(() => this.tenant()?.display_cart ?? true);
protected readonly cartEditingEnabled = computed(
() => this.tenant()?.cart_editing_enabled ?? true,
);
protected readonly cartSubtotal = computed(() => { protected readonly cartSubtotal = computed(() => {
const cart = this.cartService.cart(); const cart = this.cartService.cart();

View File

@@ -126,6 +126,7 @@ export interface Tenant {
display_categories?: boolean; display_categories?: boolean;
display_seach_bar?: boolean; display_seach_bar?: boolean;
display_cart?: boolean; display_cart?: boolean;
cart_editing_enabled?: boolean;
social_media?: SocialMedia[]; social_media?: SocialMedia[];
menues?: Menu[]; menues?: Menu[];
categories: Category[]; categories: Category[];

View File

@@ -58,6 +58,7 @@
[subtotal]="cartSubtotal()" [subtotal]="cartSubtotal()"
[discount]="cartDiscount()" [discount]="cartDiscount()"
[total]="cartTotal()" [total]="cartTotal()"
[readonly]="!cartEditingEnabled()"
[allowEditing]=" [allowEditing]="
createdPurchase()?.status === 'created' || createdPurchase()?.status === 'pending_payment' createdPurchase()?.status === 'created' || createdPurchase()?.status === 'pending_payment'
" "

View File

@@ -33,6 +33,7 @@ describe('CheckoutPageComponent payment validation', () => {
let routerStub: { navigate: ReturnType<typeof vi.fn> }; let routerStub: { navigate: ReturnType<typeof vi.fn> };
let routeQueryParamMap: ReturnType<typeof convertToParamMap>; let routeQueryParamMap: ReturnType<typeof convertToParamMap>;
let authUserState: ReturnType<typeof signal>; let authUserState: ReturnType<typeof signal>;
let tenantState: ReturnType<typeof signal<{ codigo: string; cart_editing_enabled?: boolean }>>;
beforeAll(() => { beforeAll(() => {
try { try {
@@ -88,6 +89,7 @@ describe('CheckoutPageComponent payment validation', () => {
routerStub = { navigate: vi.fn() }; routerStub = { navigate: vi.fn() };
routeQueryParamMap = convertToParamMap({}); routeQueryParamMap = convertToParamMap({});
authUserState = signal(null); authUserState = signal(null);
tenantState = signal({ codigo: 'tenant-test' });
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
imports: [CheckoutPageComponent], imports: [CheckoutPageComponent],
@@ -95,7 +97,7 @@ describe('CheckoutPageComponent payment validation', () => {
{ provide: CheckoutService, useValue: checkoutServiceStub }, { provide: CheckoutService, useValue: checkoutServiceStub },
{ provide: CatalogService, useValue: { getCatalogItem: vi.fn() } }, { provide: CatalogService, useValue: { getCatalogItem: vi.fn() } },
{ provide: CartService, useValue: cartServiceStub }, { provide: CartService, useValue: cartServiceStub },
{ provide: TenantService, useValue: { tenant: signal({ codigo: 'tenant-test' }) } }, { provide: TenantService, useValue: { tenant: tenantState } },
{ provide: AuthService, useValue: { user: authUserState } }, { provide: AuthService, useValue: { user: authUserState } },
{ {
provide: ActivatedRoute, provide: ActivatedRoute,
@@ -366,11 +368,7 @@ describe('CheckoutPageComponent payment validation', () => {
expect(component.checkoutStepIndex()).toBe(1); expect(component.checkoutStepIndex()).toBe(1);
expect(component.selectedPaymentMethod()).toBe('qr'); expect(component.selectedPaymentMethod()).toBe('qr');
expect(checkoutServiceStub.generatePaymentIntent).toHaveBeenCalledWith( expect(checkoutServiceStub.generatePaymentIntent).toHaveBeenCalledWith('tenant-test', 25, 'qr');
'tenant-test',
25,
'qr',
);
expect(component.qrData()).toBe('qr-value'); expect(component.qrData()).toBe('qr-value');
expect(component.qrPaymentStatus()).toBe('waiting'); expect(component.qrPaymentStatus()).toBe('waiting');
}); });
@@ -472,6 +470,17 @@ describe('CheckoutPageComponent payment validation', () => {
expect(component.isEditingItems()).toBe(false); expect(component.isEditingItems()).toBe(false);
}); });
it('does not allow item editing when the tenant disables cart editing', async () => {
tenantState.set({ codigo: 'tenant-test', cart_editing_enabled: false });
const { component } = createComponent();
await component.onEditingItemsChange(true);
expect(component.cartEditingEnabled()).toBe(false);
expect(component.isEditingItems()).toBe(false);
expect(checkoutServiceStub.prepareItemEditing).not.toHaveBeenCalled();
});
it('updates customer data on the existing purchase before payment', async () => { it('updates customer data on the existing purchase before payment', async () => {
const updatedPurchase = { const updatedPurchase = {
id: 25, id: 25,

View File

@@ -79,6 +79,9 @@ export class CheckoutPageComponent implements OnInit, OnDestroy {
protected readonly createdPurchase = signal<PurchaseDetailResponse | null>(null); protected readonly createdPurchase = signal<PurchaseDetailResponse | null>(null);
protected readonly isLoadingPurchase = signal(true); protected readonly isLoadingPurchase = signal(true);
protected readonly checkoutStepIndex = signal(0); protected readonly checkoutStepIndex = signal(0);
protected readonly cartEditingEnabled = computed(
() => this.tenantService.tenant()?.cart_editing_enabled ?? true,
);
protected readonly cartSubtotal = computed(() => { protected readonly cartSubtotal = computed(() => {
const purchase = this.createdPurchase(); const purchase = this.createdPurchase();
@@ -179,6 +182,10 @@ export class CheckoutPageComponent implements OnInit, OnDestroy {
return; return;
} }
if (editing && !this.cartEditingEnabled()) {
return;
}
if (!editing) { if (!editing) {
this.isEditingItems.set(false); this.isEditingItems.set(false);
@@ -224,7 +231,14 @@ export class CheckoutPageComponent implements OnInit, OnDestroy {
const purchaseId = this.createdPurchaseId(); const purchaseId = this.createdPurchaseId();
const itemId = event.item.cartItemId; const itemId = event.item.cartItemId;
if (!tenant || !purchaseId || !itemId || this.isUpdatingItem() || !this.isEditingItems()) { if (
!this.cartEditingEnabled() ||
!tenant ||
!purchaseId ||
!itemId ||
this.isUpdatingItem() ||
!this.isEditingItems()
) {
return; return;
} }