gigafibre-fsm/patches/fix_pg_groupby.py
louispaulb 607ea54b5c refactor: reduce token count, DRY code, consolidate docs
Backend services:
- targo-hub: extract deepGetValue to helpers.js, DRY disconnect reasons
  lookup map, compact CAPABILITIES, consolidate vision.js prompts/schemas,
  extract dispatch scoring weights, trim section dividers across 9 files
- modem-bridge: extract getSession() helper (6 occurrences), resetIdleTimer(),
  consolidate DM query factory, fix duplicate username fill bug, trim headers
  (server.js -36%, tplink-session.js -47%, docker-compose.yml -57%)

Frontend:
- useWifiDiagnostic: extract THRESHOLDS const, split processDiagnostic into
  6 focused helpers (processOnlineStatus, processWanIPs, processRadios,
  processMeshNodes, processClients, checkRadioIssues)
- EquipmentDetail: merge duplicate ROLE_LABELS, remove verbose comments

Documentation (17 → 13 files, -1,400 lines):
- New consolidated README.md (architecture, services, dependencies, auth)
- Merge ECOSYSTEM-OVERVIEW into ARCHITECTURE.md
- Merge MIGRATION-PLAN + ARCHITECTURE-COMPARE + FIELD-GAP + CHANGELOG → MIGRATION.md
- Merge COMPETITIVE-ANALYSIS into PLATFORM-STRATEGY.md
- Update ROADMAP.md with current phase status
- Delete CONTEXT.md (absorbed into README)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 08:39:58 -04:00

148 lines
4.7 KiB
Python

"""
ERPNext v16 PostgreSQL GROUP BY fixes.
PostgreSQL requires all non-aggregated SELECT columns in GROUP BY.
MySQL is lenient, ERPNext was built for MySQL.
Run: docker cp fix_pg_groupby.py erpnext-backend-1:/tmp/
docker exec erpnext-backend-1 bench --site erp.gigafibre.ca execute "exec(open('/tmp/fix_pg_groupby.py').read())"
"""
import os
BENCH = "/home/frappe/frappe-bench/apps/erpnext/erpnext"
patches = []
# ─── 1. controllers/trends.py — based_on_group_by for Customer, Supplier, Item ───
patches.append((
f"{BENCH}/controllers/trends.py",
[
# Customer (non-Quotation): group by needs customer_name, territory
(
'based_on_details["based_on_group_by"] = "t1.party_name" if trans == "Quotation" else "t1.customer"',
'based_on_details["based_on_group_by"] = "t1.party_name, t1.customer_name, t1.territory" if trans == "Quotation" else "t1.customer, t1.customer_name, t1.territory"',
),
# Supplier: group by needs supplier_name, supplier_group
(
'based_on_details["based_on_group_by"] = "t1.supplier"',
'based_on_details["based_on_group_by"] = "t1.supplier, t1.supplier_name, t3.supplier_group"',
),
# Item: group by needs item_name
(
'based_on_details["based_on_group_by"] = "t2.item_code"',
'based_on_details["based_on_group_by"] = "t2.item_code, t2.item_name"',
),
]
))
# ─── 2. trial_balance.py — account_currency missing from groupby ───
patches.append((
f"{BENCH}/accounts/report/trial_balance/trial_balance.py",
[
(
".groupby(closing_balance.account)",
".groupby(closing_balance.account, closing_balance.account_currency)",
),
]
))
# ─── 3. process_period_closing_voucher.py — account_currency ───
patches.append((
f"{BENCH}/accounts/doctype/process_period_closing_voucher/process_period_closing_voucher.py",
[
# Add account_currency to groupby after account
(
"query = query.groupby(gle.account)",
"query = query.groupby(gle.account, gle.account_currency)",
),
]
))
# ─── 4. exchange_rate_revaluation.py — account_currency ───
patches.append((
f"{BENCH}/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py",
[
(
'.groupby(gle.account, NullIf(gle.party_type, ""), NullIf(gle.party, ""))',
'.groupby(gle.account, NullIf(gle.party_type, ""), NullIf(gle.party, ""), gle.account_currency)',
),
]
))
# ─── 5. pos_closing_entry.py — account missing ───
patches.append((
f"{BENCH}/accounts/doctype/pos_closing_entry/pos_closing_entry.py",
[
(
".groupby(SalesInvoicePayment.mode_of_payment)",
".groupby(SalesInvoicePayment.mode_of_payment, SalesInvoicePayment.account)",
),
]
))
# ─── 6. voucher_wise_balance.py — voucher_type missing ───
patches.append((
f"{BENCH}/accounts/report/voucher_wise_balance/voucher_wise_balance.py",
[
(
".groupby(gle.voucher_no)",
".groupby(gle.voucher_no, gle.voucher_type)",
),
]
))
# ─── 7. total_stock_summary.py — item.description missing ───
patches.append((
f"{BENCH}/stock/report/total_stock_summary/total_stock_summary.py",
[
(
".groupby(item.item_code)",
".groupby(item.item_code, item.description)",
),
]
))
# ─── 8. stock_entry.py — job_card_scrap_item missing cols ───
patches.append((
f"{BENCH}/stock/doctype/stock_entry/stock_entry.py",
[
(
".groupby(job_card_scrap_item.item_code)",
".groupby(job_card_scrap_item.item_code, job_card_scrap_item.item_name, job_card_scrap_item.description, job_card_scrap_item.stock_uom)",
),
]
))
# ─── Apply patches ───
applied = 0
skipped = 0
errors = 0
for filepath, replacements in patches:
if not os.path.exists(filepath):
print(f"SKIP (not found): {filepath}")
skipped += 1
continue
with open(filepath, "r") as f:
content = f.read()
modified = False
for old, new in replacements:
if old in content:
content = content.replace(old, new, 1)
modified = True
print(f" PATCHED: {old[:60]}...")
elif new in content:
print(f" ALREADY: {new[:60]}...")
else:
print(f" NOT FOUND in {filepath}: {old[:60]}...")
errors += 1
if modified:
with open(filepath, "w") as f:
f.write(content)
applied += 1
print(f"OK: {filepath}")
print(f"\nDone: {applied} files patched, {skipped} skipped, {errors} not found")