原価からの価格設定:カテゴリ/仕入先/商品別の利益ルール、.99 丸め、利益率アラート、EU 30 日価格履歴
インストール
genpm add @core/pricing含まれるもの
- src/lib/pricing/ にソースコード(6 ファイル)。 (16.4 KB)
- 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 適用後に組み込まれる正確なツリーです。固定先:
// Panel: reglas de precio.
import { asc, count, eq } from 'drizzle-orm';
import type { AdminContext, AdminResource } from '../contracts/index.ts';
import { getDb } from '../db/index.ts';
import { PriceRuleInput, upsertPriceRule } from './pricing.ts';
import { type PriceRule, priceRules } from './schema.ts';
class PricingForbidden extends Error {
readonly code = 'forbidden';
}
async function need(ctx: AdminContext, action: 'read' | 'update') {
if (!(await ctx.can(`pricing:${action}`))) throw new PricingForbidden(`forbidden: pricing:${action}`);
}
export const priceRulesAdminResource: AdminResource<PriceRule> = {
name: 'price-rules',
label: { singular: 'Price rule', plural: 'Price rules' },
group: 'Store',
fields: [
{ name: 'name', label: 'Name', type: 'text', required: true, list: true },
{ name: 'scope', label: 'Applies to', type: 'select', list: true, options: ['all', 'category', 'supplier', 'product'].map((v) => ({ value: v, label: v })) },
{ name: 'scopeValue', label: 'Category slug / supplier id / product id', type: 'text' },
{ name: 'minCost', label: 'From cost', type: 'money' },
{ name: 'maxCost', label: 'To cost', type: 'money' },
{ name: 'markupBps', label: 'Markup (basis points, 15000 = +150%)', type: 'number', required: true, list: true },
{ name: 'fixedAdd', label: 'Fixed amount added', type: 'money' },
{ name: 'rounding', label: 'Price ending', type: 'select', options: ['none', '.99', '.95', '.90', '.00'].map((v) => ({ value: v, label: v })) },
{ name: 'minMarginBps', label: 'Minimum margin (bps)', type: 'number' },
{ name: 'priority', label: 'Priority', type: 'number' },
{ name: 'active', label: 'Active', type: 'boolean', list: true },
],
input: PriceRuleInput,
title: (r) => r.name,
async list(q, ctx) {
await need(ctx, 'read');
const [total] = await getDb().select({ n: count() }).from(priceRules);
return { rows: await getDb().select().from(priceRules).orderBy(asc(priceRules.scope), asc(priceRules.priority)).limit(q.pageSize).offset((Math.max(q.page, 1) - 1) * q.pageSize), total: total?.n ?? 0 };
},
async get(id, ctx) {
await need(ctx, 'read');
const [r] = await getDb().select().from(priceRules).where(eq(priceRules.id, id));
return r ?? null;
},
async create(input, ctx) {
await need(ctx, 'update');
return upsertPriceRule(input as never);
},
async update(id, input, ctx) {
await need(ctx, 'update');
return upsertPriceRule(input as never, id);
},
async delete(id, ctx) {
await need(ctx, 'update');
await getDb().delete(priceRules).where(eq(priceRules.id, id));
},
};
このパッケージは 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 か月以内に公開達成
- 認証済みの公開者達成
- 概要とキーワード達成
- 報告
- 問題を見つけましたか?