Merge branch 'develop' into refactor-payment-request

This commit is contained in:
Abdeali Chharchhoda
2024-11-27 12:07:16 +05:30
93 changed files with 9853 additions and 7678 deletions

View File

@@ -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": []
}
}

View File

@@ -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

View File

@@ -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",

View File

@@ -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)

View File

@@ -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:

View File

@@ -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`")

View File

@@ -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:

View File

@@ -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",

View File

@@ -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

View File

@@ -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"));

View File

@@ -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",

View File

@@ -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

View File

@@ -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",

View File

@@ -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

View File

@@ -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",

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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")

View File

@@ -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("'", ""),

View File

@@ -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",

View File

@@ -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)

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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 () {

View File

@@ -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"

View File

@@ -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":

View File

@@ -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()

View File

@@ -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]))

View File

@@ -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):

View File

@@ -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()

View File

@@ -79,7 +79,7 @@ class TestTaxesAndTotals(FrappeTestCase):
"rate": 50,
},
)
self.doc.set_missing_item_details()
calculate_taxes_and_totals(self.doc)
expected_values = {

View File

@@ -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,

View File

@@ -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,
)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -29,7 +29,6 @@ frappe.ui.form.on("BOM", {
item: row.finished_good,
is_active: 1,
docstatus: 1,
track_semi_finished_goods: 0,
},
};
});

View File

@@ -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,

View File

@@ -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);

View File

@@ -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",

View File

@@ -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)

View File

@@ -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",

View File

@@ -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

View File

@@ -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))

View File

@@ -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,
)

View File

@@ -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"),

View File

@@ -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)

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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) => {

View File

@@ -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;
}
});
}

View File

@@ -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
},

View File

@@ -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;

View File

@@ -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 },
};
},

View File

@@ -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);

View File

@@ -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 {

View File

@@ -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",

View File

@@ -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

View File

@@ -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"));
}

View File

@@ -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",

View File

@@ -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

View File

@@ -3,7 +3,6 @@
"docstatus": 0,
"doctype": "Number Card",
"document_type": "Customer",
"dynamic_filters_json": "",
"filters_json": "[[\"Customer\",\"disabled\",\"=\",\"0\"]]",
"function": "Count",
"idx": 0,

View File

@@ -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();

View File

@@ -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, {

View File

@@ -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

View File

@@ -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:

View File

@@ -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")

View File

@@ -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",

View File

@@ -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

View File

@@ -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) {

View File

@@ -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": [
{

View File

@@ -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"""

View File

@@ -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",

View File

@@ -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

View File

@@ -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);

View File

@@ -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()

View File

@@ -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

View File

@@ -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,

View File

@@ -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"))