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()"
[discount]="cartDiscount()"
[total]="cartTotal()"
[readonly]="!cartEditingEnabled()"
[backgroundColor]="'#ffffff'"
(closed)="isCartOpen.set(false)"
>

View File

@@ -645,4 +645,35 @@ describe('StoreLayoutComponent', () => {
expect(cartItem.componentInstance.quantityDisabled()).toBe(false);
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 isCreatingPurchase = signal(false);
protected readonly displayCart = computed(() => this.tenant()?.display_cart ?? true);
protected readonly cartEditingEnabled = computed(
() => this.tenant()?.cart_editing_enabled ?? true,
);
protected readonly cartSubtotal = computed(() => {
const cart = this.cartService.cart();

View File

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

View File

@@ -58,6 +58,7 @@
[subtotal]="cartSubtotal()"
[discount]="cartDiscount()"
[total]="cartTotal()"
[readonly]="!cartEditingEnabled()"
[allowEditing]="
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 routeQueryParamMap: ReturnType<typeof convertToParamMap>;
let authUserState: ReturnType<typeof signal>;
let tenantState: ReturnType<typeof signal<{ codigo: string; cart_editing_enabled?: boolean }>>;
beforeAll(() => {
try {
@@ -88,6 +89,7 @@ describe('CheckoutPageComponent payment validation', () => {
routerStub = { navigate: vi.fn() };
routeQueryParamMap = convertToParamMap({});
authUserState = signal(null);
tenantState = signal({ codigo: 'tenant-test' });
await TestBed.configureTestingModule({
imports: [CheckoutPageComponent],
@@ -95,7 +97,7 @@ describe('CheckoutPageComponent payment validation', () => {
{ provide: CheckoutService, useValue: checkoutServiceStub },
{ provide: CatalogService, useValue: { getCatalogItem: vi.fn() } },
{ provide: CartService, useValue: cartServiceStub },
{ provide: TenantService, useValue: { tenant: signal({ codigo: 'tenant-test' }) } },
{ provide: TenantService, useValue: { tenant: tenantState } },
{ provide: AuthService, useValue: { user: authUserState } },
{
provide: ActivatedRoute,
@@ -366,11 +368,7 @@ describe('CheckoutPageComponent payment validation', () => {
expect(component.checkoutStepIndex()).toBe(1);
expect(component.selectedPaymentMethod()).toBe('qr');
expect(checkoutServiceStub.generatePaymentIntent).toHaveBeenCalledWith(
'tenant-test',
25,
'qr',
);
expect(checkoutServiceStub.generatePaymentIntent).toHaveBeenCalledWith('tenant-test', 25, 'qr');
expect(component.qrData()).toBe('qr-value');
expect(component.qrPaymentStatus()).toBe('waiting');
});
@@ -472,6 +470,17 @@ describe('CheckoutPageComponent payment validation', () => {
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 () => {
const updatedPurchase = {
id: 25,

View File

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