feat: catalog read cache (api + user apps) and clearer Zarrin accounting errors

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
s.esmaeili
2026-10-11 14:31:32 +03:30
co-authored by Cursor
parent 561addc676
commit 574fa1b4aa
53 changed files with 1241 additions and 401 deletions
@@ -9,6 +9,12 @@ import { ACCOUNTING_PROVIDERS } from './providers';
type Tx = Prisma.TransactionClient | PrismaService;
type StockFields = {
accountingStockAt: Date | null;
accountingStockWeight: Prisma.Decimal | null;
accountingStockCount: Prisma.Decimal | null;
};
const OPEN: AccountingOutboxStatus[] = ['PENDING', 'SENDING'];
/**
@@ -150,15 +156,21 @@ export class AccountingHooksService {
* meaningful while the product sync is on; stock fields of products that are
* not linked under the active provider are cleared by every product pull.
*/
async stockBlocked(product: {
accountingStockAt: Date | null;
accountingStockWeight: Prisma.Decimal | null;
accountingStockCount: Prisma.Decimal | null;
}) {
if (!product.accountingStockAt) return false;
async stockBlocked(product: StockFields) {
return (await this.stockBlocker())(product);
}
/**
* The same rule as stockBlocked, with the configuration read once — for
* checking a whole list without an await per product.
*/
async stockBlocker(): Promise<(product: StockFields) => boolean> {
const c = await this.config.get();
if (c.provider === 'none' || !c.features.products) return false;
const stock = product.accountingStockWeight ?? product.accountingStockCount;
return stock !== null && new Prisma.Decimal(stock).lte(0);
const active = c.provider !== 'none' && c.features.products;
return (product) => {
if (!active || !product.accountingStockAt) return false;
const stock = product.accountingStockWeight ?? product.accountingStockCount;
return stock !== null && new Prisma.Decimal(stock).lte(0);
};
}
}