refactor: rename storefront CategoryApiModel; correct stale auth-error doc; add Phase 1 backend contract

- models/category.model.ts: Category -> CategoryApiModel, disambiguated
  from core/categories/models/category-domain.model.ts's Category (admin
  domain shape). Removes a dead unused import in item.utils.ts along the
  way. Only live consumer was services/api.service.ts, updated in place.
- BACKEND-API-REFERENCE.md §5: corrected two rows documenting the
  TOKEN_EXPIRED/INVALID_SIGNATURE auth-error bug as still open - the fix
  (reading error.error.code before falling back to HTTP status) is
  already in auth.service.ts. Doc was stale, not the code.
- Sprint 0.2 audit: AdminRole duplication and the
  PRODUCT_DATA_PROVIDER/CATEGORY_REPOSITORY dead mock branches were
  already resolved in a prior pass - verified, no code change needed.
- docs/backend/PHASE-1-MONEY-FX-PAYMENTS-CONTRACT.md: new wire contract
  for Money/FxQuote/PriceSnapshot/payment state machine, so backend can
  start Phase 1 the moment the frozen payment chain is unblocked.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
sdarbinyan
2026-08-17 21:31:11 +04:00
parent 3c72c37e31
commit ffaa6d2a1c
5 changed files with 243 additions and 11 deletions

View File

@@ -1,6 +1,11 @@
import { ItemName } from './item.model';
export interface Category {
/**
* Storefront-side raw category shape (item.service.ts/api.service.ts responses).
* Unrelated to core/categories/models/category-domain.model.ts's Category, which is
* the normalized admin-backoffice domain shape produced by CategoryMapper.
*/
export interface CategoryApiModel {
categoryID: number;
name: string;
parentID: number;

View File

@@ -2,7 +2,7 @@ import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
import { Observable, timer } from 'rxjs';
import { map, retry } from 'rxjs/operators';
import { Category, DeliveryOption, Item, Subcategory } from '../models';
import { CategoryApiModel, DeliveryOption, Item, Subcategory } from '../models';
import { normalizeDeliveryOption, normalizeOptionalNumber } from '../utils/normalization.utils';
import { environment } from '../../environments/environment';
import { ApiConfigService } from '../core/config/api-config.service';
@@ -199,7 +199,7 @@ export class ApiService {
|| (subcategory.subcategories?.length ?? 0) > 0;
}
private isDisplayableCategory(category: Category): boolean {
private isDisplayableCategory(category: CategoryApiModel): boolean {
return category.visible !== false;
}
@@ -462,8 +462,8 @@ export class ApiService {
* Normalize a category from the API response — supports both
* the flat legacy format and nested backOffice format.
*/
private normalizeCategory(raw: any): Category {
const cat: Category = { ...raw };
private normalizeCategory(raw: any): CategoryApiModel {
const cat: CategoryApiModel = { ...raw };
if (raw.id != null && raw.categoryID == null) {
cat.id = String(raw.id);
@@ -522,7 +522,7 @@ export class ApiService {
return cat;
}
private normalizeCategories(cats: any[] | null | undefined): Category[] {
private normalizeCategories(cats: any[] | null | undefined): CategoryApiModel[] {
if (!cats || !Array.isArray(cats)) return [];
return cats
.map(c => this.normalizeCategory(c))
@@ -535,7 +535,7 @@ export class ApiService {
return this.http.get<{ message: string }>(`${this.baseUrl}/ping`);
}
getCategories(): Observable<Category[]> {
getCategories(): Observable<CategoryApiModel[]> {
return this.http.get<any[]>(`${this.baseUrl}/category`)
.pipe(retry(this.retryConfig), map(cats => this.normalizeCategories(cats)));
}

View File

@@ -1,5 +1,4 @@
import { Item } from '../models';
import { Category } from '../models/category.model';
export function getDiscountedPrice(item: Item): number {
return item.price * (1 - (item.discount || 0) / 100);