Merge pull request #27288 from ankush/strict_linting
chore: cleanup linting errors and introduce strict linting checks
diff --git a/erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.json b/erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.json
index 6c60bbd..27d7c41 100644
--- a/erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.json
+++ b/erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.json
@@ -6,17 +6,17 @@
"engine": "InnoDB",
"field_order": [
"item_code",
- "item_name",
- "material_request_type",
"from_warehouse",
"warehouse",
- "column_break_4",
+ "item_name",
+ "material_request_type",
+ "actual_qty",
+ "ordered_qty",
"required_bom_qty",
+ "column_break_4",
"quantity",
"uom",
"projected_qty",
- "actual_qty",
- "ordered_qty",
"reserved_qty_for_production",
"safety_stock",
"item_details",
@@ -28,6 +28,7 @@
],
"fields": [
{
+ "columns": 2,
"fieldname": "item_code",
"fieldtype": "Link",
"in_list_view": 1,
@@ -41,6 +42,7 @@
"label": "Item Name"
},
{
+ "columns": 2,
"fieldname": "warehouse",
"fieldtype": "Link",
"in_list_view": 1,
@@ -50,10 +52,11 @@
"reqd": 1
},
{
+ "columns": 1,
"fieldname": "material_request_type",
"fieldtype": "Select",
"in_list_view": 1,
- "label": "Material Request Type",
+ "label": "Type",
"options": "\nPurchase\nMaterial Transfer\nMaterial Issue\nManufacture\nCustomer Provided"
},
{
@@ -61,10 +64,11 @@
"fieldtype": "Column Break"
},
{
+ "columns": 1,
"fieldname": "quantity",
"fieldtype": "Float",
"in_list_view": 1,
- "label": "Required Quantity",
+ "label": "Plan to Request Qty",
"no_copy": 1,
"reqd": 1
},
@@ -75,11 +79,12 @@
"read_only": 1
},
{
+ "columns": 2,
"default": "0",
"fieldname": "actual_qty",
"fieldtype": "Float",
"in_list_view": 1,
- "label": "Actual Qty",
+ "label": "Available Qty",
"no_copy": 1,
"read_only": 1
},
@@ -157,16 +162,18 @@
"read_only": 1
},
{
+ "columns": 2,
"fieldname": "required_bom_qty",
"fieldtype": "Float",
- "label": "Required Qty as per BOM",
+ "in_list_view": 1,
+ "label": "Qty As Per BOM",
"no_copy": 1,
"read_only": 1
}
],
"istable": 1,
"links": [],
- "modified": "2021-03-26 12:41:13.013149",
+ "modified": "2021-08-23 18:17:58.400462",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Material Request Plan Item",
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.js b/erpnext/manufacturing/doctype/production_plan/production_plan.js
index 847004f..7b4b7c3 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.js
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.js
@@ -254,7 +254,7 @@
get_items_for_mr: function(frm) {
if (!frm.doc.for_warehouse) {
- frappe.throw(__("Select warehouse for material requests"));
+ frappe.throw(__("To make material requests, 'Make Material Request for Warehouse' field is mandatory"));
}
if (frm.doc.ignore_existing_ordered_qty) {
@@ -265,9 +265,18 @@
title: title,
fields: [
{
- "fieldtype": "Table MultiSelect", "label": __("Source Warehouses (Optional)"),
- "fieldname": "warehouses", "options": "Production Plan Material Request Warehouse",
- "description": __("System will pickup the materials from the selected warehouses. If not specified, system will create material request for purchase."),
+ 'label': __('Target Warehouse'),
+ 'fieldtype': 'Link',
+ 'fieldname': 'target_warehouse',
+ 'read_only': true,
+ 'default': frm.doc.for_warehouse
+ },
+ {
+ 'label': __('Source Warehouses (Optional)'),
+ 'fieldtype': 'Table MultiSelect',
+ 'fieldname': 'warehouses',
+ 'options': 'Production Plan Material Request Warehouse',
+ 'description': __('If source warehouse selected then system will create the material request with type Material Transfer from Source to Target warehouse. If not selected then will create the material request with type Purchase for the target warehouse.'),
get_query: function () {
return {
filters: {
@@ -342,7 +351,11 @@
frappe.prompt(fields, (row) => {
let get_template_url = 'erpnext.manufacturing.doctype.production_plan.production_plan.download_raw_materials';
- open_url_post(frappe.request.url, { cmd: get_template_url, doc: frm.doc, warehouses: row.warehouses });
+ open_url_post(frappe.request.url, {
+ cmd: get_template_url,
+ doc: frm.doc,
+ warehouses: row.warehouses
+ });
}, __('Select Warehouses to get Stock for Materials Planning'), __('Get Stock'));
},
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.json b/erpnext/manufacturing/doctype/production_plan/production_plan.json
index 8437895..b5ed288 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.json
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.json
@@ -300,7 +300,7 @@
{
"fieldname": "for_warehouse",
"fieldtype": "Link",
- "label": "Material Request Warehouse",
+ "label": "Make Material Request for Warehouse",
"options": "Warehouse"
},
{
@@ -364,7 +364,7 @@
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2021-06-28 20:00:33.905114",
+ "modified": "2021-08-23 17:26:03.799876",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Production Plan",
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py
index 6b61c6d..2c77c9c 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py
@@ -331,7 +331,7 @@
def get_production_items(self):
item_dict = {}
for d in self.po_items:
- item_details= {
+ item_details = {
"production_item" : d.item_code,
"use_multi_level_bom" : d.include_exploded_items,
"sales_order" : d.sales_order,
@@ -346,8 +346,7 @@
"production_plan" : self.name,
"production_plan_item" : d.name,
"product_bundle_item" : d.product_bundle_item,
- "planned_start_date" : d.planned_start_date,
- "make_work_order_for_sub_assembly_items": d.get("make_work_order_for_sub_assembly_items", 0)
+ "planned_start_date" : d.planned_start_date
}
item_details.update({
@@ -458,6 +457,7 @@
warehouse = get_default_warehouse()
wo = frappe.new_doc("Work Order")
wo.update(item)
+ wo.planned_start_date = item.get('planned_start_date') or item.get('schedule_date')
if item.get("warehouse"):
wo.fg_warehouse = item.get("warehouse")
@@ -569,7 +569,10 @@
'Reserved Qty for Production', 'Safety Stock', 'Required Qty']]
doc.warehouse = None
- for d in get_items_for_material_requests(doc, warehouses=warehouses, get_parent_warehouse_data=True):
+ frappe.flags.show_qty_in_stock_uom = 1
+ items = get_items_for_material_requests(doc, warehouses=warehouses, get_parent_warehouse_data=True)
+
+ for d in items:
item_list.append([d.get('item_code'), d.get('description'), d.get('stock_uom'), d.get('warehouse'),
d.get('required_bom_qty'), d.get('projected_qty'), d.get('actual_qty'), d.get('ordered_qty'),
d.get('planned_qty'), d.get('reserved_qty_for_production'), d.get('safety_stock'), d.get('quantity')])
@@ -605,9 +608,16 @@
and bom.name=%s and item.is_stock_item in (1, {0})
group by bei.item_code, bei.stock_uom""".format(0 if include_non_stock_items else 1),
(planned_qty, company, bom_no), as_dict=1):
- item_details.setdefault(d.get('item_code'), d)
+ if not d.conversion_factor and d.purchase_uom:
+ d.conversion_factor = get_uom_conversion_factor(d.item_code, d.purchase_uom)
+ item_details.setdefault(d.get('item_code'), d)
+
return item_details
+def get_uom_conversion_factor(item_code, uom):
+ return frappe.db.get_value('UOM Conversion Detail',
+ {'parent': item_code, 'uom': uom}, 'conversion_factor')
+
def get_subitems(doc, data, item_details, bom_no, company, include_non_stock_items,
include_subcontracted_items, parent_qty, planned_qty=1):
items = frappe.db.sql("""
@@ -642,6 +652,9 @@
if d.item_code in item_details:
item_details[d.item_code].qty = item_details[d.item_code].qty + d.qty
else:
+ if not d.conversion_factor and d.purchase_uom:
+ d.conversion_factor = get_uom_conversion_factor(d.item_code, d.purchase_uom)
+
item_details[d.item_code] = d
if data.get('include_exploded_items') and d.default_bom:
@@ -669,10 +682,11 @@
row['purchase_uom'] = row['stock_uom']
if row['purchase_uom'] != row['stock_uom']:
- if not row['conversion_factor']:
+ if not (row['conversion_factor'] or frappe.flags.show_qty_in_stock_uom):
frappe.throw(_("UOM Conversion factor ({0} -> {1}) not found for item: {2}")
.format(row['purchase_uom'], row['stock_uom'], row.item_code))
- required_qty = required_qty / row['conversion_factor']
+
+ required_qty = required_qty / row['conversion_factor']
if frappe.db.get_value("UOM", row['purchase_uom'], "must_be_whole_number"):
required_qty = ceil(required_qty)
@@ -841,10 +855,8 @@
elif data.get('item_code'):
item_master = frappe.get_doc('Item', data['item_code']).as_dict()
purchase_uom = item_master.purchase_uom or item_master.stock_uom
- conversion_factor = 0
- for d in item_master.get("uoms"):
- if d.uom == purchase_uom:
- conversion_factor = d.conversion_factor
+ conversion_factor = (get_uom_conversion_factor(item_master.name, purchase_uom)
+ if item_master.purchase_uom else 1.0)
item_details[item_master.name] = frappe._dict(
{
diff --git a/erpnext/regional/germany/utils/datev/datev_csv.py b/erpnext/regional/germany/utils/datev/datev_csv.py
index 122c15f..c5c2bc4 100644
--- a/erpnext/regional/germany/utils/datev/datev_csv.py
+++ b/erpnext/regional/germany/utils/datev/datev_csv.py
@@ -33,6 +33,14 @@
if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS:
result['Belegdatum'] = pd.to_datetime(result['Belegdatum'])
+ result['Beleginfo - Inhalt 6'] = pd.to_datetime(result['Beleginfo - Inhalt 6'])
+ result['Beleginfo - Inhalt 6'] = result['Beleginfo - Inhalt 6'].dt.strftime('%d%m%Y')
+
+ result['Fälligkeit'] = pd.to_datetime(result['Fälligkeit'])
+ result['Fälligkeit'] = result['Fälligkeit'].dt.strftime('%d%m%y')
+
+ result.sort_values(by='Belegdatum', inplace=True, kind='stable', ignore_index=True)
+
if csv_class.DATA_CATEGORY == DataCategory.ACCOUNT_NAMES:
result['Sprach-ID'] = 'de-DE'
diff --git a/erpnext/regional/report/datev/datev.py b/erpnext/regional/report/datev/datev.py
index 86aed2e..b21b0f8 100644
--- a/erpnext/regional/report/datev/datev.py
+++ b/erpnext/regional/report/datev/datev.py
@@ -44,6 +44,12 @@
"width": 100
},
{
+ "label": "BU-Schlüssel",
+ "fieldname": "BU-Schlüssel",
+ "fieldtype": "Data",
+ "width": 100
+ },
+ {
"label": "Belegdatum",
"fieldname": "Belegdatum",
"fieldtype": "Date",
@@ -114,6 +120,36 @@
"fieldname": "Beleginfo - Inhalt 4",
"fieldtype": "Data",
"width": 150
+ },
+ {
+ "label": "Beleginfo - Art 5",
+ "fieldname": "Beleginfo - Art 5",
+ "fieldtype": "Data",
+ "width": 150
+ },
+ {
+ "label": "Beleginfo - Inhalt 5",
+ "fieldname": "Beleginfo - Inhalt 5",
+ "fieldtype": "Data",
+ "width": 100
+ },
+ {
+ "label": "Beleginfo - Art 6",
+ "fieldname": "Beleginfo - Art 6",
+ "fieldtype": "Data",
+ "width": 150
+ },
+ {
+ "label": "Beleginfo - Inhalt 6",
+ "fieldname": "Beleginfo - Inhalt 6",
+ "fieldtype": "Date",
+ "width": 100
+ },
+ {
+ "label": "Fälligkeit",
+ "fieldname": "Fälligkeit",
+ "fieldtype": "Date",
+ "width": 100
}
]
@@ -161,6 +197,125 @@
def get_transactions(filters, as_dict=1):
+ def run(params_method, filters):
+ extra_fields, extra_joins, extra_filters = params_method(filters)
+ return run_query(filters, extra_fields, extra_joins, extra_filters, as_dict=as_dict)
+
+ def sort_by(row):
+ # "Belegdatum" is in the fifth column when list format is used
+ return row["Belegdatum" if as_dict else 5]
+
+ type_map = {
+ # specific query methods for some voucher types
+ "Payment Entry": get_payment_entry_params,
+ "Sales Invoice": get_sales_invoice_params,
+ "Purchase Invoice": get_purchase_invoice_params
+ }
+
+ only_voucher_type = filters.get("voucher_type")
+ transactions = []
+
+ for voucher_type, get_voucher_params in type_map.items():
+ if only_voucher_type and only_voucher_type != voucher_type:
+ continue
+
+ transactions.extend(run(params_method=get_voucher_params, filters=filters))
+
+ if not only_voucher_type or only_voucher_type not in type_map:
+ # generic query method for all other voucher types
+ filters["exclude_voucher_types"] = type_map.keys()
+ transactions.extend(run(params_method=get_generic_params, filters=filters))
+
+ return sorted(transactions, key=sort_by)
+
+
+def get_payment_entry_params(filters):
+ extra_fields = """
+ , 'Zahlungsreferenz' as 'Beleginfo - Art 5'
+ , pe.reference_no as 'Beleginfo - Inhalt 5'
+ , 'Buchungstag' as 'Beleginfo - Art 6'
+ , pe.reference_date as 'Beleginfo - Inhalt 6'
+ , '' as 'Fälligkeit'
+ """
+
+ extra_joins = """
+ LEFT JOIN `tabPayment Entry` pe
+ ON gl.voucher_no = pe.name
+ """
+
+ extra_filters = """
+ AND gl.voucher_type = 'Payment Entry'
+ """
+
+ return extra_fields, extra_joins, extra_filters
+
+
+def get_sales_invoice_params(filters):
+ extra_fields = """
+ , '' as 'Beleginfo - Art 5'
+ , '' as 'Beleginfo - Inhalt 5'
+ , '' as 'Beleginfo - Art 6'
+ , '' as 'Beleginfo - Inhalt 6'
+ , si.due_date as 'Fälligkeit'
+ """
+
+ extra_joins = """
+ LEFT JOIN `tabSales Invoice` si
+ ON gl.voucher_no = si.name
+ """
+
+ extra_filters = """
+ AND gl.voucher_type = 'Sales Invoice'
+ """
+
+ return extra_fields, extra_joins, extra_filters
+
+
+def get_purchase_invoice_params(filters):
+ extra_fields = """
+ , 'Lieferanten-Rechnungsnummer' as 'Beleginfo - Art 5'
+ , pi.bill_no as 'Beleginfo - Inhalt 5'
+ , 'Lieferanten-Rechnungsdatum' as 'Beleginfo - Art 6'
+ , pi.bill_date as 'Beleginfo - Inhalt 6'
+ , pi.due_date as 'Fälligkeit'
+ """
+
+ extra_joins = """
+ LEFT JOIN `tabPurchase Invoice` pi
+ ON gl.voucher_no = pi.name
+ """
+
+ extra_filters = """
+ AND gl.voucher_type = 'Purchase Invoice'
+ """
+
+ return extra_fields, extra_joins, extra_filters
+
+
+def get_generic_params(filters):
+ # produce empty fields so all rows will have the same length
+ extra_fields = """
+ , '' as 'Beleginfo - Art 5'
+ , '' as 'Beleginfo - Inhalt 5'
+ , '' as 'Beleginfo - Art 6'
+ , '' as 'Beleginfo - Inhalt 6'
+ , '' as 'Fälligkeit'
+ """
+ extra_joins = ""
+
+ if filters.get("exclude_voucher_types"):
+ # exclude voucher types that are queried by a dedicated method
+ exclude = "({})".format(', '.join("'{}'".format(key) for key in filters.get("exclude_voucher_types")))
+ extra_filters = "AND gl.voucher_type NOT IN {}".format(exclude)
+
+ # if voucher type filter is set, allow only this type
+ if filters.get("voucher_type"):
+ extra_filters += " AND gl.voucher_type = %(voucher_type)s"
+
+ return extra_fields, extra_joins, extra_filters
+
+
+def run_query(filters, extra_fields, extra_joins, extra_filters, as_dict=1):
"""
Get a list of accounting entries.
@@ -171,8 +326,7 @@
filters -- dict of filters to be passed to the sql query
as_dict -- return as list of dicts [0,1]
"""
- filter_by_voucher = 'AND gl.voucher_type = %(voucher_type)s' if filters.get('voucher_type') else ''
- gl_entries = frappe.db.sql("""
+ query = """
SELECT
/* either debit or credit amount; always positive */
@@ -187,6 +341,9 @@
/* against number or, if empty, party against number */
%(temporary_against_account_number)s as 'Gegenkonto (ohne BU-Schlüssel)',
+ /* disable automatic VAT deduction */
+ '40' as 'BU-Schlüssel',
+
gl.posting_date as 'Belegdatum',
gl.voucher_no as 'Belegfeld 1',
LEFT(gl.remarks, 60) as 'Buchungstext',
@@ -199,30 +356,34 @@
case gl.party_type when 'Customer' then 'Debitorennummer' when 'Supplier' then 'Kreditorennummer' else NULL end as 'Beleginfo - Art 4',
par.debtor_creditor_number as 'Beleginfo - Inhalt 4'
+ {extra_fields}
+
FROM `tabGL Entry` gl
/* Kontonummer */
- left join `tabAccount` acc
- on gl.account = acc.name
+ LEFT JOIN `tabAccount` acc
+ ON gl.account = acc.name
- left join `tabCustomer` cus
- on gl.party_type = 'Customer'
- and gl.party = cus.name
+ LEFT JOIN `tabParty Account` par
+ ON par.parent = gl.party
+ AND par.parenttype = gl.party_type
+ AND par.company = %(company)s
- left join `tabSupplier` sup
- on gl.party_type = 'Supplier'
- and gl.party = sup.name
-
- left join `tabParty Account` par
- on par.parent = gl.party
- and par.parenttype = gl.party_type
- and par.company = %(company)s
+ {extra_joins}
WHERE gl.company = %(company)s
AND DATE(gl.posting_date) >= %(from_date)s
AND DATE(gl.posting_date) <= %(to_date)s
- {}
- ORDER BY 'Belegdatum', gl.voucher_no""".format(filter_by_voucher), filters, as_dict=as_dict)
+
+ {extra_filters}
+
+ ORDER BY 'Belegdatum', gl.voucher_no""".format(
+ extra_fields=extra_fields,
+ extra_joins=extra_joins,
+ extra_filters=extra_filters
+ )
+
+ gl_entries = frappe.db.sql(query, filters, as_dict=as_dict)
return gl_entries
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index c7e22c2..c5c2868 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -1200,10 +1200,10 @@
wo_item_qty = item.transferred_qty or item.required_qty
- req_qty_each = (
- (flt(wo_item_qty) - flt(item.consumed_qty)) /
- (flt(work_order_qty) - flt(wo.produced_qty))
- )
+ wo_qty_consumed = flt(wo_item_qty) - flt(item.consumed_qty)
+ wo_qty_to_produce = flt(work_order_qty) - flt(wo.produced_qty)
+
+ req_qty_each = (wo_qty_consumed) / (wo_qty_to_produce or 1)
qty = req_qty_each * flt(self.fg_completed_qty)