diff --git a/erpnext/accounts/doctype/account/chart_of_accounts/verified/philippines.json b/erpnext/accounts/doctype/account/chart_of_accounts/verified/philippines.json new file mode 100644 index 00000000000..ea3977711a7 --- /dev/null +++ b/erpnext/accounts/doctype/account/chart_of_accounts/verified/philippines.json @@ -0,0 +1,840 @@ +{ + "name": "Philippines", + "country": "Philippines", + "tree": { + "Asset": { + "account_number": "1000", + "is_group": 1, + "root_type": "Asset", + "Current Assets": { + "account_number": "1001", + "is_group": 1, + "root_type": "Asset", + "Cash": { + "account_number": "1100", + "is_group": 1, + "root_type": "Asset", + "account_type": "Cash", + "Cash on Hand": { + "account_number": "1101", + "is_group": 0, + "root_type": "Asset", + "account_type": "Cash" + }, + "Petty Cash Fund": { + "account_number": "1200", + "is_group": 1, + "root_type": "Asset", + "account_type": "Cash", + "Petty Cash Fund": { + "account_number": "1201", + "is_group": 0, + "root_type": "Asset", + "account_type": "Cash" + } + } + }, + "Bank Accounts": { + "account_number": "1102", + "is_group": 1, + "root_type": "Asset", + "account_type": "Bank" + }, + "Advances to Officers & Employees": { + "account_number": "1290", + "is_group": 1, + "root_type": "Asset", + "Advances to Officers & Employees": { + "account_number": "1291", + "is_group": 0, + "root_type": "Asset" + } + }, + "Accounts Receivable Trade": { + "account_number": "1300", + "is_group": 1, + "root_type": "Asset", + "Accounts Receivable - Trade": { + "account_number": "1301", + "is_group": 0, + "root_type": "Asset", + "account_type": "Receivable" + } + }, + "Accounts Receivable - Affiliates": { + "account_number": "1310", + "is_group": 1, + "root_type": "Asset", + "Due from Company": { + "account_number": "1311", + "is_group": 0, + "root_type": "Asset" + } + }, + "Accounts Receivable - Others": { + "account_number": "1400", + "is_group": 1, + "root_type": "Asset", + "Accounts Receivable - Others": { + "account_number": "1401", + "is_group": 0, + "root_type": "Asset" + } + }, + "Parts, Materials and Supplies": { + "account_number": "1500", + "is_group": 1, + "root_type": "Asset", + "Parts, Materials and Supplies": { + "account_number": "1501", + "is_group": 0, + "root_type": "Asset" + }, + "Raw Materials - Demo": { + "account_number": "1502", + "is_group": 0, + "root_type": "Asset" + } + }, + "Project in Progress": { + "account_number": "1510", + "is_group": 1, + "root_type": "Asset", + "Project in Progress": { + "account_number": "1511", + "is_group": 0, + "root_type": "Asset" + }, + "Factory Overhead Variance": { + "account_number": "1512", + "is_group": 0, + "root_type": "Asset" + } + }, + "Finished Goods": { + "account_number": "1520", + "is_group": 1, + "root_type": "Asset", + "Finished Goods Inventory": { + "account_number": "1531", + "is_group": 0, + "root_type": "Asset", + "account_type": "Stock" + }, + "Inventory in Transit": { + "account_number": "1532", + "is_group": 0, + "root_type": "Asset", + "account_type": "Stock Adjustment" + } + }, + "Prepayments": { + "account_number": "1600", + "is_group": 1, + "root_type": "Asset", + "Prepaid Insurance & Bonds": { + "account_number": "1601", + "is_group": 0, + "root_type": "Asset" + }, + "Prepaid Rent": { + "account_number": "1602", + "is_group": 0, + "root_type": "Asset" + } + }, + "VAT Input Tax": { + "account_number": "1610", + "is_group": 1, + "root_type": "Asset", + "VAT Input Tax - Goods": { + "account_number": "1611", + "is_group": 0, + "root_type": "Asset", + "account_type": "Tax" + } + } + }, + "Non - Current Assets": { + "account_number": "1002", + "is_group": 1, + "root_type": "Asset", + "Property, Plants And Equipments": { + "account_number": "1700", + "is_group": 1, + "root_type": "Asset", + "Land": { + "account_number": "1701", + "is_group": 0, + "root_type": "Asset", + "account_type": "Fixed Asset" + }, + "Buildings & Improvements": { + "account_number": "1702", + "is_group": 0, + "root_type": "Asset", + "account_type": "Fixed Asset" + }, + "Delivery & Trans Equipment": { + "account_number": "1703", + "is_group": 0, + "root_type": "Asset", + "account_type": "Fixed Asset" + }, + "Furniture & Fixtures": { + "account_number": "1704", + "is_group": 0, + "root_type": "Asset", + "account_type": "Fixed Asset" + }, + "Machinery & Equipment": { + "account_number": "1705", + "is_group": 0, + "root_type": "Asset", + "account_type": "Fixed Asset" + } + }, + "Accum Depr. - Property, Plants and Equipment": { + "account_number": "1800", + "is_group": 1, + "root_type": "Asset", + "Accumulated Dep Bdgs & Improv": { + "account_number": "1801", + "is_group": 0, + "root_type": "Asset", + "account_type": "Accumulated Depreciation" + }, + "Accumulated Dep Delivery & Trans": { + "account_number": "1802", + "is_group": 0, + "root_type": "Asset", + "account_type": "Accumulated Depreciation" + }, + "Accumulated Dep Furniture & Fixture": { + "account_number": "1803", + "is_group": 0, + "root_type": "Asset", + "account_type": "Accumulated Depreciation" + }, + "Accumulated Depreciation - Machinery & Equipment": { + "account_number": "1804", + "is_group": 0, + "root_type": "Asset", + "account_type": "Accumulated Depreciation" + } + } + }, + "Other Assets": { + "account_number": "1003", + "is_group": 1, + "root_type": "Asset", + "Advances To Supplier": { + "account_number": "1900", + "is_group": 1, + "root_type": "Asset", + "Advances To Supplier": { + "account_number": "1901", + "is_group": 0, + "root_type": "Asset" + } + }, + "Miscellaneous Deposits": { + "account_number": "1910", + "is_group": 1, + "root_type": "Asset", + "Miscellaneous Deposits": { + "account_number": "1911", + "is_group": 0, + "root_type": "Asset" + } + }, + "Retirement Fund": { + "account_number": "1920", + "is_group": 1, + "root_type": "Asset", + "Retirement Fund": { + "account_number": "1921", + "is_group": 0, + "root_type": "Asset" + } + }, + "Investment": { + "account_number": "1930", + "is_group": 1, + "root_type": "Asset", + "Investment": { + "account_number": "1931", + "is_group": 0, + "root_type": "Asset" + } + }, + "System Development": { + "account_number": "1940", + "is_group": 1, + "root_type": "Asset", + "System Development": { + "account_number": "1941", + "is_group": 0, + "root_type": "Asset" + } + } + } + }, + "Liability": { + "account_number": "2000", + "is_group": 1, + "root_type": "Liability", + "Current Liabilities": { + "account_number": "2001", + "is_group": 1, + "root_type": "Liability", + "Accounts Payable Trade": { + "account_number": "2100", + "is_group": 1, + "root_type": "Liability", + "Accounts Payable - Trade": { + "account_number": "2101", + "is_group": 0, + "root_type": "Liability", + "account_type": "Payable" + } + }, + "Accounts Payable Others": { + "account_number": "2110", + "is_group": 1, + "root_type": "Liability", + "Accounts Payable - Payroll": { + "account_number": "2111", + "is_group": 0, + "root_type": "Liability" + } + }, + "VAT Output Tax": { + "account_number": "2200", + "is_group": 1, + "root_type": "Liability", + "VAT Output Tax": { + "account_number": "2201", + "is_group": 0, + "root_type": "Liability", + "account_type": "Tax" + } + }, + "Withholding Taxes Payable Wages": { + "account_number": "2210", + "is_group": 1, + "root_type": "Liability", + "Withholding Taxes Payable Wages": { + "account_number": "2211", + "is_group": 0, + "root_type": "Liability", + "account_type": "Tax" + } + }, + "Withholding Taxes Payable Expanded": { + "account_number": "2220", + "is_group": 1, + "root_type": "Liability", + "Withholding Taxes Payable Expanded": { + "account_number": "2221", + "is_group": 0, + "root_type": "Liability", + "account_type": "Tax" + } + }, + "Accruals And Other Current Payables": { + "account_number": "2300", + "is_group": 1, + "root_type": "Liability", + "Stock Received But Not Billed": { + "account_number": "2301", + "is_group": 0, + "root_type": "Liability", + "account_type": "Stock Received But Not Billed" + } + }, + "Payable to Government and Other Institutions": { + "account_number": "2400", + "is_group": 1, + "root_type": "Liability", + "SSS Premium Payable": { + "account_number": "2401", + "is_group": 0, + "root_type": "Liability" + }, + "SSS Salary Loan Payable": { + "account_number": "2402", + "is_group": 0, + "root_type": "Liability" + }, + "PhilHealth Premium": { + "account_number": "2403", + "is_group": 0, + "root_type": "Liability" + }, + "Pag-ibig Loan Payable": { + "account_number": "2404", + "is_group": 0, + "root_type": "Liability" + }, + "Coop Loans": { + "account_number": "2405", + "is_group": 0, + "root_type": "Liability" + }, + "Coop Contributions": { + "account_number": "2406", + "is_group": 0, + "root_type": "Liability" + }, + "Canteen": { + "account_number": "2407", + "is_group": 0, + "root_type": "Liability" + }, + "AUB Loan Payable": { + "account_number": "2408", + "is_group": 0, + "root_type": "Liability" + }, + "HSBC Loan Payable": { + "account_number": "2409", + "is_group": 0, + "root_type": "Liability" + } + }, + "Customer Deposits": { + "account_number": "2500", + "is_group": 0, + "root_type": "Liability", + "account_type": "Payable" + } + }, + "Non Current Liabilities": { + "account_number": "2002", + "is_group": 1, + "root_type": "Liability", + "Due To Associated Company": { + "account_number": "2600", + "is_group": 1, + "root_type": "Liability", + "Due To Associated Company": { + "account_number": "2601", + "is_group": 0, + "root_type": "Liability" + } + }, + "Deferred Income": { + "account_number": "2700", + "is_group": 1, + "root_type": "Liability", + "Deferred Income": { + "account_number": "2701", + "is_group": 0, + "root_type": "Liability" + } + }, + "Notes Payable": { + "account_number": "2800", + "is_group": 1, + "root_type": "Liability", + "Notes Payable": { + "account_number": "2801", + "is_group": 0, + "root_type": "Liability" + } + }, + "Dividends Payable": { + "account_number": "2900", + "is_group": 0, + "root_type": "Liability" + } + } + }, + "Equity": { + "account_number": "3000", + "is_group": 1, + "root_type": "Equity", + "STOCKHOLDER'S EQUITY": { + "account_number": "3001", + "is_group": 1, + "root_type": "Equity", + "Capital Stocks": { + "account_number": "3100", + "is_group": 1, + "root_type": "Equity", + "Capital Stocks": { + "account_number": "3101", + "is_group": 0, + "root_type": "Equity" + } + }, + "Subscription Receivable": { + "account_number": "3200", + "is_group": 1, + "root_type": "Equity", + "Subscription Receivable": { + "account_number": "3201", + "is_group": 0, + "root_type": "Equity" + } + }, + "Retained Earnings": { + "account_number": "3300", + "is_group": 1, + "root_type": "Equity", + "Retained Earnings": { + "account_number": "3301", + "is_group": 0, + "root_type": "Equity" + } + }, + "Current Year (Profit/Loss)": { + "account_number": "3400", + "is_group": 0, + "root_type": "Equity" + }, + "Drawings": { + "account_number": "3500", + "is_group": 1, + "root_type": "Equity", + "Drawings": { + "account_number": "3501", + "is_group": 0, + "root_type": "Equity" + } + } + } + }, + "Income": { + "account_number": "4000", + "is_group": 1, + "root_type": "Income", + "Gross Sales": { + "account_number": "4100", + "is_group": 1, + "root_type": "Income", + "Sales": { + "account_number": "4101", + "is_group": 0, + "root_type": "Income" + } + }, + "Sales Adjustment": { + "account_number": "4200", + "is_group": 1, + "root_type": "Income", + "Sales Return And Allowance": { + "account_number": "4201", + "is_group": 0, + "root_type": "Income" + } + }, + "Sales Discount": { + "account_number": "4300", + "is_group": 1, + "root_type": "Income", + "Sales Discount": { + "account_number": "4301", + "is_group": 0, + "root_type": "Income" + } + }, + "Other Income": { + "account_number": "6000", + "is_group": 1, + "root_type": "Income", + "Interest Income Bank": { + "account_number": "6010", + "is_group": 1, + "root_type": "Income", + "Interest Income Bank": { + "account_number": "6011", + "is_group": 0, + "root_type": "Income" + } + }, + "Dividend Income": { + "account_number": "6020", + "is_group": 1, + "root_type": "Income", + "Dividend Income": { + "account_number": "6021", + "is_group": 0, + "root_type": "Income" + } + } + } + }, + "Expense": { + "account_number": "5000", + "is_group": 1, + "root_type": "Expense", + "Operating Expenses": { + "account_number": "5100", + "is_group": 1, + "root_type": "Expense", + "Salaries, Wages": { + "account_number": "5101", + "is_group": 0, + "root_type": "Expense" + }, + "13th Month Pay & Bonus": { + "account_number": "5102", + "is_group": 0, + "root_type": "Expense" + }, + "Overtime & Night Diff": { + "account_number": "5103", + "is_group": 0, + "root_type": "Expense" + }, + "Incentive/Performance Bonus": { + "account_number": "5104", + "is_group": 0, + "root_type": "Expense" + }, + "Employees Benefits": { + "account_number": "5105", + "is_group": 0, + "root_type": "Expense" + }, + "Advertising & Promotions": { + "account_number": "5106", + "is_group": 0, + "root_type": "Expense" + }, + "Amortization of Leasehold Improvement": { + "account_number": "5107", + "is_group": 0, + "root_type": "Expense" + }, + "Amortization of Pre-Operating": { + "account_number": "5108", + "is_group": 0, + "root_type": "Expense" + }, + "Amortization of System Development": { + "account_number": "5109", + "is_group": 0, + "root_type": "Expense" + }, + "Audit & Legal Fee": { + "account_number": "5110", + "is_group": 0, + "root_type": "Expense" + }, + "Bad Debts Expenses": { + "account_number": "5111", + "is_group": 0, + "root_type": "Expense" + }, + "Client Service & Maintenance": { + "account_number": "5112", + "is_group": 0, + "root_type": "Expense" + }, + "Commission Expenses": { + "account_number": "5113", + "is_group": 0, + "root_type": "Expense" + }, + "Communications": { + "account_number": "5114", + "is_group": 0, + "root_type": "Expense" + }, + "Contractual Services": { + "account_number": "5115", + "is_group": 0, + "root_type": "Expense" + }, + "Depreciation Expenses": { + "account_number": "5116", + "is_group": 0, + "root_type": "Expense", + "account_type": "Depreciation" + }, + "Donation & Contribution": { + "account_number": "5117", + "is_group": 0, + "root_type": "Expense" + }, + "Dues & Subscription": { + "account_number": "5118", + "is_group": 0, + "root_type": "Expense" + }, + "Employee Med/Dental/Hosp Expenses": { + "account_number": "5119", + "is_group": 0, + "root_type": "Expense" + }, + "Employee Uniforms": { + "account_number": "5120", + "is_group": 0, + "root_type": "Expense" + }, + "Equipage": { + "account_number": "5121", + "is_group": 0, + "root_type": "Expense" + }, + "Expenses for Reclassification": { + "account_number": "5122", + "is_group": 0, + "root_type": "Expense" + }, + "Gas & Oil": { + "account_number": "5123", + "is_group": 0, + "root_type": "Expense" + }, + "Insurance Expenses": { + "account_number": "5124", + "is_group": 0, + "root_type": "Expense" + }, + "Light & Water": { + "account_number": "5125", + "is_group": 0, + "root_type": "Expense" + }, + "Local/Overseas Travel": { + "account_number": "5126", + "is_group": 0, + "root_type": "Expense" + }, + "Meals & Transportation Expenses": { + "account_number": "5127", + "is_group": 0, + "root_type": "Expense" + }, + "Meeting & Conferences": { + "account_number": "5128", + "is_group": 0, + "root_type": "Expense" + }, + "Miscellaneous Expenses": { + "account_number": "5129", + "is_group": 0, + "root_type": "Expense" + }, + "Mockup Expenses": { + "account_number": "5130", + "is_group": 0, + "root_type": "Expense" + }, + "Obsolescence Expenses": { + "account_number": "5131", + "is_group": 0, + "root_type": "Expense" + }, + "Other Support Cost": { + "account_number": "5132", + "is_group": 0, + "root_type": "Expense" + }, + "Pag-ibig Contribution": { + "account_number": "5133", + "is_group": 0, + "root_type": "Expense" + }, + "Performance Bonds": { + "account_number": "5134", + "is_group": 0, + "root_type": "Expense" + }, + "Pre Employment Expenses": { + "account_number": "5135", + "is_group": 0, + "root_type": "Expense" + }, + "Professional Fees": { + "account_number": "5136", + "is_group": 0, + "root_type": "Expense" + }, + "Recruitment & Employment": { + "account_number": "5137", + "is_group": 0, + "root_type": "Expense" + }, + "Rent Expenses": { + "account_number": "5138", + "is_group": 0, + "root_type": "Expense" + }, + "Rent Expenses Others": { + "account_number": "5139", + "is_group": 0, + "root_type": "Expense" + }, + "Repairs & Maintenance": { + "account_number": "5140", + "is_group": 0, + "root_type": "Expense" + }, + "Representation Expenses": { + "account_number": "5141", + "is_group": 0, + "root_type": "Expense" + }, + "Research & Development": { + "account_number": "5142", + "is_group": 0, + "root_type": "Expense" + }, + "Security Expenses": { + "account_number": "5143", + "is_group": 0, + "root_type": "Expense" + }, + "Shared Services Fee": { + "account_number": "5144", + "is_group": 0, + "root_type": "Expense" + }, + "SSS/Medicare/EC Contributions": { + "account_number": "5145", + "is_group": 0, + "root_type": "Expense" + }, + "Stationery & Supplies": { + "account_number": "5146", + "is_group": 0, + "root_type": "Expense" + }, + "Taxes & Licenses": { + "account_number": "5147", + "is_group": 0, + "root_type": "Expense", + "account_type": "Tax" + }, + "Training & Seminar": { + "account_number": "5148", + "is_group": 0, + "root_type": "Expense" + } + }, + "Stock Adjustment": { + "account_number": "5200", + "is_group": 0, + "root_type": "Expense", + "account_type": "Stock Adjustment" + }, + "Round Off": { + "account_number": "5300", + "is_group": 0, + "root_type": "Expense", + "account_type": "Round Off" + }, + "Expenses Included In Valuation": { + "account_number": "5400", + "is_group": 0, + "root_type": "Expense", + "account_type": "Expenses Included In Valuation" + } + } + } +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json index e059777dde2..08ea31b5143 100644 --- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json +++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json @@ -109,6 +109,7 @@ "sales_invoice_item", "material_request", "material_request_item", + "delivered_by_supplier", "item_weight_details", "weight_per_unit", "total_weight", @@ -978,12 +979,21 @@ "fieldtype": "Currency", "label": "Distributed Discount Amount", "options": "currency" + }, + { + "default": "0", + "fieldname": "delivered_by_supplier", + "fieldtype": "Check", + "hidden": 1, + "label": "Delivered by Supplier", + "print_hide": 1, + "read_only": 1 } ], "idx": 1, "istable": 1, "links": [], - "modified": "2026-04-07 15:41:45.687554", + "modified": "2026-05-06 08:08:40.782395", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice Item", diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.py b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.py index 96db9d66f05..f44291249a4 100644 --- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.py +++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.py @@ -31,6 +31,7 @@ class PurchaseInvoiceItem(Document): conversion_factor: DF.Float cost_center: DF.Link | None deferred_expense_account: DF.Link | None + delivered_by_supplier: DF.Check description: DF.TextEditor | None discount_amount: DF.Currency discount_percentage: DF.Percent diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index b6a939a1b8b..61b9cf1fc06 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -140,7 +140,7 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends ( this.frm.add_custom_button( __("Payment Request"), function () { - me.make_payment_request_with_schedule(); + me.make_payment_request(); }, __("Create") ); diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 75bc8e0771e..e903ab1ca30 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -68,6 +68,7 @@ from erpnext.stock.doctype.item.item import get_uom_conv_factor from erpnext.stock.doctype.packed_item.packed_item import make_packing_list from erpnext.stock.get_item_details import ( _get_item_tax_template, + _get_item_tax_template_from_item_group, get_conversion_factor, get_item_details, get_item_tax_map, @@ -325,6 +326,7 @@ class AccountsController(TransactionBase): # Determine if drop ship applies is_drop_ship = self.doctype in { "Purchase Order", + "Purchase Invoice", "Sales Order", "Sales Invoice", } and self.is_drop_ship(self.items) @@ -3646,6 +3648,10 @@ def set_child_tax_template_and_map(item, child_item, parent_doc): } child_item.item_tax_template = _get_item_tax_template(args, item.taxes) + + if not child_item.get("item_tax_template"): + child_item.item_tax_template = _get_item_tax_template_from_item_group(args, item.item_group) + if child_item.get("item_tax_template"): child_item.item_tax_rate = get_item_tax_map( parent_doc.get("company"), child_item.item_tax_template, as_json=True diff --git a/erpnext/crm/doctype/appointment/appointment.py b/erpnext/crm/doctype/appointment/appointment.py index 1b39146bbb2..811cc784cc1 100644 --- a/erpnext/crm/doctype/appointment/appointment.py +++ b/erpnext/crm/doctype/appointment/appointment.py @@ -234,10 +234,13 @@ def _get_agents_sorted_by_asc_workload(date): return agent_list appointment_counter = Counter(agent_list) for appointment in appointments: - assigned_to = frappe.parse_json(appointment._assign) - if not assigned_to: + assign_data = appointment._assign + if isinstance(assign_data, str): + assign_data = assign_data.strip() + if not assign_data: continue - if (assigned_to[0] in agent_list) and getdate(appointment.scheduled_time) == date: + assigned_to = frappe.parse_json(assign_data) + if assigned_to and (assigned_to[0] in agent_list) and getdate(appointment.scheduled_time) == date: appointment_counter[assigned_to[0]] += 1 sorted_agent_list = appointment_counter.most_common() sorted_agent_list.reverse() diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 2de6f934a15..0584320726f 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -324,6 +324,10 @@ class WorkOrder(Document): def calculate_operating_cost(self): self.planned_operating_cost, self.actual_operating_cost = 0.0, 0.0 for d in self.get("operations"): + if not d.hour_rate: + if d.workstation: + d.hour_rate = get_hour_rate(d.workstation) + d.planned_operating_cost = flt( flt(d.hour_rate) * (flt(d.time_in_mins) / 60.0), d.precision("planned_operating_cost") ) @@ -1895,3 +1899,8 @@ def make_stock_return_entry(work_order): stock_entry.set_stock_entry_type() return stock_entry + + +@frappe.request_cache +def get_hour_rate(workstation): + return frappe.get_cached_value("Workstation", workstation, "hour_rate") or 0.0 diff --git a/erpnext/projects/doctype/task/task.json b/erpnext/projects/doctype/task/task.json index 8ff7fabc23e..0e61f636f44 100644 --- a/erpnext/projects/doctype/task/task.json +++ b/erpnext/projects/doctype/task/task.json @@ -304,7 +304,7 @@ "label": "More Info" }, { - "depends_on": "eval:doc.status == \"Closed\" || doc.status == \"Pending Review\"", + "depends_on": "eval:doc.status == \"Completed\" || doc.status == \"Pending Review\"", "fieldname": "review_date", "fieldtype": "Date", "label": "Review Date", @@ -312,7 +312,7 @@ "oldfieldtype": "Date" }, { - "depends_on": "eval:doc.status == \"Closed\"", + "depends_on": "eval:doc.status == \"Completed\"", "fieldname": "closing_date", "fieldtype": "Date", "label": "Closing Date", diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js index df2355255f4..01d458d6b16 100644 --- a/erpnext/public/js/controllers/buying.js +++ b/erpnext/public/js/controllers/buying.js @@ -25,15 +25,15 @@ erpnext.buying = { }; }); - const project_filters = { + const get_project_filters = () => ({ query: "erpnext.controllers.queries.get_project_name", filters: { - company: doc.company, + company: this.frm.doc.company, }, - }; + }); - this.frm.set_query("project", (_) => project_filters); - this.frm.set_query("project", "items", (_, __, ___) => project_filters); + this.frm.set_query("project", get_project_filters); + this.frm.set_query("project", "items", get_project_filters); if (this.frm.doc.__islocal && frappe.meta.has_field(this.frm.doc.doctype, "disable_rounded_total")) { @@ -176,9 +176,14 @@ erpnext.buying = { callback: (r) => { if (!r.message) return; - this.frm.set_value("billing_address", r.message.primary_address || ""); + if (!this.frm.doc.billing_address) { + this.frm.set_value("billing_address", r.message.primary_address || ""); + } - if (frappe.meta.has_field(this.frm.doc.doctype, "shipping_address")) { + if ( + frappe.meta.has_field(this.frm.doc.doctype, "shipping_address") && + !this.frm.doc.shipping_address + ) { this.frm.set_value("shipping_address", r.message.shipping_address || ""); } }, diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index b0b1281aeff..7c1981973a9 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1032,7 +1032,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe ["Purchase Order", "Purchase Receipt", "Purchase Invoice"].includes(this.frm.doctype) && !this.frm.doc.shipping_address ) { - let is_drop_ship = me.frm.doc.items.some((item) => item.delivered_by_supplier); + const is_drop_ship = me.frm.doc.items.some((item) => item.delivered_by_supplier); if (!is_drop_ship) { erpnext.utils.get_shipping_address(this.frm, function() { diff --git a/erpnext/public/js/queries.js b/erpnext/public/js/queries.js index 63651ec8759..8b174da244f 100644 --- a/erpnext/public/js/queries.js +++ b/erpnext/public/js/queries.js @@ -106,15 +106,19 @@ $.extend(erpnext.queries, { }); } + let filters = { link_doctype: "Company", link_name: doc.company || "" }; + const is_drop_ship = doc.items.some((item) => item.delivered_by_supplier); + if (is_drop_ship) filters = {}; + return { query: "frappe.contacts.doctype.address.address.address_query", - filters: { link_doctype: "Company", link_name: doc.company }, + filters: filters, }; }, dispatch_address_query: function (doc) { - var filters = { link_doctype: "Company", link_name: doc.company || "" }; - var is_drop_ship = doc.items.some((item) => item.delivered_by_supplier); + let filters = { link_doctype: "Company", link_name: doc.company || "" }; + const is_drop_ship = doc.items.some((item) => item.delivered_by_supplier); if (is_drop_ship) filters = {}; return { query: "frappe.contacts.doctype.address.address.address_query", diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index 1452a1c1e49..cb573b75452 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -776,7 +776,7 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex if (frappe.boot.user.in_create.includes("Payment Request")) { this.frm.add_custom_button( __("Payment Request"), - () => this.make_payment_request_with_schedule(), + () => this.make_payment_request(), __("Create") ); } diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index d7a2f9d5e26..b5de5fdc99c 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -1588,7 +1588,7 @@ class TestSalesOrder(AccountsTestMixin, FrappeTestCase): make_item( # template item "Test-WO-Tshirt", { - "has_variant": 1, + "has_variants": 1, "variant_based_on": "Item Attribute", "attributes": [{"attribute": "Test Colour"}], }, diff --git a/erpnext/setup/doctype/employee/employee.py b/erpnext/setup/doctype/employee/employee.py index db4446cce77..812613bb6af 100755 --- a/erpnext/setup/doctype/employee/employee.py +++ b/erpnext/setup/doctype/employee/employee.py @@ -311,6 +311,7 @@ def is_holiday(employee, date=None, raise_exception=True, only_non_weekly=False, @frappe.whitelist() def deactivate_sales_person(status=None, employee=None): + frappe.has_permission("Employee", doc=employee, ptype="write", throw=True) if status == "Left": sales_person = frappe.db.get_value("Sales Person", {"Employee": employee}) if sales_person: diff --git a/erpnext/stock/doctype/batch/batch.js b/erpnext/stock/doctype/batch/batch.js index 7c9c0eeb09a..726e9620afe 100644 --- a/erpnext/stock/doctype/batch/batch.js +++ b/erpnext/stock/doctype/batch/batch.js @@ -70,6 +70,7 @@ frappe.ui.form.on("Batch", { item_code: frm.doc.item, for_stock_levels: for_stock_levels, consider_negative_batches: 1, + ignore_reserved_stock: 1, }, callback: (r) => { if (!r.message) { diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index eec66717a1f..b011dcf8fcd 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -201,6 +201,7 @@ class Item(Document): self.validate_warehouse_for_reorder() self.update_bom_item_desc() + self.validate_variant() self.validate_has_variants() self.validate_attributes_in_variants() self.validate_stock_exists_for_template_item() @@ -826,6 +827,43 @@ class Item(Document): enqueue_after_commit=True, ) + def validate_variant(self): + if self.variant_of: + has_variants, based_on = frappe.get_value( + "Item", self.variant_of, ["has_variants", "variant_based_on"] + ) + if not has_variants: + frappe.throw(_("Item {0} is not a template item.").format(frappe.bold(self.variant_of))) + + if based_on == "Item Attribute": + for d in self.attributes: + if not frappe.db.exists( + "Item Variant Attribute", {"attribute": d.attribute, "parent": self.variant_of} + ): + frappe.throw( + _("Attribute {0} is not valid for the selected template.").format( + frappe.bold(d.attribute) + ) + ) + + numeric_values, disabled = frappe.get_value( + "Item Variant Attribute", + {"attribute": d.attribute, "parent": self.variant_of}, + ["numeric_values", "disabled"], + ) + + if disabled: + frappe.throw(_("Attribute {0} is disabled.").format(frappe.bold(d.attribute))) + + if not numeric_values and not frappe.db.exists( + "Item Attribute Value", {"parent": d.attribute, "attribute_value": d.attribute_value} + ): + frappe.throw( + _("Attribute Value {0} is not valid for the selected attribute {1}.").format( + frappe.bold(d.attribute_value), frappe.bold(d.attribute) + ) + ) + def validate_has_variants(self): if not self.has_variants and frappe.db.get_value("Item", self.name, "has_variants"): if frappe.db.exists("Item", {"variant_of": self.name}): diff --git a/erpnext/stock/doctype/pick_list/pick_list.js b/erpnext/stock/doctype/pick_list/pick_list.js index f22965cdb0e..b5367cd846e 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.js +++ b/erpnext/stock/doctype/pick_list/pick_list.js @@ -9,6 +9,22 @@ frappe.ui.form.on("Pick List", { }, 500); }, + set_warehouse_query: function (frm, fieldname, parentfield = null) { + const query = () => { + let filters = { company: frm.doc.company }; + + frm.doc.consider_rejected_warehouses ? null : (filters.is_rejected_warehouse = 0); + + return { filters }; + }; + + if (parentfield) { + frm.set_query(fieldname, parentfield, query); + } else { + frm.set_query(fieldname, query); + } + }, + setup: (frm) => { frm.ignore_doctypes_on_cancel_all = ["Serial and Batch Bundle"]; @@ -21,21 +37,8 @@ frappe.ui.form.on("Pick List", { "Stock Entry": "Stock Entry", }; - frm.set_query("warehouse", "locations", () => { - return { - filters: { - company: frm.doc.company, - }, - }; - }); - - frm.set_query("parent_warehouse", () => { - return { - filters: { - company: frm.doc.company, - }, - }; - }); + frm.events.set_warehouse_query(frm, "warehouse", "locations"); + frm.events.set_warehouse_query(frm, "parent_warehouse"); frm.set_query("work_order", () => { return { diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index d717bbceeb3..41624477882 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -504,9 +504,11 @@ class PickList(TransactionBase): picked_items_details = self.get_picked_items_details(items) self.item_location_map = frappe._dict() - from_warehouses = [self.parent_warehouse] if self.parent_warehouse else [] + from_warehouses = [] + if self.parent_warehouse: + from_warehouses = [self.parent_warehouse] - if self.work_order: + elif self.work_order: root_warehouse = frappe.db.get_value( "Warehouse", {"company": self.company, "parent_warehouse": ["IS", "NOT SET"], "is_group": 1} ) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 5f9e55cee50..ee832b8c6bb 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -214,7 +214,6 @@ class StockEntry(StockController): self.set_transfer_qty() self.validate_uom_is_integer("uom", "qty") self.validate_uom_is_integer("stock_uom", "transfer_qty") - self.validate_warehouse() self.validate_warehouse_of_sabb() self.validate_work_order() self.validate_source_stock_entry() @@ -227,6 +226,7 @@ class StockEntry(StockController): self.mark_finished_and_scrap_items() self.validate_finished_goods() + self.validate_warehouse() self.validate_with_material_request() self.validate_batch() self.validate_inspection() @@ -346,6 +346,9 @@ class StockEntry(StockController): def _set_serial_batch_for_disassembly_from_available_materials(self): available_materials = get_available_materials(self.work_order, self) for row in self.items: + if row.serial_no or row.batch_no or row.serial_and_batch_bundle: + continue + warehouse = row.s_warehouse or row.t_warehouse materials = available_materials.get((row.item_code, warehouse)) if not materials: @@ -772,15 +775,14 @@ class StockEntry(StockController): frappe.throw(_("Target warehouse is mandatory for row {0}").format(d.idx)) if self.purpose == "Manufacture": - if has_bom: - if d.is_finished_item or d.is_scrap_item: - d.s_warehouse = None - if not d.t_warehouse: - frappe.throw(_("Target warehouse is mandatory for row {0}").format(d.idx)) - else: - d.t_warehouse = None - if not d.s_warehouse: - frappe.throw(_("Source warehouse is mandatory for row {0}").format(d.idx)) + if d.is_finished_item or d.is_scrap_item: + d.s_warehouse = None + if not d.t_warehouse: + frappe.throw(_("Target warehouse is mandatory for row {0}").format(d.idx)) + else: + d.t_warehouse = None + if not d.s_warehouse: + frappe.throw(_("Source warehouse is mandatory for row {0}").format(d.idx)) if self.purpose == "Disassemble": if has_bom: diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 4e13bb246f2..e055de42adf 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -671,6 +671,7 @@ def get_item_tax_info(company, tax_category, item_codes, item_rates=None, item_t return out +# nosemgrep: frappe-semgrep-rules.rules.security.missing-argument-type-hint @frappe.whitelist() def get_item_tax_template(args, item=None, out=None): if isinstance(args, str): @@ -687,11 +688,7 @@ def get_item_tax_template(args, item=None, out=None): item_tax_template = _get_item_tax_template(args, item.taxes, out) if not item_tax_template: - item_group = item.item_group - while item_group and not item_tax_template: - item_group_doc = frappe.get_cached_doc("Item Group", item_group) - item_tax_template = _get_item_tax_template(args, item_group_doc.taxes, out) - item_group = item_group_doc.parent_item_group + item_tax_template = _get_item_tax_template_from_item_group(args, item.item_group, out) if out and args.get("child_doctype") and item_tax_template: out.update(get_fetch_values(args.get("child_doctype"), "item_tax_template", item_tax_template)) @@ -699,6 +696,18 @@ def get_item_tax_template(args, item=None, out=None): return item_tax_template +def _get_item_tax_template_from_item_group(args, item_group, out=None): + from frappe.utils.nestedset import get_ancestors_of + + ancestors = get_ancestors_of("Item Group", item_group) + for group in [item_group, *ancestors]: + group_doc = frappe.get_cached_doc("Item Group", group) + item_tax_template = _get_item_tax_template(args, group_doc.taxes, out) + if item_tax_template: + return item_tax_template + return None + + def _get_item_tax_template(args, taxes, out=None, for_validate=False): if out is None: out = {} diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.py b/erpnext/stock/report/stock_ageing/stock_ageing.py index 30a67324bea..7bc96941b0e 100644 --- a/erpnext/stock/report/stock_ageing/stock_ageing.py +++ b/erpnext/stock/report/stock_ageing/stock_ageing.py @@ -11,6 +11,7 @@ from frappe.query_builder.functions import Count from frappe.utils import cint, date_diff, flt, get_datetime from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos +from erpnext.stock.valuation import round_off_if_near_zero Filters = frappe._dict @@ -115,10 +116,14 @@ def get_range_age(filters: Filters, fifo_queue: list, to_date: str, item_dict: d i *= 2 range_values[i] = flt(range_values[i] + qty, precision) range_values[i + 1] = flt(range_values[i + 1] + stock_value, precision) + if range_values[i] == 0.0 and round_off_if_near_zero(range_values[i + 1], 2) == 0: + range_values[i + 1] = 0.0 break else: range_values[-2] = flt(range_values[-2] + qty, precision) range_values[-1] = flt(range_values[-1] + stock_value, precision) + if range_values[-2] == 0.0 and round_off_if_near_zero(range_values[-1], 2) == 0: + range_values[-1] = 0.0 return range_values