Close review gaps: gate fixes, cylinder register, roll tracking, costing, Uganda setup #1
@@ -0,0 +1,215 @@
|
|||||||
|
"""One-off setup of the Ugandan company and its operating structure (backlog 1, 16, 17).
|
||||||
|
|
||||||
|
Never runs on migrate. Run it by hand, dry-run first:
|
||||||
|
|
||||||
|
bench --site <site> execute makap_backend.makap_backend.setup_uganda.run
|
||||||
|
bench --site <site> execute makap_backend.makap_backend.setup_uganda.run --kwargs "{'confirm': 1}"
|
||||||
|
|
||||||
|
It creates and never deletes. The original Kenya/KES company is left alone:
|
||||||
|
once the new company is checked, remove the old one in the desk (it has no
|
||||||
|
transactions) so nobody posts to it by mistake.
|
||||||
|
|
||||||
|
Choices below are recommended defaults for Makap to confirm, not decisions
|
||||||
|
taken for them: company name, abbreviation, July-June fiscal year, VAT at 18%.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import frappe
|
||||||
|
from frappe.utils import getdate
|
||||||
|
|
||||||
|
COUNTRY = "Uganda"
|
||||||
|
CURRENCY = "UGX"
|
||||||
|
TIME_ZONE = "Africa/Kampala"
|
||||||
|
VAT_RATE = 18
|
||||||
|
|
||||||
|
# Added beside the four warehouses ERPNext creates with every company
|
||||||
|
# (Stores, Work In Progress, Finished Goods, Goods In Transit).
|
||||||
|
WAREHOUSES = [
|
||||||
|
("Raw Materials", {}),
|
||||||
|
("Quarantine", {}),
|
||||||
|
("WIP - Printing", {}),
|
||||||
|
("WIP - Lamination and Curing", {}),
|
||||||
|
("WIP - Slitting", {}),
|
||||||
|
("WIP - Bag Making", {}),
|
||||||
|
("Returns Hold", {"makap_is_returns_hold_warehouse": 1}),
|
||||||
|
("Rejected", {}),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class Plan:
|
||||||
|
def __init__(self, confirm):
|
||||||
|
self.confirm = confirm
|
||||||
|
self.steps = []
|
||||||
|
|
||||||
|
def do(self, description, action):
|
||||||
|
self.steps.append(("DONE " if self.confirm else "WOULD ") + description)
|
||||||
|
if self.confirm:
|
||||||
|
action()
|
||||||
|
|
||||||
|
def skip(self, description):
|
||||||
|
self.steps.append("SKIP " + description)
|
||||||
|
|
||||||
|
|
||||||
|
def run(company_name="Makap Uganda Limited", abbr="MUL", fiscal_year_start="2026-07-01", confirm=0):
|
||||||
|
plan = Plan(bool(int(confirm)))
|
||||||
|
|
||||||
|
_system_settings(plan)
|
||||||
|
_company(plan, company_name, abbr)
|
||||||
|
_fiscal_year(plan, company_name, getdate(fiscal_year_start))
|
||||||
|
_vat(plan, company_name, abbr)
|
||||||
|
_warehouses(plan, company_name, abbr)
|
||||||
|
_manufacturing_and_stock_settings(plan, abbr)
|
||||||
|
|
||||||
|
if plan.confirm:
|
||||||
|
frappe.db.commit()
|
||||||
|
print("\n".join(plan.steps))
|
||||||
|
if not plan.confirm:
|
||||||
|
print("\nDry run only. Re-run with --kwargs \"{'confirm': 1}\" to apply.")
|
||||||
|
return plan.steps
|
||||||
|
|
||||||
|
|
||||||
|
def _system_settings(plan):
|
||||||
|
def apply():
|
||||||
|
if not frappe.db.get_value("Currency", CURRENCY, "enabled"):
|
||||||
|
frappe.db.set_value("Currency", CURRENCY, "enabled", 1)
|
||||||
|
settings = frappe.get_single("System Settings")
|
||||||
|
settings.country = COUNTRY
|
||||||
|
settings.time_zone = TIME_ZONE
|
||||||
|
settings.currency = CURRENCY
|
||||||
|
settings.save()
|
||||||
|
|
||||||
|
plan.do(f"set System Settings to {COUNTRY}, {TIME_ZONE}, {CURRENCY}", apply)
|
||||||
|
|
||||||
|
|
||||||
|
def _company(plan, company_name, abbr):
|
||||||
|
if frappe.db.exists("Company", company_name):
|
||||||
|
plan.skip(f"company {company_name} already exists")
|
||||||
|
return
|
||||||
|
|
||||||
|
def apply():
|
||||||
|
frappe.get_doc(
|
||||||
|
{
|
||||||
|
"doctype": "Company",
|
||||||
|
"company_name": company_name,
|
||||||
|
"abbr": abbr,
|
||||||
|
"country": COUNTRY,
|
||||||
|
"default_currency": CURRENCY,
|
||||||
|
"create_chart_of_accounts_based_on": "Standard Template",
|
||||||
|
"chart_of_accounts": "Standard",
|
||||||
|
}
|
||||||
|
).insert()
|
||||||
|
defaults = frappe.get_single("Global Defaults")
|
||||||
|
defaults.default_company = company_name
|
||||||
|
defaults.default_currency = CURRENCY
|
||||||
|
defaults.country = COUNTRY
|
||||||
|
defaults.save()
|
||||||
|
|
||||||
|
plan.do(f"create company {company_name} ({abbr}) in {CURRENCY} with the Standard chart, and make it the default", apply)
|
||||||
|
|
||||||
|
|
||||||
|
def _fiscal_year(plan, company_name, start):
|
||||||
|
end = frappe.utils.add_days(frappe.utils.add_months(start, 12), -1)
|
||||||
|
overlapping = frappe.get_all(
|
||||||
|
"Fiscal Year",
|
||||||
|
filters={"year_start_date": ["<=", end], "year_end_date": [">=", start]},
|
||||||
|
pluck="name",
|
||||||
|
)
|
||||||
|
if overlapping:
|
||||||
|
plan.skip(
|
||||||
|
f"fiscal year {start}..{end}: overlaps {', '.join(overlapping)}. "
|
||||||
|
"Add the company to it, or adjust it, in the desk"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
def apply():
|
||||||
|
doc = frappe.new_doc("Fiscal Year")
|
||||||
|
doc.year = f"{start.year}-{end.year}"
|
||||||
|
doc.year_start_date = start
|
||||||
|
doc.year_end_date = end
|
||||||
|
doc.append("companies", {"company": company_name})
|
||||||
|
doc.insert()
|
||||||
|
|
||||||
|
plan.do(f"create fiscal year {start}..{end} for {company_name}", apply)
|
||||||
|
|
||||||
|
|
||||||
|
def _tax_account(company_name, abbr, account_name):
|
||||||
|
parent = frappe.db.get_value(
|
||||||
|
"Account", {"company": company_name, "account_name": "Duties and Taxes", "is_group": 1}, "name"
|
||||||
|
)
|
||||||
|
if not parent:
|
||||||
|
frappe.throw(f"No 'Duties and Taxes' group in {company_name}'s chart of accounts.")
|
||||||
|
name = f"{account_name} - {abbr}"
|
||||||
|
if not frappe.db.exists("Account", name):
|
||||||
|
frappe.get_doc(
|
||||||
|
{
|
||||||
|
"doctype": "Account",
|
||||||
|
"account_name": account_name,
|
||||||
|
"parent_account": parent,
|
||||||
|
"company": company_name,
|
||||||
|
"account_type": "Tax",
|
||||||
|
"tax_rate": VAT_RATE,
|
||||||
|
}
|
||||||
|
).insert()
|
||||||
|
return name
|
||||||
|
|
||||||
|
|
||||||
|
def _vat(plan, company_name, abbr):
|
||||||
|
def apply():
|
||||||
|
for doctype, account_name, title in (
|
||||||
|
("Sales Taxes and Charges Template", "VAT Output", "Uganda VAT 18%"),
|
||||||
|
("Purchase Taxes and Charges Template", "VAT Input", "Uganda VAT 18%"),
|
||||||
|
):
|
||||||
|
account = _tax_account(company_name, abbr, account_name)
|
||||||
|
if frappe.db.exists(doctype, {"title": title, "company": company_name}):
|
||||||
|
continue
|
||||||
|
frappe.get_doc(
|
||||||
|
{
|
||||||
|
"doctype": doctype,
|
||||||
|
"title": title,
|
||||||
|
"company": company_name,
|
||||||
|
"is_default": 1,
|
||||||
|
"taxes": [
|
||||||
|
{
|
||||||
|
"charge_type": "On Net Total",
|
||||||
|
"account_head": account,
|
||||||
|
"description": f"VAT @ {VAT_RATE}%",
|
||||||
|
"rate": VAT_RATE,
|
||||||
|
**({"category": "Total", "add_deduct_tax": "Add"} if doctype.startswith("Purchase") else {}),
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
).insert()
|
||||||
|
|
||||||
|
plan.do(f"create VAT Output/Input accounts and default {VAT_RATE}% sales and purchase tax templates", apply)
|
||||||
|
|
||||||
|
|
||||||
|
def _warehouses(plan, company_name, abbr):
|
||||||
|
def apply():
|
||||||
|
parent = frappe.db.get_value("Warehouse", {"company": company_name, "is_group": 1, "parent_warehouse": ["is", "not set"]})
|
||||||
|
for name, extra in WAREHOUSES:
|
||||||
|
if frappe.db.exists("Warehouse", f"{name} - {abbr}"):
|
||||||
|
continue
|
||||||
|
frappe.get_doc(
|
||||||
|
{"doctype": "Warehouse", "warehouse_name": name, "company": company_name, "parent_warehouse": parent, **extra}
|
||||||
|
).insert()
|
||||||
|
|
||||||
|
plan.do("create warehouses: " + ", ".join(name for name, _ in WAREHOUSES), apply)
|
||||||
|
|
||||||
|
|
||||||
|
def _manufacturing_and_stock_settings(plan, abbr):
|
||||||
|
def apply():
|
||||||
|
manufacturing = frappe.get_single("Manufacturing Settings")
|
||||||
|
manufacturing.backflush_raw_materials_based_on = "Material Transferred for Manufacture"
|
||||||
|
manufacturing.enforce_time_logs = 1
|
||||||
|
manufacturing.default_wip_warehouse = f"Work In Progress - {abbr}"
|
||||||
|
manufacturing.default_fg_warehouse = f"Finished Goods - {abbr}"
|
||||||
|
manufacturing.save()
|
||||||
|
|
||||||
|
stock = frappe.get_single("Stock Settings")
|
||||||
|
stock.enable_stock_reservation = 1
|
||||||
|
stock.save()
|
||||||
|
|
||||||
|
plan.do(
|
||||||
|
"Manufacturing Settings: backflush on Material Transferred, enforce Job Card time logs, "
|
||||||
|
"default WIP/FG warehouses; Stock Settings: enable stock reservation",
|
||||||
|
apply,
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user