菜单食品成本与定价计算器
来自 Wikiprompt,自由的提示词百科全书
菜单食品成本与定价计算器 一个完整的AI技能,用于计算食谱和菜单项目的成本,计算食品成本百分比、贡献毛利,并根据菜单工程分类建议价格。包括Python脚本、参考文档、模板以及一个实际示例。
提示词内容收藏
🌐
---
name: menu-food-cost-calculator
description: 为咖啡馆、餐厅、面包店、餐车和餐饮服务商计算食谱和菜单项目的成本 - 将采购价格和出成率转换为每份的精确成本、不含税价格上的食品成本百分比、贡献毛利,以及在目标食品成本下的建议售价,然后通过菜单工程(明星、耕马、谜题、狗)对项目进行分类,并推荐价格、份量和菜单调整。当用户询问“这道菜要花我多少钱?”、“我应该如何给我的菜单定价?”、“为什么我的食品成本这么高?”,或分享带有供应商价格的食谱时使用。
---
# 菜单食品成本与定价计算器
你帮助小型食品企业了解每道菜的真实成本,并自信地为它们定价。你基于真实的采购价格和食谱工作,展示计算过程,并且不仅考虑百分比,还考虑金额上的毛利。
## 此技能中的文件
- `scripts/cost_menu.py` - 根据JSON成本核算表计算每个食谱的成本,建议价格,并运行菜单工程(仅使用Python 3标准库)
- `references/food-cost-basics.md` - 出成率、采购价与可食用成本、食品成本百分比、税费和常见成本核算错误
- `references/pricing-strategies.md` - 目标百分比定价、基于毛利的定价、取整和菜单工程行动
- `templates/recipe-costing-sheet.md` - 脚本读取的JSON成本核算表,以及报告布局
- `examples/example-cafe-menu.md` - 对五道菜咖啡馆菜单的完整审查示例
## 工作流程
### 1. 收集输入
询问或确认:
- 货币,以及菜单价格是否包含增值税或销售税(以及税率)。
- 目标食品成本百分比(典型范围见 `references/food-cost-basics.md`;默认30)。
- 对于每种食材:采购价格、包装规格和单位,以及出成率(修剪、去皮、烹饪损耗或变质后的可用比例)。
- 对于每个项目:按制备量计算的食谱数量、每批份数、当前菜单价格、每份包装或装饰成本,以及周销量(如已知)。
如果缺少某些信息,使用明确标注的假设(例如“牛油果假设出成率90%”)并在报告中列出。
### 2. 构建成本核算表
将 `templates/recipe-costing-sheet.md` 填写为JSON。使用脚本认识的单位(g、kg、ml、l、oz、lb、each)。如果食材按件购买但按重量使用,称量一件并转换;切勿混用维度。
### 3. 运行计算器
```bash
python3 scripts/cost_menu.py menu.json
python3 scripts/cost_menu.py menu.json --target 28
python3 scripts/cost_menu.py menu.json --json
```
表格显示每份成本、菜单价格、不含税净价、食品成本百分比、贡献毛利(净价减去成本)、目标食品成本下的价格(向上取整),以及当每个项目都有周销量时的菜单工程分类。错误(未知食材、单位不匹配)和HIGH发现会使退出代码为1。
如果你无法运行脚本,请手动逐行进行相同的计算,并说明这一点。
### 4. 提出建议
使用 `references/pricing-strategies.md`:
1. 首先修复数据错误并重新运行。
2. 对于HIGH和WARN项目,在提高价格、缩减份量、更换昂贵食材或接受较高百分比(因为金额毛利强劲)之间做出选择。说明权衡。
3. 使用菜单工程分类来决定项目在菜单上的位置,以及是推广、重新定价、改进还是移除。
4. 使用建议的更改重新运行,以显示前后对比。
### 5. 报告
使用 `templates/recipe-costing-sheet.md` 中的报告布局,如 `examples/example-cafe-menu.md` 所示。
## 规则
- 至少为一个项目展示公式,以便所有者可以核对:每份成本 / 净价 x 100。
- 切勿将目标价格视为降低现有价格的指令;它是一个基准。
- 不提供税务或法律建议;仅应用用户提供的税率。
- 在建议替换时尊重过敏原和饮食声明,切勿建议降低食品安全或质量标准。
- 当供应商价格变化超过约5%时,重新检查成本。
FILE:references/food-cost-basics.md
# 食品成本基础
## 关键术语
- **采购价(AP)成本**:你为包装、箱或件支付的价格。
- **出成率百分比**:修剪、去皮、去骨、烹饪损耗或变质后的可用比例。去除皮和鱼刺的三文鱼片可能出成率85%;整牛油果中每10个有1个不可用时,按件计算出成率90%。
- **可食用部分(EP)成本** = AP成本每单位 /(出成率百分比 / 100)。食谱列出的是制备好的、可用的数量,因此按EP成本核算。
- **餐盘成本(每份成本)** = 批次中食材EP成本总和 / 份数 + 每份额外成本(包装、餐巾、装饰、酱料杯)。
- **净价** = 菜单价格 /(1 + 税率),当菜单价格包含增值税或销售税时。食品成本必须与你保留的钱衡量,而不是你收取的税。
- **食品成本百分比** = 餐盘成本 / 净价 x 100。
- **贡献毛利** = 净价 - 餐盘成本。这是每笔销售留下的用于支付人工、租金和利润的钱。
## 计算示例
三文鱼片以每公斤32.00购买,出成率85%:
- AP成本每克 = 32.00 / 1000 = 0.032
- EP成本每克 = 0.032 / 0.85 = 0.0376
- 160克份量 = 160 x 0.0376 = 6.02
## 典型食品成本目标(粗略指南)
| 概念 | 典型食品成本百分比 |
| --- | --- |
| 咖啡和浓缩咖啡饮品 | 15至25 |
| 烘焙食品 | 20至30 |
| 咖啡馆早午餐菜肴 | 28至35 |
| 休闲餐厅主菜 | 28至35 |
| 牛排和海鲜主菜 | 35至45 |
| 披萨 | 20至28 |
| 餐饮托盘 | 25至35 |
你的正确目标取决于人工、租金和销量。低人工项目可以运行更高的食品成本百分比,但仍然非常盈利。
## 常见成本核算错误
1. 忘记小项目:油、煎锅用黄油、盐、装饰、酱汁和外卖包装。添加它们或使用extras_per_portion。
2. 对蛋白质和农产品使用AP成本而不考虑出成率。
3. 针对包含税的价格衡量食品成本。
4. 核算食谱卡而不是厨房实际装盘的内容(份量漂移)。称量五份实际份量。
5. 过时的供应商价格。当价格变动约5%或更多时更新表格。
6. 混用单位:按件购买但按重量使用的食材需要称量一件。
7. 忽略浪费和员工餐;单独跟踪它们,并将实际食品成本(来自库存)与此理论成本进行比较。
## 理论成本与实际食品成本
此技能计算理论成本:如果遵循食谱,食品应该花费多少。实际食品成本 =(期初库存 + 采购 - 期末库存)/ 净食品销售额。差距超过约2至3个百分点通常意味着浪费、份量漂移、盗窃或表格上的价格错误。
FILE:references/pricing-strategies.md
# 定价策略和菜单工程
## 设定价格的方法
1. **目标食品成本百分比**:价格 = 餐盘成本 / 目标 x(1 + 税率),向上取整。简单,也是脚本的“AT TARGET”列。弱点:便宜项目最终定价过低,昂贵蛋白质定价过高。
2. **贡献毛利**:决定每个项目必须赚取的钱(例如主菜至少5.00),然后价格 =(餐盘成本 + 毛利)x(1 + 税率)。更适合高成本蛋白质。
3. **市场检查**:与附近三到五家类似场所比较。价格感知与成本同样重要。
4. **混合方法**:从目标价格开始,检查金额毛利,然后与市场进行合理性检查。
## 取整和呈现
- 向上取整到菜单使用的步长(0.10、0.50或整数)。高档菜单通常使用不带货币符号的整数;休闲菜单通常以.50或.90结尾。
- 避免一次在整个菜单上进行许多小幅涨价;首先提高毛利最弱的项目。
- 保持价格差距合理:燕麦奶升级应覆盖其额外成本(燕麦饮品每升通常约为牛奶的两倍)。
## 菜单工程
需要每个项目的周销量。脚本使用:
- **受欢迎度线**:如果项目销售至少等于份额的70%,则为受欢迎(有5个项目时,0.7 x 20% = 单位销量的14%)。
- **毛利线**:销量加权的平均贡献毛利。
| 分类 | 受欢迎度 | 毛利 | 该做什么 |
| --- | --- | --- | --- |
| 明星 | 高 | 高 | 保持质量和份量一致,放置在最佳菜单位置,小幅涨价通常是安全的。 |
| 耕马 | 高 | 低 | 稍微涨价,削减成本(份量、装饰、供应商),或搭配高毛利附加品。不要移除它。 |
| 谜题 | 低 | 高 | 推广它:更好的菜单位置、描述、员工推荐、照片。检查价格是否吓跑客人。 |
| 狗 | 低 | 低 | 改进食谱或价格,或移除它,除非它有用途(儿童菜单、饮食选项、招牌项目)。 |
## 为高食品成本项目选择修复方案
| 选项 | 何时合适 | 风险 |
| --- | --- | --- |
| 提高价格 | 项目受欢迎且市场允许 | 如果涨幅大,销量减少 |
| 缩减份量 | 份量大于客人预期 | 客人注意到;保持价值感知 |
| 更换食材 | 存在更便宜且质量相当的选项 | 过敏原和口味变化;更新菜单文本 |
| 接受较高百分比 | 金额毛利是菜单上最高的 | 需要销量才能盈利 |
| 移除项目 | 它是没有战略角色的狗 | 常客可能会想念它 |
始终使用建议的更改重新运行计算器,并显示前后对比。
FILE:templates/recipe-costing-sheet.md
# 食谱成本核算表(scripts/cost_menu.py的输入)
保存为 `menu.json`。食谱中的数量是制备好的(可用的)量。
```json
{
"currency": "EUR",
"target_food_cost_pct": 30,
"menu_price_includes_tax_pct": 10,
"price_rounding": 0.10,
"ingredients": [
{"name": "flour", "price": 0.95, "per": "1 kg"},
{"name": "butter", "price": 9.80, "per": "1 kg"},
{"name": "eggs", "price": 3.60, "per": "12 each"},
{"name": "blueberries", "price": 16.00, "per": "1 kg", "yield_pct": 95}
],
"recipes": [
{
"name": "Blueberry Muffin",
"portions": 12,
"menu_price": 3.20,
"sold_per_week": 90,
"extras_per_portion": 0.06,
"items": [["flour", "500 g"], ["butter", "180 g"], ["eggs", "3 each"], ["blueberries", "300 g"]]
}
]
}
```
字段说明:
- `per`:你购买的包装,格式为“<数量> <单位>”(g、kg、mg、ml、cl、dl、l、oz、lb、each)。
- `yield_pct`:1至100,默认100。
- `menu_price_includes_tax_pct`:如果菜单价格不含税则为0。
- `sold_per_week`:为每个项目(或都不)提供,以获得菜单工程分类。
- `extras_per_portion`:包装、餐巾、装饰、酱料杯,以金额计。
运行:`python3 scripts/cost_menu.py menu.json [--target 30] [--json]`
---
# 菜单成本核算报告:<企业> - <日期>
**目标食品成本:** <x>% **价格包含税:** <税率或无> **货币:** <代码>
**假设:** <出成率、缺失价格、份量重量>
## 结果(之前)
| 项目 | 成本/份 | 价格 | 净价 | 食品% | 毛利 | 目标价格 | 分类 |
| --- | --- | --- | --- | --- | --- | --- | --- |
## 公式检查
<一个项目逐步计算>
## 需要注意的事项
1. **<项目>** - <发现>。选项:<价格 / 份量 / 食材 / 接受>。建议:<一个>。
## 建议更改和结果(之后)
<更改,然后新表格或更改的行>
## 菜单工程行动
- 明星:<项目和行动>
- 耕马:<项目和行动>
- 谜题:<项目和行动>
- 狗:<项目和行动>
## 后续步骤
- <称量实际份量、更新供应商价格、每月跟踪实际食品成本>
FILE:examples/example-cafe-menu.md
# 示例:五道菜咖啡馆菜单
**用户:**“我们是一家小型早午餐咖啡馆。价格包含10%增值税,我想要大约30%的食品成本。以下是我的供应商价格和食谱。为什么我的毛利这么薄?”
表格有16种食材和5个项目,带有周销量(牛油果出成率90%,因为大约每10个中有1个不可用;三文鱼修剪后85%)。
**命令:**
```bash
python3 scripts/cost_menu.py cafe-menu.json
```
**输出(之前):**
```
项目 成本 价格 净价 食品% 毛利 目标价格 分类
牛油果吐司 3.13 9.50 8.64 36.2 5.51 11.50 明星
三文鱼菠菜碗 8.02 14.50 13.18 60.8 5.16 29.50 谜题
白咖啡 0.72 3.80 3.45 21.0 2.73 2.70 耕马
燕麦白咖啡 0.91 4.20 3.82 23.9 2.91 3.40 耕马
蓝莓松饼 0.79 3.20 2.91 27.2 2.12 3.00 狗
发现(8):
[HIGH] 三文鱼菠菜碗:食品成本60.8%远高于30%目标;价格EUR 29.50或削减每份成本4.06
[WARN] 牛油果吐司:食品成本36.2%高于30%目标;目标价格将为EUR 11.50
[INFO] 三文鱼菠菜碗:三文鱼片占成本的76%;其价格或份量最重要
[INFO] 菜单工程:加权平均毛利EUR 3.22,受欢迎度线为售出项目的14.0%
[INFO] 食材‘松露油’未在任何食谱中使用
```
---
# 菜单成本核算报告:早午餐咖啡馆 - 十月
**目标食品成本:** 30% **价格包含税:** 10%增值税 **货币:** EUR
**假设:** 牛油果出成率90%,三文鱼85%,菠菜90%;每道菜额外成本0.10,每杯咖啡0.12(杯和盖),每个松饼0.06。
## 公式检查(三文鱼菠菜碗,之前)
- 三文鱼160克 x(32.00 / 1000 / 0.85)= 6.02
- 菠菜70克 x(14.00 / 1000 / 0.90)= 1.09;鸡蛋0.30;西红柿0.36;橄榄油0.15;额外成本0.10
- 每份成本 = 8.02;净价 = 14.50 / 1.10 = 13.18;食品成本 = 8.02 / 13.18 x 100 = 60.8%
## 需要注意的事项
1. **三文鱼菠菜碗(HIGH,谜题)** - 食品成本60.8%;三文鱼占成本的76%。按目标定价(29.50)对咖啡馆来说不现实。建议:将三文鱼减少到120克(对碗来说仍然是慷慨的份量)并将价格提高到17.50;接受约40%的食品成本,因为毛利成为菜单上最高的。
2. **牛油果吐司(WARN,明星)** - 36.2%。它是销量最好的菜,因此涨价1.00至10.50风险低。
3. **白咖啡和燕麦白咖啡(耕马)** - 健康的百分比(21至24%),但毛利小;不要打折。保持燕麦附加费0.40:燕麦饮品每杯比牛奶贵0.19。
4. **蓝莓松饼(狗)** - 百分比不错,毛利低且销量低。在移除前尝试与咖啡捆绑销售。
5. **松露油**在表格中但不在任何食谱中:从订单或表格中移除它。
## 建议更改和结果(之后)
牛油果吐司10.50;三文鱼菠菜碗120克三文鱼,价格17.50。重新运行:`python3 scripts/cost_menu.py cafe-menu-revised.json`
```
项目 成本 价格 净价 食品% 毛利 目标价格 分类
牛油果吐司 3.13 10.50 9.55 32.8 6.42 11.50 明星
三文鱼菠菜碗 6.51 17.50 15.91 40.9 9.40 23.90 谜题
白咖啡 0.72 3.80 3.45 21.0 2.73 2.70 耕马
燕麦白咖啡 0.91 4.20 3.82 23.9 2.91 3.40 耕马
蓝莓松饼 0.79 3.20 2.91 27.2 2.12 3.00 狗
发现(6):
[WARN] 三文鱼菠菜碗:食品成本40.9%高于30%目标;目标价格将为EUR 23.90
[INFO] 菜单工程:加权平均毛利EUR 3.56,受欢迎度线为售出项目的14.0%
```
退出代码0。剩余的WARN是有意接受的:每碗毛利9.40,而之前为5.16。
## 菜单工程行动
- 明星:牛油果吐司 - 保持食谱一致,放在早午餐部分顶部。
- 耕马:白咖啡、燕麦白咖啡 - 不打折;建议每杯咖啡搭配糕点。
- 谜题:三文鱼菠菜碗 - 给它一个简短描述和员工推荐;在新价格下4周后检查销量。
- 狗:蓝莓松饼 - 测试咖啡+松饼捆绑销售4周,然后决定。
## 后续步骤
- 本周称量五份实际三文鱼份量,确认120克规格得到遵循。
- 每月更新供应商价格并重新运行表格。
- 月底与库存中的实际食品成本进行比较。
FILE:scripts/cost_menu.py
#!/usr/bin/env python3
"""根据食谱和采购价格计算菜单项目成本,并建议菜单价格。
用法:
python3 cost_menu.py menu.json [--target 30] [--json]
cat menu.json | python3 cost_menu.py -
输入JSON(见 templates/recipe-costing-sheet.md):
{
"currency": "EUR",
"target_food_cost_pct": 30, # 可选,默认30(或 --target)
"menu_price_includes_tax_pct": 10, # 可选;菜单价格中包含的增值税/销售税
"price_rounding": 0.10, # 可选;建议价格向上取整到此步长
"ingredients": [
{"name": "butter", "price": 9.80, "per": "1 kg", "yield_pct": 100}
],
"recipes": [
{"name": "Croissant", "portions": 12, "menu_price": 3.20, "sold_per_week": 180,
"extras_per_portion": 0.05, # 可选:包装、餐巾、装饰
"items": [["butter", "600 g"], ["flour", "1 kg"]]}
]
}
单位:g、kg、mg、ml、cl、dl、l、oz、lb、each(也支持pc、pcs、piece、unit、egg)。
食谱数量是制备好的(可用的)量。yield_pct是购买后修剪、去皮、烹饪损耗或变质后的可用比例。
每个食谱:每份成本、净(不含税)菜单价格的食品成本百分比、贡献毛利、目标价格下的建议价格,以及三大成本驱动因素。当每个食谱都有sold_per_week时,添加菜单工程分类(明星、耕马、谜题、狗)。
退出代码:0正常,1数据错误或HIGH发现,2用法或输入错误。
仅使用标准库。
"""
import json
import math
import re
import sys
UNITS = { # 单位 ->(维度,到基本单位g / ml / each的因子)
"mg": ("mass", 0.001), "g": ("mass", 1.0), "kg": ("mass", 1000.0),
"oz": ("mass", 28.3495), "lb": ("mass", 453.592),
"ml": ("volume", 1.0), "cl": ("volume", 10.0), "dl": ("volume", 100.0), "l": ("volume", 1000.0),
"each": ("count", 1.0), "pc": ("count", 1.0), "pcs": ("count", 1.0), "piece": ("count", 1.0),
"pieces": ("count", 1.0), "unit": ("count", 1.0), "units": ("count", 1.0), "egg": ("count", 1.0), "eggs": ("count", 1.0),
}
BASE = {"mass": "g", "volume": "ml", "count": "each"}
def usage(msg):
print(f"error: {msg}\n", file=sys.stderr)
print(__doc__.strip().split("\n\n")[1], file=sys.stderr)
sys.exit(2)
def parse_qty(text):
"""'600 g' -> (600.0, 'mass', 600.0 in base units)."""
m = re.fullmatch(r"\s*(\d+(?:[.,]\d+)?)\s*([a-zA-Z]+)?\s*", str(text))
if not m:
raise ValueError(f"cannot read quantity {text!r}")
qty = float(m.group(1).replace(",", "."))
unit = (m.group(2) or "each").lower()
if unit not in UNITS:
raise ValueError(f"unknown unit {unit!r} in {text!r}")
dim, factor = UNITS[unit]
return qty, dim, qty * factor
def round_up(value, step):
return math.ceil(round(value / step, 6)) * step
def analyze(data, target_override=None):
errors, findings = [], []
cur = data.get("currency", "")
target = float(target_override or data.get("target_food_cost_pct", 30))
tax = float(data.get("menu_price_includes_tax_pct", 0))
step = float(data.get("price_rounding", 0.10))
ingredients = {}
for ing in data.get("ingredients", []):
name = str(ing.get("name", "")).strip().lower()
try:
_, dim, base_qty = parse_qty(ing["per"])
price = float(ing["price"])
except (KeyError, ValueError, TypeError) as e:
errors.append(f"ingredient {name or '?'}: {e}")
continue
y = float(ing.get("yield_pct", 100))
if not 0 < y <= 100:
errors.append(f"ingredient {name}: yield_pct must be between 1 and 100")
continue
ingredients[name] = {"dim": dim, "cost_per_base": price / base_qty / (y / 100), "yield": y, "used": False}
results = []
for rec in data.get("recipes", []):
rname = rec.get("name", "?")
portions = float(rec.get("portions", 1) or 1)
lines, bad = [], False
for item in rec.get("items", []):
iname, qtext = str(item[0]).strip().lower(), item[1]
ing = ingredients.get(iname)
if not ing:
errors.append(f"{rname}: unknown ingredient {iname!r} (add it to ingredients)")
bad = True
continue
ing["used"] = True
try:
_, dim, base_qty = parse_qty(qtext)
except ValueError as e:
errors.append(f"{rname}: {e}")
bad = True
continue
if dim != ing["dim"]:
errors.append(f"{rname}: {iname} is bought by {BASE[ing['dim']]} but used by {BASE[dim]} ({qtext}); "
f"convert it (for example weigh one piece)")
bad = True
continue
lines.append((iname, base_qty * ing["cost_per_base"]))
if bad:
continue
batch = sum(c for _, c in lines)
extras = float(rec.get("extras_per_portion", 0))
cost = batch / portions + extras
price = float(rec.get("menu_price", 0))
net = price / (1 + tax / 100) if price else 0.0
pct = 100 * cost / net if net else None
suggested = round_up(cost / (target / 100) * (1 + tax / 100), step)
drivers = sorted(lines, key=lambda x: -x[1])[:3]
r = {"name": rname, "portions": portions, "cost_per_portion": round(cost, 3), "menu_price": price,
"net_price": round(net, 2), "food_cost_pct": None if pct is None else round(pct, 1),
"contribution_margin": round(net - cost, 2) if net else None,
"suggested_price_at_target": round(suggested, 2), "sold_per_week": rec.get("sold_per_week"),
"drivers": [{"ingredient": n, "share_pct": round(100 * c / batch, 1) if batch else 0} for n, c in drivers]}
results.append(r)
if pct is None:
findings.append(("WARN", rname, f"no menu_price; suggested {cur} {suggested:.2f} at {target:g}% food cost"))
elif pct > target + 15:
findings.append(("HIGH", rname, f"food cost {pct:.1f}% is far above the {target:g}% target; "
f"price {cur} {suggested:.2f} or cut cost {cost - net * target / 100:.2f} per portion"))
elif pct > target + 5:
findings.append(("WARN", rname, f"food cost {pct:.1f}% is above the {target:g}% target; "
f"price at target would be {cur} {suggested:.2f}"))
elif pct < target - 15:
findings.append(("INFO", rname, f"food cost only {pct:.1f}%; check the recipe lists every ingredient and portion size"))
if drivers and batch and drivers[0][1] / batch > 0.5:
findings.append(("INFO", rname, f"{drivers[0][0]} is {100 * drivers[0][1] / batch:.0f}% of the cost; "
f"its price or portion matters most"))
sold = [r for r in results if isinstance(r["sold_per_week"], (int, float)) and r["contribution_margin"] is not None]
if sold and len(sold) == len(results) and len(sold) >= 3:
total = sum(r["sold_per_week"] for r in sold)
pop_line = 0.7 / len(sold)
avg_cm = sum(r["contribution_margin"] * r["sold_per_week"] for r in sold) / total if total else 0
for r in sold:
high_pop = total and r["sold_per_week"] / total >= pop_line
high_cm = r["contribution_margin"] >= avg_cm
r["menu_class"] = {(True, True): "Star", (True, False): "Plowhorse",
(False, True): "Puzzle", (False, False): "Dog"}[(bool(high_pop), high_cm)]
findings.append(("INFO", None, f"menu engineering: weighted average margin {cur} {avg_cm:.2f}, "
f"popularity line {100 * pop_line:.1f}% of items sold"))
for name, ing in ingredients.items():
if not ing["used"]:
findings.append(("INFO", None, f"ingredient {name!r} is not used in any recipe"))
return {"currency": cur, "target_pct": target, "tax_pct": tax, "recipes": results,
"errors": errors, "findings": [{"severity": s, "recipe": n, "message": m} for s, n, m in findings]}
def main(argv):
target, as_json, paths = None, False, []
it = iter(argv)
for a in it:
if a == "--target":
try:
target = float(next(it, ""))
except ValueError:
usage("--target needs a number, for example 30")
if not 5 <= target <= 80:
usage("--target should be a food cost percent between 5 and 80")
elif a == "--json":
as_json = True
elif a.startswith("--"):
usage(f"unknown option {a}")
else:
paths.append(a)
if len(paths) != 1:
usage("give exactly one menu JSON file, or - for stdin")
try:
raw = sys.stdin.read() if paths[0] == "-" else open(paths[0], encoding="utf-8").read()
data = json.loads(raw)
except (OSError, ValueError) as e:
print(f"error: cannot read menu JSON: {e}", file=sys.stderr)
return 2
if not data.get("recipes"):
print("error: no recipes in the input", file=sys.stderr)
return 2
rep = analyze(data, target)
if as_json:
print(json.dumps(rep, indent=2))
else:
cur = rep["currency"]
print(f"Target food cost {rep['target_pct']:g}% | prices include {rep['tax_pct']:g}% tax | currency {cur}\n")
print(f"{'ITEM':<22} {'COST':>7} {'PRICE':>7} {'NET':>7} {'FOOD%':>6} {'MARGIN':>7} {'AT TARGET':>9} CLASS")
for r in rep["recipes"]:
pct = "-" if r["food_cost_pct"] is None else f"{r['food_cost_pct']:.1f}"
cm = "-" if r["contribution_margin"] is None else f"{r['contribution_margin']:.2f}"
print(f"{r['name'][:22]:<22} {r['cost_per_portion']:>7.2f} {r['menu_price']:>7.2f} {r['net_price']:>7.2f} "
f"{pct:>6} {cm:>7} {r['suggested_price_at_target']:>9.2f} {r.get('menu_class', '-')}")
print("\nTop cost drivers:")
for r in rep["recipes"]:
print(f" {r['name']}: " + ", ".join(f"{d['ingredient']} {d['share_pct']:g}%" for d in r["drivers"]))
if rep["errors"]:
print(f"\nErrors ({len(rep['errors'])}):")
for e in rep["errors"]:
print(f" [ERROR] {e}")
print(f"\nFindings ({len(rep['findings'])}):")
order = {"HIGH": 0, "WARN": 1, "INFO": 2}
for f in sorted(rep["findings"], key=lambda f: order[f["severity"]]):
print(f" [{f['severity']}] {f['recipe'] + ': ' if f['recipe'] else ''}{f['message']}")
bad = rep["errors"] or any(f["severity"] == "HIGH" for f in rep["findings"])
return 1 if bad else 0
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))
用法
此提示词专为 business 设计。复制上方内容并粘贴到你常用的 AI 工具中。
为获得最佳效果,可将占位符(方括号或大写字母标示)替换为你的具体需求。
讨论
0 条评论