mirror of
https://github.com/frappe/erpnext.git
synced 2026-04-21 07:38:29 +00:00
Merge branch 'develop' into refactor-payment-request
This commit is contained in:
@@ -13,6 +13,7 @@
|
||||
"col_break_1",
|
||||
"description",
|
||||
"included_in_paid_amount",
|
||||
"set_by_item_tax_template",
|
||||
"accounting_dimensions_section",
|
||||
"cost_center",
|
||||
"dimension_col_break",
|
||||
@@ -194,12 +195,22 @@
|
||||
"oldfieldtype": "Currency",
|
||||
"options": "Company:company:default_currency",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "set_by_item_tax_template",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 1,
|
||||
"label": "Set by Item Tax Template",
|
||||
"print_hide": 1,
|
||||
"read_only": 1,
|
||||
"report_hide": 1
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2024-09-24 06:51:07.417348",
|
||||
"modified": "2024-11-22 19:16:22.346267",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Advance Taxes and Charges",
|
||||
@@ -208,4 +219,4 @@
|
||||
"sort_field": "creation",
|
||||
"sort_order": "ASC",
|
||||
"states": []
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,7 @@ class AdvanceTaxesandCharges(Document):
|
||||
parenttype: DF.Data
|
||||
rate: DF.Float
|
||||
row_id: DF.Data | None
|
||||
set_by_item_tax_template: DF.Check
|
||||
tax_amount: DF.Currency
|
||||
total: DF.Currency
|
||||
# end: auto-generated types
|
||||
|
||||
@@ -19,16 +19,6 @@
|
||||
"currency",
|
||||
"column_break_11",
|
||||
"conversion_rate",
|
||||
"address_and_contact_section",
|
||||
"customer_address",
|
||||
"address_display",
|
||||
"contact_person",
|
||||
"contact_display",
|
||||
"column_break_16",
|
||||
"company_address",
|
||||
"company_address_display",
|
||||
"contact_mobile",
|
||||
"contact_email",
|
||||
"section_break_6",
|
||||
"dunning_type",
|
||||
"column_break_8",
|
||||
@@ -56,7 +46,21 @@
|
||||
"income_account",
|
||||
"column_break_48",
|
||||
"cost_center",
|
||||
"amended_from"
|
||||
"amended_from",
|
||||
"address_and_contact_tab",
|
||||
"address_and_contact_section",
|
||||
"customer_address",
|
||||
"address_display",
|
||||
"column_break_vodj",
|
||||
"contact_person",
|
||||
"contact_display",
|
||||
"contact_mobile",
|
||||
"contact_email",
|
||||
"section_break_xban",
|
||||
"column_break_16",
|
||||
"company_address",
|
||||
"company_address_display",
|
||||
"column_break_lqmf"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
@@ -178,10 +182,8 @@
|
||||
"label": "Rate of Interest (%) Yearly"
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"fieldname": "address_and_contact_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Address and Contact"
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "address_display",
|
||||
@@ -377,11 +379,28 @@
|
||||
{
|
||||
"fieldname": "column_break_48",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "address_and_contact_tab",
|
||||
"fieldtype": "Tab Break",
|
||||
"label": "Address & Contact"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_vodj",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_xban",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_lqmf",
|
||||
"fieldtype": "Column Break"
|
||||
}
|
||||
],
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2024-03-27 13:08:19.176146",
|
||||
"modified": "2024-11-26 13:46:07.760867",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Dunning",
|
||||
|
||||
@@ -218,11 +218,25 @@ class TestPOSClosingEntry(IntegrationTestCase):
|
||||
opening_entry = create_opening_entry(pos_profile, test_user.name)
|
||||
|
||||
pos_inv = create_pos_invoice(
|
||||
item_code=item_code, qty=5, rate=300, use_serial_batch_fields=1, batch_no=batch_no
|
||||
item_code=item_code,
|
||||
qty=5,
|
||||
rate=300,
|
||||
use_serial_batch_fields=1,
|
||||
batch_no=batch_no,
|
||||
do_not_submit=True,
|
||||
)
|
||||
pos_inv.payments[0].amount = pos_inv.grand_total
|
||||
pos_inv.submit()
|
||||
pos_inv2 = create_pos_invoice(
|
||||
item_code=item_code, qty=5, rate=300, use_serial_batch_fields=1, batch_no=batch_no
|
||||
item_code=item_code,
|
||||
qty=5,
|
||||
rate=300,
|
||||
use_serial_batch_fields=1,
|
||||
batch_no=batch_no,
|
||||
do_not_submit=True,
|
||||
)
|
||||
pos_inv2.payments[0].amount = pos_inv2.grand_total
|
||||
pos_inv2.submit()
|
||||
|
||||
batch_qty = frappe.db.get_value("Batch", batch_no, "batch_qty")
|
||||
self.assertEqual(batch_qty, 10)
|
||||
|
||||
@@ -240,6 +240,7 @@ class POSInvoice(SalesInvoice):
|
||||
from erpnext.accounts.doctype.pricing_rule.utils import update_coupon_code_count
|
||||
|
||||
update_coupon_code_count(self.coupon_code, "used")
|
||||
self.clear_unallocated_mode_of_payments()
|
||||
|
||||
def before_cancel(self):
|
||||
if (
|
||||
@@ -278,6 +279,12 @@ class POSInvoice(SalesInvoice):
|
||||
|
||||
self.delink_serial_and_batch_bundle()
|
||||
|
||||
def clear_unallocated_mode_of_payments(self):
|
||||
self.set("payments", self.get("payments", {"amount": ["not in", [0, None, ""]]}))
|
||||
|
||||
sip = frappe.qb.DocType("Sales Invoice Payment")
|
||||
frappe.qb.from_(sip).delete().where(sip.parent == self.name).where(sip.amount == 0).run()
|
||||
|
||||
def delink_serial_and_batch_bundle(self):
|
||||
for row in self.items:
|
||||
if row.serial_and_batch_bundle:
|
||||
|
||||
@@ -134,6 +134,7 @@ class TestPOSInvoiceMergeLog(IntegrationTestCase):
|
||||
},
|
||||
)
|
||||
inv.insert()
|
||||
inv.payments[0].amount = inv.grand_total
|
||||
inv.submit()
|
||||
|
||||
inv2 = create_pos_invoice(qty=1, rate=100, do_not_save=True)
|
||||
@@ -150,6 +151,7 @@ class TestPOSInvoiceMergeLog(IntegrationTestCase):
|
||||
},
|
||||
)
|
||||
inv2.insert()
|
||||
inv2.payments[0].amount = inv.grand_total
|
||||
inv2.submit()
|
||||
|
||||
consolidate_pos_invoices()
|
||||
@@ -157,16 +159,19 @@ class TestPOSInvoiceMergeLog(IntegrationTestCase):
|
||||
|
||||
consolidated_invoice = frappe.get_doc("Sales Invoice", inv.consolidated_invoice)
|
||||
item_wise_tax_detail = json.loads(consolidated_invoice.get("taxes")[0].item_wise_tax_detail)
|
||||
|
||||
tax_data = item_wise_tax_detail.get("_Test Item")
|
||||
self.assertEqual(tax_data.get("tax_rate"), 9)
|
||||
self.assertEqual(tax_data.get("tax_amount"), 9)
|
||||
self.assertEqual(tax_data.get("net_amount"), 100)
|
||||
|
||||
tax_data = item_wise_tax_detail.get("_Test Item 2")
|
||||
self.assertEqual(tax_data.get("tax_rate"), 5)
|
||||
self.assertEqual(tax_data.get("tax_amount"), 5)
|
||||
self.assertEqual(tax_data.get("net_amount"), 100)
|
||||
expected_item_wise_tax_detail = {
|
||||
"_Test Item": {
|
||||
"tax_rate": 9,
|
||||
"tax_amount": 9,
|
||||
"net_amount": 100,
|
||||
},
|
||||
"_Test Item 2": {
|
||||
"tax_rate": 5,
|
||||
"tax_amount": 5,
|
||||
"net_amount": 100,
|
||||
},
|
||||
}
|
||||
self.assertEqual(item_wise_tax_detail, expected_item_wise_tax_detail)
|
||||
finally:
|
||||
frappe.set_user("Administrator")
|
||||
frappe.db.sql("delete from `tabPOS Profile`")
|
||||
|
||||
@@ -474,6 +474,7 @@ def send_emails(document_name, from_scheduler=False, posting_date=None):
|
||||
reference_doctype="Process Statement Of Accounts",
|
||||
reference_name=document_name,
|
||||
attachments=attachments,
|
||||
expose_recipients="header",
|
||||
)
|
||||
|
||||
if doc.enable_auto_email and from_scheduler:
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
"account_head",
|
||||
"description",
|
||||
"is_tax_withholding_account",
|
||||
"set_by_item_tax_template",
|
||||
"section_break_10",
|
||||
"rate",
|
||||
"accounting_dimensions_section",
|
||||
@@ -254,12 +255,22 @@
|
||||
"options": "Company:company:default_currency",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "set_by_item_tax_template",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 1,
|
||||
"label": "Set by Item Tax Template",
|
||||
"print_hide": 1,
|
||||
"read_only": 1,
|
||||
"report_hide": 1
|
||||
}
|
||||
],
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2024-09-24 06:47:25.129901",
|
||||
"modified": "2024-11-22 19:17:02.377473",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Purchase Taxes and Charges",
|
||||
|
||||
@@ -42,6 +42,7 @@ class PurchaseTaxesandCharges(Document):
|
||||
parenttype: DF.Data
|
||||
rate: DF.Float
|
||||
row_id: DF.Data | None
|
||||
set_by_item_tax_template: DF.Check
|
||||
tax_amount: DF.Currency
|
||||
tax_amount_after_discount_amount: DF.Currency
|
||||
total: DF.Currency
|
||||
|
||||
@@ -740,20 +740,6 @@ frappe.ui.form.on("Sales Invoice", {
|
||||
};
|
||||
});
|
||||
|
||||
frm.set_query("company_address", function (doc) {
|
||||
if (!doc.company) {
|
||||
frappe.throw(__("Please set Company"));
|
||||
}
|
||||
|
||||
return {
|
||||
query: "frappe.contacts.doctype.address.address.address_query",
|
||||
filters: {
|
||||
link_doctype: "Company",
|
||||
link_name: doc.company,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
frm.set_query("pos_profile", function (doc) {
|
||||
if (!doc.company) {
|
||||
frappe.throw(__("Please set Company"));
|
||||
|
||||
@@ -90,11 +90,14 @@
|
||||
"incoming_rate",
|
||||
"item_tax_rate",
|
||||
"actual_batch_qty",
|
||||
"actual_qty",
|
||||
"section_break_eoec",
|
||||
"serial_no",
|
||||
"column_break_ytgd",
|
||||
"batch_no",
|
||||
"available_quantity_section",
|
||||
"actual_qty",
|
||||
"column_break_ogff",
|
||||
"company_total_stock",
|
||||
"edit_references",
|
||||
"sales_order",
|
||||
"so_detail",
|
||||
@@ -676,7 +679,8 @@
|
||||
"allow_on_submit": 1,
|
||||
"fieldname": "actual_qty",
|
||||
"fieldtype": "Float",
|
||||
"label": "Available Qty at Warehouse",
|
||||
"label": "Qty (Warehouse)",
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "actual_qty",
|
||||
"oldfieldtype": "Currency",
|
||||
"print_hide": 1,
|
||||
@@ -930,12 +934,30 @@
|
||||
"fieldtype": "Currency",
|
||||
"label": "Distributed Discount Amount",
|
||||
"options": "currency"
|
||||
},
|
||||
{
|
||||
"fieldname": "available_quantity_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Available Quantity"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_ogff",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"fieldname": "company_total_stock",
|
||||
"fieldtype": "Float",
|
||||
"label": "Qty (Company)",
|
||||
"no_copy": 1,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2024-10-28 15:06:40.980995",
|
||||
"modified": "2024-11-25 16:27:33.287341",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Invoice Item",
|
||||
|
||||
@@ -34,6 +34,7 @@ class SalesInvoiceItem(Document):
|
||||
base_rate_with_margin: DF.Currency
|
||||
batch_no: DF.Link | None
|
||||
brand: DF.Data | None
|
||||
company_total_stock: DF.Float
|
||||
conversion_factor: DF.Float
|
||||
cost_center: DF.Link
|
||||
customer_item_code: DF.Data | None
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"description",
|
||||
"included_in_print_rate",
|
||||
"included_in_paid_amount",
|
||||
"set_by_item_tax_template",
|
||||
"accounting_dimensions_section",
|
||||
"cost_center",
|
||||
"dimension_col_break",
|
||||
@@ -232,13 +233,23 @@
|
||||
"oldfieldtype": "Currency",
|
||||
"options": "Company:company:default_currency",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "set_by_item_tax_template",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 1,
|
||||
"label": "Set by Item Tax Template",
|
||||
"print_hide": 1,
|
||||
"read_only": 1,
|
||||
"report_hide": 1
|
||||
}
|
||||
],
|
||||
"idx": 1,
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2024-09-24 06:49:32.034074",
|
||||
"modified": "2024-11-22 19:17:31.898467",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Taxes and Charges",
|
||||
|
||||
@@ -40,6 +40,7 @@ class SalesTaxesandCharges(Document):
|
||||
parenttype: DF.Data
|
||||
rate: DF.Float
|
||||
row_id: DF.Data | None
|
||||
set_by_item_tax_template: DF.Check
|
||||
tax_amount: DF.Currency
|
||||
tax_amount_after_discount_amount: DF.Currency
|
||||
total: DF.Currency
|
||||
|
||||
@@ -4,13 +4,14 @@
|
||||
"docstatus": 0,
|
||||
"doctype": "Number Card",
|
||||
"document_type": "Purchase Invoice",
|
||||
"dynamic_filters_json": "[[\"Purchase Invoice\",\"company\",\"=\",\" frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
|
||||
"filters_json": "[[\"Purchase Invoice\",\"docstatus\",\"=\",\"1\",false],[\"Purchase Invoice\",\"posting_date\",\"Timespan\",\"this year\",false]]",
|
||||
"function": "Sum",
|
||||
"idx": 0,
|
||||
"is_public": 1,
|
||||
"is_standard": 1,
|
||||
"label": "Total Incoming Bills",
|
||||
"modified": "2020-07-22 13:06:46.045344",
|
||||
"modified": "2024-11-20 19:08:37.043777",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Total Incoming Bills",
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"docstatus": 0,
|
||||
"doctype": "Number Card",
|
||||
"document_type": "Payment Entry",
|
||||
"dynamic_filters_json": "[[\"Payment Entry\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
|
||||
"filters_json": "[[\"Payment Entry\",\"docstatus\",\"=\",\"1\",false],[\"Payment Entry\",\"posting_date\",\"Timespan\",\"this year\",false],[\"Payment Entry\",\"payment_type\",\"=\",\"Receive\",false]]",
|
||||
"function": "Sum",
|
||||
"idx": 0,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"docstatus": 0,
|
||||
"doctype": "Number Card",
|
||||
"document_type": "Sales Invoice",
|
||||
"dynamic_filters_json": "[[\"Sales Invoice\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
|
||||
"filters_json": "[[\"Sales Invoice\",\"docstatus\",\"=\",\"1\",false],[\"Sales Invoice\",\"posting_date\",\"Timespan\",\"this year\",false]]",
|
||||
"function": "Sum",
|
||||
"idx": 0,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"docstatus": 0,
|
||||
"doctype": "Number Card",
|
||||
"document_type": "Payment Entry",
|
||||
"dynamic_filters_json": "[[\"Payment Entry\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
|
||||
"filters_json": "[[\"Payment Entry\",\"docstatus\",\"=\",\"1\",false],[\"Payment Entry\",\"posting_date\",\"Timespan\",\"this year\",false],[\"Payment Entry\",\"payment_type\",\"=\",\"Pay\",false]]",
|
||||
"function": "Sum",
|
||||
"idx": 0,
|
||||
|
||||
@@ -1013,15 +1013,15 @@ class ReceivablePayableReport:
|
||||
|
||||
def get_columns(self):
|
||||
self.columns = []
|
||||
self.add_column("Posting Date", fieldtype="Date")
|
||||
self.add_column(_("Posting Date"), fieldtype="Date")
|
||||
self.add_column(
|
||||
label="Party Type",
|
||||
label=_("Party Type"),
|
||||
fieldname="party_type",
|
||||
fieldtype="Data",
|
||||
width=100,
|
||||
)
|
||||
self.add_column(
|
||||
label="Party",
|
||||
label=_("Party"),
|
||||
fieldname="party",
|
||||
fieldtype="Dynamic Link",
|
||||
options="party_type",
|
||||
@@ -1037,10 +1037,10 @@ class ReceivablePayableReport:
|
||||
|
||||
if self.party_naming_by == "Naming Series":
|
||||
if self.account_type == "Payable":
|
||||
label = "Supplier Name"
|
||||
label = _("Supplier Name")
|
||||
fieldname = "supplier_name"
|
||||
else:
|
||||
label = "Customer Name"
|
||||
label = _("Customer Name")
|
||||
fieldname = "customer_name"
|
||||
self.add_column(
|
||||
label=label,
|
||||
@@ -1066,7 +1066,7 @@ class ReceivablePayableReport:
|
||||
width=180,
|
||||
)
|
||||
|
||||
self.add_column(label="Due Date", fieldtype="Date")
|
||||
self.add_column(label=_("Due Date"), fieldtype="Date")
|
||||
|
||||
if self.account_type == "Payable":
|
||||
self.add_column(label=_("Bill No"), fieldname="bill_no", fieldtype="Data")
|
||||
|
||||
@@ -263,7 +263,6 @@ def get_report_summary(summary_data, currency):
|
||||
|
||||
def get_chart_data(columns, data, currency):
|
||||
labels = [d.get("label") for d in columns[2:]]
|
||||
print(data)
|
||||
datasets = [
|
||||
{
|
||||
"name": section.get("section").replace("'", ""),
|
||||
|
||||
@@ -619,7 +619,7 @@ def get_cost_centers_with_children(cost_centers):
|
||||
def get_columns(periodicity, period_list, accumulated_values=1, company=None, cash_flow=False):
|
||||
columns = [
|
||||
{
|
||||
"fieldname": "stub",
|
||||
"fieldname": "account",
|
||||
"label": _("Account") if not cash_flow else _("Section"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Account",
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
# MIT License. See license.txt
|
||||
|
||||
import frappe
|
||||
from frappe.desk.query_report import export_query
|
||||
from frappe.tests import IntegrationTestCase
|
||||
from frappe.utils import getdate, today
|
||||
|
||||
@@ -90,3 +91,21 @@ class TestProfitAndLossStatement(AccountsTestMixin, IntegrationTestCase):
|
||||
with self.subTest(current_period_key=current_period_key):
|
||||
self.assertEqual(acc[current_period_key], 150)
|
||||
self.assertEqual(acc["total"], 150)
|
||||
|
||||
def test_p_and_l_export(self):
|
||||
self.create_sales_invoice(qty=1, rate=150)
|
||||
|
||||
filters = self.get_report_filters()
|
||||
frappe.local.form_dict = frappe._dict(
|
||||
{
|
||||
"report_name": "Profit and Loss Statement",
|
||||
"file_format_type": "CSV",
|
||||
"filters": filters,
|
||||
"visible_idx": [0, 1, 2, 3, 4, 5, 6],
|
||||
}
|
||||
)
|
||||
export_query()
|
||||
contents = frappe.response["filecontent"].decode()
|
||||
sales_account = frappe.db.get_value("Company", self.company, "default_income_account")
|
||||
|
||||
self.assertIn(sales_account, contents)
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"docstatus": 0,
|
||||
"doctype": "Number Card",
|
||||
"document_type": "Asset",
|
||||
"dynamic_filters_json": "[[\"Asset\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
|
||||
"filters_json": "[]",
|
||||
"function": "Sum",
|
||||
"idx": 0,
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"docstatus": 0,
|
||||
"doctype": "Number Card",
|
||||
"document_type": "Asset",
|
||||
"dynamic_filters_json": "[[\"Asset\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
|
||||
"filters_json": "[[\"Asset\",\"creation\",\"Timespan\",\"this year\",false]]",
|
||||
"function": "Count",
|
||||
"idx": 0,
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"docstatus": 0,
|
||||
"doctype": "Number Card",
|
||||
"document_type": "Asset",
|
||||
"dynamic_filters_json": "[[\"Asset\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
|
||||
"filters_json": "[]",
|
||||
"function": "Count",
|
||||
"idx": 0,
|
||||
|
||||
@@ -93,7 +93,10 @@ frappe.ui.form.on("Purchase Order", {
|
||||
get_materials_from_supplier: function (frm) {
|
||||
let po_details = [];
|
||||
|
||||
if (frm.doc.supplied_items && (flt(frm.doc.per_received, 2) == 100 || frm.doc.status === "Closed")) {
|
||||
if (
|
||||
frm.doc.supplied_items &&
|
||||
(flt(frm.doc.per_received, precision("per_received")) == 100 || frm.doc.status === "Closed")
|
||||
) {
|
||||
frm.doc.supplied_items.forEach((d) => {
|
||||
if (d.total_supplied_qty && d.total_supplied_qty != d.consumed_qty) {
|
||||
po_details.push(d.name);
|
||||
@@ -329,8 +332,8 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends (
|
||||
if (!["Closed", "Delivered"].includes(doc.status)) {
|
||||
if (
|
||||
this.frm.doc.status !== "Closed" &&
|
||||
flt(this.frm.doc.per_received, 2) < 100 &&
|
||||
flt(this.frm.doc.per_billed, 2) < 100
|
||||
flt(this.frm.doc.per_received, precision("per_received")) < 100 &&
|
||||
flt(this.frm.doc.per_billed, precision("per_billed")) < 100
|
||||
) {
|
||||
if (!this.frm.doc.__onload || this.frm.doc.__onload.can_update_items) {
|
||||
this.frm.add_custom_button(__("Update Items"), () => {
|
||||
@@ -344,7 +347,10 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends (
|
||||
}
|
||||
}
|
||||
if (this.frm.has_perm("submit")) {
|
||||
if (flt(doc.per_billed, 2) < 100 || flt(doc.per_received, 2) < 100) {
|
||||
if (
|
||||
flt(doc.per_billed, precision("per_billed")) < 100 ||
|
||||
flt(doc.per_received, precision("per_received")) < 100
|
||||
) {
|
||||
if (doc.status != "On Hold") {
|
||||
this.frm.add_custom_button(
|
||||
__("Hold"),
|
||||
@@ -382,7 +388,7 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends (
|
||||
}
|
||||
if (doc.status != "Closed") {
|
||||
if (doc.status != "On Hold") {
|
||||
if (flt(doc.per_received) < 100 && allow_receipt) {
|
||||
if (flt(doc.per_received, precision("per_received")) < 100 && allow_receipt) {
|
||||
this.frm.add_custom_button(
|
||||
__("Purchase Receipt"),
|
||||
() => {
|
||||
@@ -413,7 +419,7 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends (
|
||||
}
|
||||
}
|
||||
// Please do not add precision in the below flt function
|
||||
if (flt(doc.per_billed) < 100)
|
||||
if (flt(doc.per_billed, precision("per_billed")) < 100)
|
||||
this.frm.add_custom_button(
|
||||
__("Purchase Invoice"),
|
||||
() => {
|
||||
@@ -422,7 +428,7 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends (
|
||||
__("Create")
|
||||
);
|
||||
|
||||
if (flt(doc.per_billed, 2) < 100 && doc.status != "Delivered") {
|
||||
if (flt(doc.per_billed, precision("per_billed")) < 100 && doc.status != "Delivered") {
|
||||
this.frm.add_custom_button(
|
||||
__("Payment"),
|
||||
() => this.make_payment_entry(),
|
||||
@@ -430,7 +436,7 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends (
|
||||
);
|
||||
}
|
||||
|
||||
if (flt(doc.per_billed, 2) < 100) {
|
||||
if (flt(doc.per_billed, precision("per_billed")) < 100) {
|
||||
this.frm.add_custom_button(
|
||||
__("Payment Request"),
|
||||
function () {
|
||||
|
||||
@@ -18,6 +18,7 @@ def execute(filters=None):
|
||||
|
||||
columns = get_columns(filters)
|
||||
data = get_data(filters)
|
||||
update_received_amount(data)
|
||||
|
||||
if not data:
|
||||
return [], [], None, []
|
||||
@@ -40,7 +41,6 @@ def get_data(filters):
|
||||
po = frappe.qb.DocType("Purchase Order")
|
||||
po_item = frappe.qb.DocType("Purchase Order Item")
|
||||
pi_item = frappe.qb.DocType("Purchase Invoice Item")
|
||||
pr_item = frappe.qb.DocType("Purchase Receipt Item")
|
||||
|
||||
query = (
|
||||
frappe.qb.from_(po)
|
||||
@@ -48,8 +48,6 @@ def get_data(filters):
|
||||
.on(po_item.parent == po.name)
|
||||
.left_join(pi_item)
|
||||
.on((pi_item.po_detail == po_item.name) & (pi_item.docstatus == 1))
|
||||
.left_join(pr_item)
|
||||
.on((pr_item.purchase_order_item == po_item.name) & (pr_item.docstatus == 1))
|
||||
.select(
|
||||
po.transaction_date.as_("date"),
|
||||
po_item.schedule_date.as_("required_date"),
|
||||
@@ -63,7 +61,6 @@ def get_data(filters):
|
||||
(po_item.qty - po_item.received_qty).as_("pending_qty"),
|
||||
Sum(IfNull(pi_item.qty, 0)).as_("billed_qty"),
|
||||
po_item.base_amount.as_("amount"),
|
||||
(pr_item.base_amount).as_("received_qty_amount"),
|
||||
(po_item.billed_amt * IfNull(po.conversion_rate, 1)).as_("billed_amount"),
|
||||
(po_item.base_amount - (po_item.billed_amt * IfNull(po.conversion_rate, 1))).as_(
|
||||
"pending_amount"
|
||||
@@ -95,6 +92,39 @@ def get_data(filters):
|
||||
return data
|
||||
|
||||
|
||||
def update_received_amount(data):
|
||||
pr_data = get_received_amount_data(data)
|
||||
|
||||
for row in data:
|
||||
row.received_qty_amount = flt(pr_data.get(row.name))
|
||||
|
||||
|
||||
def get_received_amount_data(data):
|
||||
pr = frappe.qb.DocType("Purchase Receipt")
|
||||
pr_item = frappe.qb.DocType("Purchase Receipt Item")
|
||||
|
||||
query = (
|
||||
frappe.qb.from_(pr)
|
||||
.inner_join(pr_item)
|
||||
.on(pr_item.parent == pr.name)
|
||||
.select(
|
||||
pr_item.purchase_order_item,
|
||||
Sum(pr_item.base_amount).as_("received_qty_amount"),
|
||||
)
|
||||
.where((pr_item.parent == pr.name) & (pr.docstatus == 1))
|
||||
.groupby(pr_item.purchase_order_item)
|
||||
)
|
||||
|
||||
query = query.where(pr_item.purchase_order_item.isin([row.name for row in data]))
|
||||
|
||||
data = query.run()
|
||||
|
||||
if not data:
|
||||
return frappe._dict()
|
||||
|
||||
return frappe._dict(data)
|
||||
|
||||
|
||||
def prepare_data(data, filters):
|
||||
completed, pending = 0, 0
|
||||
pending_field = "pending_amount"
|
||||
|
||||
@@ -961,6 +961,7 @@ class AccountsController(TransactionBase):
|
||||
"account_head": account_head,
|
||||
"rate": 0,
|
||||
"description": account_head,
|
||||
"set_by_item_tax_template": 1,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -3176,10 +3177,11 @@ def set_child_tax_template_and_map(item, child_item, parent_doc):
|
||||
)
|
||||
|
||||
child_item.item_tax_template = _get_item_tax_template(ctx, item.taxes)
|
||||
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
|
||||
)
|
||||
child_item.item_tax_rate = get_item_tax_map(
|
||||
doc=parent_doc,
|
||||
tax_template=child_item.item_tax_template,
|
||||
as_json=True,
|
||||
)
|
||||
|
||||
|
||||
def add_taxes_from_tax_template(child_item, parent_doc, db_insert=True):
|
||||
@@ -3202,6 +3204,7 @@ def add_taxes_from_tax_template(child_item, parent_doc, db_insert=True):
|
||||
"charge_type": "On Net Total",
|
||||
"account_head": tax_type,
|
||||
"rate": tax_rate,
|
||||
"set_by_item_tax_template": 1,
|
||||
}
|
||||
)
|
||||
if parent_doc.doctype == "Purchase Order":
|
||||
|
||||
@@ -21,9 +21,15 @@ class SellingController(StockController):
|
||||
|
||||
def onload(self):
|
||||
super().onload()
|
||||
if self.doctype in ("Sales Order", "Delivery Note", "Sales Invoice"):
|
||||
if self.doctype in ("Sales Order", "Delivery Note", "Sales Invoice", "Quotation"):
|
||||
for item in self.get("items") + (self.get("packed_items") or []):
|
||||
item.update(get_bin_details(item.item_code, item.warehouse, include_child_warehouses=True))
|
||||
company = self.company
|
||||
|
||||
item.update(
|
||||
get_bin_details(
|
||||
item.item_code, item.warehouse, company=company, include_child_warehouses=True
|
||||
)
|
||||
)
|
||||
|
||||
def validate(self):
|
||||
super().validate()
|
||||
|
||||
@@ -822,6 +822,9 @@ class StockController(AccountsController):
|
||||
self.update_inventory_dimensions(d, sl_dict)
|
||||
|
||||
if self.docstatus == 2:
|
||||
from erpnext.deprecation_dumpster import deprecation_warning
|
||||
|
||||
deprecation_warning("unknown", "v16", "No instructions.")
|
||||
# To handle denormalized serial no records, will br deprecated in v16
|
||||
for field in ["serial_no", "batch_no"]:
|
||||
if d.get(field):
|
||||
@@ -1008,11 +1011,13 @@ class StockController(AccountsController):
|
||||
def validate_qi_presence(self, row):
|
||||
"""Check if QI is present on row level. Warn on save and stop on submit if missing."""
|
||||
if not row.quality_inspection:
|
||||
msg = f"Row #{row.idx}: Quality Inspection is required for Item {frappe.bold(row.item_code)}"
|
||||
msg = _("Row #{0}: Quality Inspection is required for Item {1}").format(
|
||||
row.idx, frappe.bold(row.item_code)
|
||||
)
|
||||
if self.docstatus == 1:
|
||||
frappe.throw(_(msg), title=_("Inspection Required"), exc=QualityInspectionRequiredError)
|
||||
frappe.throw(msg, title=_("Inspection Required"), exc=QualityInspectionRequiredError)
|
||||
else:
|
||||
frappe.msgprint(_(msg), title=_("Inspection Required"), indicator="blue")
|
||||
frappe.msgprint(msg, title=_("Inspection Required"), indicator="blue")
|
||||
|
||||
def validate_qi_submission(self, row):
|
||||
"""Check if QI is submitted on row level, during submission"""
|
||||
@@ -1021,11 +1026,13 @@ class StockController(AccountsController):
|
||||
|
||||
if qa_docstatus != 1:
|
||||
link = frappe.utils.get_link_to_form("Quality Inspection", row.quality_inspection)
|
||||
msg = f"Row #{row.idx}: Quality Inspection {link} is not submitted for the item: {row.item_code}"
|
||||
msg = _("Row #{0}: Quality Inspection {1} is not submitted for the item: {2}").format(
|
||||
row.idx, link, row.item_code
|
||||
)
|
||||
if action == "Stop":
|
||||
frappe.throw(_(msg), title=_("Inspection Submission"), exc=QualityInspectionNotSubmittedError)
|
||||
frappe.throw(msg, title=_("Inspection Submission"), exc=QualityInspectionNotSubmittedError)
|
||||
else:
|
||||
frappe.msgprint(_(msg), alert=True, indicator="orange")
|
||||
frappe.msgprint(msg, alert=True, indicator="orange")
|
||||
|
||||
def validate_qi_rejection(self, row):
|
||||
"""Check if QI is rejected on row level, during submission"""
|
||||
@@ -1034,11 +1041,13 @@ class StockController(AccountsController):
|
||||
|
||||
if qa_status == "Rejected":
|
||||
link = frappe.utils.get_link_to_form("Quality Inspection", row.quality_inspection)
|
||||
msg = f"Row #{row.idx}: Quality Inspection {link} was rejected for item {row.item_code}"
|
||||
msg = _("Row #{0}: Quality Inspection {1} was rejected for item {2}").format(
|
||||
row.idx, link, row.item_code
|
||||
)
|
||||
if action == "Stop":
|
||||
frappe.throw(_(msg), title=_("Inspection Rejected"), exc=QualityInspectionRejectedError)
|
||||
frappe.throw(msg, title=_("Inspection Rejected"), exc=QualityInspectionRejectedError)
|
||||
else:
|
||||
frappe.msgprint(_(msg), alert=True, indicator="orange")
|
||||
frappe.msgprint(msg, alert=True, indicator="orange")
|
||||
|
||||
def update_blanket_order(self):
|
||||
blanket_orders = list(set([d.blanket_order for d in self.items if d.blanket_order]))
|
||||
|
||||
@@ -335,12 +335,18 @@ class SubcontractingController(StockController):
|
||||
|
||||
# Will be deprecated in v16
|
||||
if row.serial_no and not consumed_bundles.serial_nos:
|
||||
from erpnext.deprecation_dumpster import deprecation_warning
|
||||
|
||||
deprecation_warning("unknown", "v16", "No instructions.")
|
||||
self.available_materials[key]["serial_no"] = list(
|
||||
set(self.available_materials[key]["serial_no"]) - set(get_serial_nos(row.serial_no))
|
||||
)
|
||||
|
||||
# Will be deprecated in v16
|
||||
if row.batch_no and not consumed_bundles.batch_nos:
|
||||
from erpnext.deprecation_dumpster import deprecation_warning
|
||||
|
||||
deprecation_warning("unknown", "v16", "No instructions.")
|
||||
self.available_materials[key]["batch_no"][row.batch_no] -= row.consumed_qty
|
||||
|
||||
def get_available_materials(self):
|
||||
|
||||
@@ -8,7 +8,6 @@ import frappe
|
||||
from frappe import _, scrub
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import cint, flt, round_based_on_smallest_currency_fraction
|
||||
from frappe.utils.deprecations import deprecated
|
||||
|
||||
import erpnext
|
||||
from erpnext.accounts.doctype.journal_entry.journal_entry import get_exchange_rate
|
||||
@@ -18,6 +17,7 @@ from erpnext.controllers.accounts_controller import (
|
||||
validate_inclusive_tax,
|
||||
validate_taxes_and_charges,
|
||||
)
|
||||
from erpnext.deprecation_dumpster import deprecated
|
||||
from erpnext.stock.get_item_details import ItemDetailsCtx, _get_item_tax_template
|
||||
from erpnext.utilities.regional import temporary_flag
|
||||
|
||||
@@ -501,9 +501,7 @@ class calculate_taxes_and_totals:
|
||||
)
|
||||
|
||||
elif tax.charge_type == "On Net Total":
|
||||
if not item_tax_map:
|
||||
current_net_amount = item.net_amount
|
||||
elif tax.account_head in item_tax_map:
|
||||
if tax.account_head in item_tax_map:
|
||||
current_net_amount = item.net_amount
|
||||
current_tax_amount = (tax_rate / 100.0) * item.net_amount
|
||||
elif tax.charge_type == "On Previous Row Amount":
|
||||
@@ -569,7 +567,12 @@ class calculate_taxes_and_totals:
|
||||
tax.base_tax_amount = round(tax.base_tax_amount, 0)
|
||||
tax.base_tax_amount_after_discount_amount = round(tax.base_tax_amount_after_discount_amount, 0)
|
||||
|
||||
@deprecated
|
||||
@deprecated(
|
||||
f"{__name__}.calculate_taxes_and_totals.manipulate_grand_total_for_inclusive_tax",
|
||||
"unknown",
|
||||
"v16",
|
||||
"No known instructions.",
|
||||
)
|
||||
def manipulate_grand_total_for_inclusive_tax(self):
|
||||
# for backward compatablility - if in case used by an external application
|
||||
return self.adjust_grand_total_for_inclusive_tax()
|
||||
|
||||
@@ -79,7 +79,7 @@ class TestTaxesAndTotals(FrappeTestCase):
|
||||
"rate": 50,
|
||||
},
|
||||
)
|
||||
|
||||
self.doc.set_missing_item_details()
|
||||
calculate_taxes_and_totals(self.doc)
|
||||
|
||||
expected_values = {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"docstatus": 0,
|
||||
"doctype": "Number Card",
|
||||
"document_type": "Opportunity",
|
||||
"dynamic_filters_json": "[[\"Opportunity\",\"status\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
|
||||
"dynamic_filters_json": "[[\"Opportunity\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
|
||||
"filters_json": "[[\"Opportunity\",\"company\",\"=\",null,false]]",
|
||||
"function": "Count",
|
||||
"idx": 0,
|
||||
|
||||
@@ -5,7 +5,7 @@ This file is the final resting place (or should we say, "retirement home"?) for
|
||||
|
||||
Each function or method that checks in here comes with its own personalized decorator, complete with:
|
||||
1. The date it was marked for deprecation (its "over the hill" birthday)
|
||||
2. The ERPNext version in which it will be removed (its "graduation" to the great codebase in the sky)
|
||||
2. The ERPNext version at the beginning of which it becomes an error and at the end of which it will be removed (its "graduation" to the great codebase in the sky)
|
||||
3. A user-facing note on alternative solutions (its "parting wisdom")
|
||||
|
||||
Warning: The global namespace herein is more patched up than a sailor's favorite pair of jeans. Proceed with caution and a sense of humor!
|
||||
@@ -15,52 +15,63 @@ Remember, deprecated doesn't mean useless - it just means these functions are en
|
||||
Enjoy your stay in the Deprecation Dumpster, where every function gets a second chance to shine (or at least, to not break everything).
|
||||
"""
|
||||
|
||||
import functools
|
||||
import re
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
|
||||
def colorize(text, color_code):
|
||||
if sys.stdout.isatty():
|
||||
return f"\033[{color_code}m{text}\033[0m"
|
||||
return text
|
||||
from frappe.deprecation_dumpster import Color, _deprecated, colorize
|
||||
|
||||
|
||||
class Color:
|
||||
RED = 91
|
||||
YELLOW = 93
|
||||
CYAN = 96
|
||||
# we use Warning because DeprecationWarning has python default filters which would exclude them from showing
|
||||
# see also frappe.__init__ enabling them when a dev_server
|
||||
class ERPNextDeprecationError(Warning):
|
||||
"""Deprecated feature in current version.
|
||||
|
||||
Raises an error by default but can be configured via PYTHONWARNINGS in an emergency.
|
||||
"""
|
||||
|
||||
|
||||
class ERPNextDeprecationWarning(Warning):
|
||||
...
|
||||
"""Deprecated feature in next version"""
|
||||
|
||||
|
||||
try:
|
||||
# since python 3.13, PEP 702
|
||||
from warnings import deprecated as _deprecated
|
||||
except ImportError:
|
||||
import functools
|
||||
import warnings
|
||||
from collections.abc import Callable
|
||||
from typing import Optional, TypeVar, Union, overload
|
||||
class PendingERPNextDeprecationWarning(ERPNextDeprecationWarning):
|
||||
"""Deprecated feature in develop beyond next version.
|
||||
|
||||
T = TypeVar("T", bound=Callable)
|
||||
Warning ignored by default.
|
||||
|
||||
def _deprecated(message: str, category=ERPNextDeprecationWarning, stacklevel=1) -> Callable[[T], T]:
|
||||
def decorator(func: T) -> T:
|
||||
@functools.wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
if message:
|
||||
warning_msg = f"{func.__name__} is deprecated.\n{message}"
|
||||
else:
|
||||
warning_msg = f"{func.__name__} is deprecated."
|
||||
warnings.warn(warning_msg, category=category, stacklevel=stacklevel + 1)
|
||||
return func(*args, **kwargs)
|
||||
The deprecation decision may still be reverted or deferred at this stage.
|
||||
Regardless, using the new variant is encouraged and stable.
|
||||
"""
|
||||
|
||||
return wrapper
|
||||
wrapper.__deprecated__ = True # hint for the type checker
|
||||
|
||||
return decorator
|
||||
warnings.simplefilter("error", ERPNextDeprecationError)
|
||||
warnings.simplefilter("ignore", PendingERPNextDeprecationWarning)
|
||||
|
||||
|
||||
class V15ERPNextDeprecationWarning(ERPNextDeprecationError):
|
||||
pass
|
||||
|
||||
|
||||
class V16ERPNextDeprecationWarning(ERPNextDeprecationWarning):
|
||||
pass
|
||||
|
||||
|
||||
class V17ERPNextDeprecationWarning(PendingERPNextDeprecationWarning):
|
||||
pass
|
||||
|
||||
|
||||
def __get_deprecation_class(graduation: str | None = None, class_name: str | None = None) -> type:
|
||||
if graduation:
|
||||
# Scrub the graduation string to ensure it's a valid class name
|
||||
cleaned_graduation = re.sub(r"\W|^(?=\d)", "_", graduation.upper())
|
||||
class_name = f"{cleaned_graduation}ERPNextDeprecationWarning"
|
||||
current_module = sys.modules[__name__]
|
||||
try:
|
||||
return getattr(current_module, class_name)
|
||||
except AttributeError:
|
||||
return PendingDeprecationWarning
|
||||
|
||||
|
||||
def deprecated(original: str, marked: str, graduation: str, msg: str, stacklevel: int = 1):
|
||||
@@ -79,6 +90,7 @@ def deprecated(original: str, marked: str, graduation: str, msg: str, stacklevel
|
||||
wrapper = _deprecated(
|
||||
colorize(f"It was marked on {marked} for removal from {graduation} with note: ", Color.RED)
|
||||
+ colorize(f"{msg}", Color.YELLOW),
|
||||
category=__get_deprecation_class(graduation),
|
||||
stacklevel=stacklevel,
|
||||
)
|
||||
|
||||
@@ -103,7 +115,7 @@ def deprecation_warning(marked: str, graduation: str, msg: str):
|
||||
Color.RED,
|
||||
)
|
||||
+ colorize(f"{msg}\n", Color.YELLOW),
|
||||
category=ERPNextDeprecationWarning,
|
||||
category=__get_deprecation_class(graduation),
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
|
||||
1119
erpnext/locale/ar.po
1119
erpnext/locale/ar.po
File diff suppressed because it is too large
Load Diff
1117
erpnext/locale/bs.po
1117
erpnext/locale/bs.po
File diff suppressed because it is too large
Load Diff
1121
erpnext/locale/de.po
1121
erpnext/locale/de.po
File diff suppressed because it is too large
Load Diff
1121
erpnext/locale/eo.po
1121
erpnext/locale/eo.po
File diff suppressed because it is too large
Load Diff
1121
erpnext/locale/es.po
1121
erpnext/locale/es.po
File diff suppressed because it is too large
Load Diff
1279
erpnext/locale/fa.po
1279
erpnext/locale/fa.po
File diff suppressed because it is too large
Load Diff
1119
erpnext/locale/fr.po
1119
erpnext/locale/fr.po
File diff suppressed because it is too large
Load Diff
1117
erpnext/locale/hu.po
1117
erpnext/locale/hu.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1117
erpnext/locale/pl.po
1117
erpnext/locale/pl.po
File diff suppressed because it is too large
Load Diff
1119
erpnext/locale/ru.po
1119
erpnext/locale/ru.po
File diff suppressed because it is too large
Load Diff
1179
erpnext/locale/sv.po
1179
erpnext/locale/sv.po
File diff suppressed because it is too large
Load Diff
1125
erpnext/locale/tr.po
1125
erpnext/locale/tr.po
File diff suppressed because it is too large
Load Diff
1119
erpnext/locale/zh.po
1119
erpnext/locale/zh.po
File diff suppressed because it is too large
Load Diff
@@ -29,7 +29,6 @@ frappe.ui.form.on("BOM", {
|
||||
item: row.finished_good,
|
||||
is_active: 1,
|
||||
docstatus: 1,
|
||||
track_semi_finished_goods: 0,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
@@ -886,7 +886,7 @@ class BOM(WebsiteGenerator):
|
||||
self.cur_exploded_items = {}
|
||||
for d in self.get("items"):
|
||||
if d.bom_no:
|
||||
self.get_child_exploded_items(d.bom_no, d.stock_qty)
|
||||
self.get_child_exploded_items(d.bom_no, d.stock_qty, d.operation)
|
||||
elif d.item_code:
|
||||
self.add_to_cur_exploded_items(
|
||||
frappe._dict(
|
||||
@@ -915,7 +915,7 @@ class BOM(WebsiteGenerator):
|
||||
else:
|
||||
self.cur_exploded_items[args.item_code] = args
|
||||
|
||||
def get_child_exploded_items(self, bom_no, stock_qty):
|
||||
def get_child_exploded_items(self, bom_no, stock_qty, operation=None):
|
||||
"""Add all items from Flat BOM of child BOM"""
|
||||
# Did not use qty_consumed_per_unit in the query, as it leads to rounding loss
|
||||
child_fb_items = frappe.db.sql(
|
||||
@@ -949,7 +949,7 @@ class BOM(WebsiteGenerator):
|
||||
"item_code": d["item_code"],
|
||||
"item_name": d["item_name"],
|
||||
"source_warehouse": d["source_warehouse"],
|
||||
"operation": d["operation"],
|
||||
"operation": d["operation"] or operation,
|
||||
"description": d["description"],
|
||||
"stock_uom": d["stock_uom"],
|
||||
"stock_qty": d["qty_consumed_per_unit"] * stock_qty,
|
||||
|
||||
@@ -98,8 +98,6 @@ frappe.ui.form.on("BOM Creator", {
|
||||
],
|
||||
primary_action_label: __("Create"),
|
||||
primary_action: (values) => {
|
||||
frm.events.validate_dialog_values(frm, values);
|
||||
|
||||
values.doctype = frm.doc.doctype;
|
||||
frappe.db.insert(values).then((doc) => {
|
||||
frappe.set_route("Form", doc.doctype, doc.name);
|
||||
@@ -111,18 +109,6 @@ frappe.ui.form.on("BOM Creator", {
|
||||
dialog.show();
|
||||
},
|
||||
|
||||
validate_dialog_values(frm, values) {
|
||||
if (values.track_semi_finished_goods) {
|
||||
if (values.final_operation_time <= 0) {
|
||||
frappe.throw(__("Operation Time must be greater than 0"));
|
||||
}
|
||||
|
||||
if (!values.workstation && !values.workstation_type) {
|
||||
frappe.throw(__("Either Workstation or Workstation Type is mandatory"));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
set_queries(frm) {
|
||||
frm.set_query("bom_no", "items", function (doc, cdt, cdn) {
|
||||
let item = frappe.get_doc(cdt, cdn);
|
||||
|
||||
@@ -39,23 +39,6 @@
|
||||
"items",
|
||||
"costing_detail",
|
||||
"raw_material_cost",
|
||||
"configuration_section",
|
||||
"track_operations",
|
||||
"column_break_obzr",
|
||||
"track_semi_finished_goods",
|
||||
"final_product_operation_section",
|
||||
"operation",
|
||||
"operation_time",
|
||||
"column_break_xnlu",
|
||||
"workstation_type",
|
||||
"workstation",
|
||||
"final_product_warehouse_section",
|
||||
"skip_material_transfer",
|
||||
"backflush_from_wip_warehouse",
|
||||
"source_warehouse",
|
||||
"column_break_buha",
|
||||
"wip_warehouse",
|
||||
"fg_warehouse",
|
||||
"remarks_tab",
|
||||
"remarks",
|
||||
"section_break_yixm",
|
||||
@@ -298,104 +281,6 @@
|
||||
"label": "Error Log",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "configuration_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Operation"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"depends_on": "track_operations",
|
||||
"fieldname": "track_semi_finished_goods",
|
||||
"fieldtype": "Check",
|
||||
"label": "Track Semi Finished Goods"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_obzr",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "track_operations",
|
||||
"fieldtype": "Check",
|
||||
"label": "Track Operations"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.track_semi_finished_goods === 1",
|
||||
"fieldname": "final_product_operation_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Final Product Operation & Workstation"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_xnlu",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "operation",
|
||||
"fieldtype": "Link",
|
||||
"label": "Operation",
|
||||
"options": "Operation"
|
||||
},
|
||||
{
|
||||
"fieldname": "operation_time",
|
||||
"fieldtype": "Float",
|
||||
"label": "Operation Time (in mins)"
|
||||
},
|
||||
{
|
||||
"fieldname": "workstation",
|
||||
"fieldtype": "Link",
|
||||
"label": "Workstation",
|
||||
"options": "Workstation"
|
||||
},
|
||||
{
|
||||
"fieldname": "workstation_type",
|
||||
"fieldtype": "Link",
|
||||
"label": "Workstation Type",
|
||||
"options": "Workstation Type"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:!doc.backflush_from_wip_warehouse",
|
||||
"fieldname": "source_warehouse",
|
||||
"fieldtype": "Link",
|
||||
"label": "Source Warehouse",
|
||||
"options": "Warehouse"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:!doc.skip_material_transfer || doc.backflush_from_wip_warehouse",
|
||||
"fieldname": "wip_warehouse",
|
||||
"fieldtype": "Link",
|
||||
"label": "Work In Progress Warehouse",
|
||||
"options": "Warehouse"
|
||||
},
|
||||
{
|
||||
"fieldname": "fg_warehouse",
|
||||
"fieldtype": "Link",
|
||||
"label": "Finished Good Warehouse",
|
||||
"options": "Warehouse"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.track_semi_finished_goods === 1",
|
||||
"fieldname": "final_product_warehouse_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Final Product Warehouse"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "skip_material_transfer",
|
||||
"fieldtype": "Check",
|
||||
"label": "Skip Material Transfer"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"depends_on": "eval:doc.skip_material_transfer",
|
||||
"fieldname": "backflush_from_wip_warehouse",
|
||||
"fieldtype": "Check",
|
||||
"label": "Backflush Materials From WIP Warehouse"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_buha",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_xvld",
|
||||
"fieldtype": "Section Break",
|
||||
@@ -417,7 +302,7 @@
|
||||
"link_fieldname": "bom_creator"
|
||||
}
|
||||
],
|
||||
"modified": "2024-09-26 17:07:32.111198",
|
||||
"modified": "2024-11-25 16:41:03.047835",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "BOM Creator",
|
||||
|
||||
@@ -44,20 +44,16 @@ class BOMCreator(Document):
|
||||
from erpnext.manufacturing.doctype.bom_creator_item.bom_creator_item import BOMCreatorItem
|
||||
|
||||
amended_from: DF.Link | None
|
||||
backflush_from_wip_warehouse: DF.Check
|
||||
buying_price_list: DF.Link | None
|
||||
company: DF.Link
|
||||
conversion_rate: DF.Float
|
||||
currency: DF.Link
|
||||
default_warehouse: DF.Link | None
|
||||
error_log: DF.Text | None
|
||||
fg_warehouse: DF.Link | None
|
||||
item_code: DF.Link
|
||||
item_group: DF.Link | None
|
||||
item_name: DF.Data | None
|
||||
items: DF.Table[BOMCreatorItem]
|
||||
operation: DF.Link | None
|
||||
operation_time: DF.Float
|
||||
plc_conversion_rate: DF.Float
|
||||
price_list_currency: DF.Link | None
|
||||
project: DF.Link | None
|
||||
@@ -67,15 +63,8 @@ class BOMCreator(Document):
|
||||
rm_cost_as_per: DF.Literal["Valuation Rate", "Last Purchase Rate", "Price List"]
|
||||
routing: DF.Link | None
|
||||
set_rate_based_on_warehouse: DF.Check
|
||||
skip_material_transfer: DF.Check
|
||||
source_warehouse: DF.Link | None
|
||||
status: DF.Literal["Draft", "Submitted", "In Progress", "Completed", "Failed", "Cancelled"]
|
||||
track_operations: DF.Check
|
||||
track_semi_finished_goods: DF.Check
|
||||
uom: DF.Link | None
|
||||
wip_warehouse: DF.Link | None
|
||||
workstation: DF.Link | None
|
||||
workstation_type: DF.Link | None
|
||||
# end: auto-generated types
|
||||
|
||||
def before_save(self):
|
||||
@@ -272,15 +261,9 @@ class BOMCreator(Document):
|
||||
|
||||
try:
|
||||
for d in reverse_tree:
|
||||
if self.track_operations and self.track_semi_finished_goods and final_product == d:
|
||||
continue
|
||||
|
||||
fg_item_data = production_item_wise_rm.get(d).fg_item_data
|
||||
self.create_bom(fg_item_data, production_item_wise_rm)
|
||||
|
||||
if self.track_operations and self.track_semi_finished_goods:
|
||||
self.make_bom_for_final_product(production_item_wise_rm)
|
||||
|
||||
frappe.msgprint(_("BOMs created successfully"))
|
||||
except Exception:
|
||||
traceback = frappe.get_traceback(with_context=True)
|
||||
@@ -293,81 +276,6 @@ class BOMCreator(Document):
|
||||
|
||||
frappe.msgprint(_("BOMs creation failed"))
|
||||
|
||||
def make_bom_for_final_product(self, production_item_wise_rm):
|
||||
bom = frappe.new_doc("BOM")
|
||||
bom.update(
|
||||
{
|
||||
"item": self.item_code,
|
||||
"bom_type": "Production",
|
||||
"quantity": self.qty,
|
||||
"allow_alternative_item": 1,
|
||||
"bom_creator": self.name,
|
||||
"bom_creator_item": self.name,
|
||||
"rm_cost_as_per": "Manual",
|
||||
"with_operations": 1,
|
||||
"track_semi_finished_goods": 1,
|
||||
}
|
||||
)
|
||||
|
||||
for field in BOM_FIELDS:
|
||||
if self.get(field):
|
||||
bom.set(field, self.get(field))
|
||||
|
||||
for item in self.items:
|
||||
if not item.is_expandable or not item.operation:
|
||||
continue
|
||||
|
||||
bom.append(
|
||||
"operations",
|
||||
{
|
||||
"operation": item.operation,
|
||||
"workstation": item.workstation,
|
||||
"source_warehouse": item.source_warehouse,
|
||||
"wip_warehouse": item.wip_warehouse,
|
||||
"fg_warehouse": item.fg_warehouse,
|
||||
"finished_good": item.item_code,
|
||||
"finished_good_qty": item.qty,
|
||||
"bom_no": production_item_wise_rm[(item.item_code, item.name)].bom_no,
|
||||
"workstation_type": item.workstation_type,
|
||||
"time_in_mins": item.operation_time,
|
||||
"is_subcontracted": item.is_subcontracted,
|
||||
"skip_material_transfer": item.skip_material_transfer,
|
||||
"backflush_from_wip_warehouse": item.backflush_from_wip_warehouse,
|
||||
},
|
||||
)
|
||||
|
||||
operation_row = bom.append(
|
||||
"operations",
|
||||
{
|
||||
"operation": self.operation,
|
||||
"time_in_mins": self.operation_time,
|
||||
"workstation": self.workstation,
|
||||
"workstation_type": self.workstation_type,
|
||||
"finished_good": self.item_code,
|
||||
"finished_good_qty": self.qty,
|
||||
"source_warehouse": self.source_warehouse,
|
||||
"wip_warehouse": self.wip_warehouse,
|
||||
"fg_warehouse": self.fg_warehouse,
|
||||
"skip_material_transfer": self.skip_material_transfer,
|
||||
"backflush_from_wip_warehouse": self.backflush_from_wip_warehouse,
|
||||
},
|
||||
)
|
||||
|
||||
final_product = (self.item_code, self.name)
|
||||
items = production_item_wise_rm.get(final_product).get("items")
|
||||
|
||||
bom.set_materials_based_on_operation_bom()
|
||||
|
||||
for item in items:
|
||||
item_args = {"operation_row_id": operation_row.idx}
|
||||
for field in BOM_ITEM_FIELDS:
|
||||
item_args[field] = item.get(field)
|
||||
|
||||
bom.append("items", item_args)
|
||||
|
||||
bom.save(ignore_permissions=True)
|
||||
bom.submit()
|
||||
|
||||
def create_bom(self, row, production_item_wise_rm):
|
||||
bom_creator_item = row.name if row.name != self.name else ""
|
||||
if frappe.db.exists(
|
||||
@@ -393,25 +301,7 @@ class BOMCreator(Document):
|
||||
}
|
||||
)
|
||||
|
||||
if self.track_operations and not self.track_semi_finished_goods:
|
||||
if row.item_code == self.item_code:
|
||||
bom.with_operations = 1
|
||||
bom.transfer_material_against = "Work Order"
|
||||
for item in self.items:
|
||||
if not item.operation:
|
||||
continue
|
||||
|
||||
bom.append(
|
||||
"operations",
|
||||
{
|
||||
"operation": item.operation,
|
||||
"workstation_type": item.workstation_type,
|
||||
"workstation": item.workstation,
|
||||
"time_in_mins": item.operation_time,
|
||||
},
|
||||
)
|
||||
|
||||
elif row.item_code == self.item_code and self.routing:
|
||||
if row.item_code == self.item_code and (self.routing or self.has_operations()):
|
||||
bom.routing = self.routing
|
||||
bom.with_operations = 1
|
||||
bom.transfer_material_against = "Work Order"
|
||||
@@ -447,6 +337,13 @@ class BOMCreator(Document):
|
||||
|
||||
production_item_wise_rm[(row.item_code, row.name)].bom_no = bom.name
|
||||
|
||||
def has_operations(self):
|
||||
for row in self.items:
|
||||
if row.operation:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_default_bom(self, item_code) -> str:
|
||||
return frappe.get_cached_value("Item", item_code, "default_bom")
|
||||
@@ -562,14 +459,6 @@ def add_sub_assembly(**kwargs):
|
||||
"is_expandable": 1,
|
||||
"stock_uom": item_info.stock_uom,
|
||||
"operation": bom_item.operation,
|
||||
"workstation_type": bom_item.workstation_type,
|
||||
"operation_time": bom_item.operation_time,
|
||||
"is_subcontracted": bom_item.is_subcontracted,
|
||||
"workstation": bom_item.workstation,
|
||||
"source_warehouse": bom_item.source_warehouse,
|
||||
"wip_warehouse": bom_item.wip_warehouse,
|
||||
"fg_warehouse": bom_item.fg_warehouse,
|
||||
"skip_material_transfer": bom_item.skip_material_transfer,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -579,20 +468,6 @@ def add_sub_assembly(**kwargs):
|
||||
parent_row_no = [row.idx for row in doc.items if row.name == kwargs.fg_reference_id]
|
||||
if parent_row_no:
|
||||
parent_row_no = parent_row_no[0]
|
||||
doc.items[parent_row_no - 1].update(
|
||||
{
|
||||
"operation": bom_item.operation,
|
||||
"workstation_type": bom_item.workstation_type,
|
||||
"operation_time": bom_item.operation_time,
|
||||
"is_subcontracted": bom_item.is_subcontracted,
|
||||
"workstation": bom_item.workstation,
|
||||
"source_warehouse": bom_item.source_warehouse,
|
||||
"wip_warehouse": bom_item.wip_warehouse,
|
||||
"fg_warehouse": bom_item.fg_warehouse,
|
||||
"skip_material_transfer": bom_item.skip_material_transfer,
|
||||
"backflush_from_wip_warehouse": bom_item.backflush_from_wip_warehouse,
|
||||
}
|
||||
)
|
||||
|
||||
for row in bom_item.get("items"):
|
||||
row = frappe._dict(row)
|
||||
|
||||
@@ -17,17 +17,7 @@
|
||||
"is_subcontracted",
|
||||
"operation_section",
|
||||
"operation",
|
||||
"operation_time",
|
||||
"column_break_cbnk",
|
||||
"workstation_type",
|
||||
"workstation",
|
||||
"warehouse_section",
|
||||
"skip_material_transfer",
|
||||
"backflush_from_wip_warehouse",
|
||||
"source_warehouse",
|
||||
"column_break_xutc",
|
||||
"wip_warehouse",
|
||||
"fg_warehouse",
|
||||
"description_section",
|
||||
"description",
|
||||
"quantity_and_rate_section",
|
||||
@@ -87,13 +77,6 @@
|
||||
"options": "Item",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.skip_material_transfer && !doc.backflush_from_wip_warehouse",
|
||||
"fieldname": "source_warehouse",
|
||||
"fieldtype": "Link",
|
||||
"label": "Source Warehouse",
|
||||
"options": "Warehouse"
|
||||
},
|
||||
{
|
||||
"columns": 1,
|
||||
"default": "0",
|
||||
@@ -256,59 +239,6 @@
|
||||
"fieldname": "column_break_cbnk",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "workstation_type",
|
||||
"fieldtype": "Link",
|
||||
"label": "Workstation Type",
|
||||
"options": "Workstation Type"
|
||||
},
|
||||
{
|
||||
"description": "In Mins",
|
||||
"fieldname": "operation_time",
|
||||
"fieldtype": "Int",
|
||||
"label": "Operation Time"
|
||||
},
|
||||
{
|
||||
"fieldname": "workstation",
|
||||
"fieldtype": "Link",
|
||||
"label": "Workstation",
|
||||
"options": "Workstation"
|
||||
},
|
||||
{
|
||||
"fieldname": "warehouse_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Warehouse"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:!doc.skip_material_transfer || doc.backflush_from_wip_warehouse",
|
||||
"fieldname": "wip_warehouse",
|
||||
"fieldtype": "Link",
|
||||
"label": "Work In Progress Warehouse",
|
||||
"options": "Warehouse"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_xutc",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "fg_warehouse",
|
||||
"fieldtype": "Link",
|
||||
"label": "Finished Good Warehouse",
|
||||
"options": "Warehouse"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "skip_material_transfer",
|
||||
"fieldtype": "Check",
|
||||
"label": "Skip Material Transfer"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"depends_on": "eval:doc.skip_material_transfer",
|
||||
"fieldname": "backflush_from_wip_warehouse",
|
||||
"fieldtype": "Check",
|
||||
"label": "Backflush Materials From WIP Warehouse"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "is_subcontracted",
|
||||
@@ -320,7 +250,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2024-06-03 18:45:24.339532",
|
||||
"modified": "2024-11-25 18:13:34.542391",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "BOM Creator Item",
|
||||
|
||||
@@ -15,7 +15,6 @@ class BOMCreatorItem(Document):
|
||||
from frappe.types import DF
|
||||
|
||||
amount: DF.Currency
|
||||
backflush_from_wip_warehouse: DF.Check
|
||||
base_amount: DF.Currency
|
||||
base_rate: DF.Currency
|
||||
bom_created: DF.Check
|
||||
@@ -24,7 +23,6 @@ class BOMCreatorItem(Document):
|
||||
do_not_explode: DF.Check
|
||||
fg_item: DF.Link
|
||||
fg_reference_id: DF.Data | None
|
||||
fg_warehouse: DF.Link | None
|
||||
instruction: DF.SmallText | None
|
||||
is_expandable: DF.Check
|
||||
is_subcontracted: DF.Check
|
||||
@@ -32,22 +30,16 @@ class BOMCreatorItem(Document):
|
||||
item_group: DF.Link | None
|
||||
item_name: DF.Data | None
|
||||
operation: DF.Link | None
|
||||
operation_time: DF.Int
|
||||
parent: DF.Data
|
||||
parent_row_no: DF.Data | None
|
||||
parentfield: DF.Data
|
||||
parenttype: DF.Data
|
||||
qty: DF.Float
|
||||
rate: DF.Currency
|
||||
skip_material_transfer: DF.Check
|
||||
source_warehouse: DF.Link | None
|
||||
sourced_by_supplier: DF.Check
|
||||
stock_qty: DF.Float
|
||||
stock_uom: DF.Link | None
|
||||
uom: DF.Link | None
|
||||
wip_warehouse: DF.Link | None
|
||||
workstation: DF.Link | None
|
||||
workstation_type: DF.Link | None
|
||||
# end: auto-generated types
|
||||
|
||||
pass
|
||||
|
||||
@@ -1157,6 +1157,9 @@ class JobCard(Document):
|
||||
for employee in kwargs.employees:
|
||||
kwargs.employee = employee.get("employee")
|
||||
if kwargs.from_time and not kwargs.to_time:
|
||||
if kwargs.qty:
|
||||
kwargs.completed_qty = kwargs.qty
|
||||
|
||||
row = self.append("time_logs", kwargs)
|
||||
row.db_update()
|
||||
self.db_set("status", "Work In Progress")
|
||||
@@ -1223,6 +1226,10 @@ class JobCard(Document):
|
||||
|
||||
if kwargs.auto_submit:
|
||||
self.submit()
|
||||
|
||||
if not self.finished_good:
|
||||
return
|
||||
|
||||
self.make_stock_entry_for_semi_fg_item(kwargs.auto_submit)
|
||||
frappe.msgprint(
|
||||
_("Job Card {0} has been completed").format(get_link_to_form("Job Card", self.name))
|
||||
|
||||
@@ -53,3 +53,24 @@ class Routing(Document):
|
||||
)
|
||||
|
||||
sequence_id = row.sequence_id
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
@frappe.validate_and_sanitize_search_inputs
|
||||
def get_operations(doctype, txt, searchfield, start, page_len, filters):
|
||||
query_filters = {}
|
||||
|
||||
if txt:
|
||||
query_filters = {"operation": ["like", f"%{txt}%"]}
|
||||
|
||||
if filters.get("routing"):
|
||||
query_filters["parent"] = filters.get("routing")
|
||||
|
||||
return frappe.get_all(
|
||||
"BOM Operation",
|
||||
fields=["operation"],
|
||||
filters=query_filters,
|
||||
start=start,
|
||||
page_length=page_len,
|
||||
as_list=1,
|
||||
)
|
||||
|
||||
@@ -1645,6 +1645,7 @@ def create_job_card(work_order, row, enable_capacity_planning=False, auto_create
|
||||
"sequence_id": row.get("sequence_id"),
|
||||
"hour_rate": row.get("hour_rate"),
|
||||
"serial_no": row.get("serial_no"),
|
||||
"time_required": row.get("time_in_mins"),
|
||||
"source_warehouse": row.get("source_warehouse"),
|
||||
"target_warehouse": row.get("fg_warehouse"),
|
||||
"wip_warehouse": work_order.wip_warehouse or row.get("wip_warehouse"),
|
||||
|
||||
@@ -509,6 +509,7 @@ def update_job_card(job_card, method, **kwargs):
|
||||
if kwargs.qty and isinstance(kwargs.qty, str):
|
||||
kwargs.qty = flt(kwargs.qty)
|
||||
|
||||
print(method)
|
||||
doc = frappe.get_doc("Job Card", job_card)
|
||||
doc.run_method(method, **kwargs)
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"docstatus": 0,
|
||||
"doctype": "Number Card",
|
||||
"document_type": "Work Order",
|
||||
"dynamic_filters_json": "[[\"Work Order\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
|
||||
"filters_json": "[[\"Work Order\",\"status\",\"=\",\"Completed\"],[\"Work Order\",\"docstatus\",\"=\",1],[\"Work Order\",\"creation\",\"between\",[\"2020-06-08\",\"2020-07-08\"]]]",
|
||||
"function": "Count",
|
||||
"idx": 0,
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"docstatus": 0,
|
||||
"doctype": "Number Card",
|
||||
"document_type": "Work Order",
|
||||
"dynamic_filters_json": "[[\"Work Order\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
|
||||
"filters_json": "[[\"Work Order\",\"docstatus\",\"=\",1],[\"Work Order\",\"creation\",\"between\",[\"2020-06-08\",\"2020-07-08\"]]]",
|
||||
"function": "Count",
|
||||
"idx": 0,
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"docstatus": 0,
|
||||
"doctype": "Number Card",
|
||||
"document_type": "Job Card",
|
||||
"dynamic_filters_json": "[[\"Job Card\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
|
||||
"filters_json": "[[\"Job Card\",\"status\",\"!=\",\"Completed\"],[\"Job Card\",\"docstatus\",\"=\",1]]",
|
||||
"function": "Count",
|
||||
"idx": 0,
|
||||
|
||||
@@ -292,7 +292,7 @@ class BOMConfigurator {
|
||||
});
|
||||
}
|
||||
|
||||
get_sub_assembly_modal_fields(view, is_root = false, read_only = false, show_operations_fields = false) {
|
||||
get_sub_assembly_modal_fields(view, is_root = false, read_only = false) {
|
||||
let fields = [
|
||||
{
|
||||
label: __("Sub Assembly Item"),
|
||||
@@ -320,7 +320,7 @@ class BOMConfigurator {
|
||||
},
|
||||
];
|
||||
|
||||
if (this.frm.doc.track_operations && (is_root || show_operations_fields)) {
|
||||
if (is_root) {
|
||||
fields.push(
|
||||
...[
|
||||
{ fieldtype: "Section Break" },
|
||||
@@ -329,105 +329,18 @@ class BOMConfigurator {
|
||||
fieldname: "operation",
|
||||
fieldtype: "Link",
|
||||
options: "Operation",
|
||||
reqd: 1,
|
||||
},
|
||||
{
|
||||
label: __("Operation Time"),
|
||||
fieldname: "operation_time",
|
||||
fieldtype: "Int",
|
||||
reqd: 1,
|
||||
},
|
||||
{
|
||||
label: __("Is Subcontracted"),
|
||||
fieldname: "is_subcontracted",
|
||||
fieldtype: "Check",
|
||||
},
|
||||
{ fieldtype: "Column Break" },
|
||||
{
|
||||
label: __("Workstation Type"),
|
||||
fieldname: "workstation_type",
|
||||
fieldtype: "Link",
|
||||
options: "Workstation Type",
|
||||
},
|
||||
{
|
||||
label: __("Workstation"),
|
||||
fieldname: "workstation",
|
||||
fieldtype: "Link",
|
||||
options: "Workstation",
|
||||
},
|
||||
]
|
||||
);
|
||||
get_query() {
|
||||
let doc = view.events.frm.doc;
|
||||
|
||||
if (this.frm.doc.track_semi_finished_goods) {
|
||||
fields.push(
|
||||
...[
|
||||
{ label: __("Default Warehouse"), fieldtype: "Section Break", collapsible: 1 },
|
||||
{
|
||||
label: __("Skip Material Transfer"),
|
||||
fieldname: "skip_material_transfer",
|
||||
fieldtype: "Check",
|
||||
},
|
||||
{
|
||||
label: __("Backflush Materials From WIP"),
|
||||
fieldname: "backflush_from_wip_warehouse",
|
||||
fieldtype: "Check",
|
||||
depends_on: "eval:doc.skip_material_transfer",
|
||||
},
|
||||
{
|
||||
label: __("Source Warehouse"),
|
||||
fieldname: "source_warehouse",
|
||||
fieldtype: "Link",
|
||||
options: "Warehouse",
|
||||
depends_on: "eval:!doc.backflush_from_wip_warehouse",
|
||||
get_query() {
|
||||
if (doc.routing) {
|
||||
return {
|
||||
query: "erpnext.manufacturing.doctype.routing.routing.get_operations",
|
||||
filters: {
|
||||
company: view.events.frm.doc.company,
|
||||
routing: doc.routing,
|
||||
},
|
||||
};
|
||||
},
|
||||
}
|
||||
},
|
||||
{ fieldtype: "Column Break" },
|
||||
{
|
||||
label: __("Work In Progress Warehouse"),
|
||||
fieldname: "wip_warehouse",
|
||||
fieldtype: "Link",
|
||||
options: "Warehouse",
|
||||
depends_on:
|
||||
"eval:!doc.skip_material_transfer || doc.backflush_from_wip_warehouse",
|
||||
get_query() {
|
||||
return {
|
||||
filters: {
|
||||
company: view.events.frm.doc.company,
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
label: __("Finished Good Warehouse"),
|
||||
fieldname: "fg_warehouse",
|
||||
fieldtype: "Link",
|
||||
options: "Warehouse",
|
||||
get_query() {
|
||||
return {
|
||||
filters: {
|
||||
company: view.events.frm.doc.company,
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
} else if (this.frm.doc.routing && is_root) {
|
||||
fields.push(
|
||||
...[
|
||||
{ fieldtype: "Section Break" },
|
||||
{
|
||||
label: __("Operation"),
|
||||
fieldname: "operation",
|
||||
fieldtype: "Link",
|
||||
options: "Operation",
|
||||
},
|
||||
]
|
||||
);
|
||||
@@ -473,7 +386,7 @@ class BOMConfigurator {
|
||||
|
||||
convert_to_sub_assembly(node, view) {
|
||||
let dialog = new frappe.ui.Dialog({
|
||||
fields: view.events.get_sub_assembly_modal_fields(view, node.is_root, true, true),
|
||||
fields: view.events.get_sub_assembly_modal_fields(view, node.is_root, true),
|
||||
title: __("Add Sub Assembly"),
|
||||
});
|
||||
|
||||
@@ -556,123 +469,6 @@ class BOMConfigurator {
|
||||
let qty = node.data.qty || this.frm.doc.qty;
|
||||
let fields = [{ label: __("Qty"), fieldname: "qty", default: qty, fieldtype: "Float", reqd: 1 }];
|
||||
|
||||
if (node.expandable && this.frm.doc.track_operations) {
|
||||
let data = node.data.operation ? node.data : this.frm.doc;
|
||||
|
||||
fields = [
|
||||
...fields,
|
||||
...[
|
||||
{ fieldtype: "Section Break" },
|
||||
{
|
||||
label: __("Operation"),
|
||||
fieldname: "operation",
|
||||
fieldtype: "Link",
|
||||
options: "Operation",
|
||||
default: data.operation,
|
||||
},
|
||||
{
|
||||
label: __("Operation Time"),
|
||||
fieldname: "operation_time",
|
||||
fieldtype: "Float",
|
||||
default: data.operation_time,
|
||||
},
|
||||
{
|
||||
label: __("Is Subcontracted"),
|
||||
fieldname: "is_subcontracted",
|
||||
fieldtype: "Check",
|
||||
hidden: node?.is_root || 0,
|
||||
default: data.is_subcontracted,
|
||||
},
|
||||
{ fieldtype: "Column Break" },
|
||||
{
|
||||
label: __("Workstation Type"),
|
||||
fieldname: "workstation_type",
|
||||
fieldtype: "Link",
|
||||
options: "Workstation Type",
|
||||
default: data.workstation_type,
|
||||
},
|
||||
{
|
||||
label: __("Workstation"),
|
||||
fieldname: "workstation",
|
||||
fieldtype: "Link",
|
||||
options: "Workstation",
|
||||
default: data.workstation,
|
||||
get_query() {
|
||||
let dialog = me.frm.edit_bom_dialog;
|
||||
let workstation_type = dialog.get_value("workstation_type");
|
||||
|
||||
if (workstation_type) {
|
||||
return {
|
||||
filters: {
|
||||
workstation_type: dialog.get_value("workstation_type"),
|
||||
},
|
||||
};
|
||||
}
|
||||
},
|
||||
},
|
||||
{ fieldtype: "Section Break" },
|
||||
{
|
||||
label: __("Skip Material Transfer"),
|
||||
fieldname: "skip_material_transfer",
|
||||
fieldtype: "Check",
|
||||
default: data.skip_material_transfer,
|
||||
},
|
||||
{
|
||||
label: __("Backflush Materials From WIP"),
|
||||
fieldname: "backflush_from_wip_warehouse",
|
||||
fieldtype: "Check",
|
||||
depends_on: "eval:doc.skip_material_transfer",
|
||||
default: data.backflush_from_wip_warehouse,
|
||||
},
|
||||
{
|
||||
label: __("Source Warehouse"),
|
||||
fieldname: "source_warehouse",
|
||||
fieldtype: "Link",
|
||||
options: "Warehouse",
|
||||
default: data.source_warehouse,
|
||||
depends_on: "eval:!doc.backflush_from_wip_warehouse",
|
||||
get_query() {
|
||||
return {
|
||||
filters: {
|
||||
company: me.frm.doc.company,
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
{ fieldtype: "Column Break" },
|
||||
{
|
||||
label: __("Work In Progress Warehouse"),
|
||||
fieldname: "wip_warehouse",
|
||||
fieldtype: "Link",
|
||||
options: "Warehouse",
|
||||
default: data.wip_warehouse,
|
||||
depends_on: "eval:!doc.skip_material_transfer || doc.backflush_from_wip_warehouse",
|
||||
get_query() {
|
||||
return {
|
||||
filters: {
|
||||
company: me.frm.doc.company,
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
label: __("Finished Good Warehouse"),
|
||||
fieldname: "fg_warehouse",
|
||||
fieldtype: "Link",
|
||||
options: "Warehouse",
|
||||
default: data.fg_warehouse,
|
||||
get_query() {
|
||||
return {
|
||||
filters: {
|
||||
company: me.frm.doc.company,
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
this.frm.edit_bom_dialog = frappe.prompt(
|
||||
fields,
|
||||
(data) => {
|
||||
|
||||
@@ -336,6 +336,7 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments {
|
||||
child.charge_type = "On Net Total";
|
||||
child.account_head = tax;
|
||||
child.rate = 0;
|
||||
child.set_by_item_tax_template = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -751,6 +751,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
|
||||
child.charge_type = "On Net Total";
|
||||
child.account_head = tax;
|
||||
child.rate = 0;
|
||||
child.set_by_item_tax_template = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1914,8 +1915,14 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
|
||||
callback: function(r) {
|
||||
if (!r.exc) {
|
||||
frappe.run_serially([
|
||||
() => me.frm.set_value("price_list_currency", r.message.parent.price_list_currency),
|
||||
() => me.frm.set_value("plc_conversion_rate", r.message.parent.plc_conversion_rate),
|
||||
() => {
|
||||
if (r.message.parent.price_list_currency)
|
||||
me.frm.set_value("price_list_currency", r.message.parent.price_list_currency);
|
||||
},
|
||||
() => {
|
||||
if (r.message.parent.plc_conversion_rate)
|
||||
me.frm.set_value("plc_conversion_rate", r.message.parent.plc_conversion_rate);
|
||||
},
|
||||
() => {
|
||||
if(args.items.length) {
|
||||
me._set_values_for_item_list(r.message.children);
|
||||
@@ -2076,7 +2083,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
|
||||
return this.frm.call({
|
||||
method: "erpnext.stock.get_item_details.get_item_tax_info",
|
||||
args: {
|
||||
company: me.frm.doc.company,
|
||||
doc: me.frm.doc,
|
||||
tax_category: cstr(me.frm.doc.tax_category),
|
||||
item_codes: item_codes,
|
||||
item_rates: item_rates,
|
||||
@@ -2107,7 +2114,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
|
||||
return this.frm.call({
|
||||
method: "erpnext.stock.get_item_details.get_item_tax_map",
|
||||
args: {
|
||||
company: me.frm.doc.company,
|
||||
doc: me.frm.doc,
|
||||
item_tax_template: item.item_tax_template,
|
||||
as_json: true
|
||||
},
|
||||
|
||||
@@ -33,7 +33,7 @@ erpnext.financial_statements = {
|
||||
|
||||
return value;
|
||||
} else if (frappe.query_report.get_filter_value("selected_view") == "Margin" && data) {
|
||||
if (column.fieldname == "stub" && data.account_name == __("Income")) {
|
||||
if (column.fieldname == "account" && data.account_name == __("Income")) {
|
||||
//Taking the total income from each column (for all the financial years) as the base (100%)
|
||||
this.baseData = row;
|
||||
}
|
||||
@@ -52,7 +52,7 @@ erpnext.financial_statements = {
|
||||
}
|
||||
}
|
||||
|
||||
if (data && column.fieldname == "stub") {
|
||||
if (data && column.fieldname == "account") {
|
||||
// first column
|
||||
value = data.section_name || data.account_name || value;
|
||||
|
||||
|
||||
@@ -77,9 +77,13 @@ $.extend(erpnext.queries, {
|
||||
},
|
||||
|
||||
company_address_query: function (doc) {
|
||||
if (!doc.company) {
|
||||
frappe.throw(__("Please set {0}", [__(frappe.meta.get_label(doc.doctype, "company", doc.name))]));
|
||||
}
|
||||
|
||||
return {
|
||||
query: "frappe.contacts.doctype.address.address.address_query",
|
||||
filters: { is_your_company_address: 1, link_doctype: "Company", link_name: doc.company || "" },
|
||||
filters: { link_doctype: "Company", link_name: doc.company },
|
||||
};
|
||||
},
|
||||
|
||||
|
||||
@@ -52,6 +52,7 @@ erpnext.sales_common = {
|
||||
me.frm.set_query("customer_address", erpnext.queries.address_query);
|
||||
me.frm.set_query("shipping_address_name", erpnext.queries.address_query);
|
||||
me.frm.set_query("dispatch_address_name", erpnext.queries.dispatch_address_query);
|
||||
me.frm.set_query("company_address", erpnext.queries.company_address_query);
|
||||
|
||||
erpnext.accounts.dimensions.setup_dimension_filters(me.frm, me.frm.doctype);
|
||||
|
||||
|
||||
@@ -24,20 +24,6 @@ frappe.ui.form.on("Quotation", {
|
||||
frm.set_df_property("packed_items", "cannot_add_rows", true);
|
||||
frm.set_df_property("packed_items", "cannot_delete_rows", true);
|
||||
|
||||
frm.set_query("company_address", function (doc) {
|
||||
if (!doc.company) {
|
||||
frappe.throw(__("Please set Company"));
|
||||
}
|
||||
|
||||
return {
|
||||
query: "frappe.contacts.doctype.address.address.address_query",
|
||||
filters: {
|
||||
link_doctype: "Company",
|
||||
link_name: doc.company,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
frm.set_query("serial_and_batch_bundle", "packed_items", (doc, cdt, cdn) => {
|
||||
let row = locals[cdt][cdn];
|
||||
return {
|
||||
|
||||
@@ -24,6 +24,10 @@
|
||||
"uom",
|
||||
"conversion_factor",
|
||||
"stock_qty",
|
||||
"available_quantity_section",
|
||||
"actual_qty",
|
||||
"column_break_ylrv",
|
||||
"company_total_stock",
|
||||
"section_break_16",
|
||||
"price_list_rate",
|
||||
"base_price_list_rate",
|
||||
@@ -71,7 +75,6 @@
|
||||
"prevdoc_docname",
|
||||
"item_balance",
|
||||
"projected_qty",
|
||||
"actual_qty",
|
||||
"col_break4",
|
||||
"stock_balance",
|
||||
"item_tax_rate",
|
||||
@@ -461,9 +464,10 @@
|
||||
"report_hide": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"fieldname": "actual_qty",
|
||||
"fieldtype": "Float",
|
||||
"label": "Actual Qty",
|
||||
"label": "Qty (Warehouse)",
|
||||
"no_copy": 1,
|
||||
"print_hide": 1,
|
||||
"read_only": 1,
|
||||
@@ -669,12 +673,31 @@
|
||||
"fieldtype": "Currency",
|
||||
"label": "Distributed Discount Amount",
|
||||
"options": "currency"
|
||||
},
|
||||
{
|
||||
"fieldname": "available_quantity_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Available Quantity"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_ylrv",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"fieldname": "company_total_stock",
|
||||
"fieldtype": "Float",
|
||||
"label": "Qty (Company)",
|
||||
"no_copy": 1,
|
||||
"print_hide": 1,
|
||||
"read_only": 1,
|
||||
"report_hide": 1
|
||||
}
|
||||
],
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2024-06-02 06:21:09.508680",
|
||||
"modified": "2024-11-24 14:18:43.952844",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Selling",
|
||||
"name": "Quotation Item",
|
||||
|
||||
@@ -27,6 +27,7 @@ class QuotationItem(Document):
|
||||
blanket_order: DF.Link | None
|
||||
blanket_order_rate: DF.Currency
|
||||
brand: DF.Link | None
|
||||
company_total_stock: DF.Float
|
||||
conversion_factor: DF.Float
|
||||
customer_item_code: DF.Data | None
|
||||
description: DF.TextEditor | None
|
||||
|
||||
@@ -26,20 +26,6 @@ frappe.ui.form.on("Sales Order", {
|
||||
return doc.stock_qty <= doc.delivered_qty ? "green" : "orange";
|
||||
});
|
||||
|
||||
frm.set_query("company_address", function (doc) {
|
||||
if (!doc.company) {
|
||||
frappe.throw(__("Please set Company"));
|
||||
}
|
||||
|
||||
return {
|
||||
query: "frappe.contacts.doctype.address.address.address_query",
|
||||
filters: {
|
||||
link_doctype: "Company",
|
||||
link_name: doc.company,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
frm.set_query("bom_no", "items", function (doc, cdt, cdn) {
|
||||
var row = locals[cdt][cdn];
|
||||
return {
|
||||
@@ -57,8 +43,8 @@ frappe.ui.form.on("Sales Order", {
|
||||
if (frm.doc.docstatus === 1) {
|
||||
if (
|
||||
frm.doc.status !== "Closed" &&
|
||||
flt(frm.doc.per_delivered, 2) < 100 &&
|
||||
flt(frm.doc.per_billed, 2) < 100 &&
|
||||
flt(frm.doc.per_delivered, precision("per_delivered")) < 100 &&
|
||||
flt(frm.doc.per_billed, precision("per_billed")) < 100 &&
|
||||
frm.has_perm("write")
|
||||
) {
|
||||
frm.add_custom_button(__("Update Items"), () => {
|
||||
@@ -75,7 +61,7 @@ frappe.ui.form.on("Sales Order", {
|
||||
if (
|
||||
frm.doc.__onload &&
|
||||
frm.doc.__onload.has_unreserved_stock &&
|
||||
flt(frm.doc.per_picked) === 0
|
||||
flt(frm.doc.per_picked, precision("per_picked")) === 0
|
||||
) {
|
||||
frm.add_custom_button(
|
||||
__("Reserve"),
|
||||
@@ -604,7 +590,10 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex
|
||||
__("Status")
|
||||
);
|
||||
|
||||
if (flt(doc.per_delivered, 2) < 100 || flt(doc.per_billed, 2) < 100) {
|
||||
if (
|
||||
flt(doc.per_delivered, precision("per_delivered")) < 100 ||
|
||||
flt(doc.per_billed, precision("per_billed")) < 100
|
||||
) {
|
||||
// close
|
||||
this.frm.add_custom_button(__("Close"), () => this.close_sales_order(), __("Status"));
|
||||
}
|
||||
@@ -627,7 +616,10 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex
|
||||
) && !this.frm.doc.skip_delivery_note;
|
||||
|
||||
if (this.frm.has_perm("submit")) {
|
||||
if (flt(doc.per_delivered, 2) < 100 || flt(doc.per_billed, 2) < 100) {
|
||||
if (
|
||||
flt(doc.per_delivered, precision("per_delivered")) < 100 ||
|
||||
flt(doc.per_billed, precision("per_billed")) < 100
|
||||
) {
|
||||
// hold
|
||||
this.frm.add_custom_button(
|
||||
__("Hold"),
|
||||
@@ -645,8 +637,8 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex
|
||||
|
||||
if (
|
||||
(!doc.__onload || !doc.__onload.has_reserved_stock) &&
|
||||
flt(doc.per_picked, 2) < 100 &&
|
||||
flt(doc.per_delivered, 2) < 100 &&
|
||||
flt(doc.per_picked, precision("per_picked")) < 100 &&
|
||||
flt(doc.per_delivered, precision("per_delivered")) < 100 &&
|
||||
frappe.model.can_create("Pick List")
|
||||
) {
|
||||
this.frm.add_custom_button(
|
||||
@@ -664,7 +656,7 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex
|
||||
|
||||
// delivery note
|
||||
if (
|
||||
flt(doc.per_delivered, 2) < 100 &&
|
||||
flt(doc.per_delivered, precision("per_delivered")) < 100 &&
|
||||
(order_is_a_sale || order_is_a_custom_sale) &&
|
||||
allow_delivery
|
||||
) {
|
||||
@@ -686,7 +678,10 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex
|
||||
}
|
||||
|
||||
// sales invoice
|
||||
if (flt(doc.per_billed, 2) < 100 && frappe.model.can_create("Sales Invoice")) {
|
||||
if (
|
||||
flt(doc.per_billed, precision("per_billed")) < 100 &&
|
||||
frappe.model.can_create("Sales Invoice")
|
||||
) {
|
||||
this.frm.add_custom_button(
|
||||
__("Sales Invoice"),
|
||||
() => me.make_sales_invoice(),
|
||||
@@ -698,7 +693,7 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex
|
||||
if (
|
||||
(!doc.order_type ||
|
||||
((order_is_a_sale || order_is_a_custom_sale) &&
|
||||
flt(doc.per_delivered, 2) < 100)) &&
|
||||
flt(doc.per_delivered, precision("per_delivered")) < 100)) &&
|
||||
frappe.model.can_create("Material Request")
|
||||
) {
|
||||
this.frm.add_custom_button(
|
||||
@@ -723,7 +718,10 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex
|
||||
}
|
||||
|
||||
// maintenance
|
||||
if (flt(doc.per_delivered, 2) < 100 && (order_is_maintenance || order_is_a_custom_sale)) {
|
||||
if (
|
||||
flt(doc.per_delivered, precision("per_delivered")) < 100 &&
|
||||
(order_is_maintenance || order_is_a_custom_sale)
|
||||
) {
|
||||
if (frappe.model.can_create("Maintenance Visit")) {
|
||||
this.frm.add_custom_button(
|
||||
__("Maintenance Visit"),
|
||||
@@ -741,7 +739,10 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex
|
||||
}
|
||||
|
||||
// project
|
||||
if (flt(doc.per_delivered, 2) < 100 && frappe.model.can_create("Project")) {
|
||||
if (
|
||||
flt(doc.per_delivered, precision("per_delivered")) < 100 &&
|
||||
frappe.model.can_create("Project")
|
||||
) {
|
||||
this.frm.add_custom_button(__("Project"), () => this.make_project(), __("Create"));
|
||||
}
|
||||
|
||||
|
||||
@@ -79,11 +79,14 @@
|
||||
"against_blanket_order",
|
||||
"blanket_order",
|
||||
"blanket_order_rate",
|
||||
"available_quantity_section",
|
||||
"actual_qty",
|
||||
"column_break_jpky",
|
||||
"company_total_stock",
|
||||
"manufacturing_section_section",
|
||||
"bom_no",
|
||||
"planning_section",
|
||||
"projected_qty",
|
||||
"actual_qty",
|
||||
"ordered_qty",
|
||||
"planned_qty",
|
||||
"production_plan_qty",
|
||||
@@ -637,7 +640,7 @@
|
||||
"allow_on_submit": 1,
|
||||
"fieldname": "actual_qty",
|
||||
"fieldtype": "Float",
|
||||
"label": "Actual Qty",
|
||||
"label": "Qty (Warehouse)",
|
||||
"no_copy": 1,
|
||||
"print_hide": 1,
|
||||
"print_width": "70px",
|
||||
@@ -912,12 +915,30 @@
|
||||
"fieldtype": "Currency",
|
||||
"label": "Distributed Discount Amount",
|
||||
"options": "currency"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"fieldname": "company_total_stock",
|
||||
"fieldtype": "Float",
|
||||
"label": "Qty (Company)",
|
||||
"no_copy": 1,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_jpky",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "available_quantity_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Available Quantity"
|
||||
}
|
||||
],
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2024-06-02 06:13:40.597947",
|
||||
"modified": "2024-11-21 13:21:29.743474",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Selling",
|
||||
"name": "Sales Order Item",
|
||||
|
||||
@@ -30,6 +30,7 @@ class SalesOrderItem(Document):
|
||||
blanket_order_rate: DF.Currency
|
||||
bom_no: DF.Link | None
|
||||
brand: DF.Link | None
|
||||
company_total_stock: DF.Float
|
||||
conversion_factor: DF.Float
|
||||
customer_item_code: DF.Data | None
|
||||
delivered_by_supplier: DF.Check
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
"docstatus": 0,
|
||||
"doctype": "Number Card",
|
||||
"document_type": "Customer",
|
||||
"dynamic_filters_json": "",
|
||||
"filters_json": "[[\"Customer\",\"disabled\",\"=\",\"0\"]]",
|
||||
"function": "Count",
|
||||
"idx": 0,
|
||||
|
||||
@@ -556,7 +556,7 @@ erpnext.PointOfSale.Controller = class {
|
||||
const item_row_exists = !$.isEmptyObject(item_row);
|
||||
|
||||
const from_selector = field === "qty" && value === "+1";
|
||||
if (from_selector) value = flt(item_row.stock_qty) + flt(value);
|
||||
if (from_selector) value = flt(item_row.qty) + flt(value);
|
||||
|
||||
if (item_row_exists) {
|
||||
if (field === "qty") value = flt(value);
|
||||
@@ -695,7 +695,7 @@ erpnext.PointOfSale.Controller = class {
|
||||
const is_stock_item = resp[1];
|
||||
|
||||
frappe.dom.unfreeze();
|
||||
const bold_uom = item_row.uom.bold();
|
||||
const bold_uom = item_row.stock_uom.bold();
|
||||
const bold_item_code = item_row.item_code.bold();
|
||||
const bold_warehouse = warehouse.bold();
|
||||
const bold_available_qty = available_qty.toString().bold();
|
||||
|
||||
@@ -98,6 +98,11 @@ frappe.query_reports["Sales Analytics"] = {
|
||||
default: "select",
|
||||
reqd: 1,
|
||||
},
|
||||
{
|
||||
fieldname: "show_aggregate_value_from_subsidiary_companies",
|
||||
label: __("Show Aggregate Value from Subsidiary Companies"),
|
||||
fieldtype: "Check",
|
||||
},
|
||||
],
|
||||
get_datatable_options(options) {
|
||||
return Object.assign(options, {
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
import frappe
|
||||
from frappe import _, scrub
|
||||
from frappe.query_builder import DocType
|
||||
from frappe.query_builder.functions import IfNull
|
||||
from frappe.utils import add_days, add_to_date, flt, getdate
|
||||
|
||||
from erpnext.accounts.utils import get_fiscal_year
|
||||
@@ -78,7 +80,26 @@ class Analytics:
|
||||
]
|
||||
self.get_period_date_ranges()
|
||||
|
||||
def update_company_list_for_parent_company(self):
|
||||
company_list = [self.filters.get("company")]
|
||||
|
||||
selected_company = self.filters.get("company")
|
||||
if (
|
||||
selected_company
|
||||
and self.filters.get("show_aggregate_value_from_subsidiary_companies")
|
||||
and frappe.db.get_value("Company", selected_company, "is_group")
|
||||
):
|
||||
lft, rgt = frappe.db.get_value("Company", selected_company, ["lft", "rgt"])
|
||||
child_companies = frappe.db.get_list(
|
||||
"Company", filters={"lft": [">", lft], "rgt": ["<", rgt]}, pluck="name"
|
||||
)
|
||||
|
||||
company_list.extend(child_companies)
|
||||
|
||||
self.filters["company"] = company_list
|
||||
|
||||
def run(self):
|
||||
self.update_company_list_for_parent_company()
|
||||
self.get_columns()
|
||||
self.get_data()
|
||||
self.get_chart_data()
|
||||
@@ -176,14 +197,23 @@ class Analytics:
|
||||
else:
|
||||
value_field = "total_qty"
|
||||
|
||||
self.entries = frappe.db.sql(
|
||||
""" select s.order_type as entity, s.{value_field} as value_field, s.{date_field}
|
||||
from `tab{doctype}` s where s.docstatus = 1 and s.company = %s and s.{date_field} between %s and %s
|
||||
and ifnull(s.order_type, '') != '' order by s.order_type
|
||||
""".format(date_field=self.date_field, value_field=value_field, doctype=self.filters.doc_type),
|
||||
(self.filters.company, self.filters.from_date, self.filters.to_date),
|
||||
as_dict=1,
|
||||
)
|
||||
doctype = DocType(self.filters.doc_type)
|
||||
|
||||
self.entries = (
|
||||
frappe.qb.from_(doctype)
|
||||
.select(
|
||||
doctype.order_type.as_("entity"),
|
||||
doctype[self.date_field],
|
||||
doctype[value_field].as_("value_field"),
|
||||
)
|
||||
.where(
|
||||
(doctype.docstatus == 1)
|
||||
& (doctype.company.isin(self.filters.company))
|
||||
& (doctype[self.date_field].between(self.filters.from_date, self.filters.to_date))
|
||||
& (IfNull(doctype.order_type, "") != "")
|
||||
)
|
||||
.orderby(doctype.order_type)
|
||||
).run(as_dict=True)
|
||||
|
||||
self.get_teams()
|
||||
|
||||
@@ -216,7 +246,7 @@ class Analytics:
|
||||
fields=[entity, entity_name, value_field, self.date_field],
|
||||
filters={
|
||||
"docstatus": 1,
|
||||
"company": self.filters.company,
|
||||
"company": ["in", self.filters.company],
|
||||
self.date_field: ("between", [self.filters.from_date, self.filters.to_date]),
|
||||
},
|
||||
)
|
||||
@@ -231,16 +261,26 @@ class Analytics:
|
||||
else:
|
||||
value_field = "stock_qty"
|
||||
|
||||
self.entries = frappe.db.sql(
|
||||
"""
|
||||
select i.item_code as entity, i.item_name as entity_name, i.stock_uom, i.{value_field} as value_field, s.{date_field}
|
||||
from `tab{doctype} Item` i , `tab{doctype}` s
|
||||
where s.name = i.parent and i.docstatus = 1 and s.company = %s
|
||||
and s.{date_field} between %s and %s
|
||||
""".format(date_field=self.date_field, value_field=value_field, doctype=self.filters.doc_type),
|
||||
(self.filters.company, self.filters.from_date, self.filters.to_date),
|
||||
as_dict=1,
|
||||
)
|
||||
doctype = DocType(self.filters.doc_type)
|
||||
doctype_item = DocType(f"{self.filters.doc_type} Item")
|
||||
|
||||
self.entries = (
|
||||
frappe.qb.from_(doctype_item)
|
||||
.join(doctype)
|
||||
.on(doctype.name == doctype_item.parent)
|
||||
.select(
|
||||
doctype_item.item_code.as_("entity"),
|
||||
doctype_item.item_name.as_("entity_name"),
|
||||
doctype_item.stock_uom,
|
||||
doctype_item[value_field].as_("value_field"),
|
||||
doctype[self.date_field],
|
||||
)
|
||||
.where(
|
||||
(doctype_item.docstatus == 1)
|
||||
& (doctype.company.isin(self.filters.company))
|
||||
& (doctype[self.date_field].between(self.filters.from_date, self.filters.to_date))
|
||||
)
|
||||
).run(as_dict=True)
|
||||
|
||||
self.entity_names = {}
|
||||
for d in self.entries:
|
||||
@@ -265,7 +305,7 @@ class Analytics:
|
||||
fields=[entity_field, value_field, self.date_field],
|
||||
filters={
|
||||
"docstatus": 1,
|
||||
"company": self.filters.company,
|
||||
"company": ["in", self.filters.company],
|
||||
self.date_field: ("between", [self.filters.from_date, self.filters.to_date]),
|
||||
},
|
||||
)
|
||||
@@ -277,16 +317,24 @@ class Analytics:
|
||||
else:
|
||||
value_field = "qty"
|
||||
|
||||
self.entries = frappe.db.sql(
|
||||
f"""
|
||||
select i.item_group as entity, i.{value_field} as value_field, s.{self.date_field}
|
||||
from `tab{self.filters.doc_type} Item` i , `tab{self.filters.doc_type}` s
|
||||
where s.name = i.parent and i.docstatus = 1 and s.company = %s
|
||||
and s.{self.date_field} between %s and %s
|
||||
""",
|
||||
(self.filters.company, self.filters.from_date, self.filters.to_date),
|
||||
as_dict=1,
|
||||
)
|
||||
doctype = DocType(self.filters.doc_type)
|
||||
doctype_item = DocType(f"{self.filters.doc_type} Item")
|
||||
|
||||
self.entries = (
|
||||
frappe.qb.from_(doctype_item)
|
||||
.join(doctype)
|
||||
.on(doctype.name == doctype_item.parent)
|
||||
.select(
|
||||
doctype_item.item_group.as_("entity"),
|
||||
doctype_item[value_field].as_("value_field"),
|
||||
doctype[self.date_field],
|
||||
)
|
||||
.where(
|
||||
(doctype_item.docstatus == 1)
|
||||
& (doctype.company.isin(self.filters.company))
|
||||
& (doctype[self.date_field].between(self.filters.from_date, self.filters.to_date))
|
||||
)
|
||||
).run(as_dict=True)
|
||||
|
||||
self.get_groups()
|
||||
|
||||
@@ -306,7 +354,7 @@ class Analytics:
|
||||
fields=[entity, value_field, self.date_field],
|
||||
filters={
|
||||
"docstatus": 1,
|
||||
"company": self.filters.company,
|
||||
"company": ["in", self.filters.company],
|
||||
"project": ["!=", ""],
|
||||
self.date_field: ("between", [self.filters.from_date, self.filters.to_date]),
|
||||
},
|
||||
@@ -379,7 +427,7 @@ class Analytics:
|
||||
str(((posting_date.month - 1) // 3) + 1), str(posting_date.year)
|
||||
)
|
||||
else:
|
||||
year = get_fiscal_year(posting_date, company=self.filters.company)
|
||||
year = get_fiscal_year(posting_date, company=self.filters.company[0])
|
||||
period = str(year[0])
|
||||
return period
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import frappe
|
||||
from frappe.utils.deprecations import deprecated
|
||||
|
||||
from erpnext.deprecation_dumpster import deprecated
|
||||
|
||||
|
||||
def get_leaderboards():
|
||||
@@ -218,7 +219,7 @@ def get_all_sales_person(date_range, company, field=None, limit=0):
|
||||
)
|
||||
|
||||
|
||||
@deprecated
|
||||
@deprecated(f"{__name__}.get_date_condition", "unknown", "v16", "No known instructions.")
|
||||
def get_date_condition(date_range, field):
|
||||
date_condition = ""
|
||||
if date_range:
|
||||
|
||||
@@ -4,12 +4,18 @@ from collections import defaultdict
|
||||
import frappe
|
||||
from frappe.query_builder.functions import CombineDatetime, Sum
|
||||
from frappe.utils import flt
|
||||
from frappe.utils.deprecations import deprecated
|
||||
from pypika import Order
|
||||
|
||||
from erpnext.deprecation_dumpster import deprecated
|
||||
|
||||
|
||||
class DeprecatedSerialNoValuation:
|
||||
@deprecated
|
||||
@deprecated(
|
||||
"erpnext.stock.serial_batch_bundle.SerialNoValuation.calculate_stock_value_from_deprecarated_ledgers",
|
||||
"unknown",
|
||||
"v16",
|
||||
"No known instructions.",
|
||||
)
|
||||
def calculate_stock_value_from_deprecarated_ledgers(self):
|
||||
if not has_sle_for_serial_nos(self.sle.item_code):
|
||||
return
|
||||
@@ -35,7 +41,12 @@ class DeprecatedSerialNoValuation:
|
||||
|
||||
return serial_nos
|
||||
|
||||
@deprecated
|
||||
@deprecated(
|
||||
"erpnext.stock.serial_batch_bundle.SerialNoValuation.get_incoming_value_for_serial_nos",
|
||||
"unknown",
|
||||
"v16",
|
||||
"No known instructions.",
|
||||
)
|
||||
def get_incoming_value_for_serial_nos(self, serial_nos):
|
||||
from erpnext.stock.utils import get_combine_datetime
|
||||
|
||||
@@ -93,14 +104,24 @@ def has_sle_for_serial_nos(item_code):
|
||||
|
||||
|
||||
class DeprecatedBatchNoValuation:
|
||||
@deprecated
|
||||
@deprecated(
|
||||
"erpnext.stock.serial_batch_bundle.BatchNoValuation.calculate_avg_rate_from_deprecarated_ledgers",
|
||||
"unknown",
|
||||
"v16",
|
||||
"No known instructions.",
|
||||
)
|
||||
def calculate_avg_rate_from_deprecarated_ledgers(self):
|
||||
entries = self.get_sle_for_batches()
|
||||
for ledger in entries:
|
||||
self.stock_value_differece[ledger.batch_no] += flt(ledger.batch_value)
|
||||
self.available_qty[ledger.batch_no] += flt(ledger.batch_qty)
|
||||
|
||||
@deprecated
|
||||
@deprecated(
|
||||
"erpnext.stock.serial_batch_bundle.BatchNoValuation.get_sle_for_batches",
|
||||
"unknown",
|
||||
"v16",
|
||||
"No known instructions.",
|
||||
)
|
||||
def get_sle_for_batches(self):
|
||||
from erpnext.stock.utils import get_combine_datetime
|
||||
|
||||
@@ -147,7 +168,12 @@ class DeprecatedBatchNoValuation:
|
||||
|
||||
return query.run(as_dict=True)
|
||||
|
||||
@deprecated
|
||||
@deprecated(
|
||||
"erpnext.stock.serial_batch_bundle.BatchNoValuation.calculate_avg_rate_for_non_batchwise_valuation",
|
||||
"unknown",
|
||||
"v16",
|
||||
"No known instructions.",
|
||||
)
|
||||
def calculate_avg_rate_for_non_batchwise_valuation(self):
|
||||
if not self.non_batchwise_valuation_batches:
|
||||
return
|
||||
@@ -185,12 +211,22 @@ class DeprecatedBatchNoValuation:
|
||||
},
|
||||
)
|
||||
|
||||
@deprecated
|
||||
@deprecated(
|
||||
"erpnext.stock.serial_batch_bundle.BatchNoValuation.set_balance_value_for_non_batchwise_valuation_batches",
|
||||
"unknown",
|
||||
"v16",
|
||||
"No known instructions.",
|
||||
)
|
||||
def set_balance_value_for_non_batchwise_valuation_batches(self):
|
||||
self.set_balance_value_from_sl_entries()
|
||||
self.set_balance_value_from_bundle()
|
||||
|
||||
@deprecated
|
||||
@deprecated(
|
||||
"erpnext.stock.serial_batch_bundle.BatchNoValuation.set_balance_value_from_sl_entries",
|
||||
"unknown",
|
||||
"v16",
|
||||
"No known instructions.",
|
||||
)
|
||||
def set_balance_value_from_sl_entries(self) -> None:
|
||||
from erpnext.stock.utils import get_combine_datetime
|
||||
|
||||
@@ -237,7 +273,12 @@ class DeprecatedBatchNoValuation:
|
||||
self.non_batchwise_balance_qty[d.batch_no] += flt(d.batch_qty)
|
||||
self.available_qty[d.batch_no] += flt(d.batch_qty)
|
||||
|
||||
@deprecated
|
||||
@deprecated(
|
||||
"erpnext.stock.serial_batch_bundle.BatchNoValuation.set_balance_value_from_bundle",
|
||||
"unknown",
|
||||
"v16",
|
||||
"No known instructions.",
|
||||
)
|
||||
def set_balance_value_from_bundle(self) -> None:
|
||||
bundle = frappe.qb.DocType("Serial and Batch Bundle")
|
||||
bundle_child = frappe.qb.DocType("Serial and Batch Entry")
|
||||
|
||||
@@ -88,16 +88,19 @@
|
||||
"column_break_rxvc",
|
||||
"batch_no",
|
||||
"available_qty_section",
|
||||
"actual_batch_qty",
|
||||
"actual_qty",
|
||||
"installed_qty",
|
||||
"item_tax_rate",
|
||||
"actual_batch_qty",
|
||||
"column_break_atna",
|
||||
"company_total_stock",
|
||||
"section_break_kejd",
|
||||
"installed_qty",
|
||||
"packed_qty",
|
||||
"column_break_fguf",
|
||||
"received_qty",
|
||||
"accounting_details_section",
|
||||
"expense_account",
|
||||
"column_break_71",
|
||||
"item_tax_rate",
|
||||
"internal_transfer_section",
|
||||
"material_request",
|
||||
"purchase_order",
|
||||
@@ -520,7 +523,7 @@
|
||||
"allow_on_submit": 1,
|
||||
"fieldname": "actual_qty",
|
||||
"fieldtype": "Float",
|
||||
"label": "Available Qty at From Warehouse",
|
||||
"label": "Qty (Warehouse)",
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "actual_qty",
|
||||
"oldfieldtype": "Currency",
|
||||
@@ -914,13 +917,30 @@
|
||||
"fieldtype": "Currency",
|
||||
"label": "Distributed Discount Amount",
|
||||
"options": "currency"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"fieldname": "company_total_stock",
|
||||
"fieldtype": "Float",
|
||||
"label": "Qty (Company)",
|
||||
"no_copy": 1,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_kejd",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_fguf",
|
||||
"fieldtype": "Column Break"
|
||||
}
|
||||
],
|
||||
"idx": 1,
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2024-06-02 06:18:38.491763",
|
||||
"modified": "2024-11-21 16:37:37.441498",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Delivery Note Item",
|
||||
|
||||
@@ -30,6 +30,7 @@ class DeliveryNoteItem(Document):
|
||||
batch_no: DF.Link | None
|
||||
billed_amt: DF.Currency
|
||||
brand: DF.Link | None
|
||||
company_total_stock: DF.Float
|
||||
conversion_factor: DF.Float
|
||||
cost_center: DF.Link | None
|
||||
customer_item_code: DF.Data | None
|
||||
|
||||
@@ -685,39 +685,41 @@ $.extend(erpnext.item, {
|
||||
}
|
||||
|
||||
frm.doc.attributes.forEach(function (d) {
|
||||
let p = new Promise((resolve) => {
|
||||
if (!d.numeric_values) {
|
||||
frappe
|
||||
.call({
|
||||
method: "frappe.client.get_list",
|
||||
args: {
|
||||
doctype: "Item Attribute Value",
|
||||
filters: [["parent", "=", d.attribute]],
|
||||
fields: ["attribute_value"],
|
||||
limit_page_length: 0,
|
||||
parent: "Item Attribute",
|
||||
order_by: "idx",
|
||||
},
|
||||
})
|
||||
.then((r) => {
|
||||
if (r.message) {
|
||||
attr_val_fields[d.attribute] = r.message.map(function (d) {
|
||||
return d.attribute_value;
|
||||
});
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
let values = [];
|
||||
for (var i = d.from_range; i <= d.to_range; i = flt(i + d.increment, 6)) {
|
||||
values.push(i);
|
||||
if (!d.disabled) {
|
||||
let p = new Promise((resolve) => {
|
||||
if (!d.numeric_values) {
|
||||
frappe
|
||||
.call({
|
||||
method: "frappe.client.get_list",
|
||||
args: {
|
||||
doctype: "Item Attribute Value",
|
||||
filters: [["parent", "=", d.attribute]],
|
||||
fields: ["attribute_value"],
|
||||
limit_page_length: 0,
|
||||
parent: "Item Attribute",
|
||||
order_by: "idx",
|
||||
},
|
||||
})
|
||||
.then((r) => {
|
||||
if (r.message) {
|
||||
attr_val_fields[d.attribute] = r.message.map(function (d) {
|
||||
return d.attribute_value;
|
||||
});
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
let values = [];
|
||||
for (var i = d.from_range; i <= d.to_range; i = flt(i + d.increment, 6)) {
|
||||
values.push(i);
|
||||
}
|
||||
attr_val_fields[d.attribute] = values;
|
||||
resolve();
|
||||
}
|
||||
attr_val_fields[d.attribute] = values;
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
promises.push(p);
|
||||
promises.push(p);
|
||||
}
|
||||
}, this);
|
||||
|
||||
Promise.all(promises).then(() => {
|
||||
@@ -732,26 +734,29 @@ $.extend(erpnext.item, {
|
||||
for (var i = 0; i < frm.doc.attributes.length; i++) {
|
||||
var fieldtype, desc;
|
||||
var row = frm.doc.attributes[i];
|
||||
if (row.numeric_values) {
|
||||
fieldtype = "Float";
|
||||
desc =
|
||||
"Min Value: " +
|
||||
row.from_range +
|
||||
" , Max Value: " +
|
||||
row.to_range +
|
||||
", in Increments of: " +
|
||||
row.increment;
|
||||
} else {
|
||||
fieldtype = "Data";
|
||||
desc = "";
|
||||
|
||||
if (!row.disabled) {
|
||||
if (row.numeric_values) {
|
||||
fieldtype = "Float";
|
||||
desc =
|
||||
"Min Value: " +
|
||||
row.from_range +
|
||||
" , Max Value: " +
|
||||
row.to_range +
|
||||
", in Increments of: " +
|
||||
row.increment;
|
||||
} else {
|
||||
fieldtype = "Data";
|
||||
desc = "";
|
||||
}
|
||||
fields = fields.concat({
|
||||
label: row.attribute,
|
||||
fieldname: row.attribute,
|
||||
fieldtype: fieldtype,
|
||||
reqd: 0,
|
||||
description: desc,
|
||||
});
|
||||
}
|
||||
fields = fields.concat({
|
||||
label: row.attribute,
|
||||
fieldname: row.attribute,
|
||||
fieldtype: fieldtype,
|
||||
reqd: 0,
|
||||
description: desc,
|
||||
});
|
||||
}
|
||||
|
||||
if (frm.doc.image) {
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
"field_order": [
|
||||
"attribute_name",
|
||||
"numeric_values",
|
||||
"column_break_vbik",
|
||||
"disabled",
|
||||
"section_break_4",
|
||||
"from_range",
|
||||
"increment",
|
||||
@@ -70,15 +72,26 @@
|
||||
"fieldtype": "Table",
|
||||
"label": "Item Attribute Values",
|
||||
"options": "Item Attribute Value"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_vbik",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "disabled",
|
||||
"fieldtype": "Check",
|
||||
"label": "Disabled"
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-edit",
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2024-03-27 13:09:53.963494",
|
||||
"modified": "2024-11-26 20:05:29.421714",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Item Attribute",
|
||||
"naming_rule": "By fieldname",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
|
||||
@@ -30,6 +30,7 @@ class ItemAttribute(Document):
|
||||
from erpnext.stock.doctype.item_attribute_value.item_attribute_value import ItemAttributeValue
|
||||
|
||||
attribute_name: DF.Data
|
||||
disabled: DF.Check
|
||||
from_range: DF.Float
|
||||
increment: DF.Float
|
||||
item_attribute_values: DF.Table[ItemAttributeValue]
|
||||
@@ -44,6 +45,19 @@ class ItemAttribute(Document):
|
||||
|
||||
def on_update(self):
|
||||
self.validate_exising_items()
|
||||
self.set_enabled_disabled_in_items()
|
||||
|
||||
def set_enabled_disabled_in_items(self):
|
||||
db_value = self.get_doc_before_save()
|
||||
if not db_value or db_value.disabled != self.disabled:
|
||||
item_variant_table = frappe.qb.DocType("Item Variant Attribute")
|
||||
query = (
|
||||
frappe.qb.update(item_variant_table)
|
||||
.set(item_variant_table.disabled, self.disabled)
|
||||
.where(item_variant_table.attribute == self.name)
|
||||
)
|
||||
|
||||
query.run()
|
||||
|
||||
def validate_exising_items(self):
|
||||
"""Validate that if there are existing items with attributes, they are valid"""
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
"column_break_2",
|
||||
"attribute_value",
|
||||
"numeric_values",
|
||||
"disabled",
|
||||
"section_break_4",
|
||||
"from_range",
|
||||
"increment",
|
||||
@@ -74,11 +75,18 @@
|
||||
"fieldname": "to_range",
|
||||
"fieldtype": "Float",
|
||||
"label": "To Range"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fetch_from": "attribute.disabled",
|
||||
"fieldname": "disabled",
|
||||
"fieldtype": "Check",
|
||||
"label": "Disabled"
|
||||
}
|
||||
],
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2024-03-27 13:09:55.966900",
|
||||
"modified": "2024-11-26 20:10:49.873339",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Item Variant Attribute",
|
||||
|
||||
@@ -16,6 +16,7 @@ class ItemVariantAttribute(Document):
|
||||
|
||||
attribute: DF.Link
|
||||
attribute_value: DF.Data | None
|
||||
disabled: DF.Check
|
||||
from_range: DF.Float
|
||||
increment: DF.Float
|
||||
numeric_values: DF.Check
|
||||
|
||||
@@ -44,6 +44,14 @@ frappe.ui.form.on("Material Request", {
|
||||
});
|
||||
},
|
||||
|
||||
schedule_date(frm) {
|
||||
if (frm.doc.schedule_date) {
|
||||
frm.doc.items.forEach((d) => {
|
||||
frappe.model.set_value(d.doctype, d.name, "schedule_date", frm.doc.schedule_date);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
onload: function (frm) {
|
||||
// add item, if previous view was item
|
||||
erpnext.utils.add_item(frm);
|
||||
|
||||
@@ -6,7 +6,7 @@ import frappe
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from frappe.model.mapper import get_mapped_doc
|
||||
from frappe.utils import cint, cstr, flt, get_number_format_info
|
||||
from frappe.utils import cint, cstr, flt, get_link_to_form, get_number_format_info
|
||||
|
||||
from erpnext.stock.doctype.quality_inspection_template.quality_inspection_template import (
|
||||
get_template_details,
|
||||
@@ -73,6 +73,27 @@ class QualityInspection(Document):
|
||||
if self.readings:
|
||||
self.inspect_and_set_status()
|
||||
|
||||
self.validate_inspection_required()
|
||||
|
||||
def validate_inspection_required(self):
|
||||
if self.reference_type in ["Purchase Receipt", "Purchase Invoice"] and not frappe.get_cached_value(
|
||||
"Item", self.item_code, "inspection_required_before_purchase"
|
||||
):
|
||||
frappe.throw(
|
||||
_(
|
||||
"'Inspection Required before Purchase' has disabled for the item {0}, no need to create the QI"
|
||||
).format(get_link_to_form("Item", self.item_code))
|
||||
)
|
||||
|
||||
if self.reference_type in ["Delivery Note", "Sales Invoice"] and not frappe.get_cached_value(
|
||||
"Item", self.item_code, "inspection_required_before_delivery"
|
||||
):
|
||||
frappe.throw(
|
||||
_(
|
||||
"'Inspection Required before Delivery' has disabled for the item {0}, no need to create the QI"
|
||||
).format(get_link_to_form("Item", self.item_code))
|
||||
)
|
||||
|
||||
def before_submit(self):
|
||||
self.validate_readings_status_mandatory()
|
||||
|
||||
|
||||
@@ -98,8 +98,8 @@ def get_item_details(
|
||||
|
||||
get_item_tax_template(ctx, item, out)
|
||||
out.item_tax_rate = get_item_tax_map(
|
||||
ctx.company,
|
||||
out.item_tax_template or ctx.item_tax_template,
|
||||
doc=doc or ctx,
|
||||
tax_template=out.item_tax_template or ctx.item_tax_template,
|
||||
as_json=True,
|
||||
)
|
||||
|
||||
@@ -528,7 +528,7 @@ def get_barcode_data(items_list=None, item_code=None):
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_item_tax_info(company, tax_category, item_codes, item_rates=None, item_tax_templates=None):
|
||||
def get_item_tax_info(doc, tax_category, item_codes, item_rates=None, item_tax_templates=None):
|
||||
out = {}
|
||||
|
||||
if item_tax_templates is None:
|
||||
@@ -537,14 +537,10 @@ def get_item_tax_info(company, tax_category, item_codes, item_rates=None, item_t
|
||||
if item_rates is None:
|
||||
item_rates = {}
|
||||
|
||||
if isinstance(item_codes, str):
|
||||
item_codes = json.loads(item_codes)
|
||||
|
||||
if isinstance(item_rates, str):
|
||||
item_rates = json.loads(item_rates)
|
||||
|
||||
if isinstance(item_tax_templates, str):
|
||||
item_tax_templates = json.loads(item_tax_templates)
|
||||
doc = parse_json(doc)
|
||||
item_codes = parse_json(item_codes)
|
||||
item_rates = parse_json(item_rates)
|
||||
item_tax_templates = parse_json(item_tax_templates)
|
||||
|
||||
for item_code in item_codes:
|
||||
if not item_code or item_code[1] in out or not item_tax_templates.get(item_code[1]):
|
||||
@@ -553,7 +549,7 @@ def get_item_tax_info(company, tax_category, item_codes, item_rates=None, item_t
|
||||
out[item_code[1]] = ItemDetails()
|
||||
item = frappe.get_cached_doc("Item", item_code[0])
|
||||
ctx: ItemDetailsCtx = {
|
||||
"company": company,
|
||||
"company": doc.company,
|
||||
"tax_category": tax_category,
|
||||
"base_net_rate": item_rates.get(item_code[1]),
|
||||
}
|
||||
@@ -563,7 +559,9 @@ def get_item_tax_info(company, tax_category, item_codes, item_rates=None, item_t
|
||||
|
||||
get_item_tax_template(ctx, item, out[item_code[1]])
|
||||
out[item_code[1]]["item_tax_rate"] = get_item_tax_map(
|
||||
company, out[item_code[1]].get("item_tax_template"), as_json=True
|
||||
doc=doc,
|
||||
tax_template=out[item_code[1]].get("item_tax_template"),
|
||||
as_json=True,
|
||||
)
|
||||
|
||||
return out
|
||||
@@ -689,12 +687,16 @@ def is_within_valid_range(ctx: ItemDetailsCtx, tax) -> bool:
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_item_tax_map(company, item_tax_template, as_json=True):
|
||||
def get_item_tax_map(*, doc: str | dict | Document, tax_template: str | None = None, as_json=True):
|
||||
doc = parse_json(doc)
|
||||
item_tax_map = {}
|
||||
if item_tax_template:
|
||||
template = frappe.get_cached_doc("Item Tax Template", item_tax_template)
|
||||
for t in (t for t in (doc.get("taxes") or []) if not t.set_by_item_tax_template):
|
||||
item_tax_map[t.account_head] = t.rate
|
||||
|
||||
if tax_template:
|
||||
template = frappe.get_cached_doc("Item Tax Template", tax_template)
|
||||
for d in template.taxes:
|
||||
if frappe.get_cached_value("Account", d.tax_type, "company") == company:
|
||||
if frappe.get_cached_value("Account", d.tax_type, "company") == doc.get("company"):
|
||||
item_tax_map[d.tax_type] = d.tax_rate
|
||||
|
||||
return json.dumps(item_tax_map) if as_json else item_tax_map
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"docstatus": 0,
|
||||
"doctype": "Number Card",
|
||||
"document_type": "Warehouse",
|
||||
"dynamic_filters_json": "[[\"Warehouse\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
|
||||
"filters_json": "[[\"Warehouse\",\"disabled\",\"=\",0]]",
|
||||
"function": "Count",
|
||||
"idx": 0,
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import add_to_date, cint, flt, get_datetime, get_table_name, getdate
|
||||
from frappe.utils.deprecations import deprecated
|
||||
from pypika import functions as fn
|
||||
|
||||
from erpnext.deprecation_dumpster import deprecated
|
||||
from erpnext.stock.doctype.warehouse.warehouse import apply_warehouse_filter
|
||||
|
||||
SLE_COUNT_LIMIT = 10_000
|
||||
@@ -100,7 +100,7 @@ def get_stock_ledger_entries(filters):
|
||||
return entries
|
||||
|
||||
|
||||
@deprecated
|
||||
@deprecated(f"{__name__}.get_stock_ledger_entries_for_batch_no", "unknown", "v16", "No known instructions.")
|
||||
def get_stock_ledger_entries_for_batch_no(filters):
|
||||
if not filters.get("from_date"):
|
||||
frappe.throw(_("'From Date' is required"))
|
||||
|
||||
Reference in New Issue
Block a user