AI PLAY玩法更新持续收录
← 回到更新流

2026-09-05 · 原文发布日

拍进货单照片,盯住每道菜的成本率

这个n8n模板帮餐饮店盯住菜品成本:把供货商送货单照片丢进指定Google Drive文件夹,Gemini读出供应商、日期和明细行,换算成克、毫升等基准单位并核对小票合计,对得上才更新Sheets里的原料单价,对不上就发邮件请人核对。每天早上流程重算每道菜的理论成本和成本率,超标菜品由Gemini给出三条具体建议发到邮箱,不超则不打扰。数字全部由代码计算。

原文要点

原模板为n8n工作流:向指定Google Drive文件夹投递进货单照片触发流程,Gemini把供应商、日期、合计与明细行抽取为结构化JSON;代码节点把数量换算为克/毫升/件的基准单价并校验明细合计是否与票面一致,一致才更新Sheets的Ingredients页单价,不一致则邮件请人工核对。每日定时读取Ingredients、Recipes、Menu三页,按配方计算每道菜理论成本与成本率并写回Menu页;仅对超目标成本率的菜,由Gemini生成恰好三条建议经Gmail发出,未超标不发信。模板免费,需自备Gemini密钥及Google各项凭据;下载页未附列名细节,需在模板内核对。

这次做成什么

在n8n里按原模板思路独立搭一套「进货单照片→单价更新→每日成本率预警」流程,并用本站合成的三页表格和一张虚构进货单完成一次端到端练习,验证只有超标菜品才收到邮件。

准备什么

  • n8n可运行实例(云端或自托管1.x)及创建凭据权限
  • Google账号:一个含Ingredients/Recipes/Menu三页的Sheet、一个Drive收件文件夹、可用Gmail
  • Google Gemini API密钥(作者未注明具体型号,需在模板节点内确认)
  • 至少一份配方用量与菜品售价数据,可用下方合成CSV代替

具体怎么做

  1. 建三页Google Sheet

    新建Sheet,建三个工作表并命名为Ingredients、Recipes、Menu。Ingredients列:ingredient,unit,unit_price,updated_at;Recipes列:dish,ingredient,qty_base_unit;Menu列:dish,price,target_cost_ratio,row_number,last_cost,last_ratio,status。row_number给每道菜一个固定行号,供每日回写定位。把下方三份合成CSV内容粘入对应页。

  2. 配置Drive触发与Config

    在Drive新建文件夹如「进货单收件箱」,从文件夹网址复制folder ID。n8n中加Google Drive Trigger(事件选fileCreated,指到该文件夹),并在流程头部Config节点写入:drive_folder_id、sheets_doc_id(Sheet网址中的长串ID)、notify_email三项,下游节点统一引用,便于迁移。

  3. Gemini抽取与单位换算

    Drive Trigger后接Download节点取图片,再交给Gemini节点,使用下方「进货单抽取提示词」要求返回严格JSON(supplier、date、printed_total、items数组含name/qty/unit/line_total)。其后Code节点执行单位换算(如1箱鸡蛋=30件、1袋糖=5000克),得到每件/克/毫升单价,并累加items的line_total与printed_total比对。

  4. 分支:一致更新,不一致人工核对

    Code节点输出match布尔值。IF节点按match分流:true走Google Sheets节点,按ingredient匹配更新Ingredients的unit_price与updated_at;false走Gmail节点,邮件附上原始JSON与差额,请人工看照片核对,绝不写入猜测数字。

  5. 每日成本率计算与超标建议

    另起一条Schedule Trigger(每天8:00)分支:读取三页,用下方成本计算代码算出每道菜理论成本、成本率与状态,写回Menu页last_cost/last_ratio/status。对status=over的菜品,把菜品名、成本、成本率、目标、恢复目标所需售价的事实交给Gemini,用下方建议提示词生成恰好三条建议,再经Gmail发出;无超标则流程直接结束、不发信。

  6. 用合成数据测试

    先手工跑每日分支,确认Menu页被回写且只有超标菜触发邮件;再把虚构进货单内容按提示词格式模拟为Gemini返回JSON喂入Code节点,验证合计不一致时走人工核对分支。确认无误后再激活工作流并投真实照片。

可直接复制的材料

进货单抽取提示词(喂Gemini读照片)

本站独立编写,供Gemini节点系统/用户消息使用;若返回null行,应在后续分支按「无法读取」上报而非写入。

你是餐饮进货单识别助手。阅读这张供货商送货单照片,只输出JSON,不要任何解释文字或代码块标记。格式:{"supplier":"供应商名","date":"YYYY-MM-DD","printed_total":票面合计数字,"items":[{"name":"原料名","qty":数量数字,"unit":"票面单位原文","line_total":该行金额数字}]}。规则:1) 看不清的字段填null,绝不猜测;2) printed_total必须与票面印刷的合计完全一致;3) items按票面行序排列;4) 所有金额与数量用纯数字,不带货币符号。

单位换算与合计校验代码(Code节点)

本站编写示例。conv换算表按你供应商实际规格扩充(原模板也提示在换算表加单位)。unread行需在邮件中报告。

const conv = { '箱': { '鸡蛋': 30 }, '袋': { '白砂糖': 5000, '鸡腿肉': 3000 }, '桶': { '酱油': 5000 } };
const inv = $json; // Gemini返回的JSON
let sum = 0; const lines = []; const unread = [];
for (const it of inv.items) {
  if (it.qty == null || it.line_total == null) { unread.push(it.name || '未知名'); continue; }
  sum += it.line_total;
  const per = conv[it.unit] && conv[it.unit][it.name] ? conv[it.unit][it.name] : it.qty;
  const baseQty = conv[it.unit] && conv[it.unit][it.name] ? it.qty * per : it.qty;
  lines.push({ ingredient: it.name, base_qty: baseQty, base_unit_price: +(it.line_total / baseQty).toFixed(4) });
}
const match = inv.printed_total != null && Math.abs(sum - inv.printed_total) < 0.01;
return [{ json: { supplier: inv.supplier, date: inv.date, match, sum: +sum.toFixed(2), printed_total: inv.printed_total, lines, unread } }];

每日成本计算代码(Code节点)

本站编写示例。fix_price是「恢复目标成本率所需售价」,全部由代码计算,符合原模板「AI不产生进表数字」的设计;节点名请按你实际命名调整引用。

const ing = $('Read Ingredients').all().map(i => i.json);
const rec = $('Read Recipes').all().map(i => i.json);
const menu = $('Read Menu').all().map(i => i.json);
const price = {}; ing.forEach(r => price[r.ingredient] = r.unit_price);
const out = [];
for (const m of menu) {
  const items = rec.filter(r => r.dish === m.dish);
  const cost = +items.reduce((s, r) => s + (price[r.ingredient] || 0) * r.qty_base_unit, 0).toFixed(2);
  const ratio = +(cost / m.price).toFixed(4);
  const status = ratio > m.target_cost_ratio ? 'over' : 'ok';
  const fixPrice = +(m.target_cost_ratio > 0 ? cost / m.target_cost_ratio : 0).toFixed(2);
  out.push({ json: { row_number: m.row_number, dish: m.dish, price: m.price, last_cost: cost, last_ratio: ratio, target: m.target_cost_ratio, status, fix_price: fixPrice } });
}
return out;

超标建议提示词(喂Gemini写建议)

本站独立编写。数字事实由上游代码节点传入,AI只负责组织语言,符合原模板设定;语言与口吻可在系统消息里改。

你是餐厅成本顾问。下面JSON数组里每道菜已超过目标成本率,字段含义:dish菜名、last_cost当前理论成本、last_ratio当前成本率、target目标成本率、price当前售价、fix_price恢复目标所需售价。请对每道菜给出恰好三条具体、可执行的建议(如与供应商重谈、替换替代原料、调整配方克数、调价到fix_price附近),每条不超过40字,口吻直接。只输出JSON:[{"dish":"菜名","actions":["建议1","建议2","建议3"]}],不要解释,不要新增任何数字。

三页合成测试数据CSV

本站合成练习数据。若鸡腿肉涨到0.045元/克,照烧鸡腿饭成本=0.045*180+0.008*12≈8.20元、成本率约25.6%仍ok;涨到0.06元/克则成本≈10.90元、成本率约34.1%触发over。

Ingredients: ingredient,unit,unit_price,updated_at\n鸡蛋,件,0.80,2026-09-01\n白砂糖,克,0.008,2026-09-01\n鸡腿肉,克,0.030,2026-09-01\n\nRecipes: dish,ingredient,qty_base_unit\n照烧鸡腿饭,鸡腿肉,180\n照烧鸡腿饭,白砂糖,12\n焦糖布丁,鸡蛋,2\n焦糖布丁,白砂糖,40\n\nMenu: dish,price,target_cost_ratio,row_number,last_cost,last_ratio,status\n照烧鸡腿饭,32,0.30,2,,,\n焦糖布丁,18,0.25,3,,,

输入与输出示例

本站虚构演示,未实际运行;数值由示例配方推算,非原作者结果。

输入

虚构进货单:供应商「绿源食品」,日期2026-09-05,票面合计207.00元,两行明细:鸡腿肉 1袋(3000克) 180.00元;鸡蛋 1箱(30件) 27.00元。Menu页照烧鸡腿饭售价32元、目标成本率30%。

对应输出

Code节点输出:sum=207.00与printed_total=207.00一致,match=true;鸡腿肉基准单价0.0600元/克、鸡蛋0.9000元/件,Ingredients页两行单价被更新。每日分支重算:照烧鸡腿饭last_cost=0.06*180+0.008*12=10.80+0.10=10.90元,last_ratio=10.90/32≈0.3406,status=over,fix_price=10.90/0.30≈36.33元;焦糖布丁status=ok。Gemini据此输出恰好三条建议,如[{"dish":"照烧鸡腿饭","actions":["与绿源重谈鸡腿肉袋装价","将鸡腿肉用量从180克调至165克","售价从32元上调至36元"]}],仅这一道菜进入邮件。

做完检查这些

  • Ingredients页单价只在合计与票面一致时更新,不一致时收到含差额的核对邮件且表格不变
  • 每日运行后Menu页last_cost、last_ratio、status被回写,未超标菜不触发任何邮件
  • 成本率超标的菜收到的邮件含恰好三条建议,且邮件中成本、比率、fix_price与代码计算值一致
  • 无法辨认的行出现在核对邮件中,而不是被估算写入

容易卡住的地方

  • 三页工作表名与列名必须与节点映射完全一致,row_number缺失会导致回写错行;原页面未给出完整列名清单,需在下载的模板节点内逐一核对
  • Gemini对模糊照片可能返回null字段,流程必须把这类行当「无法读取」上报,不能静默跳过
  • 单位换算表不覆盖供应商实际规格时会得到错误基准价,先用合成数据跑通再上真实票据
  • 原页面未注明所用Gemini具体型号与调用费用,激活前在节点里确认模型并留意API用量

原文与作者资料 ↗

本站借助AI整理来源事实,独立编写中文操作指南与示例。示例不代表实际运行结果;来源效果尚未由本站独立复现。付费模板与模型、服务费用分别计算。

原文标题
Track menu cost ratios from delivery-note photos with Gemini, Google Sheets and Gmail
原作者
ayumi
原始发布日期
2026-09-05T00:17:17.757Z
来源
https://n8n.io/workflows/19226