"""記事「受注・出荷・請求を一元管理すると転記は何回減る?」(order-to-invoice-transcription-count)の計算と図。
python scripts/figs/transcription_count.py (fields/sales-management-app/ で実行)。外部のデータは使わない。

架空の食品卸1社(従業員20名、得意先120社、月1,000明細)の伝票6段で、段ごとに手で写す項目を数え、
  転記回数 = Σ(段ごとに手で写す項目の数)× 月の明細数
で今と、1つの販売管理につないだ後を同じ式で比べる。明細の写し(出荷指示〜請求書)の 17項目・月1,000明細は
top_numbers.py(トップと柱の数字)と同じ条件。金額・税額は計算で出るので数えない。会計の仕訳は得意先ごとの合計なので、
明細の式とは別に「項目数 × 得意先数」で数える。

書き出すもの:
  public/img/articles/order-to-invoice-transcription-flow.svg(図1。frontmatter の image にも使う)
  scripts/figs/out/order-to-invoice-partial.svg(図2。記事にインラインで貼る)
  scripts/figs/out/order-to-invoice-volume.svg(図3。記事にインラインで貼る)
"""
import json, pathlib, html

ROOT = pathlib.Path(__file__).resolve().parents[2]
OUT_IMG = ROOT / 'public/img/articles'; OUT_IMG.mkdir(parents=True, exist_ok=True)
OUT_INL = ROOT / 'scripts/figs/out'; OUT_INL.mkdir(parents=True, exist_ok=True)

# ---- 架空の条件
LINES = 1000          # 月の受注明細(1日50明細 × 月20日)
CUSTOMERS = 120       # 月に請求書を出す得意先
ORDER_INPUT = ['得意先', '商品', '数量', '納期']           # 受注票(最初の入力。つないだ後も残る)
COPY = {                                                   # 受注票のあとに手で写す段(top_numbers.py と同じ)
    '出荷指示': ['得意先', '商品', '数量', '納期'],
    '納品書': ['得意先', '商品', '数量', '単価'],
    '売上台帳(Excel)': ['日付', '得意先', '商品', '数量', '単価'],
    '請求書': ['日付', '商品', '数量', '単価'],
}
ACCOUNTING = ['日付', '得意先', '金額']                     # 会計ソフトへの仕訳(得意先ごとの月の合計)

per_line = sum(len(v) for v in COPY.values())
assert per_line == 17, per_line   # トップ・柱と同じ値であること

def count(connected: set[str]) -> dict:
    """connected に入れた段は、同じ明細から作られるので手で写さない(0回)。"""
    copy = sum(len(v) for k, v in COPY.items() if k not in connected) * LINES
    acc = 0 if '会計' in connected else len(ACCOUNTING) * CUSTOMERS
    order = len(ORDER_INPUT) * LINES
    return {'order': order, 'copy': copy, 'accounting': acc, 'total': order + copy + acc}

now = count(set())
allc = count(set(COPY) | {'会計'})
partial = [
    ('今(受注票のあと全部を手で写す)', count(set())),
    ('受注の入口だけ電子化(FAXの読み取りなど)', count(set())),
    ('出荷指示と納品書までつなぐ', count({'出荷指示', '納品書'})),
    ('売上台帳までつなぎ、請求書はExcel', count({'出荷指示', '納品書', '売上台帳(Excel)'})),
    ('請求書と会計までつなぐ(1つの販売管理)', allc),
]
volume = [(n, {'now': (len(ORDER_INPUT) + per_line) * n + len(ACCOUNTING) * CUSTOMERS, 'after': len(ORDER_INPUT) * n}) for n in (300, 1000, 3000)]

result = {
    'cond': {'linesPerMonth': LINES, 'customers': CUSTOMERS},
    'perLineCopy': per_line, 'orderItems': len(ORDER_INPUT),
    'now': now, 'connected': allc,
    'reduction': now['total'] - allc['total'],
    'reductionRate': round((now['total'] - allc['total']) / now['total'] * 100, 1),
    'partial': [{'label': l, **c} for l, c in partial],
    'volume': [{'lines': n, **v} for n, v in volume],
}

# ---- 図(差し色 #678b2c は面だけ、文字は #557323 / #46601d)
FONT = "'Hiragino Sans','Yu Gothic','Meiryo',sans-serif"
INK, SUB, MUTE, LINE = '#23221f', '#4c4c4c', '#706d65', '#dddbd8'
ACC, ACC_TXT, ACC_DK, ACC_SOFT, GRAY = '#678b2c', '#557323', '#46601d', '#eef3e4', '#c9c6bf'
f = lambda n: f'{n:,}'

def t(x, y, s, size=12, fill=INK, anchor='start', weight=400):
    return (f"<text x='{x}' y='{y}' font-size='{size}' fill='{fill}' text-anchor='{anchor}' "
            f"font-weight='{weight}'>{html.escape(s)}</text>")

def svg_open(w, h, tid, title, standalone):
    ns = " xmlns='http://www.w3.org/2000/svg'"
    style = '' if standalone else " style='max-width:100%;height:auto'"
    return (f"<svg{ns} viewBox='0 0 {w} {h}' width='{w}' height='{h}' role='img' aria-labelledby='{tid}' "
            f"font-family=\"{FONT}\"{style}><title id='{tid}'>{html.escape(title)}</title><rect width='{w}' height='{h}' fill='#fff'/>")

# 図1 伝票6段の流れ(今/つないだ後)
def fig_flow():
    W, H = 780, 470
    stages = [('受注票', ORDER_INPUT, '入力', LINES), *[(k, v, '写し', LINES) for k, v in COPY.items()],
              ('会計', ACCOUNTING, '得意先ごと', CUSTOMERS)]
    bw, gap, x0 = 114, 14, 18
    title = ('架空の食品卸の伝票6段の流れ図。今は受注票の入力4項目のあと、出荷指示4・納品書4・売上台帳5・請求書4の計17項目を'
             f'明細ごとに写し、月1,000明細で写しは月17,000回、会計への仕訳は月360回。1つにつなぐと手入力は受注の月4,000回だけ')
    s = [svg_open(W, H, 'flow-title', title, True)]
    s.append(t(x0, 30, '今:受注票のあと、同じ明細を段ごとに手で写す(月1,000明細)', 14, INK, weight=700))
    for i, (name, items, kind, n) in enumerate(stages):
        x = x0 + i * (bw + gap); y = 46; h = 178
        first = i == 0
        fill = ACC_SOFT if first else '#fff'; stroke = ACC if first else '#b9b4aa'
        s.append(f"<rect x='{x}' y='{y}' width='{bw}' height='{h}' rx='4' fill='{fill}' stroke='{stroke}' stroke-width='1.4'/>")
        s.append(t(x + bw / 2, y + 22, name, 12.5, INK, 'middle', 700))
        s.append(t(x + bw / 2, y + 40, f'{kind} {len(items)}項目', 11.5, ACC_TXT if first else SUB, 'middle', 700))
        for j, it in enumerate(items):
            s.append(t(x + bw / 2, y + 62 + j * 17, it, 11.5, SUB, 'middle'))
        cnt = len(items) * n
        s.append(f"<line x1='{x + 10}' y1='{y + 146}' x2='{x + bw - 10}' y2='{y + 146}' stroke='{LINE}'/>")
        s.append(t(x + bw / 2, y + 166, f'月{f(cnt)}回', 13, INK, 'middle', 700))
        if i < len(stages) - 1:
            ax = x + bw + 2
            s.append(f"<path d='M{ax} {y + 90} l{gap - 5} 0 m-5 -4 l5 4 l-5 4' fill='none' stroke='{MUTE}' stroke-width='1.4'/>")
    s.append(t(x0, 246, f'写し(出荷指示〜請求書)17項目 × 1,000明細 = 月17,000回。会計は3項目 × 得意先120社 = 月360回', 11.5, SUB))
    # つないだ後
    y2 = 290
    s.append(t(x0, y2, '1つの販売管理につないだ後:手で入れるのは受注の1回だけ', 14, INK, weight=700))
    for i, (name, items, kind, n) in enumerate(stages):
        x = x0 + i * (bw + gap); y = y2 + 16; h = 104
        first = i == 0
        s.append(f"<rect x='{x}' y='{y}' width='{bw}' height='{h}' rx='4' fill='{ACC if first else '#f4f3f0'}' stroke='{ACC if first else '#d8d5ce'}' stroke-width='1.4'/>")
        s.append(t(x + bw / 2, y + 24, name.replace('(Excel)', ''), 12.5, '#fff' if first else INK, 'middle', 700))
        sub = f'入力 {len(items)}項目' if first else ('CSVで取り込む' if name == '会計' else '同じ明細から作る')
        s.append(t(x + bw / 2, y + 46, sub, 11.5, '#fff' if first else SUB, 'middle'))
        s.append(t(x + bw / 2, y + 80, f'月{f(len(items) * n)}回' if first else '0回', 13, '#fff' if first else ACC_DK, 'middle', 700))
        if i < len(stages) - 1:
            ax = x + bw + 2
            s.append(f"<path d='M{ax} {y + 52} l{gap - 5} 0 m-5 -4 l5 4 l-5 4' fill='none' stroke='{ACC}' stroke-width='1.6'/>")
    s.append(t(x0, 444, f'手入力の合計:月{f(now["total"])}回 → 月{f(allc["total"])}回(写しの{f(now["copy"] + now["accounting"])}回が0回)', 12.5, INK, weight=700))
    s.append(t(x0, 462, '商いの帳場の試算(架空の食品卸、従業員20名・得意先120社・月1,000明細)。金額・税額は計算で出るので数えていない', 10.5, MUTE))
    s.append('</svg>')
    return ''.join(s)

# 図2 どこまでつなぐかで残る写しの回数
def fig_partial():
    W, H = 700, 300
    title = ('どこまでつなぐかで残る写しの回数の横棒グラフ。受注の入口だけ電子化しても写しは月17,360回のまま、'
             '出荷指示と納品書までつなぐと9,360回、売上台帳までで4,360回、請求書と会計までつなぐと0回')
    s = [svg_open(W, H, 'partial-title', title, False)]
    s.append(t(20, 26, '受注票のあとに手で写す回数(月、明細の写し+会計の仕訳)', 13.5, INK, weight=700))
    lx, bx, bw_max, y0, rh = 20, 300, 300, 50, 44
    mx = max(c['copy'] + c['accounting'] for _, c in partial)
    for i, (label, c) in enumerate(partial):
        v = c['copy'] + c['accounting']; y = y0 + i * rh
        last = i == len(partial) - 1
        s.append(t(lx, y + 19, label, 12, INK if last else SUB, weight=700 if last else 400))
        w = bw_max * v / mx
        s.append(f"<rect x='{bx}' y='{y + 5}' width='{w:.1f}' height='22' fill='{GRAY if i < 2 else ACC}' opacity='{1 if i < 2 else 0.55 + 0.15 * (i - 1)}'/>")
        s.append(t(bx + w + 8, y + 21, f'{f(v)}回', 12.5, ACC_DK if last else INK, weight=700))
    s.append(t(20, 284, '商いの帳場の試算(架空の食品卸・月1,000明細・得意先120社)。受注票の入力(月4,000回)はどの場合も残るので含めない', 10.5, MUTE))
    s.append('</svg>')
    return ''.join(s)

# 図3 月の明細数ごとの手入力の回数(今/つないだ後)
def fig_volume():
    W, H = 700, 290
    title = ('月の明細数ごとに、手入力の回数を今と1つにつないだ後で比べた棒グラフ。300明細で6,660回が1,200回、'
             '1,000明細で21,360回が4,000回、3,000明細で63,360回が12,000回')
    s = [svg_open(W, H, 'volume-title', title, False)]
    s.append(t(20, 26, '手入力の回数(月)= 受注の入力+写し+会計の仕訳', 13.5, INK, weight=700))
    base_y, top_y = 230, 56; mx = max(v['now'] for _, v in volume)
    for k, val in enumerate((0, 20000, 40000, 60000)):
        y = base_y - (base_y - top_y) * val / mx
        s.append(f"<line x1='80' y1='{y:.1f}' x2='680' y2='{y:.1f}' stroke='{LINE}' stroke-dasharray='2 3'/>")
        s.append(t(72, y + 4, f(val), 10.5, MUTE, 'end'))
    for i, (n, v) in enumerate(volume):
        cx = 170 + i * 190
        for j, (key, col) in enumerate((('now', GRAY), ('after', ACC))):
            h = (base_y - top_y) * v[key] / mx; x = cx - 46 + j * 50
            s.append(f"<rect x='{x}' y='{base_y - h:.1f}' width='42' height='{h:.1f}' fill='{col}'/>")
            s.append(t(x + 21, base_y - h - 6, f(v[key]), 11.5, ACC_DK if key == 'after' else INK, 'middle', 700))
        s.append(t(cx, base_y + 18, f'月{f(n)}明細', 12, INK, 'middle', 700))
    s.append(f"<rect x='420' y='14' width='12' height='12' fill='{GRAY}'/>"); s.append(t(438, 25, '今', 11.5, SUB))
    s.append(f"<rect x='470' y='14' width='12' height='12' fill='{ACC}'/>"); s.append(t(488, 25, '1つにつないだ後', 11.5, SUB))
    s.append(t(20, 278, '商いの帳場の試算(架空の食品卸・得意先120社。受注4項目+写し17項目/明細、会計3項目/得意先)', 10.5, MUTE))
    s.append('</svg>')
    return ''.join(s)

(OUT_IMG / 'order-to-invoice-transcription-flow.svg').write_text(fig_flow(), encoding='utf-8')
(OUT_INL / 'order-to-invoice-partial.svg').write_text(fig_partial(), encoding='utf-8')
(OUT_INL / 'order-to-invoice-volume.svg').write_text(fig_volume(), encoding='utf-8')
print(json.dumps(result, ensure_ascii=False, indent=1))
