원가 기반 가격: 카테고리/공급사/상품별 마진 규칙, .99 반올림, 마진 경고, EU 30일 가격 이력
설치
genpm add @core/pricing포함 내용
- src/lib/pricing/에 소스 코드, 파일 6개. (16.4kB)
- src/lib/pricing/AGENTS.md에 AI 규칙, 그리고 IDE 규칙 파일.
- @core/catalog, @core/contracts, @core/db, @core/money을(를) 자동으로 해결합니다.
README
이 패키지에는 README가 없습니다.
이것이 AI가 src/lib/pricing에서 작업할 때 읽는 내용 그대로입니다. 그 외에는 컨텍스트에 아무것도 추가되지 않습니다.
@core/pricing — rules for AI agents
Purpose
Turns costs into selling prices and keeps price changes honest. Rules (scope all / category / supplier / product,
cost range, markup in basis points, fixed add-on, charm rounding, minimum margin, priority) pick the most specific
match; repriceProduct applies them to variants with a cost. Every price change goes to price_history, which gives
the EU "prior price" (lowest of the 30 days before the current price) for any sale badge. Tables: price_rules, price_history.
Map
index.ts— public API:upsertPriceRule,priceFromCost,applyRule,repriceProduct,setVariantPrice,priorPrice,honestCompareAt,lowMarginVariants,priceRulesAdminResource.pricing.ts— logic.admin.ts— admin resource.schema.ts— tables.
Integration
- Migrations as in
src/lib/db/AGENTS.md. - Create rules in the admin (
priceRulesAdminResourceinsrc/genpm/admin.ts, permissionspricing:read|update), e.g.markupBps: 15000(+150 %), rounding.99. - Change prices only with
setVariantPrice(variantId, amount)(also from the admin and supplier syncs) so history is kept. - Product pages: show a crossed-out price only with
honestCompareAt(variantId); never with a typed "compare at". - Simulate before applying:
repriceProduct(productId, { apply: false })returns old/new price and margin per variant. - Verify: lower a price,
honestCompareAtreturns the lowest price of the previous 30 days.
Conventions
- Amounts in minor units; markup and margins in basis points (10000 = 100 %).
- Review
lowMarginVariants()after cost changes; items below their rule's minimum must not be sold silently.
Don't
- Don't update
priceAmountdirectly or invent "was" prices; that breaks the 30-day rule (Omnibus Directive). - Don't raise a price shortly before a sale to inflate the discount.
- Don't expose costs or margins in public pages or APIs.
# @core/pricing — rules for AI agents
## Purpose
Turns costs into selling prices and keeps price changes honest. Rules (scope all / category / supplier / product,
cost range, markup in basis points, fixed add-on, charm rounding, minimum margin, priority) pick the most specific
match; `repriceProduct` applies them to variants with a cost. Every price change goes to `price_history`, which gives
the EU "prior price" (lowest of the 30 days before the current price) for any sale badge. Tables: `price_rules`, `price_history`.
## Map
- `index.ts` — public API: `upsertPriceRule`, `priceFromCost`, `applyRule`, `repriceProduct`, `setVariantPrice`, `priorPrice`, `honestCompareAt`, `lowMarginVariants`, `priceRulesAdminResource`.
- `pricing.ts` — logic. `admin.ts` — admin resource. `schema.ts` — tables.
## Integration
1. Migrations as in `src/lib/db/AGENTS.md`.
2. Create rules in the admin (`priceRulesAdminResource` in `src/genpm/admin.ts`, permissions `pricing:read|update`), e.g. `markupBps: 15000` (+150 %), rounding `.99`.
3. Change prices only with `setVariantPrice(variantId, amount)` (also from the admin and supplier syncs) so history is kept.
4. Product pages: show a crossed-out price only with `honestCompareAt(variantId)`; never with a typed "compare at".
5. Simulate before applying: `repriceProduct(productId, { apply: false })` returns old/new price and margin per variant.
6. Verify: lower a price, `honestCompareAt` returns the lowest price of the previous 30 days.
## Conventions
- Amounts in minor units; markup and margins in basis points (10000 = 100 %).
- Review `lowMarginVariants()` after cost changes; items below their rule's minimum must not be sold silently.
## Don't
- Don't update `priceAmount` directly or invent "was" prices; that breaks the 30-day rule (Omnibus Directive).
- Don't raise a price shortly before a sale to inflate the discount.
- Don't expose costs or margins in public pages or APIs.
.genpmignore 적용 후 주입될 정확한 트리입니다. 고정 대상:
// Tablas de @core/pricing. Las recoge drizzle-kit vía src/lib/db/drizzle.config.ts.
import { boolean, index, integer, pgTable, text, timestamp } from 'drizzle-orm/pg-core';
import { productVariants } from '../catalog/index.ts';
import { primaryId, timestamps } from '../db/index.ts';
export const priceRules = pgTable('price_rules', {
id: primaryId('prr'),
name: text('name').notNull(),
/** A qué aplica: todo, una categoría, un proveedor o un producto (slug/id en `scopeValue`). */
scope: text('scope', { enum: ['all', 'category', 'supplier', 'product'] }).notNull().default('all'),
scopeValue: text('scope_value'),
/** Tramo de coste (unidades menores) en el que aplica. */
minCost: integer('min_cost'),
maxCost: integer('max_cost'),
/** Margen sobre el coste en puntos básicos: 15000 = +150 % (precio = coste × 2,5). */
markupBps: integer('markup_bps').notNull().default(0),
/** Importe fijo añadido tras el margen (p. ej. para cubrir comisiones). */
fixedAdd: integer('fixed_add').notNull().default(0),
rounding: text('rounding', { enum: ['none', '.99', '.95', '.90', '.00'] }).notNull().default('.99'),
/** Margen mínimo aceptable (bps sobre el precio): por debajo, alerta. */
minMarginBps: integer('min_margin_bps').notNull().default(1000),
priority: integer('priority').notNull().default(0),
active: boolean('active').notNull().default(true),
...timestamps,
});
/** Historial de precios por variante (necesario para anunciar rebajas en la UE: precio más bajo de 30 días). */
export const priceHistory = pgTable(
'price_history',
{
id: primaryId('prh'),
variantId: text('variant_id')
.notNull()
.references(() => productVariants.id, { onDelete: 'cascade' }),
amount: integer('amount').notNull(),
currency: text('currency').notNull(),
validFrom: timestamp('valid_from', { withTimezone: true, mode: 'date' }).notNull(),
validTo: timestamp('valid_to', { withTimezone: true, mode: 'date' }),
},
(t) => [index('price_history_variant_idx').on(t.variantId, t.validFrom)],
);
export type PriceRule = typeof priceRules.$inferSelect;
이 패키지는 MCP 서버를 선언하지 않습니다.
| 버전 | 커밋 | 게시일 | 검사 |
|---|---|---|---|
| 1.0.0 | 6aa4077 | 3시간 전 | 검사 통과 |
- npm
- zod ^4.0.0
- 제안됨
- GenPM은 npm 명령을 제안하고, 동의한 경우에만 실행합니다.
- 검사
- 검사 통과 · 문제 0건
- 커밋
- v1.0.0 → 6aa40774c5407480451f9b9f74d4e4e0577c06df · 가져온 뒤 검증됨
- 스크립트
- 없음. GenPM은 패키지 코드를 절대 실행하지 않습니다.
- 라이선스
- MIT
- 품질
- 100/100
- 인정된 라이선스충족
- AGENTS.md에 목적 설명충족
- AGENTS.md에 통합 단계충족
- AGENTS.md에 규칙 또는 금지 사항충족
- 테스트 포함충족
- 보안 검사 통과충족
- 최근 6개월 내 게시충족
- 인증된 게시자충족
- 요약과 키워드충족
- 신고
- 문제가 있나요?