From 91b307e7f8bc2ccc9865f2650777e49475becae7 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Wed, 11 Mar 2020 13:59:15 +0530 Subject: [PATCH 001/608] fix: project link not set in accounts other than profilt and loss accounts --- .../accounts_settings/accounts_settings.json | 416 +++++++++--------- erpnext/accounts/doctype/gl_entry/gl_entry.py | 6 - .../purchase_invoice/purchase_invoice.json | 9 +- .../purchase_invoice/purchase_invoice.py | 39 +- .../doctype/sales_invoice/sales_invoice.py | 12 +- .../sales_invoice_item.json | 9 +- erpnext/accounts/utils.py | 9 +- erpnext/controllers/stock_controller.py | 2 + .../delivery_note_item.json | 10 +- .../purchase_receipt/purchase_receipt.json | 10 +- 10 files changed, 276 insertions(+), 246 deletions(-) diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json index 4ff42129205..d2f7de3be52 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json @@ -1,210 +1,210 @@ { - "creation": "2013-06-24 15:49:57", - "description": "Settings for Accounts", - "doctype": "DocType", - "document_type": "Other", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "auto_accounting_for_stock", - "acc_frozen_upto", - "frozen_accounts_modifier", - "determine_address_tax_category_from", - "over_billing_allowance", - "column_break_4", - "credit_controller", - "check_supplier_invoice_uniqueness", - "make_payment_via_journal_entry", - "unlink_payment_on_cancellation_of_invoice", - "unlink_advance_payment_on_cancelation_of_order", - "book_asset_depreciation_entry_automatically", - "allow_cost_center_in_entry_of_bs_account", - "add_taxes_from_item_tax_template", - "automatically_fetch_payment_terms", - "print_settings", - "show_inclusive_tax_in_print", - "column_break_12", - "show_payment_schedule_in_print", - "currency_exchange_section", - "allow_stale", - "stale_days", - "report_settings_sb", - "use_custom_cash_flow" - ], - "fields": [ - { - "default": "1", - "description": "If enabled, the system will post accounting entries for inventory automatically.", - "fieldname": "auto_accounting_for_stock", - "fieldtype": "Check", - "hidden": 1, - "in_list_view": 1, - "label": "Make Accounting Entry For Every Stock Movement" - }, - { - "description": "Accounting entry frozen up to this date, nobody can do / modify entry except role specified below.", - "fieldname": "acc_frozen_upto", - "fieldtype": "Date", - "in_list_view": 1, - "label": "Accounts Frozen Upto" - }, - { - "description": "Users with this role are allowed to set frozen accounts and create / modify accounting entries against frozen accounts", - "fieldname": "frozen_accounts_modifier", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Role Allowed to Set Frozen Accounts & Edit Frozen Entries", - "options": "Role" - }, - { - "default": "Billing Address", - "description": "Address used to determine Tax Category in transactions.", - "fieldname": "determine_address_tax_category_from", - "fieldtype": "Select", - "label": "Determine Address Tax Category From", - "options": "Billing Address\nShipping Address" - }, - { - "fieldname": "column_break_4", - "fieldtype": "Column Break" - }, - { - "description": "Role that is allowed to submit transactions that exceed credit limits set.", - "fieldname": "credit_controller", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Credit Controller", - "options": "Role" - }, - { - "fieldname": "check_supplier_invoice_uniqueness", - "fieldtype": "Check", - "label": "Check Supplier Invoice Number Uniqueness" - }, - { - "fieldname": "make_payment_via_journal_entry", - "fieldtype": "Check", - "label": "Make Payment via Journal Entry" - }, - { - "default": "1", - "fieldname": "unlink_payment_on_cancellation_of_invoice", - "fieldtype": "Check", - "label": "Unlink Payment on Cancellation of Invoice" - }, - { - "default": "1", - "fieldname": "unlink_advance_payment_on_cancelation_of_order", - "fieldtype": "Check", - "label": "Unlink Advance Payment on Cancelation of Order" - }, - { - "default": "1", - "fieldname": "book_asset_depreciation_entry_automatically", - "fieldtype": "Check", - "label": "Book Asset Depreciation Entry Automatically" - }, - { - "fieldname": "allow_cost_center_in_entry_of_bs_account", - "fieldtype": "Check", - "label": "Allow Cost Center In Entry of Balance Sheet Account" - }, - { - "default": "1", - "fieldname": "add_taxes_from_item_tax_template", - "fieldtype": "Check", - "label": "Automatically Add Taxes and Charges from Item Tax Template" - }, - { - "fieldname": "print_settings", - "fieldtype": "Section Break", - "label": "Print Settings" - }, - { - "fieldname": "show_inclusive_tax_in_print", - "fieldtype": "Check", - "label": "Show Inclusive Tax In Print" - }, - { - "fieldname": "column_break_12", - "fieldtype": "Column Break" - }, - { - "fieldname": "show_payment_schedule_in_print", - "fieldtype": "Check", - "label": "Show Payment Schedule in Print" - }, - { - "fieldname": "currency_exchange_section", - "fieldtype": "Section Break", - "label": "Currency Exchange Settings" - }, - { - "default": "1", - "fieldname": "allow_stale", - "fieldtype": "Check", - "in_list_view": 1, - "label": "Allow Stale Exchange Rates" - }, - { - "default": "1", - "depends_on": "eval:doc.allow_stale==0", - "fieldname": "stale_days", - "fieldtype": "Int", - "label": "Stale Days" - }, - { - "fieldname": "report_settings_sb", - "fieldtype": "Section Break", - "label": "Report Settings" - }, - { - "default": "0", - "description": "Only select if you have setup Cash Flow Mapper documents", - "fieldname": "use_custom_cash_flow", - "fieldtype": "Check", - "label": "Use Custom Cash Flow Format" - }, - { - "fieldname": "automatically_fetch_payment_terms", - "fieldtype": "Check", - "label": "Automatically Fetch Payment Terms" - }, - { - "description": "Percentage you are allowed to bill more against the amount ordered. For example: If the order value is $100 for an item and tolerance is set as 10% then you are allowed to bill for $110.", - "fieldname": "over_billing_allowance", - "fieldtype": "Currency", - "label": "Over Billing Allowance (%)" - } - ], - "icon": "icon-cog", - "idx": 1, - "issingle": 1, - "modified": "2019-07-04 18:20:55.789946", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Accounts Settings", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "email": 1, - "print": 1, - "read": 1, - "role": "Accounts Manager", - "share": 1, - "write": 1 - }, - { - "read": 1, - "role": "Sales User" - }, - { - "read": 1, - "role": "Purchase User" - } - ], - "quick_entry": 1, - "sort_order": "ASC", - "track_changes": 1 + "creation": "2013-06-24 15:49:57", + "description": "Settings for Accounts", + "doctype": "DocType", + "document_type": "Other", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "auto_accounting_for_stock", + "acc_frozen_upto", + "frozen_accounts_modifier", + "determine_address_tax_category_from", + "over_billing_allowance", + "column_break_4", + "credit_controller", + "check_supplier_invoice_uniqueness", + "make_payment_via_journal_entry", + "unlink_payment_on_cancellation_of_invoice", + "unlink_advance_payment_on_cancelation_of_order", + "book_asset_depreciation_entry_automatically", + "add_taxes_from_item_tax_template", + "automatically_fetch_payment_terms", + "print_settings", + "show_inclusive_tax_in_print", + "column_break_12", + "show_payment_schedule_in_print", + "currency_exchange_section", + "allow_stale", + "stale_days", + "report_settings_sb", + "use_custom_cash_flow" + ], + "fields": [ + { + "default": "1", + "description": "If enabled, the system will post accounting entries for inventory automatically.", + "fieldname": "auto_accounting_for_stock", + "fieldtype": "Check", + "hidden": 1, + "in_list_view": 1, + "label": "Make Accounting Entry For Every Stock Movement" + }, + { + "description": "Accounting entry frozen up to this date, nobody can do / modify entry except role specified below.", + "fieldname": "acc_frozen_upto", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Accounts Frozen Upto" + }, + { + "description": "Users with this role are allowed to set frozen accounts and create / modify accounting entries against frozen accounts", + "fieldname": "frozen_accounts_modifier", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Role Allowed to Set Frozen Accounts & Edit Frozen Entries", + "options": "Role" + }, + { + "default": "Billing Address", + "description": "Address used to determine Tax Category in transactions.", + "fieldname": "determine_address_tax_category_from", + "fieldtype": "Select", + "label": "Determine Address Tax Category From", + "options": "Billing Address\nShipping Address" + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "description": "Role that is allowed to submit transactions that exceed credit limits set.", + "fieldname": "credit_controller", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Credit Controller", + "options": "Role" + }, + { + "default": "0", + "fieldname": "check_supplier_invoice_uniqueness", + "fieldtype": "Check", + "label": "Check Supplier Invoice Number Uniqueness" + }, + { + "default": "0", + "fieldname": "make_payment_via_journal_entry", + "fieldtype": "Check", + "label": "Make Payment via Journal Entry" + }, + { + "default": "1", + "fieldname": "unlink_payment_on_cancellation_of_invoice", + "fieldtype": "Check", + "label": "Unlink Payment on Cancellation of Invoice" + }, + { + "default": "1", + "fieldname": "unlink_advance_payment_on_cancelation_of_order", + "fieldtype": "Check", + "label": "Unlink Advance Payment on Cancelation of Order" + }, + { + "default": "1", + "fieldname": "book_asset_depreciation_entry_automatically", + "fieldtype": "Check", + "label": "Book Asset Depreciation Entry Automatically" + }, + { + "default": "1", + "fieldname": "add_taxes_from_item_tax_template", + "fieldtype": "Check", + "label": "Automatically Add Taxes and Charges from Item Tax Template" + }, + { + "fieldname": "print_settings", + "fieldtype": "Section Break", + "label": "Print Settings" + }, + { + "default": "0", + "fieldname": "show_inclusive_tax_in_print", + "fieldtype": "Check", + "label": "Show Inclusive Tax In Print" + }, + { + "fieldname": "column_break_12", + "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "show_payment_schedule_in_print", + "fieldtype": "Check", + "label": "Show Payment Schedule in Print" + }, + { + "fieldname": "currency_exchange_section", + "fieldtype": "Section Break", + "label": "Currency Exchange Settings" + }, + { + "default": "1", + "fieldname": "allow_stale", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Allow Stale Exchange Rates" + }, + { + "default": "1", + "depends_on": "eval:doc.allow_stale==0", + "fieldname": "stale_days", + "fieldtype": "Int", + "label": "Stale Days" + }, + { + "fieldname": "report_settings_sb", + "fieldtype": "Section Break", + "label": "Report Settings" + }, + { + "default": "0", + "description": "Only select if you have setup Cash Flow Mapper documents", + "fieldname": "use_custom_cash_flow", + "fieldtype": "Check", + "label": "Use Custom Cash Flow Format" + }, + { + "default": "0", + "fieldname": "automatically_fetch_payment_terms", + "fieldtype": "Check", + "label": "Automatically Fetch Payment Terms" + }, + { + "description": "Percentage you are allowed to bill more against the amount ordered. For example: If the order value is $100 for an item and tolerance is set as 10% then you are allowed to bill for $110.", + "fieldname": "over_billing_allowance", + "fieldtype": "Currency", + "label": "Over Billing Allowance (%)" } + ], + "icon": "icon-cog", + "idx": 1, + "issingle": 1, + "modified": "2020-03-11 13:09:26.235848", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Accounts Settings", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "email": 1, + "print": 1, + "read": 1, + "role": "Accounts Manager", + "share": 1, + "write": 1 + }, + { + "read": 1, + "role": "Sales User" + }, + { + "read": 1, + "role": "Purchase User" + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "ASC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py index f9e4fd77148..38b8876341f 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.py +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py @@ -75,12 +75,6 @@ class GLEntry(Document): if not self.cost_center and self.voucher_type != 'Period Closing Voucher': frappe.throw(_("{0} {1}: Cost Center is required for 'Profit and Loss' account {2}. Please set up a default Cost Center for the Company.") .format(self.voucher_type, self.voucher_no, self.account)) - else: - from erpnext.accounts.utils import get_allow_cost_center_in_entry_of_bs_account - if not get_allow_cost_center_in_entry_of_bs_account() and self.cost_center: - self.cost_center = None - if self.project: - self.project = None def validate_dimensions_for_pl_and_bs(self): diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index 3cd988ccd23..8c4fad5cb66 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -25,6 +25,7 @@ "accounting_dimensions_section", "cost_center", "dimension_col_break", + "project", "sb_14", "on_hold", "release_date", @@ -1292,13 +1293,19 @@ "fieldtype": "Check", "label": "Is Internal Supplier", "read_only": 1 + }, + { + "fieldname": "project", + "fieldtype": "Link", + "label": "Project", + "options": "Project" } ], "icon": "fa fa-file-text", "idx": 204, "is_submittable": 1, "links": [], - "modified": "2019-12-30 19:13:49.610538", + "modified": "2020-03-11 12:28:45.711416", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice", diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index cc992cec44f..14bf1748044 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -466,7 +466,8 @@ class PurchaseInvoice(BuyingController): if self.party_account_currency==self.company_currency else grand_total, "against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name, "against_voucher_type": self.doctype, - "cost_center": self.cost_center + "cost_center": self.cost_center, + "project": self.project }, self.party_account_currency) ) @@ -506,6 +507,7 @@ class PurchaseInvoice(BuyingController): "account": warehouse_account[item.warehouse]['account'], "against": warehouse_account[item.from_warehouse]["account"], "cost_center": item.cost_center, + "project": item_row.project or self.project, "remarks": self.get("remarks") or _("Accounting Entry for Stock"), "debit": warehouse_debit_amount, }, warehouse_account[item.warehouse]["account_currency"], item=item)) @@ -515,6 +517,7 @@ class PurchaseInvoice(BuyingController): "account": warehouse_account[item.from_warehouse]['account'], "against": warehouse_account[item.warehouse]["account"], "cost_center": item.cost_center, + "project": item_row.project or self.project, "remarks": self.get("remarks") or _("Accounting Entry for Stock"), "debit": -1 * flt(item.base_net_amount, item.precision("base_net_amount")), }, warehouse_account[item.from_warehouse]["account_currency"], item=item)) @@ -538,7 +541,7 @@ class PurchaseInvoice(BuyingController): "debit": warehouse_debit_amount, "remarks": self.get("remarks") or _("Accounting Entry for Stock"), "cost_center": item.cost_center, - "project": item.project + "project": item.project or self.project }, account_currency, item=item) ) @@ -551,7 +554,7 @@ class PurchaseInvoice(BuyingController): "cost_center": item.cost_center, "remarks": self.get("remarks") or _("Accounting Entry for Stock"), "credit": flt(amount), - "project": item.project + "project": item.project or self.project }, item=item)) # sub-contracting warehouse @@ -564,6 +567,7 @@ class PurchaseInvoice(BuyingController): "account": supplier_warehouse_account, "against": item.expense_account, "cost_center": item.cost_center, + "project": item.project or self.project, "remarks": self.get("remarks") or _("Accounting Entry for Stock"), "credit": flt(item.rm_supp_cost) }, warehouse_account[self.supplier_warehouse]["account_currency"], item=item)) @@ -582,7 +586,7 @@ class PurchaseInvoice(BuyingController): "against": self.supplier, "debit": amount, "cost_center": item.cost_center, - "project": item.project + "project": item.project or self.project }, account_currency, item=item)) # If asset is bought through this document and not linked to PR @@ -595,7 +599,7 @@ class PurchaseInvoice(BuyingController): "cost_center": item.cost_center, "remarks": self.get("remarks") or _("Accounting Entry for Stock"), "credit": flt(item.landed_cost_voucher_amount), - "project": item.project + "project": item.project or self.project }, item=item)) gl_entries.append(self.get_gl_dict({ @@ -604,7 +608,7 @@ class PurchaseInvoice(BuyingController): "cost_center": item.cost_center, "remarks": self.get("remarks") or _("Accounting Entry for Stock"), "debit": flt(item.landed_cost_voucher_amount), - "project": item.project + "project": item.project or self.project }, item=item)) # update gross amount of asset bought through this document @@ -630,7 +634,8 @@ class PurchaseInvoice(BuyingController): "against": self.supplier, "debit": flt(item.item_tax_amount, item.precision("item_tax_amount")), "remarks": self.remarks or "Accounting Entry for Stock", - "cost_center": self.cost_center + "cost_center": self.cost_center, + "project": item.project or self.project }, item=item) ) @@ -659,7 +664,8 @@ class PurchaseInvoice(BuyingController): "debit": base_asset_amount, "debit_in_account_currency": (base_asset_amount if arbnb_currency == self.company_currency else asset_amount), - "cost_center": item.cost_center + "cost_center": item.cost_center, + "project": item.project or self.project }, item=item)) if item.item_tax_amount: @@ -669,6 +675,7 @@ class PurchaseInvoice(BuyingController): "against": self.supplier, "remarks": self.get("remarks") or _("Accounting Entry for Asset"), "cost_center": item.cost_center, + "project": item.project or self.project, "credit": item.item_tax_amount, "credit_in_account_currency": (item.item_tax_amount if asset_eiiav_currency == self.company_currency else @@ -685,7 +692,8 @@ class PurchaseInvoice(BuyingController): "debit": base_asset_amount, "debit_in_account_currency": (base_asset_amount if cwip_account_currency == self.company_currency else asset_amount), - "cost_center": self.cost_center + "cost_center": self.cost_center, + "project": item.project or self.project }, item=item)) if item.item_tax_amount and not cint(erpnext.is_perpetual_inventory_enabled(self.company)): @@ -696,6 +704,7 @@ class PurchaseInvoice(BuyingController): "remarks": self.get("remarks") or _("Accounting Entry for Asset"), "cost_center": item.cost_center, "credit": item.item_tax_amount, + "project": item.project or self.project, "credit_in_account_currency": (item.item_tax_amount if asset_eiiav_currency == self.company_currency else item.item_tax_amount / self.conversion_rate) @@ -711,7 +720,7 @@ class PurchaseInvoice(BuyingController): "cost_center": item.cost_center, "remarks": self.get("remarks") or _("Accounting Entry for Stock"), "credit": flt(item.landed_cost_voucher_amount), - "project": item.project + "project": item.project or self.project }, item=item)) gl_entries.append(self.get_gl_dict({ @@ -720,7 +729,7 @@ class PurchaseInvoice(BuyingController): "cost_center": item.cost_center, "remarks": self.get("remarks") or _("Accounting Entry for Stock"), "debit": flt(item.landed_cost_voucher_amount), - "project": item.project + "project": item.project or self.project }, item=item)) # update gross amount of assets bought through this document @@ -755,7 +764,7 @@ class PurchaseInvoice(BuyingController): "debit": stock_adjustment_amt, "remarks": self.get("remarks") or _("Stock Adjustment"), "cost_center": item.cost_center, - "project": item.project + "project": item.project or self.project }, account_currency, item=item) ) @@ -847,7 +856,8 @@ class PurchaseInvoice(BuyingController): if self.party_account_currency==self.company_currency else self.paid_amount, "against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name, "against_voucher_type": self.doctype, - "cost_center": self.cost_center + "cost_center": self.cost_center, + "project": self.project }, self.party_account_currency) ) @@ -879,7 +889,8 @@ class PurchaseInvoice(BuyingController): if self.party_account_currency==self.company_currency else self.write_off_amount, "against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name, "against_voucher_type": self.doctype, - "cost_center": self.cost_center + "cost_center": self.cost_center, + "project": self.project }, self.party_account_currency) ) gl_entries.append( diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 7f7938db24d..6fbd630c1e1 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -777,7 +777,8 @@ class SalesInvoice(SellingController): if self.party_account_currency==self.company_currency else grand_total, "against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name, "against_voucher_type": self.doctype, - "cost_center": self.cost_center + "cost_center": self.cost_center, + "project": self.project }, self.party_account_currency) ) @@ -832,7 +833,8 @@ class SalesInvoice(SellingController): "credit_in_account_currency": (flt(item.base_net_amount, item.precision("base_net_amount")) if account_currency==self.company_currency else flt(item.net_amount, item.precision("net_amount"))), - "cost_center": item.cost_center + "cost_center": item.cost_center, + "project": item.project or self.project }, account_currency, item=item) ) @@ -913,7 +915,8 @@ class SalesInvoice(SellingController): if self.party_account_currency==self.company_currency else flt(self.change_amount), "against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name, "against_voucher_type": self.doctype, - "cost_center": self.cost_center + "cost_center": self.cost_center, + "project": self.project }, self.party_account_currency) ) @@ -946,7 +949,8 @@ class SalesInvoice(SellingController): else flt(self.write_off_amount, self.precision("write_off_amount"))), "against_voucher": self.return_against if cint(self.is_return) else self.name, "against_voucher_type": self.doctype, - "cost_center": self.cost_center + "cost_center": self.cost_center, + "project": self.project }, self.party_account_currency) ) gl_entries.append( diff --git a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json index b2294e4318f..9bc24664d10 100644 --- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json +++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json @@ -94,6 +94,7 @@ "accounting_dimensions_section", "cost_center", "dimension_col_break", + "project", "section_break_54", "page_break" ], @@ -783,12 +784,18 @@ "fieldtype": "Link", "label": "Finance Book", "options": "Finance Book" + }, + { + "fieldname": "project", + "fieldtype": "Link", + "label": "Project", + "options": "Project" } ], "idx": 1, "istable": 1, "links": [], - "modified": "2019-12-04 12:22:38.517710", + "modified": "2020-03-11 12:24:41.749986", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice Item", diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 4789063ba50..4f4c0860b0b 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -124,14 +124,12 @@ def get_balance_on(account=None, date=None, party_type=None, party=None, company # hence, assuming balance as 0.0 return 0.0 - allow_cost_center_in_entry_of_bs_account = get_allow_cost_center_in_entry_of_bs_account() - if account: report_type = acc.report_type else: report_type = "" - if cost_center and (allow_cost_center_in_entry_of_bs_account or report_type =='Profit and Loss'): + if cost_center and report_type == 'Profit and Loss': cc = frappe.get_doc("Cost Center", cost_center) if cc.is_group: cond.append(""" exists ( @@ -888,11 +886,6 @@ def get_coa(doctype, parent, is_root, chart=None): return accounts -def get_allow_cost_center_in_entry_of_bs_account(): - def generator(): - return cint(frappe.db.get_value('Accounts Settings', None, 'allow_cost_center_in_entry_of_bs_account')) - return frappe.local_cache("get_allow_cost_center_in_entry_of_bs_account", (), generator, regenerate_if_none=True) - def get_stock_accounts(company): return frappe.get_all("Account", filters = { "account_type": "Stock", diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index d452fe4ac0e..37cea28fad5 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -91,6 +91,7 @@ class StockController(AccountsController): "account": warehouse_account[sle.warehouse]["account"], "against": item_row.expense_account, "cost_center": item_row.cost_center, + "project": item_row.project or self.project if hasattr(self, 'project') else None, "remarks": self.get("remarks") or "Accounting Entry for Stock", "debit": flt(sle.stock_value_difference, precision), "is_opening": item_row.get("is_opening") or self.get("is_opening") or "No", @@ -101,6 +102,7 @@ class StockController(AccountsController): "account": item_row.expense_account, "against": warehouse_account[sle.warehouse]["account"], "cost_center": item_row.cost_center, + "project": item_row.project or self.project if hasattr(self, 'project') else None, "remarks": self.get("remarks") or "Accounting Entry for Stock", "credit": flt(sle.stock_value_difference, precision), "project": item_row.get("project") or self.get("project"), diff --git a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json index 782ac84e57d..475a8198dc6 100644 --- a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json +++ b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json @@ -1,5 +1,4 @@ { - "actions": [], "autoname": "hash", "creation": "2013-04-22 13:15:44", "doctype": "DocType", @@ -81,6 +80,7 @@ "accounting_dimensions_section", "cost_center", "dimension_col_break", + "project", "section_break_72", "page_break" ], @@ -699,12 +699,18 @@ { "fieldname": "dimension_col_break", "fieldtype": "Column Break" + }, + { + "fieldname": "project", + "fieldtype": "Link", + "label": "Project", + "options": "Project" } ], "idx": 1, "istable": 1, "links": [], - "modified": "2020-03-05 14:18:33.131672", + "modified": "2020-03-11 12:25:06.177894", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note Item", diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json index 35446ecb1fc..bc2f09e6e2a 100755 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json @@ -1,5 +1,4 @@ { - "actions": [], "allow_import": 1, "autoname": "naming_series:", "creation": "2013-05-21 16:16:39", @@ -105,6 +104,7 @@ "amended_from", "range", "column_break4", + "project", "per_billed", "is_internal_supplier", "inter_company_reference", @@ -925,6 +925,12 @@ "print_width": "50%", "width": "50%" }, + { + "fieldname": "project", + "fieldtype": "Link", + "label": "Project", + "options": "Project" + }, { "fieldname": "per_billed", "fieldtype": "Percent", @@ -1076,7 +1082,7 @@ "idx": 261, "is_submittable": 1, "links": [], - "modified": "2019-12-30 19:12:49.709711", + "modified": "2020-03-11 12:58:46.515404", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt", From 0a42d819934d9c1732a135bded575641ea9cc73b Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Wed, 11 Mar 2020 14:27:40 +0530 Subject: [PATCH 002/608] fix: cannot find get_allow_cost_center_in_entry_of_bs_account --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 55d275831ec..ef1261baf15 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -6,7 +6,7 @@ from __future__ import unicode_literals import frappe, erpnext, json from frappe import _, scrub, ValidationError from frappe.utils import flt, comma_or, nowdate, getdate -from erpnext.accounts.utils import get_outstanding_invoices, get_account_currency, get_balance_on, get_allow_cost_center_in_entry_of_bs_account +from erpnext.accounts.utils import get_outstanding_invoices, get_account_currency, get_balance_on from erpnext.accounts.party import get_party_account from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account from erpnext.setup.utils import get_exchange_rate @@ -597,7 +597,7 @@ def get_outstanding_reference_documents(args): .format(frappe.db.escape(args["voucher_type"]), frappe.db.escape(args["voucher_no"])) # Add cost center condition - if args.get("cost_center") and get_allow_cost_center_in_entry_of_bs_account(): + if args.get("cost_center"): condition += " and cost_center='%s'" % args.get("cost_center") date_fields_dict = { From 706c239cf608b75afcde837cd449747c0a60828c Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Wed, 11 Mar 2020 15:01:02 +0530 Subject: [PATCH 003/608] fix: remove enable_allow_cost_center_in_entry_of_bs_account --- .../accounts_settings/accounts_settings.py | 6 -- .../journal_entry/test_journal_entry.py | 16 +--- .../payment_entry/test_payment_entry.py | 76 +------------------ .../purchase_invoice/test_purchase_invoice.py | 13 +--- .../sales_invoice/test_sales_invoice.py | 16 +--- .../delivery_note/test_delivery_note.py | 12 +-- .../purchase_receipt/test_purchase_receipt.py | 14 +--- 7 files changed, 14 insertions(+), 139 deletions(-) diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py index 2473d715d0d..5593466fc2b 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py @@ -20,7 +20,6 @@ class AccountsSettings(Document): self.validate_stale_days() self.enable_payment_schedule_in_print() - self.enable_fields_for_cost_center_settings() def validate_stale_days(self): if not self.allow_stale and cint(self.stale_days) <= 0: @@ -33,8 +32,3 @@ class AccountsSettings(Document): for doctype in ("Sales Order", "Sales Invoice", "Purchase Order", "Purchase Invoice"): make_property_setter(doctype, "due_date", "print_hide", show_in_print, "Check") make_property_setter(doctype, "payment_schedule", "print_hide", 0 if show_in_print else 1, "Check") - - def enable_fields_for_cost_center_settings(self): - show_field = 0 if cint(self.allow_cost_center_in_entry_of_bs_account) else 1 - for doctype in ("Sales Invoice", "Purchase Invoice", "Payment Entry"): - make_property_setter(doctype, "cost_center", "hidden", show_field, "Check") diff --git a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py index 6996c775b32..0b30f9f5c9e 100644 --- a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py @@ -204,11 +204,8 @@ class TestJournalEntry(unittest.TestCase): self.assertEqual(jv.inter_company_journal_entry_reference, "") self.assertEqual(jv1.inter_company_journal_entry_reference, "") - def test_jv_for_enable_allow_cost_center_in_entry_of_bs_account(self): + def test_jv_with_cost_centre(self): from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center - accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') - accounts_settings.allow_cost_center_in_entry_of_bs_account = 1 - accounts_settings.save() cost_center = "_Test Cost Center for BS Account - _TC" create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company") jv = make_journal_entry("_Test Cash - _TC", "_Test Bank - _TC", 100, cost_center = cost_center, save=False) @@ -237,15 +234,9 @@ class TestJournalEntry(unittest.TestCase): for gle in gl_entries: self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center) - accounts_settings.allow_cost_center_in_entry_of_bs_account = 0 - accounts_settings.save() - - def test_jv_account_and_party_balance_for_enable_allow_cost_center_in_entry_of_bs_account(self): + def test_jv_account_and_party_balance_with_cost_centre(self): from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center from erpnext.accounts.utils import get_balance_on - accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') - accounts_settings.allow_cost_center_in_entry_of_bs_account = 1 - accounts_settings.save() cost_center = "_Test Cost Center for BS Account - _TC" create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company") jv = make_journal_entry("_Test Cash - _TC", "_Test Bank - _TC", 100, cost_center = cost_center, save=False) @@ -261,9 +252,6 @@ class TestJournalEntry(unittest.TestCase): account_balance = get_balance_on(account="_Test Bank - _TC", cost_center=cost_center) self.assertEqual(expected_account_balance, account_balance) - accounts_settings.allow_cost_center_in_entry_of_bs_account = 0 - accounts_settings.save() - def make_journal_entry(account1, account2, amount, cost_center=None, posting_date=None, exchange_rate=1, save=True, submit=False, project=None): if not cost_center: cost_center = "_Test Cost Center - _TC" diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py index 5303743d424..3ae5b2d5c48 100644 --- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py @@ -438,11 +438,8 @@ class TestPaymentEntry(unittest.TestCase): outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount")) self.assertEqual(outstanding_amount, 0) - def test_payment_entry_against_sales_invoice_for_enable_allow_cost_center_in_entry_of_bs_account(self): + def test_payment_entry_against_sales_invoice_with_cost_centre(self): from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center - accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') - accounts_settings.allow_cost_center_in_entry_of_bs_account = 1 - accounts_settings.save() cost_center = "_Test Cost Center for BS Account - _TC" create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company") @@ -477,39 +474,8 @@ class TestPaymentEntry(unittest.TestCase): for gle in gl_entries: self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center) - accounts_settings.allow_cost_center_in_entry_of_bs_account = 0 - accounts_settings.save() - - def test_payment_entry_against_sales_invoice_for_disable_allow_cost_center_in_entry_of_bs_account(self): - accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') - accounts_settings.allow_cost_center_in_entry_of_bs_account = 0 - accounts_settings.save() - si = create_sales_invoice(debit_to="Debtors - _TC") - - pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank - _TC") - - pe.reference_no = "112211-2" - pe.reference_date = nowdate() - pe.paid_to = "_Test Bank - _TC" - pe.paid_amount = si.grand_total - pe.insert() - pe.submit() - - gl_entries = frappe.db.sql("""select account, cost_center, account_currency, debit, credit, - debit_in_account_currency, credit_in_account_currency - from `tabGL Entry` where voucher_type='Payment Entry' and voucher_no=%s - order by account asc""", pe.name, as_dict=1) - - self.assertTrue(gl_entries) - - for gle in gl_entries: - self.assertEqual(gle.cost_center, None) - - def test_payment_entry_against_purchase_invoice_for_enable_allow_cost_center_in_entry_of_bs_account(self): + def test_payment_entry_against_purchase_invoice_with_cost_center(self): from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center - accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') - accounts_settings.allow_cost_center_in_entry_of_bs_account = 1 - accounts_settings.save() cost_center = "_Test Cost Center for BS Account - _TC" create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company") @@ -544,40 +510,9 @@ class TestPaymentEntry(unittest.TestCase): for gle in gl_entries: self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center) - accounts_settings.allow_cost_center_in_entry_of_bs_account = 0 - accounts_settings.save() - - def test_payment_entry_against_purchase_invoice_for_disable_allow_cost_center_in_entry_of_bs_account(self): - accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') - accounts_settings.allow_cost_center_in_entry_of_bs_account = 0 - accounts_settings.save() - pi = make_purchase_invoice(credit_to="Creditors - _TC") - - pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC") - - pe.reference_no = "112222-2" - pe.reference_date = nowdate() - pe.paid_from = "_Test Bank - _TC" - pe.paid_amount = pi.grand_total - pe.insert() - pe.submit() - - gl_entries = frappe.db.sql("""select account, cost_center, account_currency, debit, credit, - debit_in_account_currency, credit_in_account_currency - from `tabGL Entry` where voucher_type='Payment Entry' and voucher_no=%s - order by account asc""", pe.name, as_dict=1) - - self.assertTrue(gl_entries) - - for gle in gl_entries: - self.assertEqual(gle.cost_center, None) - - def test_payment_entry_account_and_party_balance_for_enable_allow_cost_center_in_entry_of_bs_account(self): + def test_payment_entry_account_and_party_balance_with_cost_center(self): from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center from erpnext.accounts.utils import get_balance_on - accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') - accounts_settings.allow_cost_center_in_entry_of_bs_account = 1 - accounts_settings.save() cost_center = "_Test Cost Center for BS Account - _TC" create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company") @@ -606,7 +541,4 @@ class TestPaymentEntry(unittest.TestCase): self.assertEqual(pe.cost_center, si.cost_center) self.assertEqual(expected_account_balance, account_balance) self.assertEqual(expected_party_balance, party_balance) - self.assertEqual(expected_party_account_balance, party_account_balance) - - accounts_settings.allow_cost_center_in_entry_of_bs_account = 0 - accounts_settings.save() \ No newline at end of file + self.assertEqual(expected_party_account_balance, party_account_balance) \ No newline at end of file diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index e41ad428469..c2343319abe 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -791,11 +791,8 @@ class TestPurchaseInvoice(unittest.TestCase): pi_doc = frappe.get_doc('Purchase Invoice', pi.name) self.assertEqual(pi_doc.outstanding_amount, 0) - def test_purchase_invoice_for_enable_allow_cost_center_in_entry_of_bs_account(self): + def test_purchase_invoice_with_cost_center(self): from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center - accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') - accounts_settings.allow_cost_center_in_entry_of_bs_account = 1 - accounts_settings.save() cost_center = "_Test Cost Center for BS Account - _TC" create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company") @@ -821,13 +818,7 @@ class TestPurchaseInvoice(unittest.TestCase): for gle in gl_entries: self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center) - accounts_settings.allow_cost_center_in_entry_of_bs_account = 0 - accounts_settings.save() - - def test_purchase_invoice_for_disable_allow_cost_center_in_entry_of_bs_account(self): - accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') - accounts_settings.allow_cost_center_in_entry_of_bs_account = 0 - accounts_settings.save() + def test_purchase_invoice_without_cost_center(self): cost_center = "_Test Cost Center - _TC" pi = make_purchase_invoice(credit_to="Creditors - _TC") diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index e48e6c95a3f..6636025910f 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -1633,11 +1633,8 @@ class TestSalesInvoice(unittest.TestCase): si_doc = frappe.get_doc('Sales Invoice', si.name) self.assertEqual(si_doc.outstanding_amount, 0) - def test_sales_invoice_for_enable_allow_cost_center_in_entry_of_bs_account(self): + def test_sales_invoice_with_cost_center(self): from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center - accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') - accounts_settings.allow_cost_center_in_entry_of_bs_account = 1 - accounts_settings.save() cost_center = "_Test Cost Center for BS Account - _TC" create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company") @@ -1663,13 +1660,7 @@ class TestSalesInvoice(unittest.TestCase): for gle in gl_entries: self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center) - accounts_settings.allow_cost_center_in_entry_of_bs_account = 0 - accounts_settings.save() - - def test_sales_invoice_for_disable_allow_cost_center_in_entry_of_bs_account(self): - accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') - accounts_settings.allow_cost_center_in_entry_of_bs_account = 1 - accounts_settings.save() + def test_sales_invoice_without_cost_center(self): cost_center = "_Test Cost Center - _TC" si = create_sales_invoice(debit_to="Debtors - _TC") @@ -1692,9 +1683,6 @@ class TestSalesInvoice(unittest.TestCase): for gle in gl_entries: self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center) - accounts_settings.allow_cost_center_in_entry_of_bs_account = 0 - accounts_settings.save() - def test_deferred_revenue(self): deferred_account = create_account(account_name="Deferred Revenue", parent_account="Current Liabilities - _TC", company="_Test Company") diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py index dc92c5c9ff2..2c8414ab700 100644 --- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py @@ -547,11 +547,8 @@ class TestDeliveryNote(unittest.TestCase): dt = make_delivery_trip(dn.name) self.assertEqual(dn.name, dt.delivery_stops[0].delivery_note) - def test_delivery_note_for_enable_allow_cost_center_in_entry_of_bs_account(self): + def test_delivery_note_with_cost_center(self): from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center - accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') - accounts_settings.allow_cost_center_in_entry_of_bs_account = 1 - accounts_settings.save() cost_center = "_Test Cost Center for BS Account - TCP1" create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company with perpetual inventory") @@ -577,13 +574,8 @@ class TestDeliveryNote(unittest.TestCase): } for i, gle in enumerate(gl_entries): self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center) - accounts_settings.allow_cost_center_in_entry_of_bs_account = 0 - accounts_settings.save() - def test_delivery_note_for_disable_allow_cost_center_in_entry_of_bs_account(self): - accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') - accounts_settings.allow_cost_center_in_entry_of_bs_account = 0 - accounts_settings.save() + def test_delivery_note_without_cost_center(self): cost_center = "Main - TCP1" company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company') diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index d80e8f211b4..07f89ee9ab4 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -372,11 +372,8 @@ class TestPurchaseReceipt(unittest.TestCase): location = frappe.db.get_value('Asset', assets[0].name, 'location') self.assertEquals(location, "Test Location") - def test_purchase_receipt_for_enable_allow_cost_center_in_entry_of_bs_account(self): + def test_purchase_receipt_cost_center(self): from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center - accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') - accounts_settings.allow_cost_center_in_entry_of_bs_account = 1 - accounts_settings.save() cost_center = "_Test Cost Center for BS Account - TCP1" create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company with perpetual inventory") @@ -404,14 +401,7 @@ class TestPurchaseReceipt(unittest.TestCase): for i, gle in enumerate(gl_entries): self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center) - accounts_settings.allow_cost_center_in_entry_of_bs_account = 0 - accounts_settings.save() - - def test_purchase_receipt_for_disable_allow_cost_center_in_entry_of_bs_account(self): - accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') - accounts_settings.allow_cost_center_in_entry_of_bs_account = 0 - accounts_settings.save() - + def test_purchase_receipt_without_cost_center(self): if not frappe.db.exists('Location', 'Test Location'): frappe.get_doc({ 'doctype': 'Location', From af1221bcbd9045b18fb37a0e1865eafa8c5356fe Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Thu, 12 Mar 2020 17:04:17 +0530 Subject: [PATCH 004/608] chore: add tests and remove test based on allow_cost_center_for_bs_accounts --- .../journal_entry/test_journal_entry.py | 36 +++++++++++++++++ .../purchase_invoice/test_purchase_invoice.py | 38 ++++++++++++++++++ .../sales_invoice/test_sales_invoice.py | 39 +++++++++++++++++++ .../projects/doctype/project/test_project.py | 22 ++++++++++- .../project_template/test_project_template.py | 21 +++++++++- 5 files changed, 154 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py index 0b30f9f5c9e..23ad1eef14c 100644 --- a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py @@ -234,6 +234,42 @@ class TestJournalEntry(unittest.TestCase): for gle in gl_entries: self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center) + def test_jv_with_project(self): + from erpnext.projects.doctype.project.test_project import make_project + project = make_project({ + 'project_name': 'Journal Entry Project', + 'project_template_name': 'Test Project Template', + 'start_date': '2020-01-01' + }) + + jv = make_journal_entry("_Test Cash - _TC", "_Test Bank - _TC", 100, save=False) + for d in jv.accounts: + d.project = project.project_name + jv.voucher_type = "Bank Entry" + jv.multi_currency = 0 + jv.cheque_no = "112233" + jv.cheque_date = nowdate() + jv.insert() + jv.submit() + + expected_values = { + "_Test Cash - _TC": { + "project": project.project_name + }, + "_Test Bank - _TC": { + "project": project.project_name + } + } + + gl_entries = frappe.db.sql("""select account, project, debit, credit + from `tabGL Entry` where voucher_type='Journal Entry' and voucher_no=%s + order by account asc""", jv.name, as_dict=1) + + self.assertTrue(gl_entries) + + for gle in gl_entries: + self.assertEqual(expected_values[gle.account]["project"], gle.project) + def test_jv_account_and_party_balance_with_cost_centre(self): from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center from erpnext.accounts.utils import get_balance_on diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index c2343319abe..af0eddf3de9 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -840,7 +840,45 @@ class TestPurchaseInvoice(unittest.TestCase): for gle in gl_entries: self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center) + + def test_purchase_invoice_with_project_link(self): + from erpnext.projects.doctype.project.test_project import make_project + project = make_project({ + 'project_name': 'Purchase Invoice Project', + 'project_template_name': 'Test Project Template', + 'start_date': '2020-01-01' + }) + item_project = make_project({ + 'project_name': 'Purchase Invoice Item Project', + 'project_template_name': 'Test Project Template', + 'start_date': '2019-06-01' + }) + + pi = make_purchase_invoice(credit_to="Creditors - _TC" ,do_not_save=1) + pi.items[0].project = item_project.project_name + pi.project = project.project_name + + pi.submit() + + expected_values = { + "Creditors - _TC": { + "project": project.project_name + }, + "_Test Account Cost for Goods Sold - _TC": { + "project": item_project.project_name + } + } + + gl_entries = frappe.db.sql("""select account, cost_center, project, account_currency, debit, credit, + debit_in_account_currency, credit_in_account_currency + from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s + order by account asc""", pi.name, as_dict=1) + + self.assertTrue(gl_entries) + + for gle in gl_entries: + self.assertEqual(expected_values[gle.account]["project"], gle.project) def unlink_payment_on_cancel_of_invoice(enable=1): accounts_settings = frappe.get_doc("Accounts Settings") diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 6636025910f..cb2d8c35bde 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -1659,6 +1659,45 @@ class TestSalesInvoice(unittest.TestCase): for gle in gl_entries: self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center) + + def test_sales_invoice_with_project_link(self): + from erpnext.projects.doctype.project.test_project import make_project + + project = make_project({ + 'project_name': 'Sales Invoice Project', + 'project_template_name': 'Test Project Template', + 'start_date': '2020-01-01' + }) + item_project = make_project({ + 'project_name': 'Sales Invoice Item Project', + 'project_template_name': 'Test Project Template', + 'start_date': '2019-06-01' + }) + + sales_invoice = create_sales_invoice(do_not_save=1) + sales_invoice.items[0].project = item_project.project_name + sales_invoice.project = project.project_name + + sales_invoice.submit() + + expected_values = { + "Debtors - _TC": { + "project": project.project_name + }, + "Sales - _TC": { + "project": item_project.project_name + } + } + + gl_entries = frappe.db.sql("""select account, cost_center, project, account_currency, debit, credit, + debit_in_account_currency, credit_in_account_currency + from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s + order by account asc""", sales_invoice.name, as_dict=1) + + self.assertTrue(gl_entries) + + for gle in gl_entries: + self.assertEqual(expected_values[gle.account]["project"], gle.project) def test_sales_invoice_without_cost_center(self): cost_center = "_Test Cost Center - _TC" diff --git a/erpnext/projects/doctype/project/test_project.py b/erpnext/projects/doctype/project/test_project.py index 06c62b62d2f..0c4f6f1bdfe 100644 --- a/erpnext/projects/doctype/project/test_project.py +++ b/erpnext/projects/doctype/project/test_project.py @@ -7,7 +7,7 @@ import frappe, unittest test_records = frappe.get_test_records('Project') test_ignore = ["Sales Order"] -from erpnext.projects.doctype.project_template.test_project_template import get_project_template +from erpnext.projects.doctype.project_template.test_project_template import get_project_template, make_project_template from erpnext.projects.doctype.project.project import set_project_status from frappe.utils import getdate @@ -43,4 +43,24 @@ def get_project(name): expected_start_date = '2019-01-01' )).insert() + return project + +def make_project(args): + args = frappe._dict(args) + if args.project_template_name: + template = make_project_template(args.project_template_name) + else: + template = get_project_template() + + project = frappe.get_doc(dict( + doctype = 'Project', + project_name = args.project_name, + status = 'Open', + project_template = template.name, + expected_start_date = args.start_date + )) + + if not frappe.db.exists("Project", args.project_name): + project.insert() + return project \ No newline at end of file diff --git a/erpnext/projects/doctype/project_template/test_project_template.py b/erpnext/projects/doctype/project_template/test_project_template.py index efcb2eab68b..2c5831a5dc9 100644 --- a/erpnext/projects/doctype/project_template/test_project_template.py +++ b/erpnext/projects/doctype/project_template/test_project_template.py @@ -26,4 +26,23 @@ def get_project_template(): ] )).insert() - return frappe.get_doc('Project Template', 'Test Project Template') \ No newline at end of file + return frappe.get_doc('Project Template', 'Test Project Template') + +def make_project_template(project_template_name, project_tasks=[]): + if not frappe.db.exists('Project Template', project_template_name): + frappe.get_doc(dict( + doctype = 'Project Template', + name = project_template_name, + tasks = project_tasks or [ + dict(subject='Task 1', description='Task 1 description', + start=0, duration=3), + dict(subject='Task 2', description='Task 2 description', + start=0, duration=2), + dict(subject='Task 3', description='Task 3 description', + start=2, duration=4), + dict(subject='Task 4', description='Task 4 description', + start=3, duration=2), + ] + )).insert() + + return frappe.get_doc('Project Template', project_template_name) \ No newline at end of file From 4a85b42da0838817396503b71af4831204adec3e Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Fri, 3 Apr 2020 14:31:00 +0530 Subject: [PATCH 005/608] fix: travis --- erpnext/assets/doctype/asset/depreciation.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/assets/doctype/asset/depreciation.py b/erpnext/assets/doctype/asset/depreciation.py index 522c1fef679..c50211e6ab1 100644 --- a/erpnext/assets/doctype/asset/depreciation.py +++ b/erpnext/assets/doctype/asset/depreciation.py @@ -10,7 +10,7 @@ from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import g def post_depreciation_entries(date=None): # Return if automatic booking of asset depreciation is disabled - if not cint(frappe.db.get_single_value("Accounts Settings", "book_asset_depreciation_entry_automatically")): + if not cint(frappe.db.get_value("Accounts Settings", None, "book_asset_depreciation_entry_automatically")): return if not date: @@ -58,7 +58,8 @@ def make_depreciation_entry(asset_name, date=None): "account": accumulated_depreciation_account, "credit_in_account_currency": d.depreciation_amount, "reference_type": "Asset", - "reference_name": asset.name + "reference_name": asset.name, + "cost_center": "" } debit_entry = { From aa196c0a667ee7c3f2400fc7bd65319ccbb25a5a Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Tue, 14 Apr 2020 14:03:02 +0530 Subject: [PATCH 006/608] fix: travis --- .../purchase_invoice_item/purchase_invoice_item.json | 4 ++-- erpnext/assets/doctype/asset/depreciation.py | 3 +-- erpnext/crm/doctype/opportunity/test_records.json | 1 + erpnext/healthcare/setup.py | 3 ++- erpnext/manufacturing/doctype/bom/test_records.json | 9 ++++++--- .../setup/setup_wizard/operations/install_fixtures.py | 1 - erpnext/www/book-appointment/__init__.py | 0 erpnext/www/book-appointment/verify/__init__.py | 0 8 files changed, 12 insertions(+), 9 deletions(-) create mode 100644 erpnext/www/book-appointment/__init__.py create mode 100644 erpnext/www/book-appointment/verify/__init__.py diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json index ef90b942b59..5e073f8e325 100644 --- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json +++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json @@ -761,7 +761,7 @@ "depends_on": "is_fixed_asset", "fetch_from": "item_code.asset_category", "fieldname": "asset_category", - "fieldtype": "Data", + "fieldtype": "Link", "label": "Asset Category", "options": "Asset Category", "read_only": 1 @@ -777,7 +777,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2020-03-11 14:20:17.297284", + "modified": "2020-04-14 03:33:32.981331", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice Item", diff --git a/erpnext/assets/doctype/asset/depreciation.py b/erpnext/assets/doctype/asset/depreciation.py index c50211e6ab1..ad671ba0f2c 100644 --- a/erpnext/assets/doctype/asset/depreciation.py +++ b/erpnext/assets/doctype/asset/depreciation.py @@ -58,8 +58,7 @@ def make_depreciation_entry(asset_name, date=None): "account": accumulated_depreciation_account, "credit_in_account_currency": d.depreciation_amount, "reference_type": "Asset", - "reference_name": asset.name, - "cost_center": "" + "reference_name": asset.name } debit_entry = { diff --git a/erpnext/crm/doctype/opportunity/test_records.json b/erpnext/crm/doctype/opportunity/test_records.json index a1e0ad921b4..0a6c29b637c 100644 --- a/erpnext/crm/doctype/opportunity/test_records.json +++ b/erpnext/crm/doctype/opportunity/test_records.json @@ -2,6 +2,7 @@ { "doctype": "Opportunity", "name": "_Test Opportunity 1", + "company": "Wind Power LLC", "opportunity_from": "Lead", "enquiry_type": "Sales", "party_name": "_T-Lead-00001", diff --git a/erpnext/healthcare/setup.py b/erpnext/healthcare/setup.py index 2087f49f32f..224dc8d3ad0 100644 --- a/erpnext/healthcare/setup.py +++ b/erpnext/healthcare/setup.py @@ -198,7 +198,8 @@ def add_healthcare_service_unit_tree_root(): { "doctype": "Healthcare Service Unit", "healthcare_service_unit_name": "All Healthcare Service Units", - "is_group": 1 + "is_group": 1, + "company": "Wind Power LLC" } ] insert_record(record) diff --git a/erpnext/manufacturing/doctype/bom/test_records.json b/erpnext/manufacturing/doctype/bom/test_records.json index 25730f9b9f4..3913268e1dc 100644 --- a/erpnext/manufacturing/doctype/bom/test_records.json +++ b/erpnext/manufacturing/doctype/bom/test_records.json @@ -32,7 +32,8 @@ "is_active": 1, "is_default": 1, "item": "_Test Item Home Desktop Manufactured", - "quantity": 1.0 + "quantity": 1.0, + "company": "_Test Company" }, { "scrap_items":[ @@ -78,7 +79,8 @@ "is_default": 1, "currency": "USD", "item": "_Test FG Item", - "quantity": 1.0 + "quantity": 1.0, + "company":"_Test Company" }, { "operations": [ @@ -160,6 +162,7 @@ "currency": "USD", "item": "_Test Variant Item", "quantity": 1.0, - "with_operations": 1 + "with_operations": 1, + "company": "_Test Company" } ] diff --git a/erpnext/setup/setup_wizard/operations/install_fixtures.py b/erpnext/setup/setup_wizard/operations/install_fixtures.py index ebd7b509396..432df5b901d 100644 --- a/erpnext/setup/setup_wizard/operations/install_fixtures.py +++ b/erpnext/setup/setup_wizard/operations/install_fixtures.py @@ -435,7 +435,6 @@ def install_defaults(args=None): global_defaults.update({ 'current_fiscal_year': current_fiscal_year.name, 'default_currency': args.get('currency'), - 'default_company':args.get('company_name') , "country": args.get("country"), }) diff --git a/erpnext/www/book-appointment/__init__.py b/erpnext/www/book-appointment/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/www/book-appointment/verify/__init__.py b/erpnext/www/book-appointment/verify/__init__.py new file mode 100644 index 00000000000..e69de29bb2d From 1f20c99ecfb51675d7182588001bb3c1d581f415 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Tue, 14 Apr 2020 19:40:25 +0530 Subject: [PATCH 007/608] fix: tests depending on global default company --- .../sales_invoice/test_sales_invoice.py | 1 + .../doctype/subscription/test_subscription.py | 43 +++++++++++++++++++ erpnext/assets/doctype/asset/test_asset.py | 3 +- .../test_procurement_tracker.py | 6 ++- erpnext/controllers/tests/test_mapper.py | 2 + .../doctype/opportunity/test_opportunity.py | 1 + .../plaid_settings/test_plaid_settings.py | 2 +- .../inpatient_record/test_inpatient_record.py | 3 +- .../test_compensatory_leave_request.py | 3 +- .../hr/doctype/department/test_department.py | 2 +- erpnext/hr/doctype/employee/test_employee.py | 2 +- ...test_employee_tax_exemption_declaration.py | 2 +- .../expense_claim/test_expense_claim.py | 1 + .../hr/doctype/job_offer/test_job_offer.py | 4 +- .../doctype/salary_slip/test_salary_slip.py | 2 +- .../salary_structure/test_salary_structure.py | 2 +- .../training_event/test_training_event.py | 3 +- .../delivery_note/test_delivery_note.py | 27 +----------- .../delivery_trip/test_delivery_trip.py | 2 +- .../purchase_receipt/test_purchase_receipt.py | 24 ----------- .../stock/doctype/serial_no/test_serial_no.py | 1 + 21 files changed, 72 insertions(+), 64 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 26924650515..df2cc29a160 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -1058,6 +1058,7 @@ class TestSalesInvoice(unittest.TestCase): serial_no = frappe.get_doc({ "doctype": "Serial No", "item_code": "_Test Serialized Item With Series", + "company": "Wind Power LLC", "serial_no": make_autoname("SR", "Serial No") }) serial_no.save() diff --git a/erpnext/accounts/doctype/subscription/test_subscription.py b/erpnext/accounts/doctype/subscription/test_subscription.py index 3d96f233b40..a0784e6719d 100644 --- a/erpnext/accounts/doctype/subscription/test_subscription.py +++ b/erpnext/accounts/doctype/subscription/test_subscription.py @@ -101,6 +101,7 @@ class TestSubscription(unittest.TestCase): subscription.delete() def test_invoice_is_generated_at_end_of_billing_period(self): + frappe.db.set_value("Global Defaults", None, "default_company", "_Test Company") subscription = frappe.new_doc('Subscription') subscription.customer = '_Test Customer' subscription.start = '2018-01-01' @@ -116,8 +117,10 @@ class TestSubscription(unittest.TestCase): self.assertEqual(subscription.current_invoice_start, '2018-01-01') self.assertEqual(subscription.status, 'Past Due Date') subscription.delete() + frappe.db.set_value("Global Defaults", None, "default_company", None) def test_status_goes_back_to_active_after_invoice_is_paid(self): + frappe.db.set_value("Global Defaults", None, "default_company", "_Test Company") subscription = frappe.new_doc('Subscription') subscription.customer = '_Test Customer' subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1}) @@ -141,8 +144,10 @@ class TestSubscription(unittest.TestCase): self.assertEqual(len(subscription.invoices), 1) subscription.delete() + frappe.db.set_value("Global Defaults", None, "default_company", None) def test_subscription_cancel_after_grace_period(self): + frappe.db.set_value("Global Defaults", None, "default_company", "_Test Company") settings = frappe.get_single('Subscription Settings') default_grace_period_action = settings.cancel_after_grace settings.cancel_after_grace = 1 @@ -164,8 +169,11 @@ class TestSubscription(unittest.TestCase): settings.cancel_after_grace = default_grace_period_action settings.save() subscription.delete() + frappe.db.set_value("Global Defaults", None, "default_company", None) def test_subscription_unpaid_after_grace_period(self): + frappe.db.set_value("Global Defaults", None, "default_company", "_Test Company") + settings = frappe.get_single('Subscription Settings') default_grace_period_action = settings.cancel_after_grace settings.cancel_after_grace = 0 @@ -187,8 +195,11 @@ class TestSubscription(unittest.TestCase): settings.cancel_after_grace = default_grace_period_action settings.save() subscription.delete() + frappe.db.set_value("Global Defaults", None, "default_company", None) def test_subscription_invoice_days_until_due(self): + frappe.db.set_value("Global Defaults", None, "default_company", "_Test Company") + subscription = frappe.new_doc('Subscription') subscription.customer = '_Test Customer' subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1}) @@ -200,8 +211,11 @@ class TestSubscription(unittest.TestCase): self.assertEqual(subscription.status, 'Active') subscription.delete() + frappe.db.set_value("Global Defaults", None, "default_company", None) def test_subscription_is_past_due_doesnt_change_within_grace_period(self): + frappe.db.set_value("Global Defaults", None, "default_company", "_Test Company") + settings = frappe.get_single('Subscription Settings') grace_period = settings.grace_period settings.grace_period = 1000 @@ -229,6 +243,7 @@ class TestSubscription(unittest.TestCase): settings.grace_period = grace_period settings.save() subscription.delete() + frappe.db.set_value("Global Defaults", None, "default_company", None) def test_subscription_remains_active_during_invoice_period(self): subscription = frappe.new_doc('Subscription') @@ -257,6 +272,7 @@ class TestSubscription(unittest.TestCase): subscription.delete() def test_subscription_cancelation(self): + frappe.db.set_value("Global Defaults", None, "default_company", "_Test Company") subscription = frappe.new_doc('Subscription') subscription.customer = '_Test Customer' subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1}) @@ -266,8 +282,10 @@ class TestSubscription(unittest.TestCase): self.assertEqual(subscription.status, 'Cancelled') subscription.delete() + frappe.db.set_value("Global Defaults", None, "default_company", None) def test_subscription_cancellation_invoices(self): + frappe.db.set_value("Global Defaults", None, "default_company", "_Test Company") settings = frappe.get_single('Subscription Settings') to_prorate = settings.prorate settings.prorate = 1 @@ -301,8 +319,11 @@ class TestSubscription(unittest.TestCase): subscription.delete() settings.prorate = to_prorate settings.save() + frappe.db.set_value("Global Defaults", None, "default_company", None) def test_subscription_cancellation_invoices_with_prorata_false(self): + frappe.db.set_value("Global Defaults", None, "default_company", "_Test Company") + settings = frappe.get_single('Subscription Settings') to_prorate = settings.prorate settings.prorate = 0 @@ -321,8 +342,11 @@ class TestSubscription(unittest.TestCase): settings.save() subscription.delete() + frappe.db.set_value("Global Defaults", None, "default_company", None) def test_subscription_cancellation_invoices_with_prorata_true(self): + frappe.db.set_value("Global Defaults", None, "default_company", "_Test Company") + settings = frappe.get_single('Subscription Settings') to_prorate = settings.prorate settings.prorate = 1 @@ -345,8 +369,10 @@ class TestSubscription(unittest.TestCase): settings.save() subscription.delete() + frappe.db.set_value("Global Defaults", None, "default_company", None) def test_subcription_cancellation_and_process(self): + frappe.db.set_value("Global Defaults", None, "default_company", "_Test Company") settings = frappe.get_single('Subscription Settings') default_grace_period_action = settings.cancel_after_grace settings.cancel_after_grace = 1 @@ -378,8 +404,11 @@ class TestSubscription(unittest.TestCase): settings.cancel_after_grace = default_grace_period_action settings.save() subscription.delete() + frappe.db.set_value("Global Defaults", None, "default_company", None) def test_subscription_restart_and_process(self): + frappe.db.set_value("Global Defaults", None, "default_company", "_Test Company") + settings = frappe.get_single('Subscription Settings') default_grace_period_action = settings.cancel_after_grace settings.grace_period = 0 @@ -416,8 +445,11 @@ class TestSubscription(unittest.TestCase): settings.cancel_after_grace = default_grace_period_action settings.save() subscription.delete() + frappe.db.set_value("Global Defaults", None, "default_company", None) def test_subscription_unpaid_back_to_active(self): + frappe.db.set_value("Global Defaults", None, "default_company", "_Test Company") + settings = frappe.get_single('Subscription Settings') default_grace_period_action = settings.cancel_after_grace settings.cancel_after_grace = 0 @@ -450,6 +482,7 @@ class TestSubscription(unittest.TestCase): settings.cancel_after_grace = default_grace_period_action settings.save() subscription.delete() + frappe.db.set_value("Global Defaults", None, "default_company", None) def test_restart_active_subscription(self): subscription = frappe.new_doc('Subscription') @@ -462,6 +495,8 @@ class TestSubscription(unittest.TestCase): subscription.delete() def test_subscription_invoice_discount_percentage(self): + frappe.db.set_value("Global Defaults", None, "default_company", "_Test Company") + subscription = frappe.new_doc('Subscription') subscription.customer = '_Test Customer' subscription.additional_discount_percentage = 10 @@ -475,8 +510,11 @@ class TestSubscription(unittest.TestCase): self.assertEqual(invoice.apply_discount_on, 'Grand Total') subscription.delete() + frappe.db.set_value("Global Defaults", None, "default_company", None) def test_subscription_invoice_discount_amount(self): + frappe.db.set_value("Global Defaults", None, "default_company", "_Test Company") + subscription = frappe.new_doc('Subscription') subscription.customer = '_Test Customer' subscription.additional_discount_amount = 11 @@ -490,10 +528,12 @@ class TestSubscription(unittest.TestCase): self.assertEqual(invoice.apply_discount_on, 'Grand Total') subscription.delete() + frappe.db.set_value("Global Defaults", None, "default_company", None) def test_prepaid_subscriptions(self): # Create a non pre-billed subscription, processing should not create # invoices. + frappe.db.set_value("Global Defaults", None, "default_company", "_Test Company") subscription = frappe.new_doc('Subscription') subscription.customer = '_Test Customer' subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1}) @@ -509,8 +549,10 @@ class TestSubscription(unittest.TestCase): subscription.process() self.assertEqual(len(subscription.invoices), 1) + frappe.db.set_value("Global Defaults", None, "default_company", None) def test_prepaid_subscriptions_with_prorate_true(self): + frappe.db.set_value("Global Defaults", None, "default_company", "_Test Company") settings = frappe.get_single('Subscription Settings') to_prorate = settings.prorate settings.prorate = 1 @@ -538,3 +580,4 @@ class TestSubscription(unittest.TestCase): settings.save() subscription.delete() + frappe.db.set_value("Global Defaults", None, "default_company", None) \ No newline at end of file diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index a56440de3d3..d2a70375c27 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -78,6 +78,7 @@ class TestAsset(unittest.TestCase): 'qty': 1, 'asset': asset.name }) + doc.company = 'Wind Power LLC' doc.set_missing_values() self.assertEquals(doc.items[0].is_fixed_asset, 1) @@ -595,7 +596,7 @@ def create_asset(**args): "asset_name": args.asset_name or "Macbook Pro 1", "asset_category": "Computers", "item_code": args.item_code or "Macbook Pro", - "company": args.company or"_Test Company", + "company": args.company or "_Test Company", "purchase_date": "2015-01-01", "calculate_depreciation": 0, "gross_purchase_amount": 100000, diff --git a/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py b/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py index bebf0ccec56..0c840ecbd83 100644 --- a/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py +++ b/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py @@ -36,16 +36,18 @@ class TestProcurementTracker(unittest.TestCase): mr = make_material_request(company="_Test Procurement Company", warehouse=warehouse) po = make_purchase_order(mr.name) po.supplier = "_Test Supplier" - po.get("items")[0].cost_center = "_Test Cost Center - _TC" + po.company = "_Test Procurement Company" + po.get("items")[0].cost_center = "_Test Cost Center - _TPC" po.submit() pr = make_purchase_receipt(po.name) + pr.company = "_Test Procurement Company" pr.submit() frappe.db.commit() date_obj = datetime.date(datetime.now()) expected_data = { "material_request_date": date_obj, - "cost_center": "_Test Cost Center - _TC", + "cost_center": "_Test Cost Center - _TPC", "project": None, "requesting_site": "_Test Procurement Warehouse - _TPC", "requestor": "Administrator", diff --git a/erpnext/controllers/tests/test_mapper.py b/erpnext/controllers/tests/test_mapper.py index d02308d8f21..93d1b321b4c 100644 --- a/erpnext/controllers/tests/test_mapper.py +++ b/erpnext/controllers/tests/test_mapper.py @@ -43,6 +43,7 @@ class TestMapper(unittest.TestCase): qtn = frappe.get_doc({ "doctype": "Quotation", "quotation_to": "Customer", + "company": "_Test Company", "party_name": customer, "order_type": "Sales", "transaction_date" : nowdate(), @@ -59,6 +60,7 @@ class TestMapper(unittest.TestCase): "base_amount": 1000.0, "base_rate": 100.0, "description": "CPU", + "company": "_Test Company", "doctype": "Sales Order Item", "item_code": "_Test Item Home Desktop 100", "item_name": "CPU", diff --git a/erpnext/crm/doctype/opportunity/test_opportunity.py b/erpnext/crm/doctype/opportunity/test_opportunity.py index 33d90076c4a..fc852b19a1f 100644 --- a/erpnext/crm/doctype/opportunity/test_opportunity.py +++ b/erpnext/crm/doctype/opportunity/test_opportunity.py @@ -30,6 +30,7 @@ class TestOpportunity(unittest.TestCase): new_lead_email_id = "new{}@example.com".format(random_string(5)) args = { "doctype": "Opportunity", + "company": "_Test Company", "contact_email": new_lead_email_id, "opportunity_type": "Sales", "with_items": 0, diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/test_plaid_settings.py b/erpnext/erpnext_integrations/doctype/plaid_settings/test_plaid_settings.py index 29e8fa4fec8..75184d989ae 100644 --- a/erpnext/erpnext_integrations/doctype/plaid_settings/test_plaid_settings.py +++ b/erpnext/erpnext_integrations/doctype/plaid_settings/test_plaid_settings.py @@ -108,7 +108,7 @@ class TestPlaidSettings(unittest.TestCase): } bank = json.dumps(frappe.get_doc("Bank", "Citi").as_dict(), default=json_handler) - company = frappe.db.get_single_value('Global Defaults', 'default_company') + company = frappe.db.get_single_value('Global Defaults', 'default_company') or '_Test Company' if frappe.db.get_value("Company", company, "default_bank_account") is None: frappe.db.set_value("Company", company, "default_bank_account", get_default_bank_cash_account(company, "Cash").get("account")) diff --git a/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py b/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py index e15324c55bf..7a7936a0f84 100644 --- a/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py +++ b/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py @@ -84,7 +84,8 @@ def get_healthcare_service_unit(): service_unit.service_unit_type = get_service_unit_type() service_unit.inpatient_occupancy = 1 service_unit.occupancy_status = "Vacant" - service_unit.is_group = 0 + service_unit.is_group = 0, + service_unit.company = "_Test Company" service_unit_parent_name = frappe.db.exists({ "doctype": "Healthcare Service Unit", "healthcare_service_unit_name": "All Healthcare Service Units", diff --git a/erpnext/hr/doctype/compensatory_leave_request/test_compensatory_leave_request.py b/erpnext/hr/doctype/compensatory_leave_request/test_compensatory_leave_request.py index 1615ab30f1d..1181192cbe5 100644 --- a/erpnext/hr/doctype/compensatory_leave_request/test_compensatory_leave_request.py +++ b/erpnext/hr/doctype/compensatory_leave_request/test_compensatory_leave_request.py @@ -104,7 +104,8 @@ def mark_attendance(employee, date=today(), status='Present'): "doctype": "Attendance", "employee": employee.name, "attendance_date": date, - "status": status + "status": status, + "company": "_Test Company" }) attendance.save() attendance.submit() diff --git a/erpnext/hr/doctype/department/test_department.py b/erpnext/hr/doctype/department/test_department.py index 2eeca26e303..a6e8aae625e 100644 --- a/erpnext/hr/doctype/department/test_department.py +++ b/erpnext/hr/doctype/department/test_department.py @@ -16,7 +16,7 @@ def create_department(department_name, parent_department=None): 'is_group': 0, 'parent_department': parent_department, 'department_name': department_name, - 'company': frappe.defaults.get_defaults().company + 'company': frappe.defaults.get_defaults().company or 'Wind Power LLC' }).insert() return doc diff --git a/erpnext/hr/doctype/employee/test_employee.py b/erpnext/hr/doctype/employee/test_employee.py index d3410de2eb7..eff382d973e 100644 --- a/erpnext/hr/doctype/employee/test_employee.py +++ b/erpnext/hr/doctype/employee/test_employee.py @@ -60,7 +60,7 @@ def make_employee(user, company=None): "doctype": "Employee", "naming_series": "EMP-", "first_name": user, - "company": company or erpnext.get_default_company(), + "company": company or erpnext.get_default_company() or 'Wind Power LLC', "user_id": user, "date_of_birth": "1990-05-08", "date_of_joining": "2013-01-01", diff --git a/erpnext/hr/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py b/erpnext/hr/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py index 9c87bbd1f30..d53f6a706d6 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py +++ b/erpnext/hr/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py @@ -88,7 +88,7 @@ def create_payroll_period(): payroll_period = frappe.get_doc(dict( doctype = 'Payroll Period', name = "_Test Payroll Period", - company = erpnext.get_default_company(), + company = erpnext.get_default_company() or 'Wind Power LLC', start_date = date(date.today().year, 1, 1), end_date = date(date.today().year, 12, 31) )).insert() diff --git a/erpnext/hr/doctype/expense_claim/test_expense_claim.py b/erpnext/hr/doctype/expense_claim/test_expense_claim.py index 6e97f0513d6..e7ee108142a 100644 --- a/erpnext/hr/doctype/expense_claim/test_expense_claim.py +++ b/erpnext/hr/doctype/expense_claim/test_expense_claim.py @@ -94,6 +94,7 @@ class TestExpenseClaim(unittest.TestCase): payable_account = get_payable_account(company_name) expense_claim = frappe.get_doc({ "doctype": "Expense Claim", + "company": "_Test Company", "employee": "_T-Employee-00001", "payable_account": payable_account, "approval_status": "Rejected", diff --git a/erpnext/hr/doctype/job_offer/test_job_offer.py b/erpnext/hr/doctype/job_offer/test_job_offer.py index 88865964500..1da107bfe72 100644 --- a/erpnext/hr/doctype/job_offer/test_job_offer.py +++ b/erpnext/hr/doctype/job_offer/test_job_offer.py @@ -55,7 +55,8 @@ def create_job_offer(**args): "job_applicant": args.job_applicant or job_applicant.name, "offer_date": args.offer_date or nowdate(), "designation": args.designation or "Researcher", - "status": args.status or "Accepted" + "status": args.status or "Accepted", + "company": "_Test Company" }) return job_offer @@ -68,6 +69,7 @@ def create_staffing_plan(**args): staffing_plan = frappe.get_doc({ "doctype": "Staffing Plan", "name": args.name or "Test", + "company": "_Test Company", "from_date": args.from_date or nowdate(), "to_date": args.to_date or add_days(nowdate(), 10), "staffing_details": args.staffing_details or [{ diff --git a/erpnext/hr/doctype/salary_slip/test_salary_slip.py b/erpnext/hr/doctype/salary_slip/test_salary_slip.py index ecccac7d416..cd73b94719c 100644 --- a/erpnext/hr/doctype/salary_slip/test_salary_slip.py +++ b/erpnext/hr/doctype/salary_slip/test_salary_slip.py @@ -359,7 +359,7 @@ def make_salary_component(salary_components, test_tax, company_list=None): get_salary_component_account(salary_component["salary_component"], company_list) def get_salary_component_account(sal_comp, company_list=None): - company = erpnext.get_default_company() + company = erpnext.get_default_company() or 'Wind Power LLC' if company_list and company not in company_list: company_list.append(company) diff --git a/erpnext/hr/doctype/salary_structure/test_salary_structure.py b/erpnext/hr/doctype/salary_structure/test_salary_structure.py index 6ca6dfd2c02..e465d04f4d4 100644 --- a/erpnext/hr/doctype/salary_structure/test_salary_structure.py +++ b/erpnext/hr/doctype/salary_structure/test_salary_structure.py @@ -116,7 +116,7 @@ def make_salary_structure(salary_structure, payroll_frequency, employee=None, do details = { "doctype": "Salary Structure", "name": salary_structure, - "company": company or erpnext.get_default_company(), + "company": company or erpnext.get_default_company() or "_Test Company", "earnings": make_earning_salary_component(test_tax=test_tax, company_list=["_Test Company"]), "deductions": make_deduction_salary_component(test_tax=test_tax, company_list=["_Test Company"]), "payroll_frequency": payroll_frequency, diff --git a/erpnext/hr/doctype/training_event/test_training_event.py b/erpnext/hr/doctype/training_event/test_training_event.py index 57123e304f5..5ddb99b8ea8 100644 --- a/erpnext/hr/doctype/training_event/test_training_event.py +++ b/erpnext/hr/doctype/training_event/test_training_event.py @@ -32,7 +32,8 @@ def create_training_program(training_program): frappe.get_doc({ "doctype": "Training Program", "training_program": training_program, - "description": training_program + "description": training_program, + "company": "Wind Power LLC" }).insert() def get_attendees(employee, employee2): diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py index c15fada5250..a0c69c35187 100644 --- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py @@ -192,6 +192,7 @@ class TestDeliveryNote(unittest.TestCase): serial_no = frappe.get_doc({ "doctype": "Serial No", "item_code": "_Test Serialized Item With Series", + "company": "Wind Power LLC", "serial_no": make_autoname("SR", "Serial No") }) serial_no.save() @@ -585,32 +586,6 @@ class TestDeliveryNote(unittest.TestCase): for i, gle in enumerate(gl_entries): self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center) - def test_delivery_note_without_cost_center(self): - cost_center = "Main - TCP1" - - company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company') - - set_valuation_method("_Test Item", "FIFO") - - make_stock_entry(target="Stores - TCP1", qty=5, basic_rate=100) - - stock_in_hand_account = get_inventory_account('_Test Company with perpetual inventory') - dn = create_delivery_note(company='_Test Company with perpetual inventory', warehouse='Stores - TCP1', cost_center = 'Main - TCP1', expense_account = "Cost of Goods Sold - TCP1") - - gl_entries = get_gl_entries("Delivery Note", dn.name) - - self.assertTrue(gl_entries) - expected_values = { - "Cost of Goods Sold - TCP1": { - "cost_center": cost_center - }, - stock_in_hand_account: { - "cost_center": None - } - } - for i, gle in enumerate(gl_entries): - self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center) - def test_make_sales_invoice_from_dn_for_returned_qty(self): from erpnext.selling.doctype.sales_order.sales_order import make_delivery_note from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice diff --git a/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py b/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py index eeea6da7a42..da2c97cb8c1 100644 --- a/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py +++ b/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py @@ -172,7 +172,7 @@ def create_delivery_trip(driver, address, contact=None): delivery_trip = frappe.get_doc({ "doctype": "Delivery Trip", - "company": erpnext.get_default_company(), + "company": erpnext.get_default_company() or 'Wind Power LLC', "departure_time": add_days(now_datetime(), 5), "driver": driver.name, "driver_address": address.name, diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 9d41b456865..a45fb304d13 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -432,30 +432,6 @@ class TestPurchaseReceipt(unittest.TestCase): for i, gle in enumerate(gl_entries): self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center) - def test_purchase_receipt_without_cost_center(self): - if not frappe.db.exists('Location', 'Test Location'): - frappe.get_doc({ - 'doctype': 'Location', - 'location_name': 'Test Location' - }).insert() - pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1") - - stock_in_hand_account = get_inventory_account(pr.company, pr.get("items")[0].warehouse) - gl_entries = get_gl_entries("Purchase Receipt", pr.name) - - self.assertTrue(gl_entries) - - expected_values = { - "Stock Received But Not Billed - TCP1": { - "cost_center": None - }, - stock_in_hand_account: { - "cost_center": None - } - } - for i, gle in enumerate(gl_entries): - self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center) - def test_make_purchase_invoice_from_pr_for_returned_qty(self): from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order, create_pr_against_po diff --git a/erpnext/stock/doctype/serial_no/test_serial_no.py b/erpnext/stock/doctype/serial_no/test_serial_no.py index ab061076e52..7b06ef96ce8 100644 --- a/erpnext/stock/doctype/serial_no/test_serial_no.py +++ b/erpnext/stock/doctype/serial_no/test_serial_no.py @@ -24,6 +24,7 @@ class TestSerialNo(unittest.TestCase): frappe.delete_doc_if_exists("Serial No", "_TCSER0001") sr = frappe.new_doc("Serial No") + sr.company = '_Test Company' sr.item_code = "_Test Serialized Item" sr.warehouse = "_Test Warehouse - _TC" sr.serial_no = "_TCSER0001" From 55410e03e41f8859faf18d6ac4af319a3a0ae6b4 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Wed, 15 Apr 2020 12:57:55 +0530 Subject: [PATCH 008/608] fix: tests depending on global default company --- erpnext/accounts/doctype/subscription/subscription.py | 4 ++++ .../clinical_procedure/test_clinical_procedure.py | 2 ++ erpnext/healthcare/doctype/patient/test_patient.py | 2 ++ .../test_patient_medical_record.py | 2 ++ erpnext/hr/doctype/employee/test_employee.py | 2 +- .../test_employee_tax_exemption_declaration.py | 8 ++++---- erpnext/hr/doctype/leave_period/test_leave_period.py | 4 ++-- erpnext/hr/doctype/payroll_entry/test_payroll_entry.py | 6 +++--- erpnext/hr/doctype/salary_slip/test_salary_slip.py | 10 +++++----- .../doctype/salary_structure/test_salary_structure.py | 4 ++-- 10 files changed, 27 insertions(+), 17 deletions(-) diff --git a/erpnext/accounts/doctype/subscription/subscription.py b/erpnext/accounts/doctype/subscription/subscription.py index 0933c7e8b84..fbdaf1bc95a 100644 --- a/erpnext/accounts/doctype/subscription/subscription.py +++ b/erpnext/accounts/doctype/subscription/subscription.py @@ -238,6 +238,10 @@ class Subscription(Document): Creates a `Sales Invoice`, submits it and returns it """ invoice = frappe.new_doc('Sales Invoice') + + if not invoice.company: + invoice.company = frappe.db.get_value('Global Defaults', None, 'default_company') + invoice.set_posting_time = 1 invoice.posting_date = self.current_invoice_start invoice.customer = self.customer diff --git a/erpnext/healthcare/doctype/clinical_procedure/test_clinical_procedure.py b/erpnext/healthcare/doctype/clinical_procedure/test_clinical_procedure.py index 207351ff209..100addc9ba2 100644 --- a/erpnext/healthcare/doctype/clinical_procedure/test_clinical_procedure.py +++ b/erpnext/healthcare/doctype/clinical_procedure/test_clinical_procedure.py @@ -18,6 +18,7 @@ class TestClinicalProcedure(unittest.TestCase): self.assertEquals(frappe.db.get_value('Item', procedure_template.item, 'disabled'), 1) def test_consumables(self): + frappe.db.set_value('Global Defaults', None, 'default_company', '_Test Company') patient, medical_department, practitioner = create_healthcare_docs() procedure_template = create_clinical_procedure_template() procedure_template.allow_stock_consumption = 1 @@ -38,6 +39,7 @@ class TestClinicalProcedure(unittest.TestCase): result = procedure.complete_procedure() # check consumption self.assertTrue(frappe.db.exists('Stock Entry', result)) + frappe.db.set_value('Global Defaults', None, 'default_company', None) def create_consumable(): diff --git a/erpnext/healthcare/doctype/patient/test_patient.py b/erpnext/healthcare/doctype/patient/test_patient.py index 9274b6f5e85..dfe61bd6c11 100644 --- a/erpnext/healthcare/doctype/patient/test_patient.py +++ b/erpnext/healthcare/doctype/patient/test_patient.py @@ -15,6 +15,7 @@ class TestPatient(unittest.TestCase): self.assertTrue(frappe.db.get_value('Patient', patient, 'customer')) def test_patient_registration(self): + frappe.db.set_value('Global Defaults', None, 'default_company', '_Test Company') frappe.db.sql("""delete from `tabPatient`""") settings = frappe.get_single('Healthcare Settings') settings.collect_registration_fee = 1 @@ -32,3 +33,4 @@ class TestPatient(unittest.TestCase): settings.collect_registration_fee = 0 settings.save() + frappe.db.set_value('Global Defaults', None, 'default_company', None) diff --git a/erpnext/healthcare/doctype/patient_medical_record/test_patient_medical_record.py b/erpnext/healthcare/doctype/patient_medical_record/test_patient_medical_record.py index e5a5e4c010d..4de90bc09f6 100644 --- a/erpnext/healthcare/doctype/patient_medical_record/test_patient_medical_record.py +++ b/erpnext/healthcare/doctype/patient_medical_record/test_patient_medical_record.py @@ -13,6 +13,7 @@ class TestPatientMedicalRecord(unittest.TestCase): frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 1) def test_medical_record(self): + frappe.db.set_value('Global Defaults', None, 'default_company', '_Test Company') patient, medical_department, practitioner = create_healthcare_docs() appointment = create_appointment(patient, practitioner, nowdate(), invoice=1) encounter = create_encounter(appointment) @@ -38,6 +39,7 @@ class TestPatientMedicalRecord(unittest.TestCase): # check for lab test medical_rec = frappe.db.exists('Patient Medical Record', {'status': 'Open', 'reference_name': lab_test.name}) self.assertTrue(medical_rec) + frappe.db.set_value('Global Defaults', None, 'default_company', None) def create_procedure(appointment): diff --git a/erpnext/hr/doctype/employee/test_employee.py b/erpnext/hr/doctype/employee/test_employee.py index eff382d973e..755393ea04d 100644 --- a/erpnext/hr/doctype/employee/test_employee.py +++ b/erpnext/hr/doctype/employee/test_employee.py @@ -55,7 +55,7 @@ def make_employee(user, company=None): "roles": [{"doctype": "Has Role", "role": "Employee"}] }).insert() - if not frappe.db.get_value("Employee", { "user_id": user, "company": company or erpnext.get_default_company() }): + if not frappe.db.get_value("Employee", { "user_id": user, "company": company or erpnext.get_default_company() or 'Wind Power LLC' }): employee = frappe.get_doc({ "doctype": "Employee", "naming_series": "EMP-", diff --git a/erpnext/hr/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py b/erpnext/hr/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py index d53f6a706d6..4dc77ccc514 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py +++ b/erpnext/hr/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py @@ -20,7 +20,7 @@ class TestEmployeeTaxExemptionDeclaration(unittest.TestCase): declaration = frappe.get_doc({ "doctype": "Employee Tax Exemption Declaration", "employee": frappe.get_value("Employee", {"user_id":"employee@taxexepmtion.com"}, "name"), - "company": erpnext.get_default_company(), + "company": erpnext.get_default_company() or 'Wind Power LLC', "payroll_period": "_Test Payroll Period", "declarations": [ dict(exemption_sub_category = "_Test Sub Category", @@ -37,7 +37,7 @@ class TestEmployeeTaxExemptionDeclaration(unittest.TestCase): declaration = frappe.get_doc({ "doctype": "Employee Tax Exemption Declaration", "employee": frappe.get_value("Employee", {"user_id":"employee@taxexepmtion.com"}, "name"), - "company": erpnext.get_default_company(), + "company": erpnext.get_default_company() or 'Wind Power LLC', "payroll_period": "_Test Payroll Period", "declarations": [ dict(exemption_sub_category = "_Test Sub Category", @@ -52,7 +52,7 @@ class TestEmployeeTaxExemptionDeclaration(unittest.TestCase): duplicate_declaration = frappe.get_doc({ "doctype": "Employee Tax Exemption Declaration", "employee": frappe.get_value("Employee", {"user_id":"employee@taxexepmtion.com"}, "name"), - "company": erpnext.get_default_company(), + "company": erpnext.get_default_company() or 'Wind Power LLC', "payroll_period": "_Test Payroll Period", "declarations": [ dict(exemption_sub_category = "_Test Sub Category", @@ -68,7 +68,7 @@ class TestEmployeeTaxExemptionDeclaration(unittest.TestCase): declaration = frappe.get_doc({ "doctype": "Employee Tax Exemption Declaration", "employee": frappe.get_value("Employee", {"user_id":"employee@taxexepmtion.com"}, "name"), - "company": erpnext.get_default_company(), + "company": erpnext.get_default_company() or 'Wind Power LLC', "payroll_period": "_Test Payroll Period", "declarations": [ dict(exemption_sub_category = "_Test Sub Category", diff --git a/erpnext/hr/doctype/leave_period/test_leave_period.py b/erpnext/hr/doctype/leave_period/test_leave_period.py index 1762cf917a2..06e5836738b 100644 --- a/erpnext/hr/doctype/leave_period/test_leave_period.py +++ b/erpnext/hr/doctype/leave_period/test_leave_period.py @@ -45,7 +45,7 @@ class TestLeavePeriod(unittest.TestCase): def create_leave_period(from_date, to_date, company=None): leave_period = frappe.db.get_value('Leave Period', - dict(company=company or erpnext.get_default_company(), + dict(company=company or erpnext.get_default_company() or 'Wind Power LLC', from_date=from_date, to_date=to_date, is_active=1), 'name') @@ -54,7 +54,7 @@ def create_leave_period(from_date, to_date, company=None): leave_period = frappe.get_doc({ "doctype": "Leave Period", - "company": company or erpnext.get_default_company(), + "company": company or erpnext.get_default_company() or 'Wind Power LLC', "from_date": from_date, "to_date": to_date, "is_active": 1 diff --git a/erpnext/hr/doctype/payroll_entry/test_payroll_entry.py b/erpnext/hr/doctype/payroll_entry/test_payroll_entry.py index 49671d5e224..52fb69f1a48 100644 --- a/erpnext/hr/doctype/payroll_entry/test_payroll_entry.py +++ b/erpnext/hr/doctype/payroll_entry/test_payroll_entry.py @@ -26,7 +26,7 @@ class TestPayrollEntry(unittest.TestCase): frappe.db.set_value("HR Settings", None, "email_salary_slip_to_employee", 0) def test_payroll_entry(self): # pylint: disable=no-self-use - company = erpnext.get_default_company() + company = erpnext.get_default_company() or 'Wind Power LLC' for data in frappe.get_all('Salary Component', fields = ["name"]): if not frappe.db.get_value('Salary Component Account', {'parent': data.name, 'company': company}, 'name'): @@ -109,7 +109,7 @@ def make_payroll_entry(**args): args = frappe._dict(args) payroll_entry = frappe.new_doc("Payroll Entry") - payroll_entry.company = args.company or erpnext.get_default_company() + payroll_entry.company = args.company or erpnext.get_default_company() or 'Wind Power LLC' payroll_entry.start_date = args.start_date or "2016-11-01" payroll_entry.end_date = args.end_date or "2016-11-30" payroll_entry.payment_account = get_payment_account() @@ -133,7 +133,7 @@ def make_payroll_entry(**args): def get_payment_account(): return frappe.get_value('Account', - {'account_type': 'Cash', 'company': erpnext.get_default_company(),'is_group':0}, "name") + {'account_type': 'Cash', 'company': erpnext.get_default_company() or 'Wind Power LLC', 'is_group':0}, "name") def make_holiday(holiday_list_name): if not frappe.db.exists('Holiday List', holiday_list_name): diff --git a/erpnext/hr/doctype/salary_slip/test_salary_slip.py b/erpnext/hr/doctype/salary_slip/test_salary_slip.py index cd73b94719c..ac91aec282e 100644 --- a/erpnext/hr/doctype/salary_slip/test_salary_slip.py +++ b/erpnext/hr/doctype/salary_slip/test_salary_slip.py @@ -26,7 +26,7 @@ class TestSalarySlip(unittest.TestCase): self.make_holiday_list() - frappe.db.set_value("Company", erpnext.get_default_company(), "default_holiday_list", "Salary Slip Test Holiday List") + frappe.db.set_value("Company", erpnext.get_default_company() or 'Wind Power LLC', "default_holiday_list", "Salary Slip Test Holiday List") frappe.db.set_value("HR Settings", None, "email_salary_slip_to_employee", 0) def tearDown(self): @@ -175,7 +175,7 @@ class TestSalarySlip(unittest.TestCase): self.assertEqual(ss.net_pay, (flt(ss.gross_pay) - (flt(ss.total_deduction) + flt(ss.total_loan_repayment)))) def test_payroll_frequency(self): - fiscal_year = get_fiscal_year(nowdate(), company=erpnext.get_default_company())[0] + fiscal_year = get_fiscal_year(nowdate(), company=erpnext.get_default_company() or 'Wind Power LLC')[0] month = "%02d" % getdate(nowdate()).month m = get_month_details(fiscal_year, month) @@ -295,7 +295,7 @@ class TestSalarySlip(unittest.TestCase): frappe.db.rollback() def make_holiday_list(self): - fiscal_year = get_fiscal_year(nowdate(), company=erpnext.get_default_company()) + fiscal_year = get_fiscal_year(nowdate(), company=erpnext.get_default_company() or 'Wind Power LLC') if not frappe.db.get_value("Holiday List", "Salary Slip Test Holiday List"): holiday_list = frappe.get_doc({ "doctype": "Holiday List", @@ -499,7 +499,7 @@ def create_exemption_declaration(employee, payroll_period): "doctype": "Employee Tax Exemption Declaration", "employee": employee, "payroll_period": payroll_period, - "company": erpnext.get_default_company() + "company": erpnext.get_default_company() or 'Wind Power LLC' }) declaration.append("declarations", { "exemption_sub_category": "_Test Sub Category", @@ -588,7 +588,7 @@ def create_additional_salary(employee, payroll_period, amount): frappe.get_doc({ "doctype": "Additional Salary", "employee": employee, - "company": erpnext.get_default_company(), + "company": erpnext.get_default_company() or 'Wind Power LLC', "salary_component": "Performance Bonus", "payroll_date": salary_date, "amount": amount, diff --git a/erpnext/hr/doctype/salary_structure/test_salary_structure.py b/erpnext/hr/doctype/salary_structure/test_salary_structure.py index e465d04f4d4..17087d14cf9 100644 --- a/erpnext/hr/doctype/salary_structure/test_salary_structure.py +++ b/erpnext/hr/doctype/salary_structure/test_salary_structure.py @@ -21,7 +21,7 @@ class TestSalaryStructure(unittest.TestCase): frappe.db.sql("delete from `tab%s`" % dt) self.make_holiday_list() - frappe.db.set_value("Company", erpnext.get_default_company(), "default_holiday_list", "Salary Structure Test Holiday List") + frappe.db.set_value("Company", erpnext.get_default_company() or 'Wind Power LLC', "default_holiday_list", "Salary Structure Test Holiday List") make_employee("test_employee@salary.com") make_employee("test_employee_2@salary.com") @@ -145,7 +145,7 @@ def create_salary_structure_assignment(employee, salary_structure, from_date=Non salary_structure_assignment.variable = 5000 salary_structure_assignment.from_date = from_date or add_months(nowdate(), -1) salary_structure_assignment.salary_structure = salary_structure - salary_structure_assignment.company = company or erpnext.get_default_company() + salary_structure_assignment.company = company or erpnext.get_default_company() or 'Wind Power LLC' salary_structure_assignment.save(ignore_permissions=True) salary_structure_assignment.submit() return salary_structure_assignment From d881dc47840234b37efd53ee3c45cef310115505 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Fri, 24 Apr 2020 14:33:34 +0530 Subject: [PATCH 009/608] feat: metrics for Issue --- erpnext/support/doctype/issue/issue.json | 33 ++++++++++++++-- erpnext/support/doctype/issue/issue.py | 49 +++++++++++++++++++++++- 2 files changed, 78 insertions(+), 4 deletions(-) diff --git a/erpnext/support/doctype/issue/issue.json b/erpnext/support/doctype/issue/issue.json index c12cef4a5f3..79729f2902c 100644 --- a/erpnext/support/doctype/issue/issue.json +++ b/erpnext/support/doctype/issue/issue.json @@ -34,6 +34,8 @@ "response", "mins_to_first_response", "first_responded_on", + "column_break_26", + "avg_response_time", "additional_info", "lead", "contact", @@ -50,7 +52,9 @@ "resolution_date", "content_type", "attachment", - "via_customer_portal" + "via_customer_portal", + "operational_time", + "user_operational_time" ], "fields": [ { @@ -362,12 +366,35 @@ "label": "Issue Split From", "options": "Issue", "read_only": 1 + }, + { + "fieldname": "column_break_26", + "fieldtype": "Column Break" + }, + { + "bold": 1, + "fieldname": "avg_response_time", + "fieldtype": "Time", + "label": "Average Response Time", + "read_only": 1 + }, + { + "fieldname": "operational_time", + "fieldtype": "Time", + "label": "Operational Time", + "read_only": 1 + }, + { + "fieldname": "user_operational_time", + "fieldtype": "Time", + "label": "User Operational Time", + "read_only": 1 } ], "icon": "fa fa-ticket", "idx": 7, "links": [], - "modified": "2020-03-13 02:19:49.477928", + "modified": "2020-04-24 09:58:13.499635", "modified_by": "Administrator", "module": "Support", "name": "Issue", @@ -395,4 +422,4 @@ "title_field": "subject", "track_changes": 1, "track_seen": 1 -} +} \ No newline at end of file diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index 117267f1a42..62b87ff5521 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -7,7 +7,7 @@ import json from frappe import _ from frappe import utils from frappe.model.document import Document -from frappe.utils import now, time_diff_in_hours, now_datetime, getdate, get_weekdays, add_to_date, today, get_time, get_datetime +from frappe.utils import now, time_diff_in_hours, now_datetime, getdate, get_weekdays, add_to_date, today, get_time, get_datetime, time_diff_in_seconds, time_diff from datetime import datetime, timedelta from frappe.model.mapper import get_mapped_doc from frappe.utils.user import is_website_user @@ -63,6 +63,9 @@ class Issue(Document): self.resolution_date = frappe.flags.current_time or now_datetime() if frappe.db.get_value("Issue", self.name, "agreement_fulfilled") == "Ongoing": set_service_level_agreement_variance(issue=self.name) + set_average_response_time(issue=self) + set_operational_time(issue=self) + set_user_operational_time(issue=self) self.update_agreement_status() if self.status=="Open" and status !="Open": @@ -311,6 +314,50 @@ def set_service_level_agreement_variance(issue=None): if variance < 0: frappe.db.set_value(dt="Issue", dn=doc.name, field="agreement_fulfilled", val="Failed", update_modified=False) +def set_average_response_time(issue): + communications = frappe.get_list("Communication", filters={ + "reference_doctype": issue.doctype, + "reference_name": issue.name + }, + fields=["sent_or_received", "name", "creation"], + order_by="creation" + ) + + response_times = [] + for i in range(len(communications)-1): + if communications[i].sent_or_received == "Sent" and communications[i-1].sent_or_received == "Received": + response_time = time_diff_in_seconds(communications[i].creation, communications[i-1].creation) + if response_time > 0: + response_times.append(response_time) + avg_response_time = sum(response_times) / len(response_times) + avg_response_time = str(timedelta(seconds=avg_response_time)).split(".")[0] + issue.db_set('avg_response_time', avg_response_time) + +def set_operational_time(issue): + operational_time = time_diff(now_datetime(), issue.creation) + issue.db_set('operational_time', str(operational_time).split(".")[0]) + +def set_user_operational_time(issue): + communications = frappe.get_list("Communication", filters={ + "reference_doctype": issue.doctype, + "reference_name": issue.name + }, + fields=["sent_or_received", "name", "creation"], + order_by="creation" + ) + + pending_time = [] + for i in range(len(communications)-1): + if communications[i].sent_or_received == "Received" and communications[i-1].sent_or_received == "Sent": + wait_time = time_diff_in_seconds(communications[i].creation, communications[i-1].creation) + if wait_time > 0: + pending_time.append(wait_time) + total_pending_time = timedelta(seconds=sum(pending_time)) + operational_time = frappe.db.get_value('Issue', issue.name, 'operational_time') + user_operational_time = time_diff(operational_time, total_pending_time) + issue.db_set('user_operational_time', str(user_operational_time)) + + def get_list_context(context=None): return { "title": _("Issues"), From defeb737471efab0100df5682777572ed0c4e2ee Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 24 Apr 2020 04:07:14 +0530 Subject: [PATCH 010/608] feat: Add Failed error logs fix: set defaults --- .../tally_migration/tally_migration.json | 47 ++++++++++++++----- 1 file changed, 36 insertions(+), 11 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.json b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.json index dc6f093ac9d..005c8a98c6b 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.json +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.json @@ -28,14 +28,17 @@ "vouchers", "accounts_section", "default_warehouse", - "round_off_account", + "default_round_off_account", "column_break_21", "default_cost_center", "day_book_section", "day_book_data", "column_break_27", "is_day_book_data_processed", - "is_day_book_data_imported" + "is_day_book_data_imported", + "import_log_section", + "failed_import_log", + "failed_import_preview" ], "fields": [ { @@ -57,6 +60,7 @@ "fieldname": "tally_creditors_account", "fieldtype": "Data", "label": "Tally Creditors Account", + "read_only_depends_on": "eval:doc.is_master_data_processed==1", "reqd": 1 }, { @@ -69,6 +73,7 @@ "fieldname": "tally_debtors_account", "fieldtype": "Data", "label": "Tally Debtors Account", + "read_only_depends_on": "eval:doc.is_master_data_processed==1", "reqd": 1 }, { @@ -92,7 +97,7 @@ "fieldname": "erpnext_company", "fieldtype": "Data", "label": "ERPNext Company", - "read_only_depends_on": "eval:doc.is_master_data_processed == 1" + "read_only_depends_on": "eval:doc.is_master_data_processed==1" }, { "fieldname": "processed_files_section", @@ -136,6 +141,7 @@ }, { "depends_on": "is_master_data_imported", + "description": "The accounts are set by the system automatically but do confirm these defaults", "fieldname": "accounts_section", "fieldtype": "Section Break", "label": "Accounts" @@ -146,12 +152,6 @@ "label": "Default Warehouse", "options": "Warehouse" }, - { - "fieldname": "round_off_account", - "fieldtype": "Link", - "label": "Round Off Account", - "options": "Account" - }, { "fieldname": "column_break_21", "fieldtype": "Column Break" @@ -212,11 +212,36 @@ "fieldname": "default_uom", "fieldtype": "Link", "label": "Default UOM", - "options": "UOM" + "options": "UOM", + "read_only_depends_on": "eval:doc.is_master_data_imported==1" + }, + { + "default": "[]", + "fieldname": "failed_import_log", + "fieldtype": "Code", + "hidden": 1, + "label": "Failed Import Log", + "options": "JSON" + }, + { + "fieldname": "failed_import_preview", + "fieldtype": "HTML", + "label": "Failed Import Log" + }, + { + "fieldname": "import_log_section", + "fieldtype": "Section Break", + "label": "Import Log" + }, + { + "fieldname": "default_round_off_account", + "fieldtype": "Link", + "label": "Default Round Off Account", + "options": "Account" } ], "links": [], - "modified": "2020-04-16 13:03:28.894919", + "modified": "2020-04-22 16:01:13.718842", "modified_by": "Administrator", "module": "ERPNext Integrations", "name": "Tally Migration", From 2de4a5a7c884080db1ac03acf40262ca887f8913 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Mon, 27 Apr 2020 11:34:00 +0530 Subject: [PATCH 011/608] feat: Error logging via doc fix: Handle duplicate company and COA feat: Error logging via doc feat: fetch defaults for accounts --- .../tally_migration/tally_migration.py | 99 ++++++++++++------- 1 file changed, 65 insertions(+), 34 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py index 13474e19ee1..32d11681fb0 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py @@ -6,6 +6,7 @@ from __future__ import unicode_literals import json import re +import sys import traceback import zipfile from decimal import Decimal @@ -15,13 +16,14 @@ from bs4 import BeautifulSoup as bs import frappe from erpnext import encode_company_abbr from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import create_charts +from erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer import unset_existing_data + from frappe import _ from frappe.custom.doctype.custom_field.custom_field import create_custom_field from frappe.model.document import Document from frappe.model.naming import getseries, revert_series_if_last from frappe.utils.data import format_datetime - PRIMARY_ACCOUNT = "Primary" VOUCHER_CHUNK_SIZE = 500 @@ -65,9 +67,17 @@ class TallyMigration(Document): "attached_to_name": self.name, "content": json.dumps(value), "is_private": True - }).insert() + }) + try: + f.insert() + except frappe.DuplicateEntryError: + pass setattr(self, key, f.file_url) + def set_account_defaults(self): + self.default_cost_center, self.default_round_off_account = frappe.db.get_value("Company", self.erpnext_company, ["cost_center", "round_off_account"]) + self.default_warehouse = frappe.db.get_value("Stock Settings", "Stock Settings", "default_warehouse") + def _process_master_data(self): def get_company_name(collection): return collection.find_all("REMOTECMPINFO.LIST")[0].REMOTECMPNAME.string.strip() @@ -84,7 +94,11 @@ class TallyMigration(Document): children, parents = get_children_and_parent_dict(accounts) group_set = [acc[1] for acc in accounts if acc[2]] children, customers, suppliers = remove_parties(parents, children, group_set) - coa = traverse({}, children, roots, roots, group_set) + + try: + coa = traverse({}, children, roots, roots, group_set) + except RecursionError: + self.log() for account in coa: coa[account]["root_type"] = root_type_map[account] @@ -242,12 +256,18 @@ class TallyMigration(Document): def create_company_and_coa(coa_file_url): coa_file = frappe.get_doc("File", {"file_url": coa_file_url}) frappe.local.flags.ignore_chart_of_accounts = True - company = frappe.get_doc({ - "doctype": "Company", - "company_name": self.erpnext_company, - "default_currency": "INR", - "enable_perpetual_inventory": 0, - }).insert() + + try: + company = frappe.get_doc({ + "doctype": "Company", + "company_name": self.erpnext_company, + "default_currency": "INR", + "enable_perpetual_inventory": 0, + }).insert() + except frappe.DuplicateEntryError: + company = frappe.get_doc("Company", self.erpnext_company) + unset_existing_data(self.erpnext_company) + frappe.local.flags.ignore_chart_of_accounts = False create_charts(company.name, custom_chart=json.loads(coa_file.get_content())) company.create_default_warehouses() @@ -256,36 +276,35 @@ class TallyMigration(Document): parties_file = frappe.get_doc("File", {"file_url": parties_file_url}) for party in json.loads(parties_file.get_content()): try: - frappe.get_doc(party).insert() + party_doc = frappe.get_doc(party) + party_doc.insert() except: - self.log(party) + self.log(party_doc) addresses_file = frappe.get_doc("File", {"file_url": addresses_file_url}) for address in json.loads(addresses_file.get_content()): try: - frappe.get_doc(address).insert(ignore_mandatory=True) + address_doc = frappe.get_doc(address) + address_doc.insert(ignore_mandatory=True) except: - try: - gstin = address.pop("gstin", None) - frappe.get_doc(address).insert(ignore_mandatory=True) - self.log({"address": address, "message": "Invalid GSTIN: {}. Address was created without GSTIN".format(gstin)}) - except: - self.log(address) + self.log(address_doc) def create_items_uoms(items_file_url, uoms_file_url): uoms_file = frappe.get_doc("File", {"file_url": uoms_file_url}) for uom in json.loads(uoms_file.get_content()): if not frappe.db.exists(uom): try: - frappe.get_doc(uom).insert() + uom_doc = frappe.get_doc(uom) + uom_doc.insert() except: - self.log(uom) + self.log(uom_doc) items_file = frappe.get_doc("File", {"file_url": items_file_url}) for item in json.loads(items_file.get_content()): try: - frappe.get_doc(item).insert() + item_doc = frappe.get_doc(item) + item_doc.insert() except: - self.log(item) + self.log(item_doc) try: self.publish("Import Master Data", _("Creating Company and Importing Chart of Accounts"), 1, 4) @@ -299,6 +318,7 @@ class TallyMigration(Document): self.publish("Import Master Data", _("Done"), 4, 4) + self.set_account_defaults() self.is_master_data_imported = 1 except: @@ -468,13 +488,13 @@ class TallyMigration(Document): oldest_year = new_year def create_custom_fields(doctypes): + df = { + "fieldtype": "Data", + "fieldname": "tally_guid", + "read_only": 1, + "label": "Tally GUID" + } for doctype in doctypes: - df = { - "fieldtype": "Data", - "fieldname": "tally_guid", - "read_only": 1, - "label": "Tally GUID" - } create_custom_field(doctype, df) def create_price_list(): @@ -521,11 +541,12 @@ class TallyMigration(Document): for index, voucher in enumerate(chunk, start=start): try: - doc = frappe.get_doc(voucher).insert() - doc.submit() + voucher_doc = frappe.get_doc(voucher) + voucher_doc.insert() + voucher_doc.submit() self.publish("Importing Vouchers", _("{} of {}").format(index, total), index, total) except: - self.log(voucher) + self.log(voucher_doc) if is_last: self.status = "" @@ -551,9 +572,19 @@ class TallyMigration(Document): frappe.enqueue_doc(self.doctype, self.name, "_import_day_book_data", queue="long", timeout=3600) def log(self, data=None): - data = data or self.status - message = "\n".join(["Data:", json.dumps(data, default=str, indent=4), "--" * 50, "\nException:", traceback.format_exc()]) - return frappe.log_error(title="Tally Migration Error", message=message) + if isinstance(data, frappe.model.document.Document): + if sys.exc_info()[1].__class__ != frappe.DuplicateEntryError: + failed_import_log = json.loads(self.failed_import_log) + failed_import_log.append({ + "doc": data.as_dict(), + "exc": traceback.format_exc() + }) + self.failed_import_log = json.dumps(failed_import_log) + self.save() + else: + data = data or self.status + message = "\n".join(["Data:", json.dumps(data, default=str, indent=4), "--" * 50, "\nException:", traceback.format_exc()]) + return frappe.log_error(title="Tally Migration Error", message=message) def set_status(self, status=""): self.status = status From 38c677689dd54d91e0255700d94a68b0e92b3c1e Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 23 Apr 2020 14:07:14 +0530 Subject: [PATCH 012/608] fix: set default round off acount --- .../doctype/tally_migration/tally_migration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py index 32d11681fb0..5a5759160b5 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py @@ -510,7 +510,7 @@ class TallyMigration(Document): try: frappe.db.set_value("Account", encode_company_abbr(self.tally_creditors_account, self.erpnext_company), "account_type", "Payable") frappe.db.set_value("Account", encode_company_abbr(self.tally_debtors_account, self.erpnext_company), "account_type", "Receivable") - frappe.db.set_value("Company", self.erpnext_company, "round_off_account", self.round_off_account) + frappe.db.set_value("Company", self.erpnext_company, "round_off_account", self.default_round_off_account) vouchers_file = frappe.get_doc("File", {"file_url": self.vouchers}) vouchers = json.loads(vouchers_file.get_content()) From 82e7a9de2856a27ed497d7559fe775524a1c54e0 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Mon, 27 Apr 2020 13:12:38 +0530 Subject: [PATCH 013/608] feat: error log handling fix: toggle required accounts --- .../tally_migration/tally_migration.js | 102 +++++++++++++++++- 1 file changed, 101 insertions(+), 1 deletion(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js index d84c8234efa..d06fe537ad1 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js @@ -1,7 +1,7 @@ // Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Tally Migration', { +frappe.ui.form.on("Tally Migration", { onload: function (frm) { let reload_status = true; frappe.realtime.on("tally_migration_progress_update", function (data) { @@ -36,6 +36,10 @@ frappe.ui.form.on('Tally Migration', { }); }, refresh: function (frm) { + frm.trigger("show_import_log"); + ["default_round_off_account", "default_warehouse", "default_cost_center"].forEach(account => { + frm.toggle_reqd(account, frm.doc.is_master_data_imported === 1) + }) if (frm.doc.master_data && !frm.doc.is_master_data_imported) { if (frm.doc.is_master_data_processed) { if (frm.doc.status != "Importing Master Data") { @@ -71,5 +75,101 @@ frappe.ui.form.on('Tally Migration', { frm.reload_doc(); } ); + }, + show_import_log(frm) { + let index = 0; + let import_log = JSON.parse(frm.doc.failed_import_log || "[]"); + let logs = import_log.slice(0, 20); + let hidden_logs = import_log.slice(20); + + frm.toggle_display("import_log_section", logs.length > 0); + + + const getError = (traceback) => { + let exc_error_idx = traceback.trim().lastIndexOf("\n") + 1 + let error_line = traceback.substr(exc_error_idx) + let split_str_idx = (error_line.indexOf(':') > 0) ? error_line.indexOf(':') + 1 : 0; + + return error_line.slice(split_str_idx).trim(); + } + + const cleanDoc = (obj) => { + let temp = obj; + $.each(temp, function(key, value){ + if (value === "" || value === null){ + delete obj[key]; + } else if (Object.prototype.toString.call(value) === '[object Object]') { + cleanDoc(value); + } else if ($.isArray(value)) { + $.each(value, function (k,v) { cleanDoc(v); }); + } + }); + return temp; + }; + + let rows = logs + .map(({ doc, exc }) => { + let id = frappe.dom.get_unique_id(); + let traceback = exc; + + let error_message = getError(traceback); + index++; + + let html = ` + +
+
+
${traceback}
+
+
`; + + let show_doc = ` + +
+
+
${JSON.stringify(cleanDoc(doc), null, 1)}
+
+
`; + + let create_button = ` + ` + + return ` + ${index} + +
${doc.doctype}
+ + +
${error_message}
+
${html}
+
${show_doc}
+ + +
${create_button}
+ + `; + }) + .join(""); + + frm.get_field("failed_import_preview").$wrapper.html(` + + + + + + + + ${rows} + + + +
${__("#")}${__("DocType")}${__("Error Message")}${__("Create")}
And ${hidden_logs.length} more others
+ `); } }); From 0805307514a7c9cafa6af9d6b5e0a490d42c5800 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Mon, 27 Apr 2020 13:42:00 +0530 Subject: [PATCH 014/608] perf: reduce failed_error_log size --- .../doctype/tally_migration/tally_migration.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py index 5a5759160b5..46382c1e8f5 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py @@ -575,11 +575,14 @@ class TallyMigration(Document): if isinstance(data, frappe.model.document.Document): if sys.exc_info()[1].__class__ != frappe.DuplicateEntryError: failed_import_log = json.loads(self.failed_import_log) + doc = data.as_dict() + doc_fields = { x.fieldname for x in frappe.get_doc("DocType", doc.doctype).fields } + stripped_doc = { k: v for k, v in doc.items() if k in doc_fields } failed_import_log.append({ - "doc": data.as_dict(), + "doc": stripped_doc, "exc": traceback.format_exc() }) - self.failed_import_log = json.dumps(failed_import_log) + self.failed_import_log = json.dumps(failed_import_log, separators=(',', ':')) self.save() else: data = data or self.status From 2c3f1677f8f7f2cd19bd97a3460492366f201292 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 28 Apr 2020 14:11:58 +0530 Subject: [PATCH 015/608] fix: commonify SLA and Service Level Doctypes --- .../service_level_agreement.js | 25 +----- .../service_level_agreement.json | 44 ++++----- .../service_level_agreement.py | 89 ++++++++++++++++++- .../service_level_priority.json | 23 ++--- .../support_settings/support_settings.json | 4 +- 5 files changed, 122 insertions(+), 63 deletions(-) diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.js b/erpnext/support/doctype/service_level_agreement/service_level_agreement.js index 1d486f48346..7aeaae48bab 100644 --- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.js +++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.js @@ -2,28 +2,5 @@ // For license information, please see license.txt frappe.ui.form.on('Service Level Agreement', { - service_level: function(frm) { - frm.fields_dict.support_and_resolution.grid.remove_all(); - frappe.call({ - "method": "frappe.client.get", - args: { - doctype: "Service Level", - name: frm.doc.service_level - }, - callback: function(data){ - let count = Math.max(data.message.priorities.length, data.message.support_and_resolution.length); - let i = 0; - while (i < count){ - if (data.message.priorities[i]) { - frm.add_child("priorities", data.message.priorities[i]); - } - if (data.message.support_and_resolution[i]) { - frm.add_child("support_and_resolution", data.message.support_and_resolution[i]); - } - i++; - } - frm.refresh(); - } - }); - }, + }); diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.json b/erpnext/support/doctype/service_level_agreement/service_level_agreement.json index 9a83ca7ac04..3725e15a549 100644 --- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.json +++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.json @@ -1,4 +1,5 @@ { + "actions": [], "autoname": "format:SLA-{service_level}-{####}", "creation": "2018-12-26 21:08:15.448812", "doctype": "DocType", @@ -6,12 +7,12 @@ "engine": "InnoDB", "field_order": [ "enable", + "section_break_2", "service_level", "default_service_level_agreement", - "holiday_list", "column_break_2", "employee_group", - "default_priority", + "holiday_list", "entity_section", "entity_type", "column_break_10", @@ -27,43 +28,31 @@ "support_and_resolution" ], "fields": [ - { - "default": "0", - "depends_on": "eval: !doc.customer;", - "fieldname": "default_service_level_agreement", - "fieldtype": "Check", - "label": "Default Service Level Agreement" - }, { "fieldname": "service_level", - "fieldtype": "Link", + "fieldtype": "Data", "in_list_view": 1, "in_standard_filter": 1, "label": "Service Level", - "options": "Service Level", "reqd": 1 }, { - "fetch_from": "service_level.holiday_list", "fieldname": "holiday_list", "fieldtype": "Link", "label": "Holiday List", - "options": "Holiday List", - "read_only": 1 + "options": "Holiday List" }, { "fieldname": "column_break_2", "fieldtype": "Column Break" }, { - "fetch_from": "service_level.employee_group", "fieldname": "employee_group", "fieldtype": "Link", "in_list_view": 1, "in_standard_filter": 1, "label": "Employee Group", - "options": "Employee Group", - "read_only": 1 + "options": "Employee Group" }, { "fieldname": "agreement_details_section", @@ -111,14 +100,6 @@ "label": "Priorities", "options": "Service Level Priority" }, - { - "fetch_from": "service_level.default_priority", - "fieldname": "default_priority", - "fieldtype": "Link", - "label": "Default Priority", - "options": "Issue Priority", - "read_only": 1 - }, { "default": "1", "fieldname": "active", @@ -156,9 +137,20 @@ "fieldname": "enable", "fieldtype": "Check", "label": "Enable" + }, + { + "fieldname": "section_break_2", + "fieldtype": "Section Break" + }, + { + "default": "0", + "fieldname": "default_service_level_agreement", + "fieldtype": "Check", + "label": "Default Service Level Agreement" } ], - "modified": "2019-07-09 17:22:16.402939", + "links": [], + "modified": "2020-04-28 14:10:18.767202", "modified_by": "Administrator", "module": "Support", "name": "Service Level Agreement", diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py index a399c58b168..9fa0e238f96 100644 --- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py +++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py @@ -6,11 +6,85 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document from frappe import _ -from frappe.utils import getdate +from frappe.utils import getdate, get_weekdays +from datetime import datetime class ServiceLevelAgreement(Document): def validate(self): + self.validate_doc() + self.check_priorities() + self.check_support_and_resolution() + + def check_priorities(self): + default_priority = [] + priorities = [] + + for priority in self.priorities: + # Check if response and resolution time is set for every priority + if not (priority.response_time or priority.resolution_time): + frappe.throw(_("Set Response Time and Resolution for Priority {0} at index {1}.").format(priority.priority, priority.idx)) + + priorities.append(priority.priority) + + if priority.default_priority: + default_priority.append(priority.default_priority) + + if priority.response_time_period == "Hour": + response = priority.response_time * 0.0416667 + elif priority.response_time_period == "Day": + response = priority.response_time + elif priority.response_time_period == "Week": + response = priority.response_time * 7 + + if priority.resolution_time_period == "Hour": + resolution = priority.resolution_time * 0.0416667 + elif priority.resolution_time_period == "Day": + resolution = priority.resolution_time + elif priority.resolution_time_period == "Week": + resolution = priority.resolution_time * 7 + + if response > resolution: + frappe.throw(_("Response Time for {0} at index {1} can't be greater than Resolution Time.").format(priority.priority, priority.idx)) + + # Check if repeated priority + if not len(set(priorities)) == len(priorities): + repeated_priority = get_repeated(priorities) + frappe.throw(_("Priority {0} has been repeated.").format(repeated_priority)) + + # Check if repeated default priority + if not len(set(default_priority)) == len(default_priority): + frappe.throw(_("Select only one Priority as Default.")) + + # set default priority from priorities + try: + self.default_priority = next(d.priority for d in self.priorities if d.default_priority) + except Exception: + frappe.throw(_("Select a Default Priority.")) + + def check_support_and_resolution(self): + week = get_weekdays() + support_days = [] + + for support_and_resolution in self.support_and_resolution: + # Check if start and end time is set for every support day + if not (support_and_resolution.start_time or support_and_resolution.end_time): + frappe.throw(_("Set Start Time and End Time for \ + Support Day {0} at index {1}.".format(support_and_resolution.workday, support_and_resolution.idx))) + + support_days.append(support_and_resolution.workday) + support_and_resolution.idx = week.index(support_and_resolution.workday) + 1 + + if support_and_resolution.start_time >= support_and_resolution.end_time: + frappe.throw(_("Start Time can't be greater than or equal to End Time \ + for {0}.".format(support_and_resolution.workday))) + + # Check for repeated workday + if not len(set(support_days)) == len(support_days): + repeated_days = get_repeated(support_days) + frappe.throw(_("Workday {0} has been repeated.").format(repeated_days)) + + def validate_doc(self): if not frappe.db.get_single_value("Support Settings", "track_service_level_agreement"): frappe.throw(_("Service Level Agreement tracking is not enabled.")) @@ -110,4 +184,15 @@ def get_service_level_agreement_filters(name, customer=None): return { "priority": [priority.priority for priority in frappe.get_list("Service Level Priority", filters={"parent": name}, fields=["priority"])], "service_level_agreements": [d.name for d in frappe.get_list("Service Level Agreement", filters=filters, or_filters=or_filters)] - } \ No newline at end of file + } + +def get_repeated(values): + unique_list = [] + diff = [] + for value in values: + if value not in unique_list: + unique_list.append(str(value)) + else: + if value not in diff: + diff.append(str(value)) + return " ".join(diff) diff --git a/erpnext/support/doctype/service_level_priority/service_level_priority.json b/erpnext/support/doctype/service_level_priority/service_level_priority.json index cd87a1c1131..f56008eb4cf 100644 --- a/erpnext/support/doctype/service_level_priority/service_level_priority.json +++ b/erpnext/support/doctype/service_level_priority/service_level_priority.json @@ -1,4 +1,5 @@ { + "actions": [], "creation": "2019-05-04 05:54:03.658991", "doctype": "DocType", "editable_grid": 1, @@ -16,7 +17,7 @@ ], "fields": [ { - "columns": 2, + "columns": 1, "fieldname": "priority", "fieldtype": "Link", "in_list_view": 1, @@ -28,14 +29,7 @@ "fieldtype": "Section Break" }, { - "columns": 1, - "fieldname": "response_time", - "fieldtype": "Int", - "in_list_view": 1, - "label": "Response Time" - }, - { - "columns": 1, + "columns": 2, "fieldname": "resolution_time", "fieldtype": "Int", "in_list_view": 1, @@ -66,15 +60,24 @@ "fieldtype": "Column Break" }, { + "columns": 1, "default": "0", "fieldname": "default_priority", "fieldtype": "Check", "in_list_view": 1, "label": "Default Priority" + }, + { + "columns": 2, + "fieldname": "response_time", + "fieldtype": "Int", + "in_list_view": 1, + "label": "First Response Time" } ], "istable": 1, - "modified": "2019-05-21 06:54:42.674377", + "links": [], + "modified": "2020-04-24 14:50:13.774308", "modified_by": "Administrator", "module": "Support", "name": "Service Level Priority", diff --git a/erpnext/support/doctype/support_settings/support_settings.json b/erpnext/support/doctype/support_settings/support_settings.json index be9e0645915..3f52181a465 100644 --- a/erpnext/support/doctype/support_settings/support_settings.json +++ b/erpnext/support/doctype/support_settings/support_settings.json @@ -1,4 +1,5 @@ { + "actions": [], "creation": "2017-02-17 13:07:35.686409", "doctype": "DocType", "editable_grid": 1, @@ -128,7 +129,8 @@ } ], "issingle": 1, - "modified": "2019-07-10 22:52:39.663873", + "links": [], + "modified": "2020-04-28 14:11:15.117019", "modified_by": "Administrator", "module": "Support", "name": "Support Settings", From 1b5eea691cf166562efb1de019599da1d365aede Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Tue, 28 Apr 2020 15:51:40 +0530 Subject: [PATCH 016/608] feat: order failed import log by creation, handle rollbacks and commits --- .../doctype/tally_migration/tally_migration.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py index 46382c1e8f5..cbc6019c6b4 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py @@ -29,6 +29,11 @@ VOUCHER_CHUNK_SIZE = 500 class TallyMigration(Document): + def validate(self): + failed_import_log = json.loads(self.failed_import_log) + sorted_failed_import_log = sorted(failed_import_log, key=lambda row: row["doc"]["creation"]) + self.failed_import_log = json.dumps(sorted_failed_import_log) + def autoname(self): if not self.name: self.name = "Tally Migration on " + format_datetime(self.creation) @@ -320,9 +325,11 @@ class TallyMigration(Document): self.set_account_defaults() self.is_master_data_imported = 1 + frappe.db.commit() except: self.publish("Import Master Data", _("Process Failed"), -1, 5) + frappe.db.rollback() self.log() finally: @@ -343,7 +350,9 @@ class TallyMigration(Document): processed_voucher = function(voucher) if processed_voucher: vouchers.append(processed_voucher) + frappe.db.commit() except: + frappe.db.rollback() self.log(voucher) return vouchers @@ -545,7 +554,9 @@ class TallyMigration(Document): voucher_doc.insert() voucher_doc.submit() self.publish("Importing Vouchers", _("{} of {}").format(index, total), index, total) + frappe.db.commit() except: + frappe.db.rollback() self.log(voucher_doc) if is_last: @@ -576,14 +587,14 @@ class TallyMigration(Document): if sys.exc_info()[1].__class__ != frappe.DuplicateEntryError: failed_import_log = json.loads(self.failed_import_log) doc = data.as_dict() - doc_fields = { x.fieldname for x in frappe.get_doc("DocType", doc.doctype).fields } - stripped_doc = { k: v for k, v in doc.items() if k in doc_fields } failed_import_log.append({ - "doc": stripped_doc, + "doc": doc, "exc": traceback.format_exc() }) self.failed_import_log = json.dumps(failed_import_log, separators=(',', ':')) self.save() + frappe.db.commit() + else: data = data or self.status message = "\n".join(["Data:", json.dumps(data, default=str, indent=4), "--" * 50, "\nException:", traceback.format_exc()]) From 640636bbcd6f84c12c5ed0e74a3e257acb731f52 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Tue, 28 Apr 2020 16:01:59 +0530 Subject: [PATCH 017/608] feat: maintain logs of errors successfully fixed --- .../tally_migration/tally_migration.json | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.json b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.json index 005c8a98c6b..417d9437926 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.json +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.json @@ -38,7 +38,9 @@ "is_day_book_data_imported", "import_log_section", "failed_import_log", - "failed_import_preview" + "fixed_errors_log", + "failed_import_preview", + "fixed_error_log_preview" ], "fields": [ { @@ -220,7 +222,6 @@ "fieldname": "failed_import_log", "fieldtype": "Code", "hidden": 1, - "label": "Failed Import Log", "options": "JSON" }, { @@ -238,10 +239,22 @@ "fieldtype": "Link", "label": "Default Round Off Account", "options": "Account" + }, + { + "default": "[]", + "fieldname": "fixed_errors_log", + "fieldtype": "Code", + "hidden": 1, + "options": "JSON" + }, + { + "fieldname": "fixed_error_log_preview", + "fieldtype": "HTML", + "label": "Fixed Error Log" } ], "links": [], - "modified": "2020-04-22 16:01:13.718842", + "modified": "2020-04-28 00:29:18.039826", "modified_by": "Administrator", "module": "ERPNext Integrations", "name": "Tally Migration", From 2e1b531f4fce10bdaf74218fa7a0d260d67c1d7a Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Tue, 28 Apr 2020 16:02:50 +0530 Subject: [PATCH 018/608] feat: handle resolve and unresolved errors restructured and reused components --- .../tally_migration/tally_migration.js | 299 +++++++++++++----- 1 file changed, 217 insertions(+), 82 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js index d06fe537ad1..9235bab0683 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js @@ -35,8 +35,10 @@ frappe.ui.form.on("Tally Migration", { } }); }, + refresh: function (frm) { - frm.trigger("show_import_log"); + frm.trigger("show_logs_preview"); + ["default_round_off_account", "default_warehouse", "default_cost_center"].forEach(account => { frm.toggle_reqd(account, frm.doc.is_master_data_imported === 1) }) @@ -63,6 +65,7 @@ frappe.ui.form.on("Tally Migration", { } } }, + add_button: function (frm, label, method) { frm.add_custom_button( label, @@ -76,94 +79,30 @@ frappe.ui.form.on("Tally Migration", { } ); }, - show_import_log(frm) { - let index = 0; - let import_log = JSON.parse(frm.doc.failed_import_log || "[]"); - let logs = import_log.slice(0, 20); - let hidden_logs = import_log.slice(20); - frm.toggle_display("import_log_section", logs.length > 0); - - - const getError = (traceback) => { - let exc_error_idx = traceback.trim().lastIndexOf("\n") + 1 - let error_line = traceback.substr(exc_error_idx) - let split_str_idx = (error_line.indexOf(':') > 0) ? error_line.indexOf(':') + 1 : 0; - - return error_line.slice(split_str_idx).trim(); + render_html_table(frm, shown_logs, hidden_logs, field) { + if (shown_logs && shown_logs.length > 0) { + frm.toggle_display(field, true); + } else { + frm.toggle_display(field, false); + return } + let rows = get_html_rows(shown_logs, field); + let rows_head; - const cleanDoc = (obj) => { - let temp = obj; - $.each(temp, function(key, value){ - if (value === "" || value === null){ - delete obj[key]; - } else if (Object.prototype.toString.call(value) === '[object Object]') { - cleanDoc(value); - } else if ($.isArray(value)) { - $.each(value, function (k,v) { cleanDoc(v); }); - } - }); - return temp; - }; - - let rows = logs - .map(({ doc, exc }) => { - let id = frappe.dom.get_unique_id(); - let traceback = exc; - - let error_message = getError(traceback); - index++; - - let html = ` - -
-
-
${traceback}
-
-
`; - - let show_doc = ` - -
-
-
${JSON.stringify(cleanDoc(doc), null, 1)}
-
-
`; - - let create_button = ` - ` - - return ` - ${index} - -
${doc.doctype}
- - -
${error_message}
-
${html}
-
${show_doc}
- - -
${create_button}
- - `; - }) - .join(""); - - frm.get_field("failed_import_preview").$wrapper.html(` + if (field === "fixed_error_log_preview") { + rows_head = `${__("Meta Data")} + ${__("Unresolve")}` + } else { + rows_head = `${__("Error Message")} + ${__("Create")}` + } + frm.get_field(field).$wrapper.html(` - - + ${rows_head} ${rows} @@ -171,5 +110,201 @@ frappe.ui.form.on("Tally Migration", {
${__("#")} ${__("DocType")}${__("Error Message")}${__("Create")}
`); + }, + + show_error_summary() { + let summary = import_log.reduce((summary, row) => { + if (row.doc) { + if (summary[row.doc.doctype]) { + summary[row.doc.doctype] += 1; + } else { + summary[row.doc.doctype] = 1; + } + } + return summary + }, {}); + console.table(summary); + }, + + show_logs_preview(frm) { + let empty = "[]"; + let import_log = frm.doc.failed_import_log || empty; + let completed_log = frm.doc.fixed_errors_log || empty; + let render_section = !(import_log === completed_log && import_log === empty); + + frm.toggle_display("import_log_section", render_section); + + if (render_section) { + frm.trigger("show_errored_import_log"); + frm.trigger("show_fixed_errors_log"); + } + }, + + show_errored_import_log(frm) { + let import_log = JSON.parse(frm.doc.failed_import_log || "[]"); + + let logs = import_log.slice(0, 20); + let hidden_logs = import_log.slice(20); + + frm.events.render_html_table(frm, logs, hidden_logs, "failed_import_preview"); + }, + + show_fixed_errors_log(frm) { + let completed_log = JSON.parse(frm.doc.fixed_errors_log || "[]"); + let logs = completed_log.slice(0, 20); + let hidden_logs = completed_log.slice(20); + + frm.events.render_html_table(frm, logs, hidden_logs, "fixed_error_log_preview"); } }); + +const getError = (traceback) => { + /* Extracts the Error Message from the Python Traceback or Solved error */ + let is_multiline = traceback.trim().indexOf("\n") != -1; + let message; + + if (is_multiline) { + let exc_error_idx = traceback.trim().lastIndexOf("\n") + 1 + let error_line = traceback.substr(exc_error_idx) + let split_str_idx = (error_line.indexOf(':') > 0) ? error_line.indexOf(':') + 1 : 0; + message = error_line.slice(split_str_idx).trim(); + } else { + message = traceback; + } + + return message +} + +const cleanDoc = (obj) => { + /* Strips all null and empty values of your JSON object */ + let temp = obj; + $.each(temp, function(key, value){ + if (value === "" || value === null){ + delete obj[key]; + } else if (Object.prototype.toString.call(value) === '[object Object]') { + cleanDoc(value); + } else if ($.isArray(value)) { + $.each(value, function (k,v) { cleanDoc(v); }); + } + }); + return temp; +} + +window.unresolve = (document) => { + let frm = cur_frm; + let failed_log = JSON.parse(frm.doc.failed_import_log); + let fixed_log = JSON.parse(frm.doc.fixed_errors_log); + + let modified_fixed_log = fixed_log.filter(row => { + if (!frappe.utils.deep_equal(cleanDoc(row.doc), document)) { + return row + } + }); + + failed_log.push({ doc: document, exc: `Marked unresolved on ${Date()}` }); + + frm.doc.failed_import_log = JSON.stringify(failed_log); + frm.doc.fixed_errors_log = JSON.stringify(modified_fixed_log); + + // frm.trigger('show_logs_preview') + frm.dirty(); + frm.save(); +} + +window.create_new_doc = (doctype, document) => { + let frm = cur_frm; + let failed_log = JSON.parse(frm.doc.failed_import_log); + let fixed_log = JSON.parse(frm.doc.fixed_errors_log); + + let modified_failed_log = failed_log.filter(row => { + if (!frappe.utils.deep_equal(cleanDoc(row.doc), document)) { + return row + } + }); + fixed_log.push({ doc: document, exc: `Solved on ${Date()}` }); + + frm.doc.failed_import_log = JSON.stringify(modified_failed_log); + frm.doc.fixed_errors_log = JSON.stringify(fixed_log); + + // frm.trigger('show_logs_preview') + frm.dirty(); + frm.save(); + frappe.new_doc(doctype, document); +} + +const get_html_rows = (logs, field) => { + let index = 0; + let rows = logs + .map(({ doc, exc }) => { + let id = frappe.dom.get_unique_id(); + let traceback = exc; + + let error_message = getError(traceback); + index++; + + let show_traceback = ` + +
+
+
${traceback}
+
+
`; + + let show_doc = ` + +
+
+
${JSON.stringify(cleanDoc(doc), null, 1)}
+
+
`; + + let create_button = ` + ` + + let mark_as_unresolved = ` + ` + + if (field === "fixed_error_log_preview") { + return ` + ${index} + +
${doc.doctype}
+ + +
${error_message}
+
${show_traceback}
+
${show_doc}
+ + +
${mark_as_unresolved}
+ + `; + } else { + return ` + ${index} + +
${doc.doctype}
+ + +
${error_message}
+
${show_traceback}
+
${show_doc}
+ + +
${create_button}
+ + `; + } + }) + .join(""); + + return rows +} \ No newline at end of file From 27b7172fa4b888feaf47cf2875405079e83196ef Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Tue, 28 Apr 2020 16:32:15 +0530 Subject: [PATCH 019/608] fix: run summary logger style: indent fixes --- .../doctype/tally_migration/tally_migration.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js index 9235bab0683..bf8ae8c1c6c 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js @@ -133,7 +133,7 @@ frappe.ui.form.on("Tally Migration", { let render_section = !(import_log === completed_log && import_log === empty); frm.toggle_display("import_log_section", render_section); - + frm.trigger("show_error_summary"); if (render_section) { frm.trigger("show_errored_import_log"); frm.trigger("show_fixed_errors_log"); @@ -302,9 +302,8 @@ const get_html_rows = (logs, field) => {
${create_button}
`; - } - }) - .join(""); + } + }).join(""); return rows } \ No newline at end of file From 0d61b35b0bc26ce509801b747bdc2d1d349b7d02 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 29 Apr 2020 04:01:24 +0530 Subject: [PATCH 020/608] fix: table cleanups, updated summary block --- .../tally_migration/tally_migration.js | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js index bf8ae8c1c6c..9cc3c98a569 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js @@ -88,32 +88,38 @@ frappe.ui.form.on("Tally Migration", { return } let rows = get_html_rows(shown_logs, field); - let rows_head; + let rows_head, table_caption; + + let table_footer = (hidden_logs && (hidden_logs.length > 0)) ? ` + And ${hidden_logs.length} more others + `: ""; if (field === "fixed_error_log_preview") { rows_head = `${__("Meta Data")} ${__("Unresolve")}` + table_caption = "Resolved Issues" } else { rows_head = `${__("Error Message")} ${__("Create")}` + table_caption = "Error Log" } + frm.get_field(field).$wrapper.html(` + ${rows_head} ${rows} - - - + ${table_footer}
${table_caption}
${__("#")} ${__("DocType")}
And ${hidden_logs.length} more others
`); }, - show_error_summary() { - let summary = import_log.reduce((summary, row) => { + show_error_summary(frm) { + let summary = JSON.parse(frm.doc.failed_import_log).reduce((summary, row) => { if (row.doc) { if (summary[row.doc.doctype]) { summary[row.doc.doctype] += 1; @@ -280,7 +286,6 @@ const get_html_rows = (logs, field) => {
${error_message}
-
${show_traceback}
${show_doc}
@@ -302,8 +307,8 @@ const get_html_rows = (logs, field) => {
${create_button}
`; - } - }).join(""); + } + }).join(""); return rows } \ No newline at end of file From 0e3a16e2670eb68bfca31d702d7ce38d348902bd Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 29 Apr 2020 00:13:33 +0530 Subject: [PATCH 021/608] fix: set metrics duration --- erpnext/support/doctype/issue/issue.json | 18 +++---- erpnext/support/doctype/issue/issue.py | 61 +++++++++++++++++------- 2 files changed, 53 insertions(+), 26 deletions(-) diff --git a/erpnext/support/doctype/issue/issue.json b/erpnext/support/doctype/issue/issue.json index 79729f2902c..dfe0647cfa3 100644 --- a/erpnext/support/doctype/issue/issue.json +++ b/erpnext/support/doctype/issue/issue.json @@ -53,7 +53,7 @@ "content_type", "attachment", "via_customer_portal", - "operational_time", + "resolution_time", "user_operational_time" ], "fields": [ @@ -374,27 +374,27 @@ { "bold": 1, "fieldname": "avg_response_time", - "fieldtype": "Time", + "fieldtype": "Data", "label": "Average Response Time", "read_only": 1 }, { - "fieldname": "operational_time", - "fieldtype": "Time", - "label": "Operational Time", + "fieldname": "user_operational_time", + "fieldtype": "Data", + "label": "User Operational Time", "read_only": 1 }, { - "fieldname": "user_operational_time", - "fieldtype": "Time", - "label": "User Operational Time", + "fieldname": "resolution_time", + "fieldtype": "Data", + "label": "Resolution Time", "read_only": 1 } ], "icon": "fa fa-ticket", "idx": 7, "links": [], - "modified": "2020-04-24 09:58:13.499635", + "modified": "2020-04-28 23:42:28.576580", "modified_by": "Administrator", "module": "Support", "name": "Issue", diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index 62b87ff5521..027225942c7 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -59,12 +59,12 @@ class Issue(Document): if self.status!="Open" and status =="Open" and not self.first_responded_on: self.first_responded_on = frappe.flags.current_time or now_datetime() - if self.status=="Closed" and status !="Closed": + if self.status=="Closed": self.resolution_date = frappe.flags.current_time or now_datetime() if frappe.db.get_value("Issue", self.name, "agreement_fulfilled") == "Ongoing": set_service_level_agreement_variance(issue=self.name) set_average_response_time(issue=self) - set_operational_time(issue=self) + set_resolution_time(issue=self) set_user_operational_time(issue=self) self.update_agreement_status() @@ -323,19 +323,25 @@ def set_average_response_time(issue): order_by="creation" ) - response_times = [] - for i in range(len(communications)-1): - if communications[i].sent_or_received == "Sent" and communications[i-1].sent_or_received == "Received": - response_time = time_diff_in_seconds(communications[i].creation, communications[i-1].creation) - if response_time > 0: - response_times.append(response_time) - avg_response_time = sum(response_times) / len(response_times) - avg_response_time = str(timedelta(seconds=avg_response_time)).split(".")[0] - issue.db_set('avg_response_time', avg_response_time) + if len(communications): + response_times = [] + for i in range(len(communications)-1): + if communications[i].sent_or_received == "Sent" and communications[i-1].sent_or_received == "Received": + response_time = time_diff_in_seconds(communications[i].creation, communications[i-1].creation) + if response_time > 0: + response_times.append(response_time) + + avg_response_time = sum(response_times) / len(response_times) + avg_response_time = timedelta(seconds=avg_response_time) + duration = get_duration(avg_response_time) + issue.db_set('avg_response_time', duration) + + +def set_resolution_time(issue): + resolution_time = time_diff(now_datetime(), issue.creation) + duration = get_duration(resolution_time) + issue.db_set('resolution_time', duration) -def set_operational_time(issue): - operational_time = time_diff(now_datetime(), issue.creation) - issue.db_set('operational_time', str(operational_time).split(".")[0]) def set_user_operational_time(issue): communications = frappe.get_list("Communication", filters={ @@ -352,10 +358,31 @@ def set_user_operational_time(issue): wait_time = time_diff_in_seconds(communications[i].creation, communications[i-1].creation) if wait_time > 0: pending_time.append(wait_time) + total_pending_time = timedelta(seconds=sum(pending_time)) - operational_time = frappe.db.get_value('Issue', issue.name, 'operational_time') - user_operational_time = time_diff(operational_time, total_pending_time) - issue.db_set('user_operational_time', str(user_operational_time)) + resolution_time_in_secs = time_diff_in_seconds(now_datetime(), issue.creation) + resolution_time = timedelta(seconds=resolution_time_in_secs) + user_operational_time = resolution_time - total_pending_time + duration = get_duration(user_operational_time) + issue.db_set('user_operational_time', duration) + + +def get_duration(time): + days = time.days + seconds = time.seconds + hours = time.seconds // 3600 + mins = (time.seconds // 60) % 60 + duration = "" + if days: + duration += str(days) + " day" + duration += "s " if days > 1 else " " + if hours: + duration += str(hours) + " hour" + duration += "s " if hours > 1 else " " + if mins: + duration += str(mins) + " min" + duration += "s" if mins > 1 else "" + return duration def get_list_context(context=None): From 6b850b7e71e30c2513ae8f50152eb67788604eb0 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 29 Apr 2020 10:25:18 +0530 Subject: [PATCH 022/608] perf: reduce JS memory heap and maintain namespaces --- .../tally_migration/tally_migration.js | 53 +++++++++++-------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js index 9cc3c98a569..0cb4995ac95 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js @@ -1,6 +1,8 @@ // Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt +frappe.provide("erpnext.tally_migration"); + frappe.ui.form.on("Tally Migration", { onload: function (frm) { let reload_status = true; @@ -38,10 +40,13 @@ frappe.ui.form.on("Tally Migration", { refresh: function (frm) { frm.trigger("show_logs_preview"); + erpnext.tally_migration.failed_import_log = JSON.parse(frm.doc.failed_import_log); + erpnext.tally_migration.fixed_errors_log = JSON.parse(frm.doc.fixed_errors_log); ["default_round_off_account", "default_warehouse", "default_cost_center"].forEach(account => { frm.toggle_reqd(account, frm.doc.is_master_data_imported === 1) }) + if (frm.doc.master_data && !frm.doc.is_master_data_imported) { if (frm.doc.is_master_data_processed) { if (frm.doc.status != "Importing Master Data") { @@ -53,6 +58,7 @@ frappe.ui.form.on("Tally Migration", { } } } + if (frm.doc.day_book_data && !frm.doc.is_day_book_data_imported) { if (frm.doc.is_day_book_data_processed) { if (frm.doc.status != "Importing Day Book Data") { @@ -87,7 +93,7 @@ frappe.ui.form.on("Tally Migration", { frm.toggle_display(field, false); return } - let rows = get_html_rows(shown_logs, field); + let rows = erpnext.tally_migration.get_html_rows(shown_logs, field); let rows_head, table_caption; let table_footer = (hidden_logs && (hidden_logs.length > 0)) ? ` @@ -119,7 +125,7 @@ frappe.ui.form.on("Tally Migration", { }, show_error_summary(frm) { - let summary = JSON.parse(frm.doc.failed_import_log).reduce((summary, row) => { + let summary = erpnext.tally_migration.failed_import_log.reduce((summary, row) => { if (row.doc) { if (summary[row.doc.doctype]) { summary[row.doc.doctype] += 1; @@ -147,8 +153,7 @@ frappe.ui.form.on("Tally Migration", { }, show_errored_import_log(frm) { - let import_log = JSON.parse(frm.doc.failed_import_log || "[]"); - + let import_log = erpnext.tally_migration.failed_import_log; let logs = import_log.slice(0, 20); let hidden_logs = import_log.slice(20); @@ -156,7 +161,7 @@ frappe.ui.form.on("Tally Migration", { }, show_fixed_errors_log(frm) { - let completed_log = JSON.parse(frm.doc.fixed_errors_log || "[]"); + let completed_log = erpnext.tally_migration.fixed_errors_log; let logs = completed_log.slice(0, 20); let hidden_logs = completed_log.slice(20); @@ -164,7 +169,7 @@ frappe.ui.form.on("Tally Migration", { } }); -const getError = (traceback) => { +erpnext.tally_migration.getError = (traceback) => { /* Extracts the Error Message from the Python Traceback or Solved error */ let is_multiline = traceback.trim().indexOf("\n") != -1; let message; @@ -181,28 +186,28 @@ const getError = (traceback) => { return message } -const cleanDoc = (obj) => { +erpnext.tally_migration.cleanDoc = (obj) => { /* Strips all null and empty values of your JSON object */ let temp = obj; $.each(temp, function(key, value){ if (value === "" || value === null){ delete obj[key]; } else if (Object.prototype.toString.call(value) === '[object Object]') { - cleanDoc(value); + erpnext.tally_migration.cleanDoc(value); } else if ($.isArray(value)) { - $.each(value, function (k,v) { cleanDoc(v); }); + $.each(value, function (k,v) { erpnext.tally_migration.cleanDoc(v); }); } }); return temp; } -window.unresolve = (document) => { +erpnext.tally_migration.unresolve = (document) => { let frm = cur_frm; - let failed_log = JSON.parse(frm.doc.failed_import_log); - let fixed_log = JSON.parse(frm.doc.fixed_errors_log); + let failed_log = erpnext.tally_migration.failed_import_log; + let fixed_log = erpnext.tally_migration.fixed_errors_log; let modified_fixed_log = fixed_log.filter(row => { - if (!frappe.utils.deep_equal(cleanDoc(row.doc), document)) { + if (!frappe.utils.deep_equal(erpnext.tally_migration.cleanDoc(row.doc), document)) { return row } }); @@ -217,13 +222,13 @@ window.unresolve = (document) => { frm.save(); } -window.create_new_doc = (doctype, document) => { +erpnext.tally_migration.resolve = (document) => { let frm = cur_frm; - let failed_log = JSON.parse(frm.doc.failed_import_log); - let fixed_log = JSON.parse(frm.doc.fixed_errors_log); + let failed_log = erpnext.tally_migration.failed_import_log; + let fixed_log = erpnext.tally_migration.fixed_errors_log; let modified_failed_log = failed_log.filter(row => { - if (!frappe.utils.deep_equal(cleanDoc(row.doc), document)) { + if (!frappe.utils.deep_equal(erpnext.tally_migration.cleanDoc(row.doc), document)) { return row } }); @@ -235,17 +240,21 @@ window.create_new_doc = (doctype, document) => { // frm.trigger('show_logs_preview') frm.dirty(); frm.save(); +} + +erpnext.tally_migration.create_new_doc = (doctype, document) => { + erpnext.tally_migration.resolve(document); frappe.new_doc(doctype, document); } -const get_html_rows = (logs, field) => { +erpnext.tally_migration.get_html_rows = (logs, field) => { let index = 0; let rows = logs .map(({ doc, exc }) => { let id = frappe.dom.get_unique_id(); let traceback = exc; - let error_message = getError(traceback); + let error_message = erpnext.tally_migration.getError(traceback); index++; let show_traceback = ` @@ -264,17 +273,17 @@ const get_html_rows = (logs, field) => {
-
${JSON.stringify(cleanDoc(doc), null, 1)}
+
${JSON.stringify(erpnext.tally_migration.cleanDoc(doc), null, 1)}
`; let create_button = ` - ` let mark_as_unresolved = ` - ` From f4926a8205184e21641a1770b9096a8804f2cd64 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 29 Apr 2020 11:21:16 +0530 Subject: [PATCH 023/608] fix: toggle read only for accounts post daybook data processing style: code comments and updates --- .../doctype/tally_migration/tally_migration.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js index 0cb4995ac95..e94cca54c52 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js @@ -45,6 +45,7 @@ frappe.ui.form.on("Tally Migration", { ["default_round_off_account", "default_warehouse", "default_cost_center"].forEach(account => { frm.toggle_reqd(account, frm.doc.is_master_data_imported === 1) + frm.toggle_enable(account, frm.doc.is_day_book_data_processed != 1) }) if (frm.doc.master_data && !frm.doc.is_master_data_imported) { @@ -145,8 +146,8 @@ frappe.ui.form.on("Tally Migration", { let render_section = !(import_log === completed_log && import_log === empty); frm.toggle_display("import_log_section", render_section); - frm.trigger("show_error_summary"); if (render_section) { + frm.trigger("show_error_summary"); frm.trigger("show_errored_import_log"); frm.trigger("show_fixed_errors_log"); } @@ -202,6 +203,7 @@ erpnext.tally_migration.cleanDoc = (obj) => { } erpnext.tally_migration.unresolve = (document) => { + /* Mark document migration as unresolved ie. move to failed error log */ let frm = cur_frm; let failed_log = erpnext.tally_migration.failed_import_log; let fixed_log = erpnext.tally_migration.fixed_errors_log; @@ -217,12 +219,12 @@ erpnext.tally_migration.unresolve = (document) => { frm.doc.failed_import_log = JSON.stringify(failed_log); frm.doc.fixed_errors_log = JSON.stringify(modified_fixed_log); - // frm.trigger('show_logs_preview') frm.dirty(); frm.save(); } erpnext.tally_migration.resolve = (document) => { + /* Mark document migration as resolved ie. move to fixed error log */ let frm = cur_frm; let failed_log = erpnext.tally_migration.failed_import_log; let fixed_log = erpnext.tally_migration.fixed_errors_log; @@ -237,12 +239,12 @@ erpnext.tally_migration.resolve = (document) => { frm.doc.failed_import_log = JSON.stringify(modified_failed_log); frm.doc.fixed_errors_log = JSON.stringify(fixed_log); - // frm.trigger('show_logs_preview') frm.dirty(); frm.save(); } erpnext.tally_migration.create_new_doc = (doctype, document) => { + /* Mark as resolved and create new document */ erpnext.tally_migration.resolve(document); frappe.new_doc(doctype, document); } From 51305a028be5f7a766dd099f3d93fbea0c28d93d Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 30 Apr 2020 04:07:14 +0530 Subject: [PATCH 024/608] feat: prompt that company and COA will be overwritten --- .../doctype/tally_migration/tally_migration.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js index e94cca54c52..d9811b58dde 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js @@ -73,6 +73,16 @@ frappe.ui.form.on("Tally Migration", { } }, + erpnext_company: function (frm) { + frappe.db.exists("Company", frm.doc.erpnext_company).then(exists => { + if (exists) { + frappe.msgprint( + __("Company {0} already exists. Continuing will overwrite the Company and Chart of Accounts", [frm.doc.erpnext_company]), + ); + } + }); + }, + add_button: function (frm, label, method) { frm.add_custom_button( label, From af612ddb6de0c59381d36f1a1212188401990f05 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Fri, 1 May 2020 14:39:11 +0530 Subject: [PATCH 025/608] feat: maintain tally voucher numbers feat: create seperate customer and supplier entries for same party if case exists --- .../tally_migration/tally_migration.py | 34 ++++++++++++++----- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py index cbc6019c6b4..393c5d4b02b 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py @@ -145,14 +145,18 @@ class TallyMigration(Document): def remove_parties(parents, children, group_set): customers, suppliers = set(), set() for account in parents: + found = False if self.tally_creditors_account in parents[account]: - children.pop(account, None) + found = True if account not in group_set: suppliers.add(account) - elif self.tally_debtors_account in parents[account]: - children.pop(account, None) + if self.tally_debtors_account in parents[account]: + found = True if account not in group_set: customers.add(account) + if found: + children.pop(account, None) + return children, customers, suppliers def traverse(tree, children, accounts, roots, group_set): @@ -170,6 +174,7 @@ class TallyMigration(Document): parties, addresses = [], [] for account in collection.find_all("LEDGER"): party_type = None + links = [] if account.NAME.string.strip() in customers: party_type = "Customer" parties.append({ @@ -180,7 +185,9 @@ class TallyMigration(Document): "territory": "All Territories", "customer_type": "Individual", }) - elif account.NAME.string.strip() in suppliers: + links.append({"link_doctype": party_type, "link_name": account["NAME"]}) + + if account.NAME.string.strip() in suppliers: party_type = "Supplier" parties.append({ "doctype": party_type, @@ -189,6 +196,8 @@ class TallyMigration(Document): "supplier_group": "All Supplier Groups", "supplier_type": "Individual", }) + links.append({"link_doctype": party_type, "link_name": account["NAME"]}) + if party_type: address = "\n".join([a.string.strip() for a in account.find_all("ADDRESS")]) addresses.append({ @@ -202,7 +211,7 @@ class TallyMigration(Document): "mobile": account.LEDGERPHONE.string.strip() if account.LEDGERPHONE else None, "phone": account.LEDGERPHONE.string.strip() if account.LEDGERPHONE else None, "gstin": account.PARTYGSTIN.string.strip() if account.PARTYGSTIN else None, - "links": [{"link_doctype": party_type, "link_name": account["NAME"]}], + "links": links }) return parties, addresses @@ -378,6 +387,7 @@ class TallyMigration(Document): journal_entry = { "doctype": "Journal Entry", "tally_guid": voucher.GUID.string.strip(), + "tally_voucher_no": voucher.VOUCHERNUMBER.string.strip() if voucher.VOUCHERNUMBER else "", "posting_date": voucher.DATE.string.strip(), "company": self.erpnext_company, "accounts": accounts, @@ -406,6 +416,7 @@ class TallyMigration(Document): "doctype": doctype, party_field: voucher.PARTYNAME.string.strip(), "tally_guid": voucher.GUID.string.strip(), + "tally_voucher_no": voucher.VOUCHERNUMBER.string.strip() if voucher.VOUCHERNUMBER else "", "posting_date": voucher.DATE.string.strip(), "due_date": voucher.DATE.string.strip(), "items": get_voucher_items(voucher, doctype), @@ -497,14 +508,21 @@ class TallyMigration(Document): oldest_year = new_year def create_custom_fields(doctypes): - df = { + tally_guid_df = { "fieldtype": "Data", "fieldname": "tally_guid", "read_only": 1, "label": "Tally GUID" } - for doctype in doctypes: - create_custom_field(doctype, df) + tally_voucher_no_df = { + "fieldtype": "Data", + "fieldname": "tally_voucher_no", + "read_only": 1, + "label": "Tally Voucher Number" + } + for df in [tally_guid_df, tally_voucher_no_df]: + for doctype in doctypes: + create_custom_field(doctype, df) def create_price_list(): frappe.get_doc({ From 0c353a64a44fb5567e1de315ca0fef0092c0aa68 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Sat, 2 May 2020 18:58:33 +0530 Subject: [PATCH 026/608] fix: un-using buggy JS frappe.new_doc --- .../tally_migration/tally_migration.js | 20 ++++++++++++++++--- .../tally_migration/tally_migration.py | 10 ++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js index d9811b58dde..fd16d1e84aa 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js @@ -253,10 +253,24 @@ erpnext.tally_migration.resolve = (document) => { frm.save(); } -erpnext.tally_migration.create_new_doc = (doctype, document) => { +erpnext.tally_migration.create_new_doc = (document) => { /* Mark as resolved and create new document */ erpnext.tally_migration.resolve(document); - frappe.new_doc(doctype, document); + return frappe.call({ + type: "POST", + method: 'erpnext.erpnext_integrations.doctype.tally_migration.tally_migration.new_doc', + args: { + document + }, + freeze: true, + callback: function(r) { + if(!r.exc) { + frappe.model.sync(r.message); + frappe.get_doc(r.message.doctype, r.message.name).__run_link_triggers = true; + frappe.set_route("Form", r.message.doctype, r.message.name); + } + } + }); } erpnext.tally_migration.get_html_rows = (logs, field) => { @@ -290,7 +304,7 @@ erpnext.tally_migration.get_html_rows = (logs, field) => { `; let create_button = ` - ` diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py index 393c5d4b02b..d9c5852a6ed 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py @@ -28,6 +28,16 @@ PRIMARY_ACCOUNT = "Primary" VOUCHER_CHUNK_SIZE = 500 +@frappe.whitelist() +def new_doc(document): + document = json.loads(document) + doctype = document.pop("doctype") + document.pop("name", None) + doc = frappe.new_doc(doctype) + doc.update(document) + + return doc + class TallyMigration(Document): def validate(self): failed_import_log = json.loads(self.failed_import_log) From 100453c64fdf9bb8d480ea37242b421499bd656e Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Mon, 4 May 2020 19:32:20 +0530 Subject: [PATCH 027/608] feat: added open count tag --- erpnext/projects/desk_page/projects/projects.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/erpnext/projects/desk_page/projects/projects.json b/erpnext/projects/desk_page/projects/projects.json index a07cdffcbeb..52e502bbed6 100644 --- a/erpnext/projects/desk_page/projects/projects.json +++ b/erpnext/projects/desk_page/projects/projects.json @@ -28,7 +28,7 @@ "idx": 0, "is_standard": 1, "label": "Projects", - "modified": "2020-04-01 11:28:51.245756", + "modified": "2020-05-04 18:08:54.363929", "modified_by": "Administrator", "module": "Projects", "name": "Projects", @@ -37,6 +37,7 @@ "pin_to_top": 0, "shortcuts": [ { + "color": "#4d4da8", "format": "{} Assigned", "label": "Task", "link_to": "Task", @@ -44,8 +45,11 @@ "type": "DocType" }, { + "color": "#4d4da8", + "format": "{} Open", "label": "Project", "link_to": "Project", + "stats_filter": "{\n \"status\": \"Open\"\n}", "type": "DocType" }, { From 1607891a3c3bbbbad10c2b6c4509a166df547301 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Mon, 4 May 2020 20:16:56 +0530 Subject: [PATCH 028/608] feat: added basic project summary report --- .../report/project_summary/__init__.py | 0 .../report/project_summary/project_summary.js | 23 +++ .../project_summary/project_summary.json | 27 ++++ .../report/project_summary/project_summary.py | 142 ++++++++++++++++++ 4 files changed, 192 insertions(+) create mode 100644 erpnext/projects/report/project_summary/__init__.py create mode 100644 erpnext/projects/report/project_summary/project_summary.js create mode 100644 erpnext/projects/report/project_summary/project_summary.json create mode 100644 erpnext/projects/report/project_summary/project_summary.py diff --git a/erpnext/projects/report/project_summary/__init__.py b/erpnext/projects/report/project_summary/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/projects/report/project_summary/project_summary.js b/erpnext/projects/report/project_summary/project_summary.js new file mode 100644 index 00000000000..15367acd7d3 --- /dev/null +++ b/erpnext/projects/report/project_summary/project_summary.js @@ -0,0 +1,23 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Project Summary"] = { + "filters": [ + { + "fieldname": "company", + "label": __("Company"), + "fieldtype": "Link", + "options": "Company", + "default": frappe.defaults.get_user_default("Company"), + "reqd": 1 + }, + { + "fieldname": "status", + "label": __("Status"), + "fieldtype": "Select", + "options": "Open\nComplete\nCancelled", + "default": "Open" + } + ] +}; diff --git a/erpnext/projects/report/project_summary/project_summary.json b/erpnext/projects/report/project_summary/project_summary.json new file mode 100644 index 00000000000..0b18b3e2784 --- /dev/null +++ b/erpnext/projects/report/project_summary/project_summary.json @@ -0,0 +1,27 @@ +{ + "add_total_row": 0, + "creation": "2020-05-04 19:31:54.575765", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "modified": "2020-05-04 19:32:53.177213", + "modified_by": "Administrator", + "module": "Projects", + "name": "Project Summary", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Project", + "report_name": "Project Summary", + "report_type": "Script Report", + "roles": [ + { + "role": "Projects User" + }, + { + "role": "Projects Manager" + } + ] +} \ No newline at end of file diff --git a/erpnext/projects/report/project_summary/project_summary.py b/erpnext/projects/report/project_summary/project_summary.py new file mode 100644 index 00000000000..c11c4ca9ff1 --- /dev/null +++ b/erpnext/projects/report/project_summary/project_summary.py @@ -0,0 +1,142 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe import _ + +def execute(filters=None): + columns = get_columns() + data = [] + + data = frappe.db.get_all("Project", filters=filters, fields=["name", 'status', "percent_complete", "expected_start_date", "expected_end_date"], order_by="expected_end_date") + + for project in data: + project["total_tasks"] = frappe.db.count("Task", filters={"project": project.name}) + project["completed_tasks"] = frappe.db.count("Task", filters={"project": project.name, "status": "Completed"}) + project["overdue_tasks"] = frappe.db.count("Task", filters={"project": project.name, "status": "Overdue"}) + + chart = get_chart_data(data) + report_summary = get_report_summary(data) + + return columns, data, None, chart, report_summary + +def get_columns(): + return [ + { + "fieldname": "name", + "label": _("Project"), + "fieldtype": "Link", + "options": "Project", + "width": 200 + }, + { + "fieldname": "status", + "label": _("Status"), + "fieldtype": "Data", + "width": 120 + }, + { + "fieldname": "total_tasks", + "label": _("Total Tasks"), + "fieldtype": "Data", + "width": 120 + }, + { + "fieldname": "completed_tasks", + "label": _("Tasks Completed"), + "fieldtype": "Data", + "width": 120 + }, + { + "fieldname": "overdue_tasks", + "label": _("Tasks Overdue"), + "fieldtype": "Data", + "width": 120 + }, + { + "fieldname": "percent_complete", + "label": _("Completion"), + "fieldtype": "Data", + "width": 120 + }, + { + "fieldname": "expected_start_date", + "label": _("Start Date"), + "fieldtype": "Date", + "width": 120 + }, + { + "fieldname": "expected_end_date", + "label": _("End Date"), + "fieldtype": "Date", + "width": 120 + }, + ] + +def get_chart_data(data): + labels = [] + total = [] + completed = [] + overdue = [] + + for project in data: + labels.append(project.name) + total.append(project.total_tasks) + completed.append(project.completed_tasks) + overdue.append(project.overdue_tasks) + + return { + "data": { + 'labels': labels, + 'datasets': [ + { + "name": "Total Tasks", + "values": total + }, + { + "name": "Tasks Completed", + "values": completed + }, + { + "name": "Tasks Overdue", + "values": overdue + } + ] + }, + "type": "bar", + "colors": ["#7679fc", "#98d85b", "#fc4f51"] + } + +def get_report_summary(data): + avg_completion = sum([project.percent_complete for project in data]) / len(data) + total = sum([project.total_tasks for project in data]) + total_overdue = sum([project.overdue_tasks for project in data]) + completed = sum([project.completed_tasks for project in data]) + + return [ + { + "value": avg_completion, + "indicator": "Green" if avg_completion > 50 else "Red", + "label": "Average Completion", + "datatype": "Percent", + }, + { + "value": total, + "indicator": "Blue", + "label": "Total Tasks", + "datatype": "Int", + }, + { + "value": completed, + "indicator": "Green", + "label": "Completed Tasks", + "datatype": "Int", + }, + { + "value": total_overdue, + "indicator": "Green" if total_overdue == 0 else "Red", + "label": "Overdue Tasks", + "datatype": "Int", + } + ] From 813bc498689bf53cc7dcb7f9e8bed8737caec730 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Mon, 4 May 2020 20:27:26 +0530 Subject: [PATCH 029/608] feat: added Report Summary Chart as fixture --- .../setup_wizard/data/dashboard_charts.py | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/erpnext/setup/setup_wizard/data/dashboard_charts.py b/erpnext/setup/setup_wizard/data/dashboard_charts.py index bb8c1319bf3..ebe183e4c5e 100644 --- a/erpnext/setup/setup_wizard/data/dashboard_charts.py +++ b/erpnext/setup/setup_wizard/data/dashboard_charts.py @@ -31,7 +31,14 @@ def get_default_dashboards(): { "chart": "Income" }, { "chart": "Expenses" } ] - } + }, + { + "doctype": "Dashboard", + "dashboard_name": "Project", + "charts": [ + { "chart": "Report Summary", "width": "Full" } + ] + }, ], "Charts": [ { @@ -107,7 +114,18 @@ def get_default_dashboards(): "document_type": "Sales Invoice", "type": "Bar", "width": "Half" - } + }, + { + 'doctype': 'Dashboard Chart', + 'name': 'Project Summary', + 'chart_name': 'Project Summary', + 'chart_type': 'Report', + 'report_name': 'Project Summary', + 'is_public': 1, + 'filters_json': json.dumps({"company": company.name, "status": "Open"}), + 'type': 'Bar', + 'custom_options': '{"type": "bar", "colors": ["#7679fc", "#98d85b", "#fc4f51"], "axisOptions": {"shortenYAxisNumbers": 1}, "tooltipOptions": {}}', + }, ] } From c7f5724fad8259d381b91f62c47fd96f0c70c8ff Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Mon, 4 May 2020 20:28:24 +0530 Subject: [PATCH 030/608] feat: added project summary chart to desktop --- erpnext/projects/desk_page/projects/projects.json | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/erpnext/projects/desk_page/projects/projects.json b/erpnext/projects/desk_page/projects/projects.json index 52e502bbed6..4d4450dbf53 100644 --- a/erpnext/projects/desk_page/projects/projects.json +++ b/erpnext/projects/desk_page/projects/projects.json @@ -17,18 +17,22 @@ } ], "category": "Modules", - "charts": [], + "charts": [ + { + "chart_name": "Project Summary", + "label": "Open Projects" + } + ], "creation": "2020-03-02 15:46:04.874669", "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, - "icon": "", "idx": 0, "is_standard": 1, "label": "Projects", - "modified": "2020-05-04 18:08:54.363929", + "modified": "2020-05-04 20:27:51.591365", "modified_by": "Administrator", "module": "Projects", "name": "Projects", From 4ea122662a59952b75144b182115c44c597777be Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Mon, 4 May 2020 20:29:51 +0530 Subject: [PATCH 031/608] refactor: less verbose labels for charts --- erpnext/projects/report/project_summary/project_summary.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/projects/report/project_summary/project_summary.py b/erpnext/projects/report/project_summary/project_summary.py index c11c4ca9ff1..81e63cf70da 100644 --- a/erpnext/projects/report/project_summary/project_summary.py +++ b/erpnext/projects/report/project_summary/project_summary.py @@ -95,11 +95,11 @@ def get_chart_data(data): "values": total }, { - "name": "Tasks Completed", + "name": "Completed", "values": completed }, { - "name": "Tasks Overdue", + "name": "Overdue", "values": overdue } ] From ec24afb283d2af641266dd250f70479f0b8eb43b Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 5 May 2020 10:02:14 +0530 Subject: [PATCH 032/608] fix: change response and resolution fieldtype to Duration in SLA --- .../service_level_agreement.py | 19 +++----------- .../service_level_priority.json | 26 +++---------------- 2 files changed, 7 insertions(+), 38 deletions(-) diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py index 9fa0e238f96..530230e1e84 100644 --- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py +++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py @@ -30,19 +30,8 @@ class ServiceLevelAgreement(Document): if priority.default_priority: default_priority.append(priority.default_priority) - if priority.response_time_period == "Hour": - response = priority.response_time * 0.0416667 - elif priority.response_time_period == "Day": - response = priority.response_time - elif priority.response_time_period == "Week": - response = priority.response_time * 7 - - if priority.resolution_time_period == "Hour": - resolution = priority.resolution_time * 0.0416667 - elif priority.resolution_time_period == "Day": - resolution = priority.resolution_time - elif priority.resolution_time_period == "Week": - resolution = priority.resolution_time * 7 + response = priority.response_time + resolution = priority.resolution_time if response > resolution: frappe.throw(_("Response Time for {0} at index {1} can't be greater than Resolution Time.").format(priority.priority, priority.idx)) @@ -109,9 +98,7 @@ class ServiceLevelAgreement(Document): return frappe._dict({ "priority": priority.priority, "response_time": priority.response_time, - "response_time_period": priority.response_time_period, - "resolution_time": priority.resolution_time, - "resolution_time_period": priority.resolution_time_period + "resolution_time": priority.resolution_time }) def check_agreement_status(): diff --git a/erpnext/support/doctype/service_level_priority/service_level_priority.json b/erpnext/support/doctype/service_level_priority/service_level_priority.json index f56008eb4cf..6377d1a962a 100644 --- a/erpnext/support/doctype/service_level_priority/service_level_priority.json +++ b/erpnext/support/doctype/service_level_priority/service_level_priority.json @@ -10,10 +10,8 @@ "default_priority", "sb_00", "response_time", - "response_time_period", "cb_00", - "resolution_time", - "resolution_time_period" + "resolution_time" ], "fields": [ { @@ -31,7 +29,7 @@ { "columns": 2, "fieldname": "resolution_time", - "fieldtype": "Int", + "fieldtype": "Duration", "in_list_view": 1, "label": "Resolution Time" }, @@ -39,22 +37,6 @@ "fieldname": "cb_00", "fieldtype": "Column Break" }, - { - "columns": 2, - "fieldname": "response_time_period", - "fieldtype": "Select", - "in_list_view": 1, - "label": "Response Time Period", - "options": "Hour\nDay\nWeek" - }, - { - "columns": 2, - "fieldname": "resolution_time_period", - "fieldtype": "Select", - "in_list_view": 1, - "label": "Resolution Time Period", - "options": "Hour\nDay\nWeek" - }, { "fieldname": "cb_01", "fieldtype": "Column Break" @@ -70,14 +52,14 @@ { "columns": 2, "fieldname": "response_time", - "fieldtype": "Int", + "fieldtype": "Duration", "in_list_view": 1, "label": "First Response Time" } ], "istable": 1, "links": [], - "modified": "2020-04-24 14:50:13.774308", + "modified": "2020-05-04 22:08:04.503949", "modified_by": "Administrator", "module": "Support", "name": "Service Level Priority", From 39fe68880941e79cc8bbf50a717df7326ee3aeea Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 5 May 2020 13:05:38 +0530 Subject: [PATCH 033/608] fix: dashboard chart link --- erpnext/setup/setup_wizard/data/dashboard_charts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/setup/setup_wizard/data/dashboard_charts.py b/erpnext/setup/setup_wizard/data/dashboard_charts.py index ebe183e4c5e..7edd5e3d4ac 100644 --- a/erpnext/setup/setup_wizard/data/dashboard_charts.py +++ b/erpnext/setup/setup_wizard/data/dashboard_charts.py @@ -36,7 +36,7 @@ def get_default_dashboards(): "doctype": "Dashboard", "dashboard_name": "Project", "charts": [ - { "chart": "Report Summary", "width": "Full" } + { "chart": "Project Summary", "width": "Full" } ] }, ], From cc7488a90d579da62b17d8f404d967633bfe36ef Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 5 May 2020 13:05:53 +0530 Subject: [PATCH 034/608] feat: rerun patch to add default dashboards --- erpnext/patches.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 52953996956..1f8fabd2990 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -630,7 +630,7 @@ execute:frappe.reload_doc('desk', 'doctype', 'dashboard') execute:frappe.reload_doc('desk', 'doctype', 'dashboard_chart_source') execute:frappe.reload_doc('desk', 'doctype', 'dashboard_chart') execute:frappe.reload_doc('desk', 'doctype', 'dashboard_chart_field') -erpnext.patches.v12_0.add_default_dashboards +erpnext.patches.v12_0.add_default_dashboards # 2020-05-05 erpnext.patches.v12_0.remove_bank_remittance_custom_fields erpnext.patches.v12_0.generate_leave_ledger_entries execute:frappe.delete_doc_if_exists("Report", "Loan Repayment") From 5f1240bcc66d1873dc4e84c09df2e449f4033627 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 5 May 2020 18:44:02 +0530 Subject: [PATCH 035/608] feat: updated charts to use stacked layout --- .../projects/report/project_summary/project_summary.py | 10 +++++----- erpnext/setup/setup_wizard/data/dashboard_charts.py | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/erpnext/projects/report/project_summary/project_summary.py b/erpnext/projects/report/project_summary/project_summary.py index 81e63cf70da..7f534bf5245 100644 --- a/erpnext/projects/report/project_summary/project_summary.py +++ b/erpnext/projects/report/project_summary/project_summary.py @@ -90,10 +90,6 @@ def get_chart_data(data): "data": { 'labels': labels, 'datasets': [ - { - "name": "Total Tasks", - "values": total - }, { "name": "Completed", "values": completed @@ -101,7 +97,11 @@ def get_chart_data(data): { "name": "Overdue", "values": overdue - } + }, + { + "name": "Total Tasks", + "values": total + }, ] }, "type": "bar", diff --git a/erpnext/setup/setup_wizard/data/dashboard_charts.py b/erpnext/setup/setup_wizard/data/dashboard_charts.py index 7edd5e3d4ac..ccb23c07e85 100644 --- a/erpnext/setup/setup_wizard/data/dashboard_charts.py +++ b/erpnext/setup/setup_wizard/data/dashboard_charts.py @@ -124,7 +124,7 @@ def get_default_dashboards(): 'is_public': 1, 'filters_json': json.dumps({"company": company.name, "status": "Open"}), 'type': 'Bar', - 'custom_options': '{"type": "bar", "colors": ["#7679fc", "#98d85b", "#fc4f51"], "axisOptions": {"shortenYAxisNumbers": 1}, "tooltipOptions": {}}', + 'custom_options': '{"type": "bar", "colors": ["#98d85b", "#fc4f51", "#7679fc"], "axisOptions": { "shortenYAxisNumbers": 1}, "barOptions": { "stacked": 1 }}', }, ] } From 945aa08d92185549265c01a6045fba6e15053e8b Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 5 May 2020 18:46:21 +0530 Subject: [PATCH 036/608] feat: show stacked in report --- erpnext/projects/report/project_summary/project_summary.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/projects/report/project_summary/project_summary.py b/erpnext/projects/report/project_summary/project_summary.py index 7f534bf5245..dc641766704 100644 --- a/erpnext/projects/report/project_summary/project_summary.py +++ b/erpnext/projects/report/project_summary/project_summary.py @@ -105,7 +105,10 @@ def get_chart_data(data): ] }, "type": "bar", - "colors": ["#7679fc", "#98d85b", "#fc4f51"] + "colors": ["#98d85b", "#fc4f51", "#7679fc"], + "barOptions": { + "stacked": True + } } def get_report_summary(data): From 81f8fbb042eb3554e540a6f25053fb47f8eb052d Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 5 May 2020 18:53:29 +0530 Subject: [PATCH 037/608] feat: update colors --- .../projects/report/project_summary/project_summary.py | 10 +++++----- erpnext/setup/setup_wizard/data/dashboard_charts.py | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/erpnext/projects/report/project_summary/project_summary.py b/erpnext/projects/report/project_summary/project_summary.py index dc641766704..66d68bda06a 100644 --- a/erpnext/projects/report/project_summary/project_summary.py +++ b/erpnext/projects/report/project_summary/project_summary.py @@ -90,14 +90,14 @@ def get_chart_data(data): "data": { 'labels': labels, 'datasets': [ - { - "name": "Completed", - "values": completed - }, { "name": "Overdue", "values": overdue }, + { + "name": "Completed", + "values": completed + }, { "name": "Total Tasks", "values": total @@ -105,7 +105,7 @@ def get_chart_data(data): ] }, "type": "bar", - "colors": ["#98d85b", "#fc4f51", "#7679fc"], + "colors": ["#fc4f51", "#78d6ff", "#7575ff"], "barOptions": { "stacked": True } diff --git a/erpnext/setup/setup_wizard/data/dashboard_charts.py b/erpnext/setup/setup_wizard/data/dashboard_charts.py index d54462e1be4..9ce64eb9d92 100644 --- a/erpnext/setup/setup_wizard/data/dashboard_charts.py +++ b/erpnext/setup/setup_wizard/data/dashboard_charts.py @@ -125,7 +125,7 @@ def get_default_dashboards(): 'is_public': 1, 'filters_json': json.dumps({"company": company.name, "status": "Open"}), 'type': 'Bar', - 'custom_options': '{"type": "bar", "colors": ["#98d85b", "#fc4f51", "#7679fc"], "axisOptions": { "shortenYAxisNumbers": 1}, "barOptions": { "stacked": 1 }}', + 'custom_options': '{"type": "bar", "colors": ["#fc4f51", "#78d6ff", "#7575ff"], "axisOptions": { "shortenYAxisNumbers": 1}, "barOptions": { "stacked": 1 }}', }, { "doctype": "Dashboard Chart", From 35d853a476b689443b44b0c1fee11609a7ea86d9 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 5 May 2020 18:54:50 +0530 Subject: [PATCH 038/608] fix: SLA in issues with Duration Fieldtype --- erpnext/support/doctype/issue/issue.py | 35 ++++++++------------------ 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index 027225942c7..1489f431384 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -59,7 +59,7 @@ class Issue(Document): if self.status!="Open" and status =="Open" and not self.first_responded_on: self.first_responded_on = frappe.flags.current_time or now_datetime() - if self.status=="Closed": + if self.status=="Closed" and status !="Closed": self.resolution_date = frappe.flags.current_time or now_datetime() if frappe.db.get_value("Issue", self.name, "agreement_fulfilled") == "Ongoing": set_service_level_agreement_variance(issue=self.name) @@ -230,24 +230,14 @@ def get_expected_time_for(parameter, service_level, start_date_time): start_time = None end_time = None - # lets assume response time is in days by default if parameter == 'response': - allotted_days = service_level.get("response_time") - time_period = service_level.get("response_time_period") + allotted_seconds = service_level.get("response_time") elif parameter == 'resolution': - allotted_days = service_level.get("resolution_time") - time_period = service_level.get("resolution_time_period") + allotted_seconds = service_level.get("resolution_time") else: frappe.throw(_("{0} parameter is invalid").format(parameter)) - allotted_hours = 0 - if time_period == 'Hour': - allotted_hours = allotted_days - allotted_days = 0 - elif time_period == 'Week': - allotted_days *= 7 - - expected_time_is_set = 1 if allotted_days == 0 and time_period in ['Day', 'Week'] else 0 + expected_time_is_set = 0 support_days = {} for service in service_level.get("support_and_resolution"): @@ -267,25 +257,22 @@ def get_expected_time_for(parameter, service_level, start_date_time): if getdate(current_date_time) == getdate(start_date_time) and get_time_in_timedelta(current_date_time.time()) > support_days[current_weekday].start_time \ else support_days[current_weekday].start_time end_time = support_days[current_weekday].end_time - time_left_today = time_diff_in_hours(end_time, start_time) + time_left_today = time_diff_in_seconds(end_time, start_time) # no time left for support today - if time_left_today < 0: pass - elif time_period == 'Hour': - if time_left_today >= allotted_hours: + if time_left_today <= 0: pass + elif allotted_seconds: + if time_left_today >= allotted_seconds: expected_time = datetime.combine(getdate(current_date_time), get_time(start_time)) - expected_time = add_to_date(expected_time, hours=allotted_hours) + expected_time = add_to_date(expected_time, seconds=allotted_seconds) expected_time_is_set = 1 else: - allotted_hours = allotted_hours - time_left_today - else: - allotted_days -= 1 - expected_time_is_set = allotted_days <= 0 + allotted_seconds = allotted_seconds - time_left_today if not expected_time_is_set: current_date_time = add_to_date(current_date_time, days=1) - if end_time and time_period != 'Hour': + if end_time and allotted_seconds >= 86400: current_date_time = datetime.combine(getdate(current_date_time), get_time(end_time)) else: current_date_time = expected_time From cc989b62bd6e881b797cf3f3eebd236cdcb07284 Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 5 May 2020 23:58:08 +0530 Subject: [PATCH 039/608] feat: Buying Desk, Dashboard and Reports --- erpnext/buying/desk_page/buying/buying.json | 79 +++-- .../purchase_order_analysis/__init__.py | 0 .../purchase_order_analysis.js | 86 ++++++ .../purchase_order_analysis.json | 33 +++ .../purchase_order_analysis.py | 277 ++++++++++++++++++ .../requested_items_to_order/__init__.py | 0 .../requested_items_to_order.js | 64 ++++ .../requested_items_to_order.json | 34 +++ .../requested_items_to_order.py | 211 +++++++++++++ 9 files changed, 753 insertions(+), 31 deletions(-) create mode 100644 erpnext/buying/report/purchase_order_analysis/__init__.py create mode 100644 erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js create mode 100644 erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.json create mode 100644 erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py create mode 100644 erpnext/buying/report/requested_items_to_order/__init__.py create mode 100644 erpnext/buying/report/requested_items_to_order/requested_items_to_order.js create mode 100644 erpnext/buying/report/requested_items_to_order/requested_items_to_order.json create mode 100644 erpnext/buying/report/requested_items_to_order/requested_items_to_order.py diff --git a/erpnext/buying/desk_page/buying/buying.json b/erpnext/buying/desk_page/buying/buying.json index 5e764cf8bbe..432f3c557b8 100644 --- a/erpnext/buying/desk_page/buying/buying.json +++ b/erpnext/buying/desk_page/buying/buying.json @@ -2,24 +2,24 @@ "cards": [ { "hidden": 0, - "label": "Supplier", - "links": "[\n {\n \"description\": \"Supplier database.\",\n \"label\": \"Supplier\",\n \"name\": \"Supplier\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Supplier Group master.\",\n \"label\": \"Supplier Group\",\n \"name\": \"Supplier Group\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"All Contacts.\",\n \"label\": \"Contact\",\n \"name\": \"Contact\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"All Addresses.\",\n \"label\": \"Address\",\n \"name\": \"Address\",\n \"type\": \"doctype\"\n }\n]" + "label": "Buying", + "links": "[ \n {\n \"dependencies\": [\n \"Item\"\n ],\n \"description\": \"Request for purchase.\",\n \"label\": \"Material Request\",\n \"name\": \"Material Request\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Supplier\"\n ],\n \"description\": \"Purchase Orders given to Suppliers.\",\n \"label\": \"Purchase Order\",\n \"name\": \"Purchase Order\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Supplier\"\n ],\n \"label\": \"Purchase Invoice\",\n \"name\": \"Purchase Invoice\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Supplier\"\n ],\n \"description\": \"Request for quotation.\",\n \"label\": \"Request for Quotation\",\n \"name\": \"Request for Quotation\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Supplier\"\n ],\n \"description\": \"Quotations received from Suppliers.\",\n \"label\": \"Supplier Quotation\",\n \"name\": \"Supplier Quotation\",\n \"type\": \"doctype\"\n }\n]" }, { "hidden": 0, - "label": "Purchasing", - "links": "[\n {\n \"dependencies\": [\n \"Item\",\n \"Supplier\"\n ],\n \"description\": \"Purchase Orders given to Suppliers.\",\n \"label\": \"Purchase Order\",\n \"name\": \"Purchase Order\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Supplier\"\n ],\n \"label\": \"Purchase Invoice\",\n \"name\": \"Purchase Invoice\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"description\": \"Request for purchase.\",\n \"label\": \"Material Request\",\n \"name\": \"Material Request\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Supplier\"\n ],\n \"description\": \"Request for quotation.\",\n \"label\": \"Request for Quotation\",\n \"name\": \"Request for Quotation\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Supplier\"\n ],\n \"description\": \"Quotations received from Suppliers.\",\n \"label\": \"Supplier Quotation\",\n \"name\": \"Supplier Quotation\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Items and Pricing", - "links": "[\n {\n \"description\": \"All Products or Services.\",\n \"label\": \"Item\",\n \"name\": \"Item\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Multiple Item prices.\",\n \"label\": \"Item Price\",\n \"name\": \"Item Price\",\n \"onboard\": 1,\n \"route\": \"#Report/Item Price\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Price List master.\",\n \"label\": \"Price List\",\n \"name\": \"Price List\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Bundle items at time of sale.\",\n \"label\": \"Product Bundle\",\n \"name\": \"Product Bundle\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tree of Item Groups.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Item Group\",\n \"link\": \"Tree/Item Group\",\n \"name\": \"Item Group\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Rules for applying different promotional schemes.\",\n \"label\": \"Promotional Scheme\",\n \"name\": \"Promotional Scheme\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Rules for applying pricing and discount.\",\n \"label\": \"Pricing Rule\",\n \"name\": \"Pricing Rule\",\n \"type\": \"doctype\"\n }\n]" + "label": "Items & Pricing", + "links": "[\n {\n \"description\": \"All Products or Services.\",\n \"label\": \"Item\",\n \"name\": \"Item\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Multiple Item prices.\",\n \"label\": \"Item Price\",\n \"name\": \"Item Price\",\n \"onboard\": 1,\n \"route\": \"#Report/Item Price\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Price List master.\",\n \"label\": \"Price List\",\n \"name\": \"Price List\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Bundle items at time of sale.\",\n \"label\": \"Product Bundle\",\n \"name\": \"Product Bundle\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tree of Item Groups.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Item Group\",\n \"link\": \"Tree/Item Group\",\n \"name\": \"Item Group\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Rules for applying different promotional schemes.\",\n \"label\": \"Promotional Scheme\",\n \"name\": \"Promotional Scheme\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Rules for applying pricing and discount.\",\n \"label\": \"Pricing Rule\",\n \"name\": \"Pricing Rule\",\n \"type\": \"doctype\"\n }\n]" }, { "hidden": 0, "label": "Settings", "links": "[\n {\n \"description\": \"Default settings for buying transactions.\",\n \"label\": \"Buying Settings\",\n \"name\": \"Buying Settings\",\n \"settings\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tax template for buying transactions.\",\n \"label\": \"Purchase Taxes and Charges Template\",\n \"name\": \"Purchase Taxes and Charges Template\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Template of terms or contract.\",\n \"label\": \"Terms and Conditions Template\",\n \"name\": \"Terms and Conditions\",\n \"type\": \"doctype\"\n }\n]" }, + { + "hidden": 0, + "label": "Supplier", + "links": "[\n {\n \"description\": \"Supplier database.\",\n \"label\": \"Supplier\",\n \"name\": \"Supplier\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Supplier Group master.\",\n \"label\": \"Supplier Group\",\n \"name\": \"Supplier Group\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"All Contacts.\",\n \"label\": \"Contact\",\n \"name\": \"Contact\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"All Addresses.\",\n \"label\": \"Address\",\n \"name\": \"Address\",\n \"type\": \"doctype\"\n }\n]" + }, { "hidden": 0, "label": "Supplier Scorecard", @@ -28,32 +28,33 @@ { "hidden": 0, "label": "Key Reports", - "links": "[\n {\n \"is_query_report\": true,\n \"label\": \"Purchase Analytics\",\n \"name\": \"Purchase Analytics\",\n \"onboard\": 1,\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Supplier-Wise Sales Analytics\",\n \"name\": \"Supplier-Wise Sales Analytics\",\n \"onboard\": 1,\n \"reference_doctype\": \"Stock Ledger Entry\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Purchase Order Trends\",\n \"name\": \"Purchase Order Trends\",\n \"onboard\": 1,\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Procurement Tracker\",\n \"name\": \"Procurement Tracker\",\n \"onboard\": 1,\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Requested Items To Be Ordered\",\n \"name\": \"Requested Items To Be Ordered\",\n \"onboard\": 1,\n \"reference_doctype\": \"Material Request\",\n \"type\": \"report\"\n }\n]" + "links": "[\n {\n \"is_query_report\": true,\n \"label\": \"Purchase Analytics\",\n \"name\": \"Purchase Analytics\",\n \"onboard\": 1,\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Purchase Order Analysis\",\n \"name\": \"Purchase Order Analysis\",\n \"onboard\": 1,\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Supplier-Wise Sales Analytics\",\n \"name\": \"Supplier-Wise Sales Analytics\",\n \"onboard\": 1,\n \"reference_doctype\": \"Stock Ledger Entry\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Requested Items to Order\",\n \"name\": \"Requested Items to Order\",\n \"onboard\": 1,\n \"reference_doctype\": \"Material Request\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Purchase Order Trends\",\n \"name\": \"Purchase Order Trends\",\n \"onboard\": 1,\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Procurement Tracker\",\n \"name\": \"Procurement Tracker\",\n \"onboard\": 1,\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n }\n]" }, { "hidden": 0, "label": "Other Reports", - "links": "[\n {\n \"is_query_report\": true,\n \"label\": \"Items To Be Requested\",\n \"name\": \"Items To Be Requested\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Item-wise Purchase History\",\n \"name\": \"Item-wise Purchase History\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Material Requests for which Supplier Quotations are not created\",\n \"name\": \"Material Requests for which Supplier Quotations are not created\",\n \"reference_doctype\": \"Material Request\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Supplier Addresses And Contacts\",\n \"name\": \"Address And Contacts\",\n \"reference_doctype\": \"Address\",\n \"route_options\": {\n \"party_type\": \"Supplier\"\n },\n \"type\": \"report\"\n }\n]" + "links": "[\n {\n \"is_query_report\": true,\n \"label\": \"Items To Be Requested\",\n \"name\": \"Items To Be Requested\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Item-wise Purchase History\",\n \"name\": \"Item-wise Purchase History\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Subcontracted Raw Materials To Be Transferred\",\n \"name\": \"Subcontracted Raw Materials To Be Transferred\",\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Subcontracted Item To Be Received\",\n \"name\": \"Subcontracted Item To Be Received\",\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Quoted Item Comparison\",\n \"name\": \"Quoted Item Comparison\",\n \"reference_doctype\": \"Supplier Quotation\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Material Requests for which Supplier Quotations are not created\",\n \"name\": \"Material Requests for which Supplier Quotations are not created\",\n \"reference_doctype\": \"Material Request\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Supplier Addresses And Contacts\",\n \"name\": \"Address And Contacts\",\n \"reference_doctype\": \"Address\",\n \"route_options\": {\n \"party_type\": \"Supplier\"\n },\n \"type\": \"report\"\n }\n]" } ], + "cards_label": "Masters & Reports ", "category": "Modules", "charts": [ { - "chart_name": "Expenses", - "label": "Expenses" + "chart_name": "Purchase Analytics", + "label": "Buying Analytics" } ], + "charts_label": "Buying Dashboard", "creation": "2020-01-28 11:50:26.195467", "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, - "icon": "", "idx": 0, "is_standard": 1, "label": "Buying", - "modified": "2020-04-01 11:28:51.192097", + "modified": "2020-05-05 23:48:25.788598", "modified_by": "Administrator", "module": "Buying", "name": "Buying", @@ -62,32 +63,48 @@ "pin_to_top": 0, "shortcuts": [ { - "format": "{} Unpaid", - "label": "Purchase Invoice", - "link_to": "Purchase Invoice", - "stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\": \"Unpaid\"\n}", - "type": "DocType" - }, - { - "format": "{} to receive", + "color": "#ffe8cd", + "format": "{} to Receive", "label": "Purchase Order", "link_to": "Purchase Order", - "stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\": \"To Receive\"\n}", + "stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\":[\"in\", [\"To Receive\", \"To Receive and Bill\"]]\n}", "type": "DocType" }, { + "color": "#ffe8cd", + "format": "{} Pending", + "label": "Material Request", + "link_to": "Material Request", + "stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\": \"Pending\"\n}", + "type": "DocType" + }, + { + "color": "#ffe8cd", + "format": "{} to Bill ", + "label": "Purchase Receipt", + "link_to": "Purchase Receipt", + "stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\": \"To Bill\"\n}", + "type": "DocType" + }, + { + "color": "#ffe8cd", + "format": "{} Unpaid / Overdue", + "label": "Purchase Invoice", + "link_to": "Purchase Invoice", + "stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\": [\"in\", [\"Unpaid\", \"Overdue\"]]\n}", + "type": "DocType" + }, + { + "color": "#cef6d1", + "format": "{} Active", "label": "Supplier Quotation", "link_to": "Supplier Quotation", + "stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\":[\"!=\", \"Expired\"]\n}", "type": "DocType" }, { - "label": "Accounts Payable", - "link_to": "Accounts Payable", - "type": "Report" - }, - { - "label": "Purchase Register", - "link_to": "Purchase Register", + "label": "Item-wise Purchase Register", + "link_to": "Item-wise Purchase Register", "type": "Report" } ] diff --git a/erpnext/buying/report/purchase_order_analysis/__init__.py b/erpnext/buying/report/purchase_order_analysis/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js new file mode 100644 index 00000000000..24abb6d44ab --- /dev/null +++ b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js @@ -0,0 +1,86 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Purchase Order Analysis"] = { + "filters": [ + { + "fieldname": "company", + "label": __("Company"), + "fieldtype": "Link", + "width": "80", + "options": "Company", + "reqd": 1, + "default": frappe.defaults.get_default("company") + }, + { + "fieldname":"from_date", + "label": __("From Date"), + "fieldtype": "Date", + "width": "80", + "reqd": 1, + "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1), + }, + { + "fieldname":"to_date", + "label": __("To Date"), + "fieldtype": "Date", + "width": "80", + "reqd": 1, + "default": frappe.datetime.get_today() + }, + { + "fieldname": "purchase_order", + "label": __("Purchase Order"), + "fieldtype": "Link", + "width": "80", + "options": "Purchase Order", + "get_query": () =>{ + return { + filters: { "docstatus": 1 } + } + } + }, + { + "fieldname": "status", + "label": __("Status"), + "fieldtype": "MultiSelectList", + "width": "80", + get_data: function(txt) { + let status = ["To Bill", "To Receive", "To Receive and Bill", "Completed"] + let options = [] + for (let option of status){ + options.push({ + "value": option, + "description": "" + }) + } + return options + } + }, + { + "fieldname": "chart_based_on", + "label": __("Chart Based On"), + "fieldtype": "Select", + "width": "80", + "options": "Quantity\nAmount", + "default": "Quantity" + }, + { + "fieldname": "group_by_po", + "label": __("Group by Purchase Order"), + "fieldtype": "Check", + "default": 0 + } + ], + + "formatter": function (value, row, column, data, default_formatter) { + value = default_formatter(value, row, column, data); + let format_fields = ["received_qty", "billed_amount"]; + + if (in_list(format_fields, column.fieldname) && data && data[column.fieldname] > 0) { + value = "" + value + ""; + } + return value; + } +}; diff --git a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.json b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.json new file mode 100644 index 00000000000..196aaaed224 --- /dev/null +++ b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.json @@ -0,0 +1,33 @@ +{ + "add_total_row": 0, + "creation": "2020-05-04 18:41:28.625119", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "modified": "2020-05-04 18:41:28.625119", + "modified_by": "Administrator", + "module": "Buying", + "name": "Purchase Order Analysis", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Purchase Order", + "report_name": "Purchase Order Analysis", + "report_type": "Script Report", + "roles": [ + { + "role": "Purchase Manager" + }, + { + "role": "Purchase User" + }, + { + "role": "Stock User" + }, + { + "role": "Supplier" + } + ] +} \ No newline at end of file diff --git a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py new file mode 100644 index 00000000000..78b86636e07 --- /dev/null +++ b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py @@ -0,0 +1,277 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +import copy +from frappe import _ +from frappe.utils import flt, date_diff, getdate + +def execute(filters=None): + if not filters: + return [], [] + + validate_filters(filters) + + columns = get_columns(filters) + conditions = get_conditions(filters) + + data = get_data(conditions, filters) + + if not data: + return [], [] + + data, chart_data = prepare_data(data, filters) + + return columns, data, None, chart_data + +def validate_filters(filters): + from_date, to_date = filters.get("from_date"), filters.get("to_date") + + if not from_date and to_date: + frappe.throw(_("From and To Dates are required.")) + elif date_diff(to_date, from_date) < 0: + frappe.throw(_("To Date cannot be before From Date.")) + +def get_conditions(filters): + conditions = "" + if filters.get("from_date") and filters.get("to_date"): + conditions += " and po.transaction_date between '{0}' and '{1}'".format(filters.get("from_date"),filters.get("to_date")) + + if filters.get("company"): + conditions += " and po.company = '{0}'".format(filters.get("company")) + + if filters.get("purchase_order"): + conditions += " and po.name = '{0}'".format(filters.get("purchase_order")) + + if filters.get("status"): + conditions += " and po.status in (%s)" % ', '.join(['%s']*len(filters.get("status"))) + + return conditions + +def get_data(conditions, filters): + status = filters.get("status") + # temporary fix for dashboard chart + if status is None: + status = [] + + data = frappe.db.sql(""" + SELECT + po.transaction_date as date, + poi.schedule_date as required_date, + po.name as purchase_order, + po.status, po.supplier, poi.item_code, + poi.qty, poi.received_qty, + (poi.qty - poi.received_qty) AS pending_qty, + IFNULL(pii.qty, 0) as billed_qty, + poi.base_amount as amount, + (poi.received_qty * poi.base_rate) as received_qty_amount, + (poi.billed_amt * IFNULL(po.conversion_rate, 1)) as billed_amount, + (poi.base_amount - (poi.billed_amt * IFNULL(po.conversion_rate, 1))) as pending_amount, + po.set_warehouse as warehouse, + po.company, poi.name + FROM + `tabPurchase Order` po, + `tabPurchase Order Item` poi + LEFT JOIN `tabPurchase Invoice Item` pii + ON pii.po_detail = poi.name + WHERE + poi.parent = po.name + and po.status not in ('Stopped', 'Closed') + and po.docstatus = 1 + {0} + GROUP BY poi.name + ORDER BY po.transaction_date ASC + """.format(conditions), tuple(status), as_dict=1) + + return data + +def prepare_data(data, filters): + completed, pending = 0,0 + chart_based_on = filters.get("chart_based_on") + pending_field = "pending_qty" if chart_based_on == "Quantity" else "pending_amount" + completed_field = "received_qty" if chart_based_on == "Quantity" else "billed_amount" + + if filters.get("group_by_po"): + purchase_order_map = {} + + for row in data: + # sum data for chart + completed += row[completed_field] + pending += row[pending_field] + + # prepare data for report view + row["qty_to_bill"] = flt(row["qty"]) - flt(row["billed_qty"]) + + if filters.get("group_by_po"): + po_name = row["purchase_order"] + + if not po_name in purchase_order_map: + # create an entry + row_copy = copy.deepcopy(row) + purchase_order_map[po_name] = row_copy + else: + # update existing entry + po_row = purchase_order_map[po_name] + po_row["required_date"] = min(getdate(po_row["required_date"]), getdate(row["required_date"])) + + # sum numeric columns + fields = ["qty", "received_qty", "pending_qty", "billed_qty", "qty_to_bill", "amount", + "received_qty_amount", "billed_amount", "pending_amount"] + for field in fields: + po_row[field] = flt(row[field]) + flt(po_row[field]) + + chart_data = prepare_chart_data(chart_based_on, pending, completed) + + if filters.get("group_by_po"): + data = [] + for po in purchase_order_map: + data.append(purchase_order_map[po]) + return data, chart_data + + return data, chart_data + +def prepare_chart_data(chart_based_on, pending, completed): + labels = ["Qty to Receive","Received Qty"] if chart_based_on == "Quantity" else ["Amount to Bill","Billed Amount"] + + return { + "data" : { + "labels": labels, + "datasets": [ + {"values": [pending, completed]} + ] + }, + "type": 'donut', + "height": 300 + } + +def get_columns(filters): + columns = [ + { + "label":_("Date"), + "fieldname": "date", + "fieldtype": "Date", + "width": 90 + }, + { + "label":_("Required By"), + "fieldname": "required_date", + "fieldtype": "Date", + "width": 90 + }, + { + "label": _("Purchase Order"), + "fieldname": "purchase_order", + "fieldtype": "Link", + "options": "Purchase Order", + "width": 160 + }, + { + "label":_("Status"), + "fieldname": "status", + "fieldtype": "Data", + "width": 130 + }, + { + "label": _("Supplier"), + "fieldname": "supplier", + "fieldtype": "Link", + "options": "Supplier", + "width": 130 + }] + + if not filters.get("group_by_po"): + columns.append({ + "label":_("Item Code"), + "fieldname": "item_code", + "fieldtype": "Link", + "options": "Item", + "width": 100 + }) + + columns.extend([ + { + "label": _("Qty"), + "fieldname": "qty", + "fieldtype": "Float", + "width": 120, + "convertible": "qty" + }, + { + "label": _("Received Qty"), + "fieldname": "received_qty", + "fieldtype": "Float", + "width": 120, + "convertible": "qty" + }, + { + "label": _("Pending Qty"), + "fieldname": "pending_qty", + "fieldtype": "Float", + "width": 80, + "convertible": "qty" + }, + { + "label": _("Billed Qty"), + "fieldname": "billed_qty", + "fieldtype": "Float", + "width": 80, + "convertible": "qty" + }, + { + "label": _("Qty to Bill"), + "fieldname": "qty_to_bill", + "fieldtype": "Float", + "width": 80, + "convertible": "qty" + }, + { + "label": _("Amount"), + "fieldname": "amount", + "fieldtype": "Currency", + "width": 110, + "options": "Company:company:default_currency", + "convertible": "rate" + }, + { + "label": _("Billed Amount"), + "fieldname": "billed_amount", + "fieldtype": "Currency", + "width": 110, + "options": "Company:company:default_currency", + "convertible": "rate" + }, + { + "label": _("Pending Amount"), + "fieldname": "pending_amount", + "fieldtype": "Currency", + "width": 130, + "options": "Company:company:default_currency", + "convertible": "rate" + }, + { + "label": _("Received Qty Amount"), + "fieldname": "received_qty_amount", + "fieldtype": "Currency", + "width": 130, + "options": "Company:company:default_currency", + "convertible": "rate" + }, + { + "label": _("Warehouse"), + "fieldname": "warehouse", + "fieldtype": "Link", + "options": "Warehouse", + "width": 100 + }, + { + "label": _("Company"), + "fieldname": "company", + "fieldtype": "Link", + "options": "Company", + "width": 100 + } + ]) + + return columns + diff --git a/erpnext/buying/report/requested_items_to_order/__init__.py b/erpnext/buying/report/requested_items_to_order/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/buying/report/requested_items_to_order/requested_items_to_order.js b/erpnext/buying/report/requested_items_to_order/requested_items_to_order.js new file mode 100644 index 00000000000..21adb135476 --- /dev/null +++ b/erpnext/buying/report/requested_items_to_order/requested_items_to_order.js @@ -0,0 +1,64 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Requested Items to Order"] = { + "filters": [ + { + "fieldname": "company", + "label": __("Company"), + "fieldtype": "Link", + "width": "80", + "options": "Company", + "reqd": 1, + "default": frappe.defaults.get_default("company") + }, + { + "fieldname":"from_date", + "label": __("From Date"), + "fieldtype": "Date", + "width": "80", + "reqd": 1, + "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1), + }, + { + "fieldname":"to_date", + "label": __("To Date"), + "fieldtype": "Date", + "width": "80", + "reqd": 1, + "default": frappe.datetime.get_today() + }, + { + "fieldname": "material_request", + "label": __("Material Request"), + "fieldtype": "Link", + "width": "80", + "options": "Material Request", + "get_query": () =>{ + return { + filters: { + "docstatus": 1, + "material_request_type": "Purchase", + "per_received": ["<", 100] + } + } + } + }, + { + "fieldname": "group_by_mr", + "label": __("Group by Material Request"), + "fieldtype": "Check", + "default": 0 + } + ], + + "formatter": function (value, row, column, data, default_formatter) { + value = default_formatter(value, row, column, data); + + if (column.fieldname == "ordered_qty" && data && data.ordered_qty > 0) { + value = "" + value + ""; + } + return value; + } +}; diff --git a/erpnext/buying/report/requested_items_to_order/requested_items_to_order.json b/erpnext/buying/report/requested_items_to_order/requested_items_to_order.json new file mode 100644 index 00000000000..4a0578be4bf --- /dev/null +++ b/erpnext/buying/report/requested_items_to_order/requested_items_to_order.json @@ -0,0 +1,34 @@ +{ + "add_total_row": 1, + "creation": "2020-05-04 20:23:57.750719", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "modified": "2020-05-05 13:05:51.723951", + "modified_by": "Administrator", + "module": "Buying", + "name": "Requested Items to Order", + "owner": "Administrator", + "prepared_report": 0, + "query": "", + "ref_doctype": "Material Request", + "report_name": "Requested Items to Order", + "report_type": "Script Report", + "roles": [ + { + "role": "Purchase Manager" + }, + { + "role": "Stock Manager" + }, + { + "role": "Stock User" + }, + { + "role": "Purchase User" + } + ] +} \ No newline at end of file diff --git a/erpnext/buying/report/requested_items_to_order/requested_items_to_order.py b/erpnext/buying/report/requested_items_to_order/requested_items_to_order.py new file mode 100644 index 00000000000..a021d3c1cab --- /dev/null +++ b/erpnext/buying/report/requested_items_to_order/requested_items_to_order.py @@ -0,0 +1,211 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +import copy +from frappe import _ +from frappe.utils import flt, date_diff, getdate + +def execute(filters=None): + if not filters: + return [],[] + + validate_filters(filters) + + columns = get_columns(filters) + conditions = get_conditions(filters) + + #get queried data + data = get_data(filters, conditions) + + #prepare data for report and chart views + data, chart_data = prepare_data(data, filters) + + return columns, data, None, chart_data + +def validate_filters(filters): + from_date, to_date = filters.get("from_date"), filters.get("to_date") + + if not from_date and to_date: + frappe.throw(_("From and To Dates are required.")) + elif date_diff(to_date, from_date) < 0: + frappe.throw(_("To Date cannot be before From Date.")) + +def get_conditions(filters): + conditions = '' + + if filters.get("from_date") and filters.get("to_date"): + conditions += " and mr.transaction_date between '{0}' and '{1}'".format(filters.get("from_date"),filters.get("to_date")) + + if filters.get("company"): + conditions += " and mr.company = '{0}'".format(filters.get("company")) + + if filters.get("material_request"): + conditions += " and mr.name = '{0}'".format(filters.get("material_request")) + + return conditions + +def get_data(filters, conditions): + data = frappe.db.sql(""" + select + mr.name as material_request, + mr.transaction_date as date, + mr_item.schedule_date as required_date, + mr_item.item_code as item_code, + sum(ifnull(mr_item.stock_qty, 0)) as qty, + ifnull(mr_item.stock_uom, '') as uom, + sum(ifnull(mr_item.ordered_qty, 0)) as ordered_qty, + (sum(mr_item.stock_qty) - sum(ifnull(mr_item.ordered_qty, 0))) as qty_to_order, + mr_item.item_name as item_name, + mr.company as company + from + `tabMaterial Request` mr, `tabMaterial Request Item` mr_item + where + mr_item.parent = mr.name + and mr.material_request_type = "Purchase" + and mr.docstatus = 1 + and mr.status != "Stopped" + {conditions} + group by mr.name, mr_item.item_code + having + sum(ifnull(mr_item.ordered_qty, 0)) < sum(ifnull(mr_item.stock_qty, 0)) + order by mr.transaction_date, mr.schedule_date""".format(conditions=conditions), as_dict=1) + + return data + +def prepare_data(data, filters): + """Prepare consolidated Report data and Chart data""" + material_request_map = {} + + for row in data: + if not row["material_request"] in material_request_map: + # create an entry with mr as key + row_copy = copy.deepcopy(row) + material_request_map[row["material_request"]] = row_copy + else: + mr_row = material_request_map[row["material_request"]] + mr_row["required_date"] = min(getdate(mr_row["required_date"]), getdate(row["required_date"])) + + #sum numeric rows + fields = ["qty", "ordered_qty", "qty_to_order"] + for field in fields: + mr_row[field] = flt(mr_row[field]) + flt(row[field]) + + chart_data = prepare_chart_data(material_request_map) + + if filters.get("group_by_mr"): + data =[] + for mr in material_request_map: + data.append(material_request_map[mr]) + return data, chart_data + + return data, chart_data + +def prepare_chart_data(data): + labels, qty_to_order, ordered_qty = [], [], [] + + for row in data: + mr_row = data[row] + labels.append(mr_row["material_request"]) + qty_to_order.append(mr_row["qty_to_order"]) + ordered_qty.append(mr_row["ordered_qty"]) + + chart_data = { + "data" : { + "labels": labels, + "datasets": [ + { + 'name': _('Qty to Order'), + 'values': qty_to_order + }, + { + 'name': _('Ordered Qty'), + 'values': ordered_qty + } + ] + }, + "type": "bar", + "barOptions": { + "stacked": 1 + }, + } + + return chart_data + +def get_columns(filters): + columns = [ + { + "label": _("Material Request"), + "fieldname": "material_request", + "fieldtype": "Link", + "options": "Material Request", + "width": 150 + }, + { + "label":_("Date"), + "fieldname": "date", + "fieldtype": "Date", + "width": 90 + }, + { + "label":_("Required By"), + "fieldname": "required_date", + "fieldtype": "Date", + "width": 100 + } + ] + + if not filters.get("group_by_mr"): + columns.extend([{ + "label":_("Item Code"), + "fieldname": "item_code", + "fieldtype": "Link", + "options": "Item", + "width": 100 + }, + { + "label":_("Item Name"), + "fieldname": "item_name", + "fieldtype": "Data", + "width": 100 + }, + { + "label": _("UOM"), + "fieldname": "uom", + "fieldtype": "Data", + "width": 100, + }]) + + columns.extend([ + { + "label": _("Qty"), + "fieldname": "qty", + "fieldtype": "Float", + "width": 120, + "convertible": "qty" + }, + { + "label": _("Ordered Qty"), + "fieldname": "ordered_qty", + "fieldtype": "Float", + "width": 120, + "convertible": "qty" + }, + { + "label": _("Qty to Order"), + "fieldname": "qty_to_order", + "fieldtype": "Float", + "width": 120, + "convertible": "qty" + }, + { + "label": _("Company"), + "fieldname": "company", + "fieldtype": "Link", + "options": "Company", + "width": 100 + } + ]) + + return columns From a2a1e257ae92fce590a208e26a970bfc6fe604ef Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 6 May 2020 11:09:33 +0530 Subject: [PATCH 040/608] fix: set Issue metrics using Duration fieldtype --- erpnext/support/doctype/issue/issue.py | 38 ++++++-------------------- 1 file changed, 9 insertions(+), 29 deletions(-) diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index 1489f431384..df0a2f662dd 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -302,6 +302,7 @@ def set_service_level_agreement_variance(issue=None): frappe.db.set_value(dt="Issue", dn=doc.name, field="agreement_fulfilled", val="Failed", update_modified=False) def set_average_response_time(issue): + # avg response time for all the responses communications = frappe.get_list("Communication", filters={ "reference_doctype": issue.doctype, "reference_name": issue.name @@ -319,18 +320,17 @@ def set_average_response_time(issue): response_times.append(response_time) avg_response_time = sum(response_times) / len(response_times) - avg_response_time = timedelta(seconds=avg_response_time) - duration = get_duration(avg_response_time) - issue.db_set('avg_response_time', duration) + issue.db_set('avg_response_time', avg_response_time) def set_resolution_time(issue): - resolution_time = time_diff(now_datetime(), issue.creation) - duration = get_duration(resolution_time) - issue.db_set('resolution_time', duration) + # total time taken from issue creation to closing + resolution_time = time_diff_in_seconds(now_datetime(), issue.creation) + issue.db_set('resolution_time', resolution_time) def set_user_operational_time(issue): + # total time taken by a user to close the issue apart from wait_time communications = frappe.get_list("Communication", filters={ "reference_doctype": issue.doctype, "reference_name": issue.name @@ -346,30 +346,10 @@ def set_user_operational_time(issue): if wait_time > 0: pending_time.append(wait_time) - total_pending_time = timedelta(seconds=sum(pending_time)) + total_pending_time = sum(pending_time) resolution_time_in_secs = time_diff_in_seconds(now_datetime(), issue.creation) - resolution_time = timedelta(seconds=resolution_time_in_secs) - user_operational_time = resolution_time - total_pending_time - duration = get_duration(user_operational_time) - issue.db_set('user_operational_time', duration) - - -def get_duration(time): - days = time.days - seconds = time.seconds - hours = time.seconds // 3600 - mins = (time.seconds // 60) % 60 - duration = "" - if days: - duration += str(days) + " day" - duration += "s " if days > 1 else " " - if hours: - duration += str(hours) + " hour" - duration += "s " if hours > 1 else " " - if mins: - duration += str(mins) + " min" - duration += "s" if mins > 1 else "" - return duration + user_operational_time = resolution_time_in_secs - total_pending_time + issue.db_set('user_operational_time', user_operational_time) def get_list_context(context=None): From fc4c795661d17a682b4abe97fa194fd501ec196f Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 6 May 2020 11:42:43 +0530 Subject: [PATCH 041/608] fix: reset issue metrics on Reopen and Split --- erpnext/support/doctype/issue/issue.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index df0a2f662dd..f2ee75498e7 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -71,6 +71,7 @@ class Issue(Document): if self.status=="Open" and status !="Open": # if no date, it should be set as None and not a blank string "", as per mysql strict config self.resolution_date = None + self.reset_issue_metrics() def update_agreement_status(self): if self.service_level_agreement and self.agreement_fulfilled == "Ongoing": @@ -131,6 +132,7 @@ class Issue(Document): replicated_issue.response_by_variance = None replicated_issue.resolution_by = None replicated_issue.resolution_by_variance = None + replicated_issue.reset_issue_metrics() frappe.get_doc(replicated_issue).insert() @@ -224,6 +226,12 @@ class Issue(Document): self.agreement_fulfilled = "Ongoing" self.save() + def reset_issue_metrics(self): + self.db_set('resolution_time', 0) + self.db_set('user_operational_time', 0) + self.db_set('avg_response_time',0) + + def get_expected_time_for(parameter, service_level, start_date_time): current_date_time = start_date_time expected_time = current_date_time From f77d7243dc6ef71fcfbf29f20464581cd7ac96b3 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 6 May 2020 11:47:52 +0530 Subject: [PATCH 042/608] fix: remove Service Level DocType --- .../support/doctype/service_level/__init__.py | 0 .../doctype/service_level/service_level.js | 6 - .../doctype/service_level/service_level.json | 111 ------------- .../doctype/service_level/service_level.py | 95 ----------- .../service_level/service_level_dashboard.py | 12 -- .../service_level/test_service_level.py | 149 ------------------ .../service_level_agreement.json | 11 +- 7 files changed, 7 insertions(+), 377 deletions(-) delete mode 100644 erpnext/support/doctype/service_level/__init__.py delete mode 100644 erpnext/support/doctype/service_level/service_level.js delete mode 100644 erpnext/support/doctype/service_level/service_level.json delete mode 100644 erpnext/support/doctype/service_level/service_level.py delete mode 100644 erpnext/support/doctype/service_level/service_level_dashboard.py delete mode 100644 erpnext/support/doctype/service_level/test_service_level.py diff --git a/erpnext/support/doctype/service_level/__init__.py b/erpnext/support/doctype/service_level/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/erpnext/support/doctype/service_level/service_level.js b/erpnext/support/doctype/service_level/service_level.js deleted file mode 100644 index abe254bd036..00000000000 --- a/erpnext/support/doctype/service_level/service_level.js +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Service Level', { - -}); diff --git a/erpnext/support/doctype/service_level/service_level.json b/erpnext/support/doctype/service_level/service_level.json deleted file mode 100644 index dced3aa9e90..00000000000 --- a/erpnext/support/doctype/service_level/service_level.json +++ /dev/null @@ -1,111 +0,0 @@ -{ - "autoname": "field:service_level", - "creation": "2018-11-19 12:44:30.407502", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "service_level", - "employee_group", - "column_break_2", - "holiday_list", - "default_priority", - "response_and_resoution_time", - "priorities", - "section_break_01", - "support_and_resolution" - ], - "fields": [ - { - "fieldname": "service_level", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Level", - "reqd": 1, - "unique": 1 - }, - { - "fieldname": "column_break_2", - "fieldtype": "Column Break" - }, - { - "fieldname": "holiday_list", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Holiday List (ignored during SLA calculation)", - "options": "Holiday List", - "reqd": 1 - }, - { - "fieldname": "employee_group", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Employee Group", - "options": "Employee Group" - }, - { - "fieldname": "response_and_resoution_time", - "fieldtype": "Section Break", - "label": "Response and Resoution Time" - }, - { - "fieldname": "section_break_01", - "fieldtype": "Section Break", - "label": "Support Hours" - }, - { - "fieldname": "support_and_resolution", - "fieldtype": "Table", - "label": "Support and Resolution", - "options": "Service Day", - "reqd": 1 - }, - { - "fieldname": "priorities", - "fieldtype": "Table", - "label": "Priorities", - "options": "Service Level Priority", - "reqd": 1 - }, - { - "fieldname": "default_priority", - "fieldtype": "Link", - "label": "Default Priority", - "options": "Issue Priority", - "read_only": 1 - } - ], - "modified": "2019-06-06 12:58:03.464056", - "modified_by": "Administrator", - "module": "Support", - "name": "Service Level", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "share": 1, - "write": 1 - }, - { - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "All", - "share": 1, - "write": 1 - } - ], - "sort_field": "modified", - "sort_order": "DESC" -} \ No newline at end of file diff --git a/erpnext/support/doctype/service_level/service_level.py b/erpnext/support/doctype/service_level/service_level.py deleted file mode 100644 index 89fa25c2338..00000000000 --- a/erpnext/support/doctype/service_level/service_level.py +++ /dev/null @@ -1,95 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - -from __future__ import unicode_literals -import frappe -from frappe import _ -from frappe.model.document import Document -from datetime import datetime -from frappe.utils import get_weekdays - -class ServiceLevel(Document): - - def validate(self): - self.check_priorities() - self.check_support_and_resolution() - - def check_priorities(self): - default_priority = [] - priorities = [] - - for priority in self.priorities: - # Check if response and resolution time is set for every priority - if not (priority.response_time or priority.resolution_time): - frappe.throw(_("Set Response Time and Resolution for Priority {0} at index {1}.").format(priority.priority, priority.idx)) - - priorities.append(priority.priority) - - if priority.default_priority: - default_priority.append(priority.default_priority) - - if priority.response_time_period == "Hour": - response = priority.response_time * 0.0416667 - elif priority.response_time_period == "Day": - response = priority.response_time - elif priority.response_time_period == "Week": - response = priority.response_time * 7 - - if priority.resolution_time_period == "Hour": - resolution = priority.resolution_time * 0.0416667 - elif priority.resolution_time_period == "Day": - resolution = priority.resolution_time - elif priority.resolution_time_period == "Week": - resolution = priority.resolution_time * 7 - - if response > resolution: - frappe.throw(_("Response Time for {0} at index {1} can't be greater than Resolution Time.").format(priority.priority, priority.idx)) - - # Check if repeated priority - if not len(set(priorities)) == len(priorities): - repeated_priority = get_repeated(priorities) - frappe.throw(_("Priority {0} has been repeated.").format(repeated_priority)) - - # Check if repeated default priority - if not len(set(default_priority)) == len(default_priority): - frappe.throw(_("Select only one Priority as Default.")) - - # set default priority from priorities - try: - self.default_priority = next(d.priority for d in self.priorities if d.default_priority) - except Exception: - frappe.throw(_("Select a Default Priority.")) - - def check_support_and_resolution(self): - week = get_weekdays() - support_days = [] - - for support_and_resolution in self.support_and_resolution: - # Check if start and end time is set for every support day - if not (support_and_resolution.start_time or support_and_resolution.end_time): - frappe.throw(_("Set Start Time and End Time for \ - Support Day {0} at index {1}.".format(support_and_resolution.workday, support_and_resolution.idx))) - - support_days.append(support_and_resolution.workday) - support_and_resolution.idx = week.index(support_and_resolution.workday) + 1 - - if support_and_resolution.start_time >= support_and_resolution.end_time: - frappe.throw(_("Start Time can't be greater than or equal to End Time \ - for {0}.".format(support_and_resolution.workday))) - - # Check for repeated workday - if not len(set(support_days)) == len(support_days): - repeated_days = get_repeated(support_days) - frappe.throw(_("Workday {0} has been repeated.").format(repeated_days)) - -def get_repeated(values): - unique_list = [] - diff = [] - for value in values: - if value not in unique_list: - unique_list.append(str(value)) - else: - if value not in diff: - diff.append(str(value)) - return " ".join(diff) diff --git a/erpnext/support/doctype/service_level/service_level_dashboard.py b/erpnext/support/doctype/service_level/service_level_dashboard.py deleted file mode 100644 index 393095e1179..00000000000 --- a/erpnext/support/doctype/service_level/service_level_dashboard.py +++ /dev/null @@ -1,12 +0,0 @@ -from frappe import _ - -def get_data(): - return { - 'fieldname': 'service_level', - 'transactions': [ - { - 'label': _('Service Level Agreement'), - 'items': ['Service Level Agreement'] - } - ] - } \ No newline at end of file diff --git a/erpnext/support/doctype/service_level/test_service_level.py b/erpnext/support/doctype/service_level/test_service_level.py deleted file mode 100644 index 09577df1663..00000000000 --- a/erpnext/support/doctype/service_level/test_service_level.py +++ /dev/null @@ -1,149 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt -from __future__ import unicode_literals -from erpnext.hr.doctype.employee_group.test_employee_group import make_employee_group -from erpnext.support.doctype.issue_priority.test_issue_priority import make_priorities - -import frappe -import unittest - -class TestServiceLevel(unittest.TestCase): - - def test_service_level(self): - employee_group = make_employee_group() - make_holiday_list() - make_priorities() - - # Default Service Level - test_make_service_level = create_service_level("__Test Service Level", "__Test Holiday List", employee_group, 4, 6) - get_make_service_level = get_service_level("__Test Service Level") - - self.assertEqual(test_make_service_level.name, get_make_service_level.name) - self.assertEqual(test_make_service_level.holiday_list, get_make_service_level.holiday_list) - self.assertEqual(test_make_service_level.employee_group, get_make_service_level.employee_group) - - # Service Level - test_make_service_level = create_service_level("_Test Service Level", "__Test Holiday List", employee_group, 2, 3) - get_make_service_level = get_service_level("_Test Service Level") - - self.assertEqual(test_make_service_level.name, get_make_service_level.name) - self.assertEqual(test_make_service_level.holiday_list, get_make_service_level.holiday_list) - self.assertEqual(test_make_service_level.employee_group, get_make_service_level.employee_group) - - -def create_service_level(service_level, holiday_list, employee_group, response_time, resolution_time): - sl = frappe.get_doc({ - "doctype": "Service Level", - "service_level": service_level, - "holiday_list": holiday_list, - "employee_group": employee_group, - "priorities": [ - { - "priority": "Low", - "response_time": response_time, - "response_time_period": "Hour", - "resolution_time": resolution_time, - "resolution_time_period": "Hour", - }, - { - "priority": "Medium", - "response_time": response_time, - "default_priority": 1, - "response_time_period": "Hour", - "resolution_time": resolution_time, - "resolution_time_period": "Hour", - }, - { - "priority": "High", - "response_time": response_time, - "response_time_period": "Hour", - "resolution_time": resolution_time, - "resolution_time_period": "Hour", - } - ], - "support_and_resolution": [ - { - "workday": "Monday", - "start_time": "10:00:00", - "end_time": "18:00:00", - }, - { - "workday": "Tuesday", - "start_time": "10:00:00", - "end_time": "18:00:00", - }, - { - "workday": "Wednesday", - "start_time": "10:00:00", - "end_time": "18:00:00", - }, - { - "workday": "Thursday", - "start_time": "10:00:00", - "end_time": "18:00:00", - }, - { - "workday": "Friday", - "start_time": "10:00:00", - "end_time": "18:00:00", - }, - { - "workday": "Saturday", - "start_time": "10:00:00", - "end_time": "18:00:00", - }, - { - "workday": "Sunday", - "start_time": "10:00:00", - "end_time": "18:00:00", - } - ] - }) - - sl_exists = frappe.db.exists("Service Level", {"service_level": service_level}) - - if not sl_exists: - sl.insert() - return sl - else: - return frappe.get_doc("Service Level", {"service_level": service_level}) - -def get_service_level(service_level): - return frappe.get_doc("Service Level", service_level) - -def make_holiday_list(): - holiday_list = frappe.db.exists("Holiday List", "__Test Holiday List") - if not holiday_list: - now = frappe.utils.now_datetime() - holiday_list = frappe.get_doc({ - "doctype": "Holiday List", - "holiday_list_name": "__Test Holiday List", - "from_date": "2019-01-01", - "to_date": "2019-12-31", - "holidays": [ - { - "description": "Test Holiday 1", - "holiday_date": "2019-03-05" - }, - { - "description": "Test Holiday 2", - "holiday_date": "2019-03-07" - }, - { - "description": "Test Holiday 3", - "holiday_date": "2019-02-11" - }, - ] - }).insert() - -def create_service_level_for_sla(): - employee_group = make_employee_group() - make_holiday_list() - make_priorities() - - # Default Service Level - create_service_level("__Test Service Level", "__Test Holiday List", employee_group, 4, 6) - - # Service Level - create_service_level("_Test Service Level", "__Test Holiday List", employee_group, 2, 3) diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.json b/erpnext/support/doctype/service_level_agreement/service_level_agreement.json index 3725e15a549..2d33c3e033f 100644 --- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.json +++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.json @@ -40,7 +40,8 @@ "fieldname": "holiday_list", "fieldtype": "Link", "label": "Holiday List", - "options": "Holiday List" + "options": "Holiday List", + "reqd": 1 }, { "fieldname": "column_break_2", @@ -92,13 +93,15 @@ "fieldname": "support_and_resolution", "fieldtype": "Table", "label": "Support and Resolution", - "options": "Service Day" + "options": "Service Day", + "reqd": 1 }, { "fieldname": "priorities", "fieldtype": "Table", "label": "Priorities", - "options": "Service Level Priority" + "options": "Service Level Priority", + "reqd": 1 }, { "default": "1", @@ -150,7 +153,7 @@ } ], "links": [], - "modified": "2020-04-28 14:10:18.767202", + "modified": "2020-05-06 11:46:38.834810", "modified_by": "Administrator", "module": "Support", "name": "Service Level Agreement", From f2d36364f5ce821ca172a2c76c711ee6c5b47746 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 6 May 2020 12:32:55 +0530 Subject: [PATCH 043/608] fix: handle issue metrics on Reopen and Close --- erpnext/support/doctype/issue/issue.json | 30 ++++++++++++++---------- erpnext/support/doctype/issue/issue.py | 18 +++++++------- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/erpnext/support/doctype/issue/issue.json b/erpnext/support/doctype/issue/issue.json index dfe0647cfa3..131e1cb9bfb 100644 --- a/erpnext/support/doctype/issue/issue.json +++ b/erpnext/support/doctype/issue/issue.json @@ -54,7 +54,7 @@ "attachment", "via_customer_portal", "resolution_time", - "user_operational_time" + "user_resolution_time" ], "fields": [ { @@ -374,27 +374,33 @@ { "bold": 1, "fieldname": "avg_response_time", - "fieldtype": "Data", + "fieldtype": "Duration", "label": "Average Response Time", - "read_only": 1 - }, - { - "fieldname": "user_operational_time", - "fieldtype": "Data", - "label": "User Operational Time", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "resolution_time", - "fieldtype": "Data", + "fieldtype": "Duration", "label": "Resolution Time", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "user_resolution_time", + "fieldtype": "Duration", + "label": "User Resolution Time", + "read_only": 1, + "show_days": 1, + "show_seconds": 1 } ], "icon": "fa fa-ticket", "idx": 7, "links": [], - "modified": "2020-04-28 23:42:28.576580", + "modified": "2020-05-06 12:28:58.093654", "modified_by": "Administrator", "module": "Support", "name": "Issue", diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index f2ee75498e7..4fb2d8a2b9b 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -63,10 +63,10 @@ class Issue(Document): self.resolution_date = frappe.flags.current_time or now_datetime() if frappe.db.get_value("Issue", self.name, "agreement_fulfilled") == "Ongoing": set_service_level_agreement_variance(issue=self.name) - set_average_response_time(issue=self) - set_resolution_time(issue=self) - set_user_operational_time(issue=self) self.update_agreement_status() + set_average_response_time(issue=self) + set_resolution_time(issue=self) + set_user_resolution_time(issue=self) if self.status=="Open" and status !="Open": # if no date, it should be set as None and not a blank string "", as per mysql strict config @@ -227,9 +227,9 @@ class Issue(Document): self.save() def reset_issue_metrics(self): - self.db_set('resolution_time', 0) - self.db_set('user_operational_time', 0) - self.db_set('avg_response_time',0) + self.db_set('resolution_time', None) + self.db_set('user_resolution_time', None) + self.db_set('avg_response_time', None) def get_expected_time_for(parameter, service_level, start_date_time): @@ -337,7 +337,7 @@ def set_resolution_time(issue): issue.db_set('resolution_time', resolution_time) -def set_user_operational_time(issue): +def set_user_resolution_time(issue): # total time taken by a user to close the issue apart from wait_time communications = frappe.get_list("Communication", filters={ "reference_doctype": issue.doctype, @@ -356,8 +356,8 @@ def set_user_operational_time(issue): total_pending_time = sum(pending_time) resolution_time_in_secs = time_diff_in_seconds(now_datetime(), issue.creation) - user_operational_time = resolution_time_in_secs - total_pending_time - issue.db_set('user_operational_time', user_operational_time) + user_resolution_time = resolution_time_in_secs - total_pending_time + issue.db_set('user_resolution_time', user_resolution_time) def get_list_context(context=None): From 3d891f8e8937e6233055ce89f176e35e9b818dcf Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 6 May 2020 13:00:33 +0530 Subject: [PATCH 044/608] fix: set SLA as Ongoing on Issue Reopen --- erpnext/support/doctype/issue/issue.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index 4fb2d8a2b9b..2d9392c572c 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -72,6 +72,7 @@ class Issue(Document): # if no date, it should be set as None and not a blank string "", as per mysql strict config self.resolution_date = None self.reset_issue_metrics() + self.agreement_fulfilled = "Ongoing" def update_agreement_status(self): if self.service_level_agreement and self.agreement_fulfilled == "Ongoing": From 8993d38dafd8515eff56ed026e9cfa5ff0e80ee1 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 6 May 2020 13:37:10 +0530 Subject: [PATCH 045/608] fix: set avg response time only when there are responses --- erpnext/support/doctype/issue/issue.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index 2d9392c572c..a9c4897017f 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -322,14 +322,14 @@ def set_average_response_time(issue): if len(communications): response_times = [] - for i in range(len(communications)-1): + for i in range(len(communications)): if communications[i].sent_or_received == "Sent" and communications[i-1].sent_or_received == "Received": response_time = time_diff_in_seconds(communications[i].creation, communications[i-1].creation) if response_time > 0: response_times.append(response_time) - - avg_response_time = sum(response_times) / len(response_times) - issue.db_set('avg_response_time', avg_response_time) + if response_times: + avg_response_time = sum(response_times) / len(response_times) + issue.db_set('avg_response_time', avg_response_time) def set_resolution_time(issue): @@ -349,7 +349,7 @@ def set_user_resolution_time(issue): ) pending_time = [] - for i in range(len(communications)-1): + for i in range(len(communications)): if communications[i].sent_or_received == "Received" and communications[i-1].sent_or_received == "Sent": wait_time = time_diff_in_seconds(communications[i].creation, communications[i-1].creation) if wait_time > 0: From 631260b632f01b5b0b16e7fd61a7fd1b40bd562f Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 6 May 2020 18:12:26 +0530 Subject: [PATCH 046/608] chore: Added fixtures and Onboarding. --- erpnext/buying/desk_page/buying/buying.json | 6 +- erpnext/buying/onboarding/buying/buying.json | 42 +++++++++ .../buying_settings/buying_settings.json | 16 ++++ .../create_a_supplier/create_a_supplier.json | 16 ++++ .../introduction_to_buying.json | 16 ++++ .../setup_wizard/data/dashboard_charts.py | 93 +++++++++++++++++++ 6 files changed, 187 insertions(+), 2 deletions(-) create mode 100644 erpnext/buying/onboarding/buying/buying.json create mode 100644 erpnext/buying/onboarding_step/buying_settings/buying_settings.json create mode 100644 erpnext/buying/onboarding_step/create_a_supplier/create_a_supplier.json create mode 100644 erpnext/buying/onboarding_step/introduction_to_buying/introduction_to_buying.json diff --git a/erpnext/buying/desk_page/buying/buying.json b/erpnext/buying/desk_page/buying/buying.json index 432f3c557b8..9749f3c97fc 100644 --- a/erpnext/buying/desk_page/buying/buying.json +++ b/erpnext/buying/desk_page/buying/buying.json @@ -54,10 +54,11 @@ "idx": 0, "is_standard": 1, "label": "Buying", - "modified": "2020-05-05 23:48:25.788598", + "modified": "2020-05-06 18:10:12.760321", "modified_by": "Administrator", "module": "Buying", "name": "Buying", + "onboarding": "Buying", "owner": "Administrator", "pin_to_bottom": 0, "pin_to_top": 0, @@ -107,5 +108,6 @@ "link_to": "Item-wise Purchase Register", "type": "Report" } - ] + ], + "shortcuts_label": "Quick Access" } \ No newline at end of file diff --git a/erpnext/buying/onboarding/buying/buying.json b/erpnext/buying/onboarding/buying/buying.json new file mode 100644 index 00000000000..c35309964e7 --- /dev/null +++ b/erpnext/buying/onboarding/buying/buying.json @@ -0,0 +1,42 @@ +{ + "allow_roles": [ + { + "role": "Purchase Manager" + }, + { + "role": "Purchase User" + }, + { + "role": "Stock Manager" + }, + { + "role": "Stock User" + } + ], + "creation": "2020-05-06 15:56:35.049205", + "docstatus": 0, + "doctype": "Onboarding", + "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/buying", + "idx": 0, + "is_complete": 0, + "modified": "2020-05-06 16:49:17.433261", + "modified_by": "Administrator", + "module": "Buying", + "name": "Buying", + "owner": "Administrator", + "steps": [ + { + "step": "Introduction to Buying" + }, + { + "step": "Create a Supplier" + }, + { + "step": "Buying Settings" + } + ], + "subtitle": "Products, Purchases, Analysis and more.", + "success_message": "The Buying Module is all set up!", + "title": "Let's Setup the Buying Module.", + "user_can_dismiss": 1 +} \ No newline at end of file diff --git a/erpnext/buying/onboarding_step/buying_settings/buying_settings.json b/erpnext/buying/onboarding_step/buying_settings/buying_settings.json new file mode 100644 index 00000000000..7274c0d62d1 --- /dev/null +++ b/erpnext/buying/onboarding_step/buying_settings/buying_settings.json @@ -0,0 +1,16 @@ +{ + "action": "Update Settings", + "creation": "2020-05-06 15:53:44.667414", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_skipped": 0, + "modified": "2020-05-06 16:51:56.862206", + "modified_by": "Administrator", + "name": "Buying Settings", + "owner": "Administrator", + "reference_document": "Buying Settings", + "title": "Configure Buying Settings." +} \ No newline at end of file diff --git a/erpnext/buying/onboarding_step/create_a_supplier/create_a_supplier.json b/erpnext/buying/onboarding_step/create_a_supplier/create_a_supplier.json new file mode 100644 index 00000000000..14164c33037 --- /dev/null +++ b/erpnext/buying/onboarding_step/create_a_supplier/create_a_supplier.json @@ -0,0 +1,16 @@ +{ + "action": "Create Entry", + "creation": "2020-05-06 15:46:09.019329", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 1, + "is_skipped": 0, + "modified": "2020-05-06 17:00:41.446942", + "modified_by": "Administrator", + "name": "Create a Supplier", + "owner": "Administrator", + "reference_document": "Supplier", + "title": "Create a Supplier" +} \ No newline at end of file diff --git a/erpnext/buying/onboarding_step/introduction_to_buying/introduction_to_buying.json b/erpnext/buying/onboarding_step/introduction_to_buying/introduction_to_buying.json new file mode 100644 index 00000000000..f1a027af369 --- /dev/null +++ b/erpnext/buying/onboarding_step/introduction_to_buying/introduction_to_buying.json @@ -0,0 +1,16 @@ +{ + "action": "Watch Video", + "creation": "2020-05-06 15:37:09.477765", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_skipped": 0, + "modified": "2020-05-06 16:47:47.378049", + "modified_by": "Administrator", + "name": "Introduction to Buying", + "owner": "Administrator", + "title": "Introduction to Buying", + "video_url": "https://youtu.be/efFajTTQBa8" +} \ No newline at end of file diff --git a/erpnext/setup/setup_wizard/data/dashboard_charts.py b/erpnext/setup/setup_wizard/data/dashboard_charts.py index b182dfc103f..8d293973906 100644 --- a/erpnext/setup/setup_wizard/data/dashboard_charts.py +++ b/erpnext/setup/setup_wizard/data/dashboard_charts.py @@ -32,6 +32,17 @@ def get_default_dashboards(): { "chart": "Expenses" }, { "chart": "Patient Appointments" } ] + }, + { + "doctype": "Dashboard", + "dashboard_name": "Buying", + "charts": [ + { "chart": "Purchase Analytics" }, + { "chart": "Material Request Purchase Analysis" }, + { "chart": "Purchase Order Analysis" }, + { "chart": "Requested Items to Order" }, + ] + } ], "Charts": [ @@ -123,6 +134,88 @@ def get_default_dashboards(): "document_type": "Patient Appointment", "type": "Line", "width": "Half" + }, + { + "doctype": "Dashboard Chart", + "owner": "Administrator", + "report_name": "Purchase Analytics", + "filters_json": json.dumps({ + "tree_type": "Item", + "doc_type": "Purchase Receipt", + "value_quantity": "Value", + "from_date": "2020-03-01", + "to_date": "2020-07-31", + "company": company.name, + "range": "Weekly" + }), + "type": "Line", + 'timeseries': 0, + "chart_type": "Report", + "chart_name": "Purchase Analytics", + "custom_options": json.dumps({ + "x_field": "entity", + "chart_type": "Line", + "y_axis_fields": [{"idx": 1, "__islocal": "true", "y_field": "total"}], + "y_fields": ["total"], + "lineOptions": {"regionFill": 1} + }) + }, + { + "doctype": "Dashboard Chart", + "owner": "Administrator", + "document_type": "Material Request", + "filters_json": '[["Material Request","status","not in",["Draft","Cancelled","Stopped",null],false],["Material Request","material_request_type","=","Purchase",false],["Material Request","company","=", "{company}", false]]'.format(company=company.name), + "is_custom": 0, + "type": "Donut", + "timeseries": 0, + "chart_type": "Group By", + "group_by_based_on": "status", + "chart_name": "Material Request Purchase Analysis", + "group_by_type": "Count", + "custom_options": json.dumps({"height": 300}) + + }, + { + "doctype": "Dashboard Chart", + "owner": "Administrator", + "report_name": "Purchase Order Analysis", + "filters_json": json.dumps({ + "company": company.name, + "from_date": "2020-04-04", + "to_date": "2020-07-04", + "chart_based_on": "Quantity" + }), + "is_custom": 1, + "type": "Donut", + "timeseries": 0, + "chart_type": "Report", + "chart_name": "Purchase Order Analysis", + "custom_options": json.dumps({ + "type": "donut", + "height": 300, + "axisOptions": {"shortenYAxisNumbers": 1} + }) + }, + { + "doctype": "Dashboard Chart", + "owner": "Administrator", + "report_name": "Requested Items to Order", + "filters_json": json.dumps({ + "company": company.name, + "from_date": "2020-04-01", + "to_date": "2020-07-01", + "group_by_mr": 0 + }), + "is_custom": 1, + "type": "Bar", + "timeseries": 0, + "chart_type": "Report", + "chart_name": "Requested Items to Order", + "custom_options": json.dumps({ + "type": "bar", + "barOptions": {"stacked": 1}, + "axisOptions": {"shortenYAxisNumbers": 1} + }) } ] } From 4a73bc672b45d612fbdd3623479ac80765dfe306 Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 6 May 2020 18:34:07 +0530 Subject: [PATCH 047/608] fix: Old buying config update & bar chart on desk - Line chart was inapprpriate as the values were not continuous. --- erpnext/config/buying.py | 2 +- erpnext/setup/setup_wizard/data/dashboard_charts.py | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/erpnext/config/buying.py b/erpnext/config/buying.py index 1d4054786e6..16b49a1e578 100644 --- a/erpnext/config/buying.py +++ b/erpnext/config/buying.py @@ -166,7 +166,7 @@ def get_data(): { "type": "report", "is_query_report": True, - "name": "Requested Items To Be Ordered", + "name": "Requested Items To Order", "reference_doctype": "Material Request", "onboard": 1, }, diff --git a/erpnext/setup/setup_wizard/data/dashboard_charts.py b/erpnext/setup/setup_wizard/data/dashboard_charts.py index 8d293973906..9c654a829d5 100644 --- a/erpnext/setup/setup_wizard/data/dashboard_charts.py +++ b/erpnext/setup/setup_wizard/data/dashboard_charts.py @@ -154,10 +154,9 @@ def get_default_dashboards(): "chart_name": "Purchase Analytics", "custom_options": json.dumps({ "x_field": "entity", - "chart_type": "Line", + "chart_type": "Bar", "y_axis_fields": [{"idx": 1, "__islocal": "true", "y_field": "total"}], - "y_fields": ["total"], - "lineOptions": {"regionFill": 1} + "y_fields": ["total"] }) }, { From 20a1d375f3898cf510b0e0bbbdf9e2e3ff754f93 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Wed, 6 May 2020 20:26:00 +0530 Subject: [PATCH 048/608] feat: add fixture for project dashboards --- erpnext/projects/dashboard_fixtures.py | 48 ++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 erpnext/projects/dashboard_fixtures.py diff --git a/erpnext/projects/dashboard_fixtures.py b/erpnext/projects/dashboard_fixtures.py new file mode 100644 index 00000000000..63b3893b76a --- /dev/null +++ b/erpnext/projects/dashboard_fixtures.py @@ -0,0 +1,48 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +import frappe +import json + +def get_company_for_dashboards(): + company = frappe.defaults.get_defaults().company + if company: + return company + else: + company_list = frappe.get_list("Company") + if company_list: + return company_list[0].name + return None + +def get_data(): + return frappe._dict({ + "dashboards": get_dashboards(), + "charts": get_charts(), + }) + +def get_dashboards(): + return [{ + "doctype": "Dashboard", + "name": "Project", + "dashboard_name": "Project", + "charts": [ + { "chart": "Project Summary", "width": "Full" } + ] + }] + +def get_charts(): + company = frappe.get_doc("Company", get_company_for_dashboards()) + + return [ + { + 'doctype': 'Dashboard Chart', + 'name': 'Project Summary', + 'chart_name': 'Project Summary', + 'chart_type': 'Report', + 'report_name': 'Project Summary', + 'is_public': 1, + 'filters_json': json.dumps({"company": company.name, "status": "Open"}), + 'type': 'Bar', + 'custom_options': '{"type": "bar", "colors": ["#fc4f51", "#78d6ff", "#7575ff"], "axisOptions": { "shortenYAxisNumbers": 1}, "barOptions": { "stacked": 1 }}', + } + ] \ No newline at end of file From 318affedb8d63fc37544c59ca79caba581f8efb3 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Wed, 6 May 2020 20:26:12 +0530 Subject: [PATCH 049/608] fix: divide by zero error --- erpnext/projects/report/project_summary/project_summary.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/projects/report/project_summary/project_summary.py b/erpnext/projects/report/project_summary/project_summary.py index 66d68bda06a..a20d7f25a3a 100644 --- a/erpnext/projects/report/project_summary/project_summary.py +++ b/erpnext/projects/report/project_summary/project_summary.py @@ -112,6 +112,9 @@ def get_chart_data(data): } def get_report_summary(data): + if not data: + return None + avg_completion = sum([project.percent_complete for project in data]) / len(data) total = sum([project.total_tasks for project in data]) total_overdue = sum([project.overdue_tasks for project in data]) From 2f81f754acc4c2db7ee4632e85b411a1b9736634 Mon Sep 17 00:00:00 2001 From: Myuddin khatri Date: Thu, 7 May 2020 15:11:39 +0530 Subject: [PATCH 050/608] fix(crm): fix lead while updating contact details it use to throw error while updating contact details for lead "Next Contact Date cannot be in the past" is being solved --- erpnext/crm/utils.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/erpnext/crm/utils.py b/erpnext/crm/utils.py index 38bf79e5fcb..95b19ec21ec 100644 --- a/erpnext/crm/utils.py +++ b/erpnext/crm/utils.py @@ -19,6 +19,5 @@ def update_lead_phone_numbers(contact, method): mobile_no = primary_mobile_nos[0] lead = frappe.get_doc("Lead", contact_lead) - lead.phone = phone - lead.mobile_no = mobile_no - lead.save() + lead.db_set("phone", phone) + lead.db_set("mobile_no", mobile_no) From da41724d9da94acf82c44912b3352535ca304fb9 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Fri, 8 May 2020 15:12:08 +0530 Subject: [PATCH 051/608] feat: save shipping address to woocommerce customer --- .../connectors/woocommerce_connection.py | 57 +++++++++++-------- 1 file changed, 34 insertions(+), 23 deletions(-) diff --git a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py index 618865200cf..54fa6085d03 100644 --- a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py +++ b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py @@ -49,12 +49,13 @@ def _order(*args, **kwargs): if event == "created": sys_lang = frappe.get_single("System Settings").language or 'en' raw_billing_data = order.get("billing") + raw_shipping_data = order.get("shipping") customer_name = raw_billing_data.get("first_name") + " " + raw_billing_data.get("last_name") - link_customer_and_address(raw_billing_data, customer_name) + link_customer_and_address(raw_billing_data, raw_shipping_data, customer_name) link_items(order.get("line_items"), woocommerce_settings, sys_lang) create_sales_order(order, woocommerce_settings, customer_name, sys_lang) -def link_customer_and_address(raw_billing_data, customer_name): +def link_customer_and_address(raw_billing_data, raw_shipping_data, customer_name): customer_woo_com_email = raw_billing_data.get("email") customer_exists = frappe.get_value("Customer", {"woocommerce_email": customer_woo_com_email}) if not customer_exists: @@ -68,38 +69,48 @@ def link_customer_and_address(raw_billing_data, customer_name): customer.customer_name = customer_name customer.woocommerce_email = customer_woo_com_email customer.flags.ignore_mandatory = True - customer.save() + customer.save() if customer_exists: frappe.rename_doc("Customer", old_name, customer_name) - address = frappe.get_doc("Address", {"woocommerce_email": customer_woo_com_email}) + billing_address = frappe.get_doc("Address", {"woocommerce_email": customer_woo_com_email, "address_type": "Billing"}) + shipping_address = frappe.get_doc("Address", {"woocommerce_email": customer_woo_com_email, "address_type": "Shipping"}) else: - address = frappe.new_doc("Address") + billing_address = create_address(raw_billing_data, customer, "Billing") + shipping_address = create_address(raw_shipping_data, customer, "Shipping") - address.address_line1 = raw_billing_data.get("address_1", "Not Provided") - address.address_line2 = raw_billing_data.get("address_2", "Not Provided") - address.city = raw_billing_data.get("city", "Not Provided") - address.woocommerce_email = customer_woo_com_email - address.address_type = "Billing" - address.country = frappe.get_value("Country", {"code": raw_billing_data.get("country", "IN").lower()}) - address.state = raw_billing_data.get("state") - address.pincode = raw_billing_data.get("postcode") - address.phone = raw_billing_data.get("phone") - address.email_id = customer_woo_com_email + if customer_exists: + rename_address(billing_address, customer) + rename_address(shipping_address, customer) + +def create_address(raw_data, customer, address_type): + address = frappe.new_doc("Address") + + address.address_line1 = raw_data.get("address_1", "Not Provided") + address.address_line2 = raw_data.get("address_2", "Not Provided") + address.city = raw_data.get("city", "Not Provided") + address.woocommerce_email = customer.woocommerce_email + address.address_type = address_type + address.country = frappe.get_value("Country", {"code": raw_data.get("country", "IN").lower()}) + address.state = raw_data.get("state") + address.pincode = raw_data.get("postcode") + address.phone = raw_data.get("phone") + address.email_id = customer.woocommerce_email address.append("links", { "link_doctype": "Customer", "link_name": customer.customer_name }) + address.flags.ignore_mandatory = True address = address.save() - if customer_exists: - old_address_title = address.name - new_address_title = customer.customer_name + "-billing" - address.address_title = customer.customer_name - address.save() +def rename_address(address, customer): + old_address_title = address.name + new_address_title = customer.customer_name + "-" + address.address_type + address.address_title = customer.customer_name + address.save() - frappe.rename_doc("Address", old_address_title, new_address_title) + frappe.rename_doc("Address", old_address_title, new_address_title) def link_items(items_list, woocommerce_settings, sys_lang): for item_data in items_list: @@ -111,7 +122,7 @@ def link_items(items_list, woocommerce_settings, sys_lang): else: #Create Item item = frappe.new_doc("Item") - + item.item_name = item_data.get("name") item.item_code = _("woocommerce - {0}", sys_lang).format(item_data.get("product_id")) item.woocommerce_id = item_data.get("product_id") @@ -171,7 +182,7 @@ def set_items_in_sales_order(new_sales_order, woocommerce_settings, order, sys_l add_tax_details(new_sales_order, order.get("shipping_tax"), "Shipping Tax", woocommerce_settings.f_n_f_account) add_tax_details(new_sales_order, order.get("shipping_total"), "Shipping Total", woocommerce_settings.f_n_f_account) - + def add_tax_details(sales_order, price, desc, tax_account_head): sales_order.append("taxes", { "charge_type":"Actual", From a788ab281e8daf24d57ede10325b0f88966d086d Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Fri, 8 May 2020 15:13:36 +0530 Subject: [PATCH 052/608] fix: typo in function name --- erpnext/tests/test_woocommerce.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/tests/test_woocommerce.py b/erpnext/tests/test_woocommerce.py index ce0f47d685f..df715ab2027 100644 --- a/erpnext/tests/test_woocommerce.py +++ b/erpnext/tests/test_woocommerce.py @@ -24,7 +24,7 @@ class TestWoocommerce(unittest.TestCase): woo_settings.creation_user = "Administrator" woo_settings.save(ignore_permissions=True) - def test_sales_order_for_woocommerece(self): + def test_sales_order_for_woocommerce(self): frappe.flags.woocomm_test_order_data = {"id":75,"parent_id":0,"number":"74","order_key":"wc_order_5aa1281c2dacb","created_via":"checkout","version":"3.3.3","status":"processing","currency":"INR","date_created":"2018-03-08T12:10:04","date_created_gmt":"2018-03-08T12:10:04","date_modified":"2018-03-08T12:10:04","date_modified_gmt":"2018-03-08T12:10:04","discount_total":"0.00","discount_tax":"0.00","shipping_total":"150.00","shipping_tax":"0.00","cart_tax":"0.00","total":"649.00","total_tax":"0.00","prices_include_tax":False,"customer_id":12,"customer_ip_address":"103.54.99.5","customer_user_agent":"mozilla\\/5.0 (x11; linux x86_64) applewebkit\\/537.36 (khtml, like gecko) chrome\\/64.0.3282.186 safari\\/537.36","customer_note":"","billing":{"first_name":"Tony","last_name":"Stark","company":"Woocommerce","address_1":"Mumbai","address_2":"","city":"Dadar","state":"MH","postcode":"123","country":"IN","email":"tony@gmail.com","phone":"123457890"},"shipping":{"first_name":"Tony","last_name":"Stark","company":"","address_1":"Mumbai","address_2":"","city":"Dadar","state":"MH","postcode":"123","country":"IN"},"payment_method":"cod","payment_method_title":"Cash on delivery","transaction_id":"","date_paid":"","date_paid_gmt":"","date_completed":"","date_completed_gmt":"","cart_hash":"8e76b020d5790066496f244860c4703f","meta_data":[],"line_items":[{"id":80,"name":"Marvel","product_id":56,"variation_id":0,"quantity":1,"tax_class":"","subtotal":"499.00","subtotal_tax":"0.00","total":"499.00","total_tax":"0.00","taxes":[],"meta_data":[],"sku":"","price":499}],"tax_lines":[],"shipping_lines":[{"id":81,"method_title":"Flat rate","method_id":"flat_rate:1","total":"150.00","total_tax":"0.00","taxes":[],"meta_data":[{"id":623,"key":"Items","value":"Marvel × 1"}]}],"fee_lines":[],"coupon_lines":[],"refunds":[]} order() From cb1376b036edb7d3161c331cdf4f7451a2d97a3e Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Sat, 9 May 2020 12:05:33 +0530 Subject: [PATCH 053/608] chore: verbose error message for coa recursion --- .../doctype/tally_migration/tally_migration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py index d9c5852a6ed..462685f5e71 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py @@ -113,7 +113,7 @@ class TallyMigration(Document): try: coa = traverse({}, children, roots, roots, group_set) except RecursionError: - self.log() + self.log(_("Error occured while parsing Chart of Accounts: Please make sure that no two accounts have the same name")) for account in coa: coa[account]["root_type"] = root_type_map[account] From 0defefda92ee28158ceb54d8cd1b639e76df94fc Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 11 May 2020 12:14:46 +0530 Subject: [PATCH 054/608] feat: Standard accounts dashboard --- erpnext/accounts/accounts | 0 .../account_balance_timeline.py | 2 +- erpnext/accounts/dashboard_fixtures.py | 153 ++++++++++++++++++ .../accounts_receivable.py | 2 +- .../setup_wizard/data/dashboard_charts.py | 97 ----------- 5 files changed, 155 insertions(+), 99 deletions(-) create mode 100644 erpnext/accounts/accounts create mode 100644 erpnext/accounts/dashboard_fixtures.py diff --git a/erpnext/accounts/accounts b/erpnext/accounts/accounts new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.py b/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.py index c3e2f7db124..5decccb4869 100644 --- a/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.py +++ b/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.py @@ -6,7 +6,7 @@ import frappe, json from frappe import _ from frappe.utils import add_to_date, date_diff, getdate, nowdate, get_last_day, formatdate, get_link_to_form from erpnext.accounts.report.general_ledger.general_ledger import execute -from frappe.core.page.dashboard.dashboard import cache_source, get_from_date_from_timespan +from frappe.utils.dashboard import cache_source, get_from_date_from_timespan from frappe.desk.doctype.dashboard_chart.dashboard_chart import get_period_ending from frappe.utils.nestedset import get_descendants_of diff --git a/erpnext/accounts/dashboard_fixtures.py b/erpnext/accounts/dashboard_fixtures.py new file mode 100644 index 00000000000..746eb649437 --- /dev/null +++ b/erpnext/accounts/dashboard_fixtures.py @@ -0,0 +1,153 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +import frappe +import json +from frappe.utils import nowdate, add_months + +def get_company_for_dashboards(): + company = frappe.defaults.get_defaults().company + if company: + return company + else: + company_list = frappe.get_list("Company") + if company_list: + return company_list[0].name + return None + +def get_data(): + return frappe._dict({ + "dashboards": get_dashboards(), + "charts": get_charts() + }) + +def get_dashboards(): + + return [{ + "name": "Accounts Dashboard", + "dashboard_name": "Accounts", + "doctype": "Dashboard", + "charts": [ + { "chart": "Profit and Loss" , "width": "Full"}, + { "chart": "Incoming Bills"}, + { "chart": "Outgoing Bills"}, + { "chart": "Accounts Receivable Ageing"}, + { "chart": "Accounts Payable Ageing"}, + { "chart": "Bank Balance", "width": "Full"}, + ] + }] + +def get_charts(): + company = frappe.get_doc("Company", get_company_for_dashboards()) + bank_account = company.default_bank_account or get_account("Bank", company.name) + + return [ + { + "doctype": "Dashboard Charts", + "name": "Profit and Loss", + "owner": "Administrator", + "report_name": "Profit and Loss Statement", + "filters_json": json.dumps({ + "company": company.name, + "filter_based_on": "Date Range", + "period_start_date": add_months(nowdate(), -4), + "period_end_date": nowdate(), + "periodicity": "Monthly", + "include_default_book_entries": 1 + }), + "type": "Bar", + 'timeseries': 0, + "chart_type": "Report", + "chart_name": "Profit and Loss", + "is_custom": 1 + }, + { + "doctype": "Dashboard Chart", + "time_interval": "Monthly", + "name": "Incoming Bills", + "chart_name": "Incoming Bills (Purchase Invoice)", + "timespan": "Last Year", + "color": "#a83333", + "value_based_on": "base_grand_total", + "filters_json": json.dumps({}), + "chart_type": "Sum", + "timeseries": 1, + "based_on": "posting_date", + "owner": "Administrator", + "document_type": "Purchase Invoice", + "type": "Bar", + "width": "Half" + }, + { + "doctype": "Dashboard Chart", + "name": "Outgoing Bills", + "time_interval": "Monthly", + "chart_name": "Outgoing Bills (Sales Invoice)", + "timespan": "Last Year", + "color": "#7b933d", + "value_based_on": "base_grand_total", + "filters_json": json.dumps({}), + "chart_type": "Sum", + "timeseries": 1, + "based_on": "posting_date", + "owner": "Administrator", + "document_type": "Sales Invoice", + "type": "Bar", + "width": "Half" + }, + { + "doctype": "Dashboard Charts", + "name": "Accounts Receivable Ageing", + "owner": "Administrator", + "report_name": "Accounts Receivable", + "filters_json": json.dumps({ + "company": company.name, + "report_date": nowdate(), + "ageing_based_on": "Due Date", + "range1": 30, + "range2": 60, + "range3": 90, + "range4": 120 + }), + "type": "Donut", + 'timeseries': 0, + "chart_type": "Report", + "chart_name": "Accounts Receivable Ageing", + "is_custom": 1 + }, + { + "doctype": "Dashboard Charts", + "name": "Accounts Payable Ageing", + "owner": "Administrator", + "report_name": "Accounts Payable", + "filters_json": json.dumps({ + "company": company.name, + "report_date": nowdate(), + "ageing_based_on": "Due Date", + "range1": 30, + "range2": 60, + "range3": 90, + "range4": 120 + }), + "type": "Donut", + 'timeseries': 0, + "chart_type": "Report", + "chart_name": "Accounts Payable Ageing", + "is_custom": 1 + }, + { + "doctype": "Dashboard Charts", + "name": "Bank Balance", + "time_interval": "Quarterly", + "chart_name": "Bank Balance", + "timespan": "Last Year", + "filters_json": json.dumps({"company": company.name, "account": bank_account}), + "source": "Account Balance Timeline", + "chart_type": "Custom", + "timeseries": 1, + "owner": "Administrator", + "type": "Line", + "width": "Half" + }, + + ] \ No newline at end of file diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index e9c286fcf0d..a0a1b9783ac 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -546,7 +546,7 @@ class ReceivablePayableReport(object): self.filters.range1, self.filters.range2, self.filters.range3, self.filters.range4 = 30, 60, 90, 120 for i, days in enumerate([self.filters.range1, self.filters.range2, self.filters.range3, self.filters.range4]): - if row.age <= days: + if cint(row.age) <= cint(days): index = i break diff --git a/erpnext/setup/setup_wizard/data/dashboard_charts.py b/erpnext/setup/setup_wizard/data/dashboard_charts.py index b182dfc103f..2828307204d 100644 --- a/erpnext/setup/setup_wizard/data/dashboard_charts.py +++ b/erpnext/setup/setup_wizard/data/dashboard_charts.py @@ -3,21 +3,8 @@ from frappe import _ import frappe import json -def get_company_for_dashboards(): - company = frappe.defaults.get_defaults().company - if company: - return company - else: - company_list = frappe.get_list("Company") - if company_list: - return company_list[0].name - return None def get_default_dashboards(): - company = frappe.get_doc("Company", get_company_for_dashboards()) - income_account = company.default_income_account or get_account("Income Account", company.name) - expense_account = company.default_expense_account or get_account("Expense Account", company.name) - bank_account = company.default_bank_account or get_account("Bank", company.name) return { "Dashboards": [ @@ -25,90 +12,11 @@ def get_default_dashboards(): "doctype": "Dashboard", "dashboard_name": "Accounts", "charts": [ - { "chart": "Outgoing Bills (Sales Invoice)" }, - { "chart": "Incoming Bills (Purchase Invoice)" }, - { "chart": "Bank Balance" }, - { "chart": "Income" }, - { "chart": "Expenses" }, { "chart": "Patient Appointments" } ] } ], "Charts": [ - { - "doctype": "Dashboard Chart", - "time_interval": "Quarterly", - "chart_name": "Income", - "timespan": "Last Year", - "color": None, - "filters_json": json.dumps({"company": company.name, "account": income_account}), - "source": "Account Balance Timeline", - "chart_type": "Custom", - "timeseries": 1, - "owner": "Administrator", - "type": "Line", - "width": "Half" - }, - { - "doctype": "Dashboard Chart", - "time_interval": "Quarterly", - "chart_name": "Expenses", - "timespan": "Last Year", - "color": None, - "filters_json": json.dumps({"company": company.name, "account": expense_account}), - "source": "Account Balance Timeline", - "chart_type": "Custom", - "timeseries": 1, - "owner": "Administrator", - "type": "Line", - "width": "Half" - }, - { - "doctype": "Dashboard Chart", - "time_interval": "Quarterly", - "chart_name": "Bank Balance", - "timespan": "Last Year", - "color": "#ffb868", - "filters_json": json.dumps({"company": company.name, "account": bank_account}), - "source": "Account Balance Timeline", - "chart_type": "Custom", - "timeseries": 1, - "owner": "Administrator", - "type": "Line", - "width": "Half" - }, - { - "doctype": "Dashboard Chart", - "time_interval": "Monthly", - "chart_name": "Incoming Bills (Purchase Invoice)", - "timespan": "Last Year", - "color": "#a83333", - "value_based_on": "base_grand_total", - "filters_json": json.dumps({}), - "chart_type": "Sum", - "timeseries": 1, - "based_on": "posting_date", - "owner": "Administrator", - "document_type": "Purchase Invoice", - "type": "Bar", - "width": "Half" - }, - { - "doctype": "Dashboard Chart", - "time_interval": "Monthly", - "chart_name": "Outgoing Bills (Sales Invoice)", - "timespan": "Last Year", - "color": "#7b933d", - "value_based_on": "base_grand_total", - "filters_json": json.dumps({}), - "chart_type": "Sum", - "timeseries": 1, - "based_on": "posting_date", - "owner": "Administrator", - "document_type": "Sales Invoice", - "type": "Bar", - "width": "Half" - }, { "doctype": "Dashboard Chart", "time_interval": "Daily", @@ -126,8 +34,3 @@ def get_default_dashboards(): } ] } - -def get_account(account_type, company): - accounts = frappe.get_list("Account", filters={"account_type": account_type, "company": company}) - if accounts: - return accounts[0].name From 7a7add5001f65c74237c0e8d04daaa50be9a7866 Mon Sep 17 00:00:00 2001 From: barredterra Date: Mon, 11 May 2020 18:36:57 +0200 Subject: [PATCH 055/608] fix(report view): explicitly set column width for --- .../regional/report/datev/datev_constants.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/erpnext/regional/report/datev/datev_constants.py b/erpnext/regional/report/datev/datev_constants.py index a059ed365a9..e0637030051 100644 --- a/erpnext/regional/report/datev/datev_constants.py +++ b/erpnext/regional/report/datev/datev_constants.py @@ -465,60 +465,71 @@ QUERY_REPORT_COLUMNS = [ "label": "Umsatz (ohne Soll/Haben-Kz)", "fieldname": "Umsatz (ohne Soll/Haben-Kz)", "fieldtype": "Currency", + "width": 100 }, { "label": "Soll/Haben-Kennzeichen", "fieldname": "Soll/Haben-Kennzeichen", "fieldtype": "Data", + "width": 100 }, { "label": "Konto", "fieldname": "Konto", "fieldtype": "Data", + "width": 100 }, { "label": "Gegenkonto (ohne BU-Schlüssel)", "fieldname": "Gegenkonto (ohne BU-Schlüssel)", "fieldtype": "Data", + "width": 100 }, { "label": "Belegdatum", "fieldname": "Belegdatum", "fieldtype": "Date", + "width": 100 }, { "label": "Belegfeld 1", "fieldname": "Belegfeld 1", "fieldtype": "Data", + "width": 150 }, { "label": "Buchungstext", "fieldname": "Buchungstext", "fieldtype": "Text", + "width": 300 }, { "label": "Beleginfo - Art 1", "fieldname": "Beleginfo - Art 1", "fieldtype": "Link", - "options": "DocType" + "options": "DocType", + "width": 100 }, { "label": "Beleginfo - Inhalt 1", "fieldname": "Beleginfo - Inhalt 1", "fieldtype": "Dynamic Link", - "options": "Beleginfo - Art 1" + "options": "Beleginfo - Art 1", + "width": 150 }, { "label": "Beleginfo - Art 2", "fieldname": "Beleginfo - Art 2", "fieldtype": "Link", - "options": "DocType" + "options": "DocType", + "width": 100 }, { "label": "Beleginfo - Inhalt 2", "fieldname": "Beleginfo - Inhalt 2", "fieldtype": "Dynamic Link", - "options": "Beleginfo - Art 2" + "options": "Beleginfo - Art 2", + "width": 150 } ] From 55c048f56c1b2ed91e467edd4700cf8da448c514 Mon Sep 17 00:00:00 2001 From: barredterra Date: Mon, 11 May 2020 18:50:02 +0200 Subject: [PATCH 056/608] refactor: query meta data only once --- erpnext/regional/report/datev/datev.py | 35 +++++++++++++++++--------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/erpnext/regional/report/datev/datev.py b/erpnext/regional/report/datev/datev.py index a8e40cc4930..02296a93567 100644 --- a/erpnext/regional/report/datev/datev.py +++ b/erpnext/regional/report/datev/datev.py @@ -8,17 +8,18 @@ Provide a report and downloadable CSV according to the German DATEV format. all required columns. Used to import the data into the DATEV Software. """ from __future__ import unicode_literals + import datetime import json -import zlib import zipfile import six +import frappe +import pandas as pd + +from frappe import _ from csv import QUOTE_NONNUMERIC from six import BytesIO from six import string_types -import frappe -from frappe import _ -import pandas as pd from .datev_constants import DataCategory from .datev_constants import Transactions from .datev_constants import DebtorsCreditors @@ -287,9 +288,7 @@ def get_datev_csv(data, filters, csv_class): def get_header(filters, csv_class): - coa = frappe.get_value("Company", filters.get("company"), "chart_of_accounts") - description = filters.get("voucher_type", csv_class.FORMAT_NAME) - coa_used = "04" if "SKR04" in coa else ("03" if "SKR03" in coa else "") + description = filters.get('voucher_type', csv_class.FORMAT_NAME) header = [ # DATEV format @@ -316,13 +315,13 @@ def get_header(filters, csv_class): # J = Imported by -- stays empty '', # K = Tax consultant number (Beraternummer) - frappe.get_value("DATEV Settings", filters.get("company"), "consultant_number"), + filters.get('consultant_number', '0000000'), # L = Tax client number (Mandantennummer) - frappe.get_value("DATEV Settings", filters.get("company"), "client_number"), + filters.get('client_number', '00000'), # M = Start of the fiscal year (Wirtschaftsjahresbeginn) frappe.utils.formatdate(frappe.defaults.get_user_default("year_start_date"), "yyyyMMdd"), # N = Length of account numbers (Sachkontenlänge) - '4', + '%d' % filters.get('acc_len', 4), # O = Transaction batch start date (YYYYMMDD) frappe.utils.formatdate(filters.get('from_date'), "yyyyMMdd"), # P = Transaction batch end date (YYYYMMDD) @@ -348,7 +347,7 @@ def get_header(filters, csv_class): # TODO: Filter by Accounting Period. In export for closed Accounting Period, this will be "1" '0', # V = Default currency, for example, "EUR" - '"%s"' % frappe.get_value("Company", filters.get("company"), "default_currency"), + '"%s"' % filters.get('default_currency', 'EUR'), # reserviert '', # Derivatskennzeichen @@ -358,7 +357,7 @@ def get_header(filters, csv_class): # reserviert '', # SKR - '"%s"' % coa_used, + '"%s"' % filters.get('skr', '04'), # Branchen-Lösungs-ID '', # reserviert @@ -389,6 +388,18 @@ def download_datev_csv(filters=None): validate(filters) + # set chart of accounts used + coa = frappe.get_value('Company', filters.get('company'), 'chart_of_accounts') + filters['skr'] = '04' if 'SKR04' in coa else ('03' if 'SKR03' in coa else '') + + # set account number length + account_numbers = frappe.get_list('Account', fields=['account_number'], filters={'is_group': 0, 'account_number': ('!=', '')}) + filters['acc_len'] = max([len(a.account_number) for a in account_numbers]) + + filters['consultant_number'] = frappe.get_value('DATEV Settings', filters.get('company'), 'consultant_number') + filters['client_number'] = frappe.get_value('DATEV Settings', filters.get('company'), 'client_number') + filters['default_currency'] = frappe.get_value('Company', filters.get('company'), 'default_currency') + # This is where my zip will be written zip_buffer = BytesIO() # This is my zip file From 2976831560ddf9e16aae6b500f0e4f1b621e60d2 Mon Sep 17 00:00:00 2001 From: barredterra Date: Mon, 11 May 2020 19:15:03 +0200 Subject: [PATCH 057/608] fix: truncate account names to max length --- erpnext/regional/report/datev/datev.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/erpnext/regional/report/datev/datev.py b/erpnext/regional/report/datev/datev.py index 02296a93567..51ddfc780da 100644 --- a/erpnext/regional/report/datev/datev.py +++ b/erpnext/regional/report/datev/datev.py @@ -227,9 +227,18 @@ def get_suppliers(filters): def get_account_names(filters): - return frappe.get_list("Account", - fields=["account_number as Konto", "name as Kontenbeschriftung"], - filters={"company": filters.get("company"), "is_group": "0"}) + return frappe.db.sql(""" + SELECT + + account_number as 'Konto', + LEFT(account_name, 40) as 'Kontenbeschriftung', + 'de-DE' as 'Sprach-ID' + + FROM `tabAccount` + WHERE company = %(company)s + AND is_group = 0 + AND account_number != '' + """, filters, as_dict=1) def get_datev_csv(data, filters, csv_class): From 53445aa25adc664d5eac55ba9e16a1eb4365ea38 Mon Sep 17 00:00:00 2001 From: barredterra Date: Mon, 11 May 2020 19:15:49 +0200 Subject: [PATCH 058/608] fix: customer and supplier data --- erpnext/regional/report/datev/datev.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/erpnext/regional/report/datev/datev.py b/erpnext/regional/report/datev/datev.py index 51ddfc780da..d7036c55af9 100644 --- a/erpnext/regional/report/datev/datev.py +++ b/erpnext/regional/report/datev/datev.py @@ -131,8 +131,10 @@ def get_customers(filters): SELECT acc.account_number as 'Konto', - cus.customer_name as 'Name (Adressatentyp Unternehmen)', - case cus.customer_type when 'Individual' then 1 when 'Company' then 2 else 0 end as 'Adressatentyp', + CASE cus.customer_type WHEN 'Company' THEN cus.customer_name ELSE null END as 'Name (Adressatentyp Unternehmen)', + CASE cus.customer_type WHEN 'Individual' THEN con.last_name ELSE null END as 'Name (Adressatentyp natürl. Person)', + CASE cus.customer_type WHEN 'Individual' THEN con.first_name ELSE null END as 'Vorname (Adressatentyp natürl. Person)', + CASE cus.customer_type WHEN 'Individual' THEN '1' WHEN 'Company' THEN '2' ELSE '0' end as 'Adressatentyp', adr.address_line1 as 'Straße', adr.pincode as 'Postleitzahl', adr.city as 'Ort', @@ -141,8 +143,7 @@ def get_customers(filters): con.email_id as 'E-Mail', coalesce(con.mobile_no, con.phone) as 'Telefon', cus.website as 'Internet', - cus.tax_id as 'Steuernummer', - ccl.credit_limit as 'Kreditlimit (Debitor)' + cus.tax_id as 'Steuernummer' FROM `tabParty Account` par @@ -161,10 +162,6 @@ def get_customers(filters): left join `tabContact` con on con.name = cus.customer_primary_contact - left join `tabCustomer Credit Limit` ccl - on ccl.parent = cus.name - and ccl.company = par.company - WHERE par.company = %(company)s AND par.parenttype = 'Customer'""", filters, as_dict=1) @@ -180,8 +177,10 @@ def get_suppliers(filters): SELECT acc.account_number as 'Konto', - sup.supplier_name as 'Name (Adressatentyp Unternehmen)', - case sup.supplier_type when 'Individual' then '1' when 'Company' then '2' else '0' end as 'Adressatentyp', + CASE sup.supplier_type WHEN 'Company' THEN sup.supplier_name ELSE null END as 'Name (Adressatentyp Unternehmen)', + CASE sup.supplier_type WHEN 'Individual' THEN con.last_name ELSE null END as 'Name (Adressatentyp natürl. Person)', + CASE sup.supplier_type WHEN 'Individual' THEN con.first_name ELSE null END as 'Vorname (Adressatentyp natürl. Person)', + CASE sup.supplier_type WHEN 'Individual' THEN '1' WHEN 'Company' THEN '2' ELSE '0' end as 'Adressatentyp', adr.address_line1 as 'Straße', adr.pincode as 'Postleitzahl', adr.city as 'Ort', From 30d194d8a7eab77d056999aac61d00666518c005 Mon Sep 17 00:00:00 2001 From: barredterra Date: Mon, 11 May 2020 19:23:54 +0200 Subject: [PATCH 059/608] fix: hide transaction-specific for master data --- erpnext/regional/report/datev/datev.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/regional/report/datev/datev.py b/erpnext/regional/report/datev/datev.py index d7036c55af9..7fec94e740c 100644 --- a/erpnext/regional/report/datev/datev.py +++ b/erpnext/regional/report/datev/datev.py @@ -331,11 +331,11 @@ def get_header(filters, csv_class): # N = Length of account numbers (Sachkontenlänge) '%d' % filters.get('acc_len', 4), # O = Transaction batch start date (YYYYMMDD) - frappe.utils.formatdate(filters.get('from_date'), "yyyyMMdd"), + frappe.utils.formatdate(filters.get('from_date'), "yyyyMMdd") if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '', # P = Transaction batch end date (YYYYMMDD) - frappe.utils.formatdate(filters.get('to_date'), "yyyyMMdd"), + frappe.utils.formatdate(filters.get('to_date'), "yyyyMMdd") if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '', # Q = Description (for example, "Sales Invoice") Max. 30 chars - '"{}"'.format(_(description)), + '"{}"'.format(_(description)) if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '', # R = Diktatkürzel '', # S = Buchungstyp @@ -350,12 +350,12 @@ def get_header(filters, csv_class): # 40 = Kalkulatorik # 11 = Reserviert # 12 = Reserviert - '0', + '0' if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '', # U = Festschreibung # TODO: Filter by Accounting Period. In export for closed Accounting Period, this will be "1" '0', # V = Default currency, for example, "EUR" - '"%s"' % filters.get('default_currency', 'EUR'), + '"%s"' % filters.get('default_currency', 'EUR') if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '', # reserviert '', # Derivatskennzeichen From d99f85bf3c514cd018dd7a0f3d851598a265026b Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 12 May 2020 13:16:05 +0530 Subject: [PATCH 060/608] chore: Fixtures using new sync api --- erpnext/buying/dashboard_fixtures.py | 28 +++++++ .../setup_wizard/data/dashboard_charts.py | 81 ------------------- 2 files changed, 28 insertions(+), 81 deletions(-) create mode 100644 erpnext/buying/dashboard_fixtures.py diff --git a/erpnext/buying/dashboard_fixtures.py b/erpnext/buying/dashboard_fixtures.py new file mode 100644 index 00000000000..199765bacec --- /dev/null +++ b/erpnext/buying/dashboard_fixtures.py @@ -0,0 +1,28 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +import frappe +import json + + +def get_data(): + return frappe._dict({ + "dashboards": get_dashboards(), + "charts": get_charts(), + "number_cards": get_number_cards(), + }) + +def get_dashboards(): + return [{ + "name": "", + "dashboard_name": "", + "charts": [ + { "chart": } + ] + }] + +def get_charts(): + return [ { ... } ] + +def get_number_cards(): + return [ { ... } ] \ No newline at end of file diff --git a/erpnext/setup/setup_wizard/data/dashboard_charts.py b/erpnext/setup/setup_wizard/data/dashboard_charts.py index 9c654a829d5..88d42590fc9 100644 --- a/erpnext/setup/setup_wizard/data/dashboard_charts.py +++ b/erpnext/setup/setup_wizard/data/dashboard_charts.py @@ -134,87 +134,6 @@ def get_default_dashboards(): "document_type": "Patient Appointment", "type": "Line", "width": "Half" - }, - { - "doctype": "Dashboard Chart", - "owner": "Administrator", - "report_name": "Purchase Analytics", - "filters_json": json.dumps({ - "tree_type": "Item", - "doc_type": "Purchase Receipt", - "value_quantity": "Value", - "from_date": "2020-03-01", - "to_date": "2020-07-31", - "company": company.name, - "range": "Weekly" - }), - "type": "Line", - 'timeseries': 0, - "chart_type": "Report", - "chart_name": "Purchase Analytics", - "custom_options": json.dumps({ - "x_field": "entity", - "chart_type": "Bar", - "y_axis_fields": [{"idx": 1, "__islocal": "true", "y_field": "total"}], - "y_fields": ["total"] - }) - }, - { - "doctype": "Dashboard Chart", - "owner": "Administrator", - "document_type": "Material Request", - "filters_json": '[["Material Request","status","not in",["Draft","Cancelled","Stopped",null],false],["Material Request","material_request_type","=","Purchase",false],["Material Request","company","=", "{company}", false]]'.format(company=company.name), - "is_custom": 0, - "type": "Donut", - "timeseries": 0, - "chart_type": "Group By", - "group_by_based_on": "status", - "chart_name": "Material Request Purchase Analysis", - "group_by_type": "Count", - "custom_options": json.dumps({"height": 300}) - - }, - { - "doctype": "Dashboard Chart", - "owner": "Administrator", - "report_name": "Purchase Order Analysis", - "filters_json": json.dumps({ - "company": company.name, - "from_date": "2020-04-04", - "to_date": "2020-07-04", - "chart_based_on": "Quantity" - }), - "is_custom": 1, - "type": "Donut", - "timeseries": 0, - "chart_type": "Report", - "chart_name": "Purchase Order Analysis", - "custom_options": json.dumps({ - "type": "donut", - "height": 300, - "axisOptions": {"shortenYAxisNumbers": 1} - }) - }, - { - "doctype": "Dashboard Chart", - "owner": "Administrator", - "report_name": "Requested Items to Order", - "filters_json": json.dumps({ - "company": company.name, - "from_date": "2020-04-01", - "to_date": "2020-07-01", - "group_by_mr": 0 - }), - "is_custom": 1, - "type": "Bar", - "timeseries": 0, - "chart_type": "Report", - "chart_name": "Requested Items to Order", - "custom_options": json.dumps({ - "type": "bar", - "barOptions": {"stacked": 1}, - "axisOptions": {"shortenYAxisNumbers": 1} - }) } ] } From cdeb897fff6613bc0dd103638e0f7fdb3a4f82bd Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 12 May 2020 17:23:15 +0530 Subject: [PATCH 061/608] fix: Account shortcut in desk page --- .../desk_page/accounting/accounting.json | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/erpnext/accounts/desk_page/accounting/accounting.json b/erpnext/accounts/desk_page/accounting/accounting.json index 0d6aca65b16..648ebe81a15 100644 --- a/erpnext/accounts/desk_page/accounting/accounting.json +++ b/erpnext/accounts/desk_page/accounting/accounting.json @@ -45,11 +45,6 @@ "label": "Bank Statement", "links": "[\n {\n \"label\": \"Bank\",\n \"name\": \"Bank\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Account\",\n \"name\": \"Bank Account\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Statement Transaction Entry\",\n \"name\": \"Bank Statement Transaction Entry\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Statement Settings\",\n \"name\": \"Bank Statement Settings\",\n \"type\": \"doctype\"\n }\n]" }, - { - "hidden": 0, - "links": "[\n {\n \"description\": \"Match non-linked Invoices and Payments.\",\n \"label\": \"Match Payments with Invoices\",\n \"name\": \"Payment Reconciliation\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Update bank payment dates with journals.\",\n \"label\": \"Update Bank Clearance Dates\",\n \"name\": \"Bank Clearance\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Invoice Discounting\",\n \"name\": \"Invoice Discounting\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Journal Entry\"\n ],\n \"doctype\": \"Journal Entry\",\n \"is_query_report\": true,\n \"label\": \"Bank Reconciliation Statement\",\n \"name\": \"Bank Reconciliation Statement\",\n \"type\": \"report\"\n },\n {\n \"icon\": \"fa fa-bar-chart\",\n \"label\": \"Bank Reconciliation\",\n \"name\": \"bank-reconciliation\",\n \"type\": \"page\"\n },\n {\n \"dependencies\": [\n \"Journal Entry\"\n ],\n \"doctype\": \"Journal Entry\",\n \"is_query_report\": true,\n \"label\": \"Bank Clearance Summary\",\n \"name\": \"Bank Clearance Summary\",\n \"type\": \"report\"\n },\n {\n \"label\": \"Bank Guarantee\",\n \"name\": \"Bank Guarantee\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Setup cheque dimensions for printing\",\n \"label\": \"Cheque Print Template\",\n \"name\": \"Cheque Print Template\",\n \"type\": \"doctype\"\n }\n]", - "title": "Banking and Payments" - }, { "hidden": 0, "label": "Subscription Management", @@ -99,11 +94,10 @@ "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, - "icon": "", "idx": 0, "is_standard": 1, "label": "Accounting", - "modified": "2020-04-29 12:17:34.844397", + "modified": "2020-05-12 17:20:27.573449", "modified_by": "Administrator", "module": "Accounts", "name": "Accounting", @@ -112,10 +106,20 @@ "pin_to_top": 0, "shortcuts": [ { - "label": "Account", + "label": "Chart Of Accounts", "link_to": "Account", "type": "DocType" }, + { + "label": "Sales Invoice", + "link_to": "Sales Invoice", + "type": "DocType" + }, + { + "label": "Purchase Invoice", + "link_to": "Purchase Invoice", + "type": "DocType" + }, { "label": "Journal Entry", "link_to": "Journal Entry", From 354b01617effcfae1cf2cd03252f4a60a5814c99 Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 12 May 2020 17:35:17 +0530 Subject: [PATCH 062/608] chore: Dashboard Fixtures --- erpnext/buying/dashboard_fixtures.py | 112 +++++++++++++++++++++++++-- 1 file changed, 107 insertions(+), 5 deletions(-) diff --git a/erpnext/buying/dashboard_fixtures.py b/erpnext/buying/dashboard_fixtures.py index 199765bacec..0bd9a1fd488 100644 --- a/erpnext/buying/dashboard_fixtures.py +++ b/erpnext/buying/dashboard_fixtures.py @@ -12,17 +12,119 @@ def get_data(): "number_cards": get_number_cards(), }) +def get_company_for_dashboards(): + company = frappe.defaults.get_defaults().company + if company: + return company + else: + company_list = frappe.get_list("Company") + if company_list: + return company_list[0].name + return None + +company = frappe.get_doc("Company", get_company_for_dashboards()) + def get_dashboards(): return [{ - "name": "", - "dashboard_name": "", + "name": "Buying", + "dashboard_name": "Buying", "charts": [ - { "chart": } + { "chart": "Purchase Analytics", "width": "Full"}, + { "chart": "Material Request Purchase Analysis", "width": "Half"}, + { "chart": "Purchase Order Analysis", "width": "Half"}, + { "chart": "Requested Items to Order", "width": "Full"} ] }] def get_charts(): - return [ { ... } ] + return [ + { + "name": "Purchase Analytics", + "doctype": "Dashboard Chart", + "owner": "Administrator", + "report_name": "Purchase Analytics", + "filters_json": json.dumps({ + "tree_type": "Item", + "doc_type": "Purchase Receipt", + "value_quantity": "Value", + "from_date": "2020-03-01", + "to_date": "2020-07-31", + "company": company.name, + "range": "Weekly" + }), + "x_field": "entity", + "type": "Bar", + 'timeseries': 0, + "chart_type": "Report", + "chart_name": "Purchase Analytics", + "custom_options": json.dumps({ + "x_field": "entity", + "chart_type": "Bar", + "y_axis_fields": [{"idx": 1, "__islocal": "true", "y_field": "total"}], + "y_fields": ["total"] + }) + }, + { + "name": "Material Request Purchase Analysis", + "doctype": "Dashboard Chart", + "owner": "Administrator", + "document_type": "Material Request", + "filters_json": '[["Material Request","status","not in",["Draft","Cancelled","Stopped",null],false],["Material Request","material_request_type","=","Purchase",false],["Material Request","company","=", "{company}", false]]'.format(company=company.name), + "is_custom": 0, + "type": "Donut", + "timeseries": 0, + "chart_type": "Group By", + "group_by_based_on": "status", + "chart_name": "Material Request Purchase Analysis", + "group_by_type": "Count", + "custom_options": json.dumps({"height": 300}) + + }, + { + "name": "Purchase Order Analysis", + "doctype": "Dashboard Chart", + "owner": "Administrator", + "report_name": "Purchase Order Analysis", + "filters_json": json.dumps({ + "company": company.name, + "from_date": "2020-04-04", + "to_date": "2020-07-04", + "chart_based_on": "Quantity" + }), + "is_custom": 1, + "type": "Donut", + "timeseries": 0, + "chart_type": "Report", + "chart_name": "Purchase Order Analysis", + "custom_options": json.dumps({ + "type": "donut", + "height": 300, + "axisOptions": {"shortenYAxisNumbers": 1} + }) + }, + { + "name": "Requested Items to Order", + "doctype": "Dashboard Chart", + "owner": "Administrator", + "report_name": "Requested Items to Order", + "filters_json": json.dumps({ + "company": company.name, + "from_date": "2020-04-01", + "to_date": "2020-07-01", + "group_by_mr": 0 + }), + "is_custom": 1, + "type": "Bar", + "timeseries": 0, + "chart_type": "Report", + "chart_name": "Requested Items to Order", + "custom_options": json.dumps({ + "type": "bar", + "barOptions": {"stacked": 1}, + "axisOptions": {"shortenYAxisNumbers": 1} + }) + } + ] def get_number_cards(): - return [ { ... } ] \ No newline at end of file + return [{}] \ No newline at end of file From 7adca986fbecb95ad4c209838dd0042092cb78ce Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 12 May 2020 18:40:35 +0530 Subject: [PATCH 063/608] chore: Onboarding v2 --- erpnext/buying/onboarding/buying/buying.json | 11 ++++++++++- .../buying_settings/buying_settings.json | 2 +- .../create_a_product/create_a_product.json | 16 ++++++++++++++++ .../create_a_supplier/create_a_supplier.json | 4 ++-- .../create_a_warehouse/create_a_warehouse.json | 16 ++++++++++++++++ .../create_your_first_purchase_order.json | 16 ++++++++++++++++ .../introduction_to_buying.json | 2 +- 7 files changed, 62 insertions(+), 5 deletions(-) create mode 100644 erpnext/buying/onboarding_step/create_a_product/create_a_product.json create mode 100644 erpnext/buying/onboarding_step/create_a_warehouse/create_a_warehouse.json create mode 100644 erpnext/buying/onboarding_step/create_your_first_purchase_order/create_your_first_purchase_order.json diff --git a/erpnext/buying/onboarding/buying/buying.json b/erpnext/buying/onboarding/buying/buying.json index c35309964e7..1ee98877bd8 100644 --- a/erpnext/buying/onboarding/buying/buying.json +++ b/erpnext/buying/onboarding/buying/buying.json @@ -19,7 +19,7 @@ "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/buying", "idx": 0, "is_complete": 0, - "modified": "2020-05-06 16:49:17.433261", + "modified": "2020-05-12 18:32:05.085967", "modified_by": "Administrator", "module": "Buying", "name": "Buying", @@ -31,8 +31,17 @@ { "step": "Create a Supplier" }, + { + "step": "Create a Warehouse" + }, + { + "step": "Create a Product" + }, { "step": "Buying Settings" + }, + { + "step": "Create your first Purchase Order" } ], "subtitle": "Products, Purchases, Analysis and more.", diff --git a/erpnext/buying/onboarding_step/buying_settings/buying_settings.json b/erpnext/buying/onboarding_step/buying_settings/buying_settings.json index 7274c0d62d1..3b3208f5f04 100644 --- a/erpnext/buying/onboarding_step/buying_settings/buying_settings.json +++ b/erpnext/buying/onboarding_step/buying_settings/buying_settings.json @@ -7,7 +7,7 @@ "is_complete": 0, "is_mandatory": 0, "is_skipped": 0, - "modified": "2020-05-06 16:51:56.862206", + "modified": "2020-05-12 18:30:06.323797", "modified_by": "Administrator", "name": "Buying Settings", "owner": "Administrator", diff --git a/erpnext/buying/onboarding_step/create_a_product/create_a_product.json b/erpnext/buying/onboarding_step/create_a_product/create_a_product.json new file mode 100644 index 00000000000..dce1a215053 --- /dev/null +++ b/erpnext/buying/onboarding_step/create_a_product/create_a_product.json @@ -0,0 +1,16 @@ +{ + "action": "Create Entry", + "creation": "2020-05-12 18:16:06.624554", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_skipped": 0, + "modified": "2020-05-12 18:30:02.489949", + "modified_by": "Administrator", + "name": "Create a Product", + "owner": "Administrator", + "reference_document": "Item", + "title": "Create a Product" +} \ No newline at end of file diff --git a/erpnext/buying/onboarding_step/create_a_supplier/create_a_supplier.json b/erpnext/buying/onboarding_step/create_a_supplier/create_a_supplier.json index 14164c33037..f87a43dbc39 100644 --- a/erpnext/buying/onboarding_step/create_a_supplier/create_a_supplier.json +++ b/erpnext/buying/onboarding_step/create_a_supplier/create_a_supplier.json @@ -5,9 +5,9 @@ "doctype": "Onboarding Step", "idx": 0, "is_complete": 0, - "is_mandatory": 1, + "is_mandatory": 0, "is_skipped": 0, - "modified": "2020-05-06 17:00:41.446942", + "modified": "2020-05-12 18:25:29.121647", "modified_by": "Administrator", "name": "Create a Supplier", "owner": "Administrator", diff --git a/erpnext/buying/onboarding_step/create_a_warehouse/create_a_warehouse.json b/erpnext/buying/onboarding_step/create_a_warehouse/create_a_warehouse.json new file mode 100644 index 00000000000..20744f6624b --- /dev/null +++ b/erpnext/buying/onboarding_step/create_a_warehouse/create_a_warehouse.json @@ -0,0 +1,16 @@ +{ + "action": "Create Entry", + "creation": "2020-05-12 18:00:03.027704", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_skipped": 0, + "modified": "2020-05-12 18:27:44.128737", + "modified_by": "Administrator", + "name": "Create a Warehouse", + "owner": "Administrator", + "reference_document": "Warehouse", + "title": "Setup your Purchase Warehouse" +} \ No newline at end of file diff --git a/erpnext/buying/onboarding_step/create_your_first_purchase_order/create_your_first_purchase_order.json b/erpnext/buying/onboarding_step/create_your_first_purchase_order/create_your_first_purchase_order.json new file mode 100644 index 00000000000..0efadbd7887 --- /dev/null +++ b/erpnext/buying/onboarding_step/create_your_first_purchase_order/create_your_first_purchase_order.json @@ -0,0 +1,16 @@ +{ + "action": "Create Entry", + "creation": "2020-05-12 18:17:49.976035", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_skipped": 0, + "modified": "2020-05-12 18:31:56.856112", + "modified_by": "Administrator", + "name": "Create your first Purchase Order", + "owner": "Administrator", + "reference_document": "Purchase Order", + "title": "Create your first Purchase Order" +} \ No newline at end of file diff --git a/erpnext/buying/onboarding_step/introduction_to_buying/introduction_to_buying.json b/erpnext/buying/onboarding_step/introduction_to_buying/introduction_to_buying.json index f1a027af369..73f22df48c1 100644 --- a/erpnext/buying/onboarding_step/introduction_to_buying/introduction_to_buying.json +++ b/erpnext/buying/onboarding_step/introduction_to_buying/introduction_to_buying.json @@ -7,7 +7,7 @@ "is_complete": 0, "is_mandatory": 0, "is_skipped": 0, - "modified": "2020-05-06 16:47:47.378049", + "modified": "2020-05-12 18:25:08.509900", "modified_by": "Administrator", "name": "Introduction to Buying", "owner": "Administrator", From e4fc5ba83cd3360e58d779b91d15f77c14a489ca Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 12 May 2020 18:58:54 +0530 Subject: [PATCH 064/608] fix: Report conditional query simplification. --- .../purchase_order_analysis.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py index 78b86636e07..96e2fc8a209 100644 --- a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py +++ b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py @@ -36,25 +36,20 @@ def validate_filters(filters): def get_conditions(filters): conditions = "" if filters.get("from_date") and filters.get("to_date"): - conditions += " and po.transaction_date between '{0}' and '{1}'".format(filters.get("from_date"),filters.get("to_date")) + conditions += " and po.transaction_date between %(from_date)s and %(to_date)s" if filters.get("company"): - conditions += " and po.company = '{0}'".format(filters.get("company")) + conditions += " and po.company = %(company)s" if filters.get("purchase_order"): - conditions += " and po.name = '{0}'".format(filters.get("purchase_order")) + conditions += " and po.name = %(purchase_order)s" if filters.get("status"): - conditions += " and po.status in (%s)" % ', '.join(['%s']*len(filters.get("status"))) + conditions += " and po.status in %(status)s" return conditions def get_data(conditions, filters): - status = filters.get("status") - # temporary fix for dashboard chart - if status is None: - status = [] - data = frappe.db.sql(""" SELECT po.transaction_date as date, @@ -82,7 +77,7 @@ def get_data(conditions, filters): {0} GROUP BY poi.name ORDER BY po.transaction_date ASC - """.format(conditions), tuple(status), as_dict=1) + """.format(conditions), filters, as_dict=1) return data From 36aea71fd76d04641ee18b0a363ad182da7ad89a Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Wed, 13 May 2020 10:47:36 +0530 Subject: [PATCH 065/608] fix: new dashboard and report --- erpnext/hr/dashboard_fixtures.py | 154 ++++++++++++++++++ erpnext/hr/doctype/attendance/attendance.py | 6 +- .../leave_application/leave_application.py | 4 +- .../department_analytics.json | 28 ---- .../__init__.py | 0 .../employee_analytics.js} | 12 +- .../employee_analytics.json | 30 ++++ .../employee_analytics.py} | 60 ++++--- .../monthly_attendance_sheet.py | 101 +++++++++--- .../production_analytics.py | 1 - erpnext/patches.txt | 1 + 11 files changed, 314 insertions(+), 83 deletions(-) create mode 100644 erpnext/hr/dashboard_fixtures.py delete mode 100644 erpnext/hr/report/department_analytics/department_analytics.json rename erpnext/hr/report/{department_analytics => employee_analytics}/__init__.py (100%) rename erpnext/hr/report/{department_analytics/department_analytics.js => employee_analytics/employee_analytics.js} (55%) create mode 100644 erpnext/hr/report/employee_analytics/employee_analytics.json rename erpnext/hr/report/{department_analytics/department_analytics.py => employee_analytics/employee_analytics.py} (51%) diff --git a/erpnext/hr/dashboard_fixtures.py b/erpnext/hr/dashboard_fixtures.py new file mode 100644 index 00000000000..dafacaada41 --- /dev/null +++ b/erpnext/hr/dashboard_fixtures.py @@ -0,0 +1,154 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +import frappe +import erpnext +import json + +def get_data(): + return frappe._dict({ + "dashboards": get_dashboards(), + "charts": get_charts(), + "number_cards": get_number_cards(), + }) + +def get_dashboards(): + dashboards = [] + dashboards.append(get_human_resource_dashboard()) + return dashboards + +def get_human_resource_dashboard(): + return { + "name": "Human Resource", + "dashboard_name": "Human Resource", + "is_default": 1, + "charts": [ + { "chart": "Gender Diversity Ratio", "width": "Half"}, + { "chart": "Employee Count", "width": "Half"}, + { "chart": "Outgoing Salary", "width": "Full"}, + { "chart": "Attendance Count", "width": "Full"} + ], + "cards": [ + {"card": "Total Employees"}, + {"card": "New Joinees"}, + {'card': "Job Applicants"}, + {'card': "Employees Left"} + ] + } + +def get_recruitment_dashboard(): + pass + # return { + # "name": "Human Resource", + # "dashboard_name": "Human Resource", + # "is_default": 1, + # "charts": [ + # ], + # "cards": [ + # ] + # } + + +def get_charts(): + company = erpnext.get_default_company() + + if not company: + company = frappe.db.get_value("Company", {"is_group": 0}, "name") + + dashboard_charts = [ + get_dashboards_chart_doc('Gender Diversity Ratio', "Group By", "Donut",document_type = "Employee", group_by_type="Count", group_by_based_on="gender", filters_json = json.dumps([["Employee","status","=","Active"]]), time_interval = "Monthly") + ] + + dashboard_charts.append( + get_dashboards_chart_doc('Outgoing salary', "Sum", "Line",document_type = "Salary Slip", group_by_type="Count", based_on="end_date", value_based_on = "rounded_total", time_interval = "Monthly", timeseries = 1 , filters_json = json.dumps([["Salary Slip","docstatus","=","1"]])) + ) + + custom_options = '''{"type": "bar", "axisOptions": {"shortenYAxisNumbers": 1}, "tooltipOptions": {}, "barOptions":{"stacked": 1}}''' + filters_json = json.dumps({"month":"May","year":"2020","company":company}) + + dashboard_charts.append( + get_dashboards_chart_doc('Attendance Count', "Report", "Bar",report_name = "Monthly Attendance Sheet", is_custom =1,group_by_type="Count", timeseries = 1 , filters_json = filters_json, custom_options=custom_options) + ) + + custom_options = """{"type": "donut", "axisOptions": {"shortenYAxisNumbers": 1}}""" + filters_json = json.dumps({"company":company ,"parameter":"Department"}) + + dashboard_charts.append( + get_dashboards_chart_doc('Employee Count', "Report", "Donut",report_name = "Employee Analytics", is_custom =1, group_by_type="Count", timeseries = 1 , filters_json = filters_json, custom_options=custom_options) + ) + + + + +def get_number_cards(): + number_cards = [] + + number_cards = [ + get_number_cards_doc("Employee", "Total Employees", filters_json = json.dumps([ + ["Employee","status","=","Active"] + ]) + ) + ] + number_cards.append( + get_number_cards_doc("Employee", "New Joinees", filters_json = json.dumps([ + ["Employee","date_of_joining","Previous","6 months"], + ["Employee","status","=","Active"] + ]), + stats_time_interval = "Daily") + ) + number_cards.append( + get_number_cards_doc("Employee", "Employees Left", filters_json = json.dumps([ + ["Employee","status","=","Left"] + ]) + ) + ) + number_cards.append( + get_number_cards_doc("Job Applicant", "Job Applicants", filters_json = json.dumps([ + ["Job Applicant","status","!=","Rejected"] + ]) + ) + ) + + return number_cards + + +def get_number_cards_doc(document_type, label, **args): + args = frappe._dict(args) + + return { + "doctype": "Number Card", + "document_type": document_type, + "function": args.func or "Count", + "is_public": args.is_public or 1, + "label": label, + "name": args.name or label, + "show_percentage_stats": args.show_percentage_stats or 1, + "stats_time_interval": args.stats_time_interval or 'Monthly', + "filters_json": args.filters_json or '[]', + } + +def get_dashboards_chart_doc(name, chart_type, graph_type, **args): + + args = frappe._dict(args) + + return { + "name": name, + "chart_name": args.chart_name or name, + "chart_type": chart_type, + "document_type": args.document_type or None, + "report_name": args.report_name or None, + "is_custom": args.is_custom or 0, + "group_by_type": args.group_by_type or None, + "group_by_based_on": args.group_by_based_on or None, + "based_on": args.based_on or None, + "value_based_on": args.value_based_on or None, + "number_of_groups": args.number_of_groups or 0, + "is_public": args.is_public or 1, + "timespan": args.timespan or "Last Year", + "time_interval": args.time_interval or "Yearly", + "timeseries": args.timeseries or 0, + "filters_json": args.filters_json or '[]', + "type": graph_type, + "custom_options": args.custom_options or '', + "doctype": "Dashboard Chart", + } \ No newline at end of file diff --git a/erpnext/hr/doctype/attendance/attendance.py b/erpnext/hr/doctype/attendance/attendance.py index b6c80655c2e..ba804ceca57 100644 --- a/erpnext/hr/doctype/attendance/attendance.py +++ b/erpnext/hr/doctype/attendance/attendance.py @@ -41,7 +41,7 @@ class Attendance(Document): leave_record = frappe.db.sql(""" select leave_type, half_day, half_day_date from `tabLeave Application` - where employee = %s + where employee = %s and %s between from_date and to_date and status = 'Approved' and docstatus = 1 @@ -172,8 +172,8 @@ def get_unmarked_days(employee, month): records = frappe.get_all("Attendance", fields = ['attendance_date', 'employee'] , filters = [ - ["attendance_date", ">", month_start], - ["attendance_date", "<", month_end], + ["attendance_date", ">=", month_start], + ["attendance_date", "<=", month_end], ["employee", "=", employee], ["docstatus", "!=", 2] ]) diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index 47b1bb7684e..d3a08cd3c73 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -131,6 +131,8 @@ class LeaveApplication(Document): for dt in daterange(getdate(self.from_date), getdate(self.to_date)): date = dt.strftime("%Y-%m-%d") status = "Half Day" if getdate(date) == getdate(self.half_day_date) else "On Leave" + print("-------->>>", status) + # frappe.throw("Hello") attendance_name = frappe.db.exists('Attendance', dict(employee = self.employee, attendance_date = date, docstatus = ('!=', 2))) @@ -596,7 +598,7 @@ def get_leave_entries(employee, leave_type, from_date, to_date): is_carry_forward, is_expired FROM `tabLeave Ledger Entry` WHERE employee=%(employee)s AND leave_type=%(leave_type)s - AND docstatus=1 + AND docstatus=1 AND (leaves<0 OR is_expired=1) AND (from_date between %(from_date)s AND %(to_date)s diff --git a/erpnext/hr/report/department_analytics/department_analytics.json b/erpnext/hr/report/department_analytics/department_analytics.json deleted file mode 100644 index 1e26b33c530..00000000000 --- a/erpnext/hr/report/department_analytics/department_analytics.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "add_total_row": 0, - "creation": "2018-05-15 15:37:20.883263", - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 0, - "is_standard": "Yes", - "modified": "2018-05-15 17:19:32.934321", - "modified_by": "Administrator", - "module": "HR", - "name": "Department Analytics", - "owner": "Administrator", - "ref_doctype": "Employee", - "report_name": "Department Analytics", - "report_type": "Script Report", - "roles": [ - { - "role": "Employee" - }, - { - "role": "HR User" - }, - { - "role": "HR Manager" - } - ] -} \ No newline at end of file diff --git a/erpnext/hr/report/department_analytics/__init__.py b/erpnext/hr/report/employee_analytics/__init__.py similarity index 100% rename from erpnext/hr/report/department_analytics/__init__.py rename to erpnext/hr/report/employee_analytics/__init__.py diff --git a/erpnext/hr/report/department_analytics/department_analytics.js b/erpnext/hr/report/employee_analytics/employee_analytics.js similarity index 55% rename from erpnext/hr/report/department_analytics/department_analytics.js rename to erpnext/hr/report/employee_analytics/employee_analytics.js index 29fedcd7350..8620a65a909 100644 --- a/erpnext/hr/report/department_analytics/department_analytics.js +++ b/erpnext/hr/report/employee_analytics/employee_analytics.js @@ -1,7 +1,8 @@ // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt +/* eslint-disable */ -frappe.query_reports["Department Analytics"] = { +frappe.query_reports["Employee Analytics"] = { "filters": [ { "fieldname":"company", @@ -11,5 +12,12 @@ frappe.query_reports["Department Analytics"] = { "default": frappe.defaults.get_user_default("Company"), "reqd": 1 }, + { + "fieldname":"parameter", + "label": __("Parameter"), + "fieldtype": "Select", + "options": ["Branch","Grade","Department","Designation", "Employment Type"], + "reqd": 1 + } ] -}; \ No newline at end of file +}; diff --git a/erpnext/hr/report/employee_analytics/employee_analytics.json b/erpnext/hr/report/employee_analytics/employee_analytics.json new file mode 100644 index 00000000000..5a7ab9a251e --- /dev/null +++ b/erpnext/hr/report/employee_analytics/employee_analytics.json @@ -0,0 +1,30 @@ +{ + "add_total_row": 0, + "creation": "2020-05-12 13:52:50.631086", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "modified": "2020-05-12 13:52:50.631086", + "modified_by": "Administrator", + "module": "HR", + "name": "Employee Analytics", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Employee", + "report_name": "Employee Analytics", + "report_type": "Script Report", + "roles": [ + { + "role": "Employee" + }, + { + "role": "HR User" + }, + { + "role": "HR Manager" + } + ] +} \ No newline at end of file diff --git a/erpnext/hr/report/department_analytics/department_analytics.py b/erpnext/hr/report/employee_analytics/employee_analytics.py similarity index 51% rename from erpnext/hr/report/department_analytics/department_analytics.py rename to erpnext/hr/report/employee_analytics/employee_analytics.py index b28eac43f8b..6f8f161c3db 100644 --- a/erpnext/hr/report/department_analytics/department_analytics.py +++ b/erpnext/hr/report/employee_analytics/employee_analytics.py @@ -13,12 +13,13 @@ def execute(filters=None): columns = get_columns() employees = get_employees(filters) - departments_result = get_department(filters) - departments = [] - if departments_result: - for department in departments_result: - departments.append(department) - chart = get_chart_data(departments,employees) + parameters_result = get_parameters(filters) + parameters = [] + if parameters_result: + for department in parameters_result: + parameters.append(department) + + chart = get_chart_data(parameters,employees, filters) return columns, employees, None, chart def get_columns(): @@ -29,10 +30,8 @@ def get_columns(): ] def get_conditions(filters): - conditions = "" - if filters.get("department"): conditions += " and department = '%s'" % \ - filters["department"].replace("'", "\\'") - + conditions = " and "+filters.get("parameter").lower().replace(" ","_")+" IS NOT NULL " + if filters.get("company"): conditions += " and company = '%s'" % \ filters["company"].replace("'", "\\'") return conditions @@ -43,25 +42,38 @@ def get_employees(filters): branch, department, designation, gender, company from `tabEmployee` where status = 'Active' %s""" % conditions, as_list=1) -def get_department(filters): - return frappe.db.sql("""select name from `tabDepartment` where company = %s""", (filters["company"]), as_list=1) - -def get_chart_data(departments,employees): - if not departments: - departments = [] +def get_parameters(filters): + return frappe.db.sql("""select name from `tab"""+filters.get("parameter")+"""` """, as_list=1) + +def get_chart_data(parameters,employees, filters): + if not parameters: + parameters = [] datasets = [] - for department in departments: - if department: - total_employee = frappe.db.sql("""select count(*) from \ - `tabEmployee` where \ - department = %s""" ,(department[0]), as_list=1) + parameter_field_name = filters.get("parameter").lower().replace(" ","_") + label = [] + for parameter in parameters: + if parameter: + total_employee = frappe.db.sql("""select count(*) from + `tabEmployee` where """+ + parameter_field_name + """ = %s and company = %s""" ,( parameter[0], filters.get("company")), as_list=1) + if total_employee[0][0]: + label.append(parameter) datasets.append(total_employee[0][0]) + + values = [ value for value in datasets if value !=0] + + total_employee = frappe.db.count('Employee', {'status':'Active'}) + others = total_employee - sum(values) + + label.append(["Others"]) + values.append(others) + chart = { "data": { - 'labels': departments, - 'datasets': [{'name': 'Employees','values': datasets}] + 'labels': label, + 'datasets': [{'name': 'Employees','values': values}] } } - chart["type"] = "bar" + chart["type"] = "donut" return chart diff --git a/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py b/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py index 82ed27715f7..a6509766c7c 100644 --- a/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py +++ b/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py @@ -31,7 +31,7 @@ def execute(filters=None): if not filters: filters = {} conditions, filters = get_conditions(filters) - columns = get_columns(filters) + columns, days = get_columns(filters) att_map = get_attendance_list(conditions, filters) if filters.group_by: @@ -60,20 +60,77 @@ def execute(filters=None): columns.extend([_("Total Late Entries") + ":Float:120", _("Total Early Exits") + ":Float:120"]) if filters.group_by: + emp_att_map = {} for parameter in group_by_parameters: data.append([ ""+ parameter + ""]) - record = add_data(emp_map[parameter], att_map, filters, holiday_map, conditions, leave_list=leave_list) + record, aaa = add_data(emp_map[parameter], att_map, filters, holiday_map, conditions, leave_list=leave_list) + emp_att_map.update(aaa) data += record else: - record = add_data(emp_map, att_map, filters, holiday_map, conditions, leave_list=leave_list) + record, emp_att_map = add_data(emp_map, att_map, filters, holiday_map, conditions, leave_list=leave_list) data += record - return columns, data + chart_data = get_chart_data(emp_att_map, days) + + return columns, data, None, chart_data + +def get_chart_data(emp_att_map, days): + from pprint import pprint + pprint(emp_att_map) + labels = [] + datasets = [ + {"name": "Absent", "values": []}, + {"name": "Present", "values": []}, + {"name": "Leave", "values": []}, + {"name": "Unmarked", "values": []} + ] + for idx, day in enumerate(days, start=0): + p = day.replace("::65", "") + labels.append(day.replace("::65", "")) + total_absent_on_day = 0 + total_leave_on_day = 0 + total_present_on_day = 0 + total_holiday = 0 + total_unmarked_on_day = 0 + for emp in emp_att_map.keys(): + if emp_att_map[emp][idx]: + if emp_att_map[emp][idx] == "A": + total_absent_on_day += 1 + if emp_att_map[emp][idx] in ["P", "WFH"]: + total_present_on_day += 1 + if emp_att_map[emp][idx] == "HD": + total_present_on_day += 0.5 + total_leave_on_day += 0.5 + if emp_att_map[emp][idx] == "L": + total_leave_on_day += 1 + + total_marked = total_absent_on_day + total_present_on_day + total_leave_on_day + + datasets[0]["values"].append(total_absent_on_day) + datasets[1]["values"].append(total_present_on_day) + datasets[2]["values"].append(total_leave_on_day) + datasets[3]["values"].append(frappe.db.count('Employee', {'status':'Active'}) - total_marked) -def add_data(employee_map, att_map, filters, holiday_map, conditions, leave_list=None): + chart = { + "data": { + 'labels': labels, + 'datasets': datasets + } + } + + chart["type"] = "bar" + # chart["spaceRatio"] = 0.1 + + + return chart + + + +def add_data(employee_map, att_map, filters, holiday_map, conditions,leave_list=None): record = [] + emp_att_map = {} for emp in employee_map: emp_det = employee_map.get(emp) if not emp_det or emp not in att_map: @@ -85,6 +142,7 @@ def add_data(employee_map, att_map, filters, holiday_map, conditions, leave_list row += [emp, emp_det.employee_name] total_p = total_a = total_l = total_h = total_um= 0.0 + ggg = [] for day in range(filters["total_days_in_month"]): status = None status = att_map.get(emp).get(day + 1) @@ -101,19 +159,12 @@ def add_data(employee_map, att_map, filters, holiday_map, conditions, leave_list status = "Holiday" total_h += 1 - - # if emp_holiday_list in holiday_map and (day+1) in holiday_map[emp_holiday_list][0]: - # if holiday_map[emp_holiday_list][1]: - # status= "Weekly Off" - # else: - # status = "Holiday" - - # += 1 + ggg.append(status_map.get(status, "")) if not filters.summarized_view: - row.append(status_map.get(status, "")) + row += ggg else: - if status == "Present": + if status == "Present" or status == "Work From Home": total_p += 1 elif status == "Absent": total_a += 1 @@ -159,10 +210,10 @@ def add_data(employee_map, att_map, filters, holiday_map, conditions, leave_list row.append("0.0") row.extend([time_default_counts[0][0],time_default_counts[0][1]]) + emp_att_map[emp] = ggg record.append(row) - - return record + return record, emp_att_map def get_columns(filters): @@ -174,15 +225,17 @@ def get_columns(filters): columns += [ _("Employee") + ":Link/Employee:120", _("Employee Name") + ":Link/Employee:120" ] - + days = [] + for day in range(filters["total_days_in_month"]): + date = str(filters.year) + "-" + str(filters.month)+ "-" + str(day+1) + day_name = day_abbr[getdate(date).weekday()] + days.append(cstr(day+1)+ " " +day_name +"::65") if not filters.summarized_view: - for day in range(filters["total_days_in_month"]): - date = str(filters.year) + "-" + str(filters.month)+ "-" + str(day+1) - day_name = day_abbr[getdate(date).weekday()] - columns.append(cstr(day+1)+ " " +day_name +"::65") - else: + columns += days + + if filters.summarized_view: columns += [_("Total Present") + ":Float:120", _("Total Leaves") + ":Float:120", _("Total Absent") + ":Float:120", _("Total Holidays") + ":Float:120", _("Unmarked Days")+ ":Float:120"] - return columns + return columns, days def get_attendance_list(conditions, filters): attendance_list = frappe.db.sql("""select employee, day(attendance_date) as day_of_month, diff --git a/erpnext/manufacturing/report/production_analytics/production_analytics.py b/erpnext/manufacturing/report/production_analytics/production_analytics.py index 7447a1f6705..145e4a79b51 100644 --- a/erpnext/manufacturing/report/production_analytics/production_analytics.py +++ b/erpnext/manufacturing/report/production_analytics/production_analytics.py @@ -61,7 +61,6 @@ def get_periodic_data(filters, entry): elif getdate(d.planned_start_date) < getdate(from_date) : periodic_data = update_periodic_data(periodic_data, "Overdue", period) - else: periodic_data = update_periodic_data(periodic_data, "Not Started", period) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 3f90d36916e..accfa69743f 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -680,3 +680,4 @@ erpnext.patches.v12_0.update_appointment_reminder_scheduler_entry erpnext.patches.v12_0.retain_permission_rules_for_video_doctype erpnext.patches.v13_0.patch_to_fix_reverse_linking_in_additional_salary_encashment_and_incentive execute:frappe.delete_doc_if_exists("Page", "appointment-analytic") +execute:frappe.delete_doc("Report", "Department Analytics") From 021acd0a0113a8a46013d8766cd50f45f3009977 Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 14 May 2020 13:20:43 +0530 Subject: [PATCH 066/608] chore: Stock Ageing and Item Shortage Reports with Charts --- .../item_shortage_report.js | 26 +++ .../item_shortage_report.json | 41 ++--- .../item_shortage_report.py | 162 ++++++++++++++++++ .../stock/report/stock_ageing/stock_ageing.py | 32 +++- 4 files changed, 240 insertions(+), 21 deletions(-) create mode 100644 erpnext/stock/report/item_shortage_report/item_shortage_report.js create mode 100644 erpnext/stock/report/item_shortage_report/item_shortage_report.py diff --git a/erpnext/stock/report/item_shortage_report/item_shortage_report.js b/erpnext/stock/report/item_shortage_report/item_shortage_report.js new file mode 100644 index 00000000000..ca42a331e91 --- /dev/null +++ b/erpnext/stock/report/item_shortage_report/item_shortage_report.js @@ -0,0 +1,26 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Item Shortage Report"] = { + "filters": [ + { + "fieldname": "company", + "label": __("Company"), + "fieldtype": "Link", + "width": "80", + "options": "Company", + "reqd": 1, + "default": frappe.defaults.get_default("company") + }, + { + "fieldname": "warehouse", + "label": __("Warehouse"), + "fieldtype": "MultiSelectList", + "width": "100", + get_data: function(txt) { + return frappe.db.get_link_options('Warehouse', txt); + } + } + ] +}; diff --git a/erpnext/stock/report/item_shortage_report/item_shortage_report.json b/erpnext/stock/report/item_shortage_report/item_shortage_report.json index 577a8530b7e..17285c09de4 100644 --- a/erpnext/stock/report/item_shortage_report/item_shortage_report.json +++ b/erpnext/stock/report/item_shortage_report/item_shortage_report.json @@ -1,29 +1,30 @@ { - "add_total_row": 0, - "apply_user_permissions": 1, - "creation": "2013-08-20 13:43:30", - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 3, - "is_standard": "Yes", - "json": "{\"add_total_row\": 0, \"sort_by\": \"Bin.projected_qty\", \"sort_order\": \"asc\", \"sort_by_next\": \"\", \"filters\": [[\"Bin\", \"projected_qty\", \"<\", \"0\"]], \"sort_order_next\": \"desc\", \"columns\": [[\"warehouse\", \"Bin\"], [\"item_code\", \"Bin\"], [\"actual_qty\", \"Bin\"], [\"ordered_qty\", \"Bin\"], [\"planned_qty\", \"Bin\"], [\"reserved_qty\", \"Bin\"], [\"projected_qty\", \"Bin\"]]}", - "modified": "2017-02-24 20:00:46.439935", - "modified_by": "Administrator", - "module": "Stock", - "name": "Item Shortage Report", - "owner": "Administrator", - "query": "SELECT bin.warehouse as \"Warehouse:Link/Warehouse:150\",\n\tbin.item_code as \"Item Code:Link/Item:100\",\n\tbin.actual_qty as \"Actual Quantity:Float:120\",\n\tbin.ordered_qty as \"Ordered Quantity:Float:120\",\n\tbin.planned_qty as \"Planned Quantity:Float:120\",\n\tbin.reserved_qty as \"Reserved Quantity:Float:120\",\n\tbin.projected_qty as \"Project Quantity:Float:120\",\n\titem.item_name as \"Item Name:Data:150\",\n\titem.description as \"Description::200\"\nFROM tabBin as bin\nINNER JOIN tabItem as item\nON bin.item_code=item.name\nWHERE bin.projected_qty<0\nORDER BY bin.projected_qty;", - "ref_doctype": "Bin", - "report_name": "Item Shortage Report", - "report_type": "Query Report", + "add_total_row": 0, + "creation": "2013-08-20 13:43:30", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 3, + "is_standard": "Yes", + "json": "{\"add_total_row\": 0, \"sort_by\": \"Bin.projected_qty\", \"sort_order\": \"asc\", \"sort_by_next\": \"\", \"filters\": [[\"Bin\", \"projected_qty\", \"<\", \"0\"]], \"sort_order_next\": \"desc\", \"columns\": [[\"warehouse\", \"Bin\"], [\"item_code\", \"Bin\"], [\"actual_qty\", \"Bin\"], [\"ordered_qty\", \"Bin\"], [\"planned_qty\", \"Bin\"], [\"reserved_qty\", \"Bin\"], [\"projected_qty\", \"Bin\"]]}", + "modified": "2020-05-14 12:32:07.158991", + "modified_by": "Administrator", + "module": "Stock", + "name": "Item Shortage Report", + "owner": "Administrator", + "prepared_report": 0, + "query": "", + "ref_doctype": "Bin", + "report_name": "Item Shortage Report", + "report_type": "Script Report", "roles": [ { "role": "Sales User" - }, + }, { "role": "Purchase User" - }, + }, { "role": "Stock User" } diff --git a/erpnext/stock/report/item_shortage_report/item_shortage_report.py b/erpnext/stock/report/item_shortage_report/item_shortage_report.py new file mode 100644 index 00000000000..07749ebec57 --- /dev/null +++ b/erpnext/stock/report/item_shortage_report/item_shortage_report.py @@ -0,0 +1,162 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe import _ + +def execute(filters=None): + columns = get_columns() + conditions = get_conditions(filters) + data = get_data(conditions, filters) + + if not data: + return [], [] + + chart_data = get_chart_data(data) + + return columns, data, None, chart_data + +def get_conditions(filters): + conditions = "" + + if filters.get("warehouse"): + conditions += "AND warehouse in %(warehouse)s" + if filters.get("company"): + conditions += "AND company = %(company)s" + + return conditions + +def get_data(conditions, filters): + data = frappe.db.sql(""" + SELECT + bin.warehouse, + bin.item_code, + bin.actual_qty , + bin.ordered_qty , + bin.planned_qty , + bin.reserved_qty , + bin.reserved_qty_for_production, + bin.projected_qty , + warehouse.company, + item.item_name , + item.description + FROM + `tabBin` bin, + `tabWarehouse` warehouse, + `tabItem` item + WHERE + bin.projected_qty<0 + AND warehouse.name = bin.warehouse + AND bin.item_code=item.name + {0} + ORDER BY bin.projected_qty;""".format(conditions), filters, as_dict=1) + + return data + +def get_chart_data(data): + labels, datapoints = [], [] + + for row in data: + labels.append(row.get("item_code")) + datapoints.append(row.get("projected_qty")) + + if len(data) > 10: + labels = labels[:10] + datapoints = datapoints[:10] + + return { + "data": { + "labels": labels, + "datasets":[ + { + "name": _("Projected Qty"), + "values": datapoints + } + ] + }, + "type": "bar" + } + +def get_columns(): + columns = [ + { + "label": _("Warehouse"), + "fieldname": "warehouse", + "fieldtype": "Link", + "options": "Warehouse", + "width": 150 + }, + { + "label": _("Item"), + "fieldname": "item_code", + "fieldtype": "Link", + "options": "Item", + "width": 150 + }, + { + "label": _("Actual Quantity"), + "fieldname": "actual_qty", + "fieldtype": "Float", + "width": 120, + "convertible": "qty" + }, + { + "label": _("Ordered Quantity"), + "fieldname": "ordered_qty", + "fieldtype": "Float", + "width": 120, + "convertible": "qty" + }, + { + "label": _("Planned Quantity"), + "fieldname": "planned_qty", + "fieldtype": "Float", + "width": 120, + "convertible": "qty" + }, + { + "label": _("Reserved Quantity"), + "fieldname": "reserved_qty", + "fieldtype": "Float", + "width": 120, + "convertible": "qty" + }, + { + "label": _("Reserved Quantity for Production"), + "fieldname": "reserved_qty_for_production", + "fieldtype": "Float", + "width": 120, + "convertible": "qty" + }, + { + "label": _("Projected Quantity"), + "fieldname": "projected_qty", + "fieldtype": "Float", + "width": 120, + "convertible": "qty" + }, + { + "label": _("Company"), + "fieldname": "company", + "fieldtype": "Link", + "options": "Company", + "width": 120 + }, + { + "label": _("Item Name"), + "fieldname": "item_name", + "fieldtype": "Data", + "width": 100 + }, + { + "label": _("Description"), + "fieldname": "description", + "fieldtype": "Data", + "width": 120 + } + ] + + return columns + + diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.py b/erpnext/stock/report/stock_ageing/stock_ageing.py index 803a5c81a3b..4ce967d972a 100644 --- a/erpnext/stock/report/stock_ageing/stock_ageing.py +++ b/erpnext/stock/report/stock_ageing/stock_ageing.py @@ -37,7 +37,9 @@ def execute(filters=None): data.append(row) - return columns, data + chart_data = get_chart_data(data, filters) + + return columns, data, None, chart_data def get_average_age(fifo_queue, to_date): batch_age = age_qty = total_qty = 0.0 @@ -230,3 +232,31 @@ def get_sle_conditions(filters): where wh.lft >= {0} and rgt <= {1})""".format(lft, rgt)) return "and {}".format(" and ".join(conditions)) if conditions else "" + +def get_chart_data(data, filters): + labels, datapoints = [], [] + + if filters.get("show_warehouse_wise_stock"): + return {} + + if len(data) > 10: + data = data[:10] + + for row in data: + labels.append(row[0]) + datapoints.append(row[6]) + + print(labels) + print(datapoints) + return { + "data" : { + "labels": labels, + "datasets": [ + { + "name": _("Average Age"), + "values": datapoints + } + ] + }, + "type" : "bar" + } \ No newline at end of file From 10bd2417ff9d01866e48cf2569532903c2e3dfb6 Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 14 May 2020 15:35:18 +0530 Subject: [PATCH 067/608] feat: Custom Dashboard 'Warehouse wise Stock Value' with Chart Source --- .../stock/dashboard_chart_source/__init__.py | 0 .../warehouse_wise_stock_value/__init__.py | 0 .../warehouse_wise_stock_value.js | 14 ++++++ .../warehouse_wise_stock_value.json | 13 ++++++ .../warehouse_wise_stock_value.py | 44 +++++++++++++++++++ .../stock/report/stock_ageing/stock_ageing.py | 2 - 6 files changed, 71 insertions(+), 2 deletions(-) create mode 100644 erpnext/stock/dashboard_chart_source/__init__.py create mode 100644 erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/__init__.py create mode 100644 erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.js create mode 100644 erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.json create mode 100644 erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py diff --git a/erpnext/stock/dashboard_chart_source/__init__.py b/erpnext/stock/dashboard_chart_source/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/__init__.py b/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.js b/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.js new file mode 100644 index 00000000000..a4137547f7e --- /dev/null +++ b/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.js @@ -0,0 +1,14 @@ +frappe.provide('frappe.dashboards.chart_sources'); + +frappe.dashboards.chart_sources["Warehouse wise Stock Value"] = { + method: "erpnext.stock.dashboard_chart_source.warehouse_wise_stock_value.warehouse_wise_stock_value.get", + filters: [ + { + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company") + } + ] +}; \ No newline at end of file diff --git a/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.json b/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.json new file mode 100644 index 00000000000..6d967c0fc00 --- /dev/null +++ b/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.json @@ -0,0 +1,13 @@ +{ + "creation": "2020-05-14 14:27:44.108017", + "docstatus": 0, + "doctype": "Dashboard Chart Source", + "idx": 0, + "modified": "2020-05-14 14:27:44.108017", + "modified_by": "Administrator", + "module": "Stock", + "name": "Warehouse wise Stock Value", + "owner": "Administrator", + "source_name": "Warehouse wise Stock Value ", + "timeseries": 0 +} \ No newline at end of file diff --git a/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py b/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py new file mode 100644 index 00000000000..0ee0a6cc77b --- /dev/null +++ b/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py @@ -0,0 +1,44 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe, json +from frappe import _ +from frappe.utils.dashboard import cache_source +from erpnext.stock.utils import get_stock_value_from_bin + +@frappe.whitelist() +@cache_source +def get(chart_name = None, chart = None, no_cache = None, filters = None, from_date = None, + to_date = None, timespan = None, time_interval = None): + labels, datapoints = [], [] + filters = frappe.parse_json(filters) + + warehouse_filters = [['is_group', '=', 0]] + if filters and filters.get("company"): + warehouse_filters.append(['company', '=', filters.get("company")]) + + warehouses = frappe.get_list("Warehouse", fields=['name'], filters=warehouse_filters, order_by='name') + + for wh in warehouses: + balance = get_stock_value_from_bin(warehouse=wh.name) + wh["balance"] = balance[0][0] + + warehouses = [x for x in warehouses if not (x.get('balance') == None)] + sorted_warehouse_map = sorted(warehouses, key = lambda i: i['balance'],reverse=True) + + if len(sorted_warehouse_map) > 10: + sorted_warehouse_map = sorted_warehouse_map[:10] + + for warehouse in sorted_warehouse_map: + labels.append(_(warehouse.get("name"))) + datapoints.append(warehouse.get("balance")) + + return{ + "labels": labels, + "datasets": [{ + "name": _("Stock Value"), + "values": datapoints + }], + "type": "bar" + } \ No newline at end of file diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.py b/erpnext/stock/report/stock_ageing/stock_ageing.py index 4ce967d972a..48f22c2c24d 100644 --- a/erpnext/stock/report/stock_ageing/stock_ageing.py +++ b/erpnext/stock/report/stock_ageing/stock_ageing.py @@ -246,8 +246,6 @@ def get_chart_data(data, filters): labels.append(row[0]) datapoints.append(row[6]) - print(labels) - print(datapoints) return { "data" : { "labels": labels, From 79a8bd1cdff67149d4a2bcc32c06b35f46fbb09b Mon Sep 17 00:00:00 2001 From: Anupam K Date: Sun, 10 May 2020 23:36:33 +0530 Subject: [PATCH 068/608] CRM Onboarding --- erpnext/crm/dashboard_fixtures.py | 203 ++++++++++++++++++ erpnext/crm/desk_page/crm/crm.json | 10 +- erpnext/crm/module_onboarding/crm/crm.json | 45 ++++ erpnext/crm/onboarding/crm/crm.json | 45 ++++ .../create_and_send_quotation.json | 17 ++ .../create_lead/create_lead.json | 17 ++ .../create_opportunity.json | 17 ++ .../introduction_to_crm.json | 17 ++ .../start_campaign/start_campaign.json | 17 ++ 9 files changed, 385 insertions(+), 3 deletions(-) create mode 100644 erpnext/crm/dashboard_fixtures.py create mode 100644 erpnext/crm/module_onboarding/crm/crm.json create mode 100644 erpnext/crm/onboarding/crm/crm.json create mode 100644 erpnext/crm/onboarding_step/create_and_send_quotation/create_and_send_quotation.json create mode 100644 erpnext/crm/onboarding_step/create_lead/create_lead.json create mode 100644 erpnext/crm/onboarding_step/create_opportunity/create_opportunity.json create mode 100644 erpnext/crm/onboarding_step/introduction_to_crm/introduction_to_crm.json create mode 100644 erpnext/crm/onboarding_step/start_campaign/start_campaign.json diff --git a/erpnext/crm/dashboard_fixtures.py b/erpnext/crm/dashboard_fixtures.py new file mode 100644 index 00000000000..5b1ac9bf5e2 --- /dev/null +++ b/erpnext/crm/dashboard_fixtures.py @@ -0,0 +1,203 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +import frappe, erpnext, json + +def get_data(): + return frappe._dict({ + "dashboards": get_dashboards(), + "charts": get_charts(), + "number_cards": get_number_cards() + }) + +def get_dashboards(): + return [{ + "doctype": "Dashboard", + "name": "CRM", + "dashboard_name": "CRM", + "charts": [ + { "chart": "Lead", "width": "Full" }, + { "chart": "Opportunity", "width": "Full"}, + { "chart": "Campaign", "width": "Half" }, + { "chart": "Opportunities Won", "width": "Half" }, + { "chart": "Territory Wise Opportunity", "width": "Half"}, + { "chart": "Territory Wise Sales", "width": "Half"}, + { "chart": "Qualified For Call", "width": "Full"}, + { "chart": "Lead Source", "width": "Half"} + ], + "cards": [ + { "card": "New Lead" }, + { "card": "New Opportunity" }, + { "card": "Won Opportunity" }, + ] + }] + +def get_company_for_dashboards(): + company = frappe.defaults.get_defaults().company + if company: + return company + else: + company_list = frappe.get_list("Company") + if company_list: + return company_list[0].name + return None + +def get_charts(): + company = get_company_for_dashboards() + + return [{ + "name": "Lead", + "doctype": "Dashboard Chart", + "time_interval": "Yearly", + "chart_type": "Count", + "chart_name": "Lead", + "timespan": "Last Quarter", + "time_interval": "Weekly", + "document_type": "Lead", + "based_on": "creation", + 'is_public': 1, + 'timeseries': 1, + "owner": "Administrator", + "filters_json": json.dumps([["Opportunity", "company", "=", company, False]]), + "type": "Bar" + }, + { + "name": "Opportunity", + "doctype": "Dashboard Chart", + "time_interval": "Yearly", + "chart_type": "Count", + "chart_name": "Opportunity", + "timespan": "Last Quarter", + "time_interval": "Weekly", + "document_type": "Opportunity", + "based_on": "creation", + 'is_public': 1, + 'timeseries': 1, + "owner": "Administrator", + "filters_json": json.dumps([["Opportunity", "company", "=", company, False]]), + "type": "Bar" + }, + { + "name": "Campaign", + "doctype": "Dashboard Chart", + "time_interval": "Yearly", + "chart_type": "Count", + "chart_name": "Campaign", + "timespan": "Last Year", + "time_interval": "Monthly", + "document_type": "Campaign", + "based_on": "creation", + 'is_public': 1, + 'timeseries': 1, + "owner": "Administrator", + "filters_json": json.dumps([["Opportunity", "company", "=", company, False]]), + "type": "Line", + "color": "#428b46" + }, + { + "name": "Opportunities Won", + "doctype": "Dashboard Chart", + "time_interval": "Yearly", + "chart_type": "Count", + "chart_name": "Opportunities Won", + "timespan": "Last Year", + "time_interval": "Monthly", + "document_type": "Opportunity", + "based_on": "modified", + 'is_public': 1, + 'timeseries': 1, + "owner": "Administrator", + "filters_json": json.dumps([["Opportunity", "company", "=", company, False],["Opportunity", "sales_stage", "=", "Converted", False]]), + "type": "Bar" + }, + { + "name": "Territory Wise Opportunity", + "doctype": "Dashboard Chart", + "chart_type": "Group By", + "group_by_type": "Count", + "group_by_based_on": "territory", + "chart_name": "Territory Wise Opportunity", + "document_type": "Opportunity", + 'is_public': 1, + "owner": "Administrator", + "type": "Line" + }, + { + "name": "Territory Wise Sales", + "doctype": "Dashboard Chart", + "chart_type": "Group By", + "group_by_type": "Sum", + "group_by_based_on": "territory", + "chart_name": "Territory Wise Sales", + "aggregate_function_based_on": "opportunity_amount", + "document_type": "Opportunity", + 'is_public': 1, + "owner": "Administrator", + "filters_json": json.dumps([["Opportunity", "company", "=", company, False],["Opportunity", "sales_stage", "=", "Converted", False]]), + "type": "Bar", + "color": "#7575ff" + }, + { + "name": "Qualified For Call", + "doctype": "Dashboard Chart", + "time_interval": "Yearly", + "chart_type": "Count", + "chart_name": "Qualified For Call", + "timespan": "Last Quarter", + "time_interval": "Weekly", + "document_type": "Opportunity", + "based_on": "modified", + 'is_public': 1, + 'timeseries': 1, + "owner": "Administrator", + "filters_json": json.dumps([["Opportunity", "company", "=", company, False],["Opportunity", "sales_stage", "=", "Qualification", False]]), + "type": "Line", + "color": "#fff168" + }, + { + "name": "Lead Source", + "doctype": "Dashboard Chart", + "chart_type": "Group By", + "group_by_type": "Count", + "group_by_based_on": "source", + "chart_name": "Lead Source", + "document_type": "Lead", + 'is_public': 1, + "owner": "Administrator", + "type": "Donut" + }] + +def get_number_cards(): + return [{ + "doctype": "Number Card", + "document_type": "Lead", + "name": "New Lead", + "filters_json": json.dumps([["Lead","status","=","Lead",False]]), + "function": "Count", + "is_public": 1, + "label": "New Lead", + "show_percentage_stats": 1, + "stats_time_interval": "Daily" + }, + { + "doctype": "Number Card", + "document_type": "Opportunity", + "name": "New Opportunity", + "filters_json": json.dumps([["Opportunity","status","=","Open",False]]), + "function": "Count", + "is_public": 1, + "label": "New Opportunity", + "show_percentage_stats": 1, + "stats_time_interval": "Daily" + }, + { + "doctype": "Number Card", + "document_type": "Opportunity", + "name": "Won Opportunity", + "filters_json": json.dumps([["Opportunity","status","=","Converted",False]]), + "function": "Count", + "is_public": 1, + "label": "Won Opportunity", + "show_percentage_stats": 1, + "stats_time_interval": "Daily" + }] \ No newline at end of file diff --git a/erpnext/crm/desk_page/crm/crm.json b/erpnext/crm/desk_page/crm/crm.json index ca13d6abb66..80d3d18de97 100644 --- a/erpnext/crm/desk_page/crm/crm.json +++ b/erpnext/crm/desk_page/crm/crm.json @@ -27,21 +27,25 @@ } ], "category": "Modules", - "charts": [], + "charts": [ + { + "chart_name": "Territory Wise Sales" + } + ], "creation": "2020-01-23 14:48:30.183272", "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, - "icon": "", "idx": 0, "is_standard": 1, "label": "CRM", - "modified": "2020-04-27 22:32:26.682911", + "modified": "2020-05-14 17:20:07.636751", "modified_by": "Administrator", "module": "CRM", "name": "CRM", + "onboarding": "CRM", "owner": "Administrator", "pin_to_bottom": 0, "pin_to_top": 0, diff --git a/erpnext/crm/module_onboarding/crm/crm.json b/erpnext/crm/module_onboarding/crm/crm.json new file mode 100644 index 00000000000..9129c2625a0 --- /dev/null +++ b/erpnext/crm/module_onboarding/crm/crm.json @@ -0,0 +1,45 @@ +{ + "allow_roles": [ + { + "role": "Sales Master Manager" + }, + { + "role": "Administrator" + }, + { + "role": "Sales Manager" + } + ], + "creation": "2020-05-09 23:42:50.901548", + "docstatus": 0, + "doctype": "Module Onboarding", + "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/CRM", + "idx": 0, + "is_complete": 0, + "modified": "2020-05-14 17:41:11.083917", + "modified_by": "Administrator", + "module": "CRM", + "name": "CRM", + "owner": "Administrator", + "steps": [ + { + "step": "Introduction to CRM" + }, + { + "step": "Start Campaign" + }, + { + "step": "Create Lead" + }, + { + "step": "Create Opportunity" + }, + { + "step": "Create and Send Quotation" + } + ], + "subtitle": "Campaign, Lead, Opportunity, Customer and more", + "success_message": "CRM Module is all setup!", + "title": "Let's Setup Your CRM", + "user_can_dismiss": 1 +} \ No newline at end of file diff --git a/erpnext/crm/onboarding/crm/crm.json b/erpnext/crm/onboarding/crm/crm.json new file mode 100644 index 00000000000..016a8307dd7 --- /dev/null +++ b/erpnext/crm/onboarding/crm/crm.json @@ -0,0 +1,45 @@ +{ + "allow_roles": [ + { + "role": "Sales Master Manager" + }, + { + "role": "Administrator" + }, + { + "role": "Sales Manager" + } + ], + "creation": "2020-05-09 23:42:50.901548", + "docstatus": 0, + "doctype": "Onboarding", + "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/CRM", + "idx": 0, + "is_complete": 0, + "modified": "2020-05-09 23:42:50.901548", + "modified_by": "Administrator", + "module": "CRM", + "name": "CRM", + "owner": "Administrator", + "steps": [ + { + "step": "Introduction to CRM" + }, + { + "step": "Start Campaign" + }, + { + "step": "Create Lead" + }, + { + "step": "Convert Lead to Customer" + }, + { + "step": "Create and Send Quotation" + } + ], + "subtitle": "Campaign, Lead, Opportunity, Customer and more", + "success_message": "CRM Module is all setup!", + "title": "Let's Setup Your CRM", + "user_can_dismiss": 1 +} \ No newline at end of file diff --git a/erpnext/crm/onboarding_step/create_and_send_quotation/create_and_send_quotation.json b/erpnext/crm/onboarding_step/create_and_send_quotation/create_and_send_quotation.json new file mode 100644 index 00000000000..0997017933a --- /dev/null +++ b/erpnext/crm/onboarding_step/create_and_send_quotation/create_and_send_quotation.json @@ -0,0 +1,17 @@ +{ + "action": "Create Entry", + "creation": "2020-05-09 23:42:46.592075", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-14 17:30:07.887411", + "modified_by": "Administrator", + "name": "Create and Send Quotation", + "owner": "Administrator", + "reference_document": "Quotation", + "title": "Create and Send Quotation" +} \ No newline at end of file diff --git a/erpnext/crm/onboarding_step/create_lead/create_lead.json b/erpnext/crm/onboarding_step/create_lead/create_lead.json new file mode 100644 index 00000000000..b1076e0bf95 --- /dev/null +++ b/erpnext/crm/onboarding_step/create_lead/create_lead.json @@ -0,0 +1,17 @@ +{ + "action": "Create Entry", + "creation": "2020-05-09 23:40:25.192503", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-14 17:28:36.441387", + "modified_by": "Administrator", + "name": "Create Lead", + "owner": "Administrator", + "reference_document": "Lead", + "title": "Create Lead" +} \ No newline at end of file diff --git a/erpnext/crm/onboarding_step/create_opportunity/create_opportunity.json b/erpnext/crm/onboarding_step/create_opportunity/create_opportunity.json new file mode 100644 index 00000000000..20949a435bc --- /dev/null +++ b/erpnext/crm/onboarding_step/create_opportunity/create_opportunity.json @@ -0,0 +1,17 @@ +{ + "action": "Create Entry", + "creation": "2020-05-14 17:38:27.496696", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-14 17:38:27.496696", + "modified_by": "Administrator", + "name": "Create Opportunity", + "owner": "Administrator", + "reference_document": "Opportunity", + "title": "Create Opportunity" +} \ No newline at end of file diff --git a/erpnext/crm/onboarding_step/introduction_to_crm/introduction_to_crm.json b/erpnext/crm/onboarding_step/introduction_to_crm/introduction_to_crm.json new file mode 100644 index 00000000000..c923a77e1b9 --- /dev/null +++ b/erpnext/crm/onboarding_step/introduction_to_crm/introduction_to_crm.json @@ -0,0 +1,17 @@ +{ + "action": "Watch Video", + "creation": "2020-05-09 23:37:08.926812", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-14 17:28:16.448676", + "modified_by": "Administrator", + "name": "Introduction to CRM", + "owner": "Administrator", + "title": "Introduction to CRM", + "video_url": "https://www.youtube.com/watch?v=o9XCSZHJfpA" +} \ No newline at end of file diff --git a/erpnext/crm/onboarding_step/start_campaign/start_campaign.json b/erpnext/crm/onboarding_step/start_campaign/start_campaign.json new file mode 100644 index 00000000000..53c3becea3b --- /dev/null +++ b/erpnext/crm/onboarding_step/start_campaign/start_campaign.json @@ -0,0 +1,17 @@ +{ + "action": "Create Entry", + "creation": "2020-05-09 23:38:10.284957", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-14 17:28:54.285958", + "modified_by": "Administrator", + "name": "Start Campaign", + "owner": "Administrator", + "reference_document": "Campaign", + "title": "Start Campaign" +} \ No newline at end of file From 8bbac6defc464dc6a7ad4b71675b3cefe4064225 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 14 May 2020 22:57:11 +0530 Subject: [PATCH 069/608] fix: Onboarding and Dashboard for accounts module --- erpnext/accounts/dashboard_fixtures.py | 60 ++++++++++-- .../desk_page/accounting/accounting.json | 20 ++-- .../module_onboarding/accounts/accounts.json | 51 ++++++++++ .../chart_of_accounts/chart_of_accounts.json | 20 ++++ .../configure_account_settings.json | 19 ++++ .../create_a_customer/create_a_customer.json | 19 ++++ .../create_a_product/create_a_product.json | 19 ++++ .../create_a_supplier/create_a_supplier.json | 19 ++++ .../create_your_first_purchase_invoice.json | 19 ++++ .../create_your_first_sales_invoice.json | 19 ++++ .../setup_taxes/setup_taxes.json | 19 ++++ .../budget_variance_report.py | 96 ++++++++++++++++--- 12 files changed, 347 insertions(+), 33 deletions(-) create mode 100644 erpnext/accounts/module_onboarding/accounts/accounts.json create mode 100644 erpnext/accounts/onboarding_step/chart_of_accounts/chart_of_accounts.json create mode 100644 erpnext/accounts/onboarding_step/configure_account_settings/configure_account_settings.json create mode 100644 erpnext/accounts/onboarding_step/create_a_customer/create_a_customer.json create mode 100644 erpnext/accounts/onboarding_step/create_a_product/create_a_product.json create mode 100644 erpnext/accounts/onboarding_step/create_a_supplier/create_a_supplier.json create mode 100644 erpnext/accounts/onboarding_step/create_your_first_purchase_invoice/create_your_first_purchase_invoice.json create mode 100644 erpnext/accounts/onboarding_step/create_your_first_sales_invoice/create_your_first_sales_invoice.json create mode 100644 erpnext/accounts/onboarding_step/setup_taxes/setup_taxes.json diff --git a/erpnext/accounts/dashboard_fixtures.py b/erpnext/accounts/dashboard_fixtures.py index 46d1d6a66af..8b9eca5b2d3 100644 --- a/erpnext/accounts/dashboard_fixtures.py +++ b/erpnext/accounts/dashboard_fixtures.py @@ -4,6 +4,8 @@ import frappe import json from frappe.utils import nowdate, add_months +from frappe import _ +from erpnext.accounts.utils import get_fiscal_year def get_company_for_dashboards(): company = frappe.defaults.get_defaults().company @@ -18,11 +20,11 @@ def get_company_for_dashboards(): def get_data(): return frappe._dict({ "dashboards": get_dashboards(), - "charts": get_charts() + "charts": get_charts(), + "number_cards": get_number_cards() }) def get_dashboards(): - return [{ "name": "Accounts Dashboard", "dashboard_name": "Accounts", @@ -33,13 +35,19 @@ def get_dashboards(): { "chart": "Outgoing Bills"}, { "chart": "Accounts Receivable Ageing"}, { "chart": "Accounts Payable Ageing"}, + { "chart": "Budget Variance", "width": "Full"}, { "chart": "Bank Balance", "width": "Full"}, + ], + "cards": [ + {"card": "Total Payment Received"} ] }] def get_charts(): company = frappe.get_doc("Company", get_company_for_dashboards()) bank_account = company.default_bank_account or get_account("Bank", company.name) + fiscal_year = get_fiscal_year(date=nowdate()) + default_cost_center = company.cost_center return [ { @@ -58,14 +66,14 @@ def get_charts(): "type": "Bar", 'timeseries': 0, "chart_type": "Report", - "chart_name": "Profit and Loss", + "chart_name": _("Profit and Loss"), "is_custom": 1 }, { "doctype": "Dashboard Chart", "time_interval": "Monthly", "name": "Incoming Bills", - "chart_name": "Incoming Bills (Purchase Invoice)", + "chart_name": _("Incoming Bills (Purchase Invoice)"), "timespan": "Last Year", "color": "#a83333", "value_based_on": "base_grand_total", @@ -82,7 +90,7 @@ def get_charts(): "doctype": "Dashboard Chart", "name": "Outgoing Bills", "time_interval": "Monthly", - "chart_name": "Outgoing Bills (Sales Invoice)", + "chart_name": _("Outgoing Bills (Sales Invoice)"), "timespan": "Last Year", "color": "#7b933d", "value_based_on": "base_grand_total", @@ -112,7 +120,7 @@ def get_charts(): "type": "Donut", 'timeseries': 0, "chart_type": "Report", - "chart_name": "Accounts Receivable Ageing", + "chart_name": _("Accounts Receivable Ageing"), "is_custom": 1 }, { @@ -128,11 +136,29 @@ def get_charts(): "range2": 60, "range3": 90, "range4": 120 - }), + }), "type": "Donut", 'timeseries': 0, "chart_type": "Report", - "chart_name": "Accounts Payable Ageing", + "chart_name": _("Accounts Payable Ageing"), + "is_custom": 1 + }, + { + "doctype": "Dashboard Charts", + "name": "Budget Variance", + "owner": "Administrator", + "report_name": "Budget Variance Report", + "filters_json": json.dumps({ + "company": company.name, + "from_fiscal_year": fiscal_year[0], + "to_fiscal_year": fiscal_year[0], + "period": "Monthly", + "budget_against": "Cost Center" + }), + "type": "Bar", + "timeseries": 0, + "chart_type": "Report", + "chart_name": _("Budget Variance"), "is_custom": 1 }, { @@ -149,5 +175,21 @@ def get_charts(): "type": "Line", "width": "Half" }, - + ] + +def get_number_cards(): + return [ + { + "doctype": "Number Card", + "document_type": "Payment Entry", + "name": "Total Payment Received", + "filters_json": json.dumps([]), + "label": _("Total Payment Received"), + "function": "Sum", + "aggregate_function_based_on": "base_received_amount", + "is_public": 1, + "is_custom": 1, + "show_percentage_stats": 1, + "stats_time_interval": "Daily" + } ] diff --git a/erpnext/accounts/desk_page/accounting/accounting.json b/erpnext/accounts/desk_page/accounting/accounting.json index 648ebe81a15..682eb8fcede 100644 --- a/erpnext/accounts/desk_page/accounting/accounting.json +++ b/erpnext/accounts/desk_page/accounting/accounting.json @@ -82,12 +82,7 @@ } ], "category": "Modules", - "charts": [ - { - "chart_name": "Bank Balance", - "label": "Bank Balance" - } - ], + "charts": [], "creation": "2020-03-02 15:41:59.515192", "developer_mode_only": 0, "disable_user_customization": 0, @@ -97,10 +92,11 @@ "idx": 0, "is_standard": 1, "label": "Accounting", - "modified": "2020-05-12 17:20:27.573449", + "modified": "2020-05-14 22:28:25.262409", "modified_by": "Administrator", "module": "Accounts", "name": "Accounting", + "onboarding": "Accounts", "owner": "Administrator", "pin_to_bottom": 0, "pin_to_top": 0, @@ -120,6 +116,11 @@ "link_to": "Purchase Invoice", "type": "DocType" }, + { + "label": "Accounts Dashboard", + "link_to": "Accounts Dashboard", + "type": "Dashboard" + }, { "label": "Journal Entry", "link_to": "Journal Entry", @@ -140,11 +141,6 @@ "link_to": "General Ledger", "type": "Report" }, - { - "label": "Profit and Loss Statement", - "link_to": "Profit and Loss Statement", - "type": "Report" - }, { "label": "Trial Balance", "link_to": "Trial Balance", diff --git a/erpnext/accounts/module_onboarding/accounts/accounts.json b/erpnext/accounts/module_onboarding/accounts/accounts.json new file mode 100644 index 00000000000..12da4400284 --- /dev/null +++ b/erpnext/accounts/module_onboarding/accounts/accounts.json @@ -0,0 +1,51 @@ +{ + "allow_roles": [ + { + "role": "Accounts Manager" + }, + { + "role": "Accounts User" + } + ], + "creation": "2020-05-13 19:03:32.564049", + "docstatus": 0, + "doctype": "Module Onboarding", + "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/accounts", + "idx": 0, + "is_complete": 0, + "modified": "2020-05-14 22:11:06.475938", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Accounts", + "owner": "Administrator", + "steps": [ + { + "step": "Chart Of Accounts" + }, + { + "step": "Setup Taxes" + }, + { + "step": "Create a Product" + }, + { + "step": "Create a Supplier" + }, + { + "step": "Create Your First Purchase Invoice" + }, + { + "step": "Create a Customer" + }, + { + "step": "Create Your First Sales Invoice" + }, + { + "step": "Configure Account Settings" + } + ], + "subtitle": "Accounts, invoices and taxation.", + "success_message": "The Accounts module is now set up!", + "title": "Let's Setup Your Accounts and Taxes.", + "user_can_dismiss": 1 +} \ No newline at end of file diff --git a/erpnext/accounts/onboarding_step/chart_of_accounts/chart_of_accounts.json b/erpnext/accounts/onboarding_step/chart_of_accounts/chart_of_accounts.json new file mode 100644 index 00000000000..cbd022bfdbe --- /dev/null +++ b/erpnext/accounts/onboarding_step/chart_of_accounts/chart_of_accounts.json @@ -0,0 +1,20 @@ +{ + "action": "Go to Page", + "creation": "2020-05-13 19:58:20.928127", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-14 17:40:28.410447", + "modified_by": "Administrator", + "name": "Chart Of Accounts", + "owner": "Administrator", + "path": "Tree/Account", + "reference_document": "Account", + "show_full_form": 0, + "title": "Review Chart Of Accounts", + "validate_action": 0 +} \ No newline at end of file diff --git a/erpnext/accounts/onboarding_step/configure_account_settings/configure_account_settings.json b/erpnext/accounts/onboarding_step/configure_account_settings/configure_account_settings.json new file mode 100644 index 00000000000..c8be357de0a --- /dev/null +++ b/erpnext/accounts/onboarding_step/configure_account_settings/configure_account_settings.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-05-14 17:53:00.876946", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 1, + "is_skipped": 0, + "modified": "2020-05-14 18:06:25.212923", + "modified_by": "Administrator", + "name": "Configure Account Settings", + "owner": "Administrator", + "reference_document": "Accounts Settings", + "show_full_form": 1, + "title": "Configure Account Settings", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/accounts/onboarding_step/create_a_customer/create_a_customer.json b/erpnext/accounts/onboarding_step/create_a_customer/create_a_customer.json new file mode 100644 index 00000000000..bb396d268ae --- /dev/null +++ b/erpnext/accounts/onboarding_step/create_a_customer/create_a_customer.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-05-14 17:46:41.831517", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-14 17:46:41.831517", + "modified_by": "Administrator", + "name": "Create a Customer", + "owner": "Administrator", + "reference_document": "Customer", + "show_full_form": 0, + "title": "Create a Customer", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/accounts/onboarding_step/create_a_product/create_a_product.json b/erpnext/accounts/onboarding_step/create_a_product/create_a_product.json new file mode 100644 index 00000000000..450bee1f40b --- /dev/null +++ b/erpnext/accounts/onboarding_step/create_a_product/create_a_product.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-05-14 17:45:28.554605", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-14 17:45:28.554605", + "modified_by": "Administrator", + "name": "Create a Product", + "owner": "Administrator", + "reference_document": "Item", + "show_full_form": 0, + "title": "Create a Product", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/accounts/onboarding_step/create_a_supplier/create_a_supplier.json b/erpnext/accounts/onboarding_step/create_a_supplier/create_a_supplier.json new file mode 100644 index 00000000000..7a64224bd43 --- /dev/null +++ b/erpnext/accounts/onboarding_step/create_a_supplier/create_a_supplier.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-05-14 22:09:10.043554", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-14 22:09:10.043554", + "modified_by": "Administrator", + "name": "Create a Supplier", + "owner": "Administrator", + "reference_document": "Supplier", + "show_full_form": 0, + "title": "Create a Supplier", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/accounts/onboarding_step/create_your_first_purchase_invoice/create_your_first_purchase_invoice.json b/erpnext/accounts/onboarding_step/create_your_first_purchase_invoice/create_your_first_purchase_invoice.json new file mode 100644 index 00000000000..3a2b8d39253 --- /dev/null +++ b/erpnext/accounts/onboarding_step/create_your_first_purchase_invoice/create_your_first_purchase_invoice.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-05-14 22:10:07.049704", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-14 22:10:07.049704", + "modified_by": "Administrator", + "name": "Create Your First Purchase Invoice", + "owner": "Administrator", + "reference_document": "Purchase Invoice", + "show_full_form": 1, + "title": "Create Your First Purchase Invoice ", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/accounts/onboarding_step/create_your_first_sales_invoice/create_your_first_sales_invoice.json b/erpnext/accounts/onboarding_step/create_your_first_sales_invoice/create_your_first_sales_invoice.json new file mode 100644 index 00000000000..473de5079f5 --- /dev/null +++ b/erpnext/accounts/onboarding_step/create_your_first_sales_invoice/create_your_first_sales_invoice.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-05-14 17:48:21.019019", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-14 17:48:21.019019", + "modified_by": "Administrator", + "name": "Create Your First Sales Invoice", + "owner": "Administrator", + "reference_document": "Sales Invoice", + "show_full_form": 1, + "title": "Create Your First Sales Invoice ", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/accounts/onboarding_step/setup_taxes/setup_taxes.json b/erpnext/accounts/onboarding_step/setup_taxes/setup_taxes.json new file mode 100644 index 00000000000..8e0006762d1 --- /dev/null +++ b/erpnext/accounts/onboarding_step/setup_taxes/setup_taxes.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-05-13 19:29:43.844463", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-14 17:40:16.014413", + "modified_by": "Administrator", + "name": "Setup Taxes", + "owner": "Administrator", + "reference_document": "Sales Taxes and Charges Template", + "show_full_form": 1, + "title": "Lets create a Tax Template for Sales ", + "validate_action": 0 +} \ No newline at end of file diff --git a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py index 49c1d0f2ccd..05dc2826611 100644 --- a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py +++ b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py @@ -56,14 +56,26 @@ def execute(filters=None): row += totals data.append(row) - return columns, data + chart = get_chart_data(filters, columns, data) + return columns, data, None, chart def get_columns(filters): columns = [ - _(filters.get("budget_against")) - + ":Link/%s:150" % (filters.get("budget_against")), - _("Account") + ":Link/Account:150" + { + 'label': _(filters.get("budget_against")), + 'fieldtype': 'Link', + 'fieldname': 'budget_against', + 'options': filters.get('budget_against'), + 'width': 150 + }, + { + 'label': _('Account'), + 'fieldname': 'Account', + 'fieldtype': 'Link', + 'options': 'Account', + 'width': 150 + } ] group_months = False if filters["period"] == "Monthly" else True @@ -79,7 +91,12 @@ def get_columns(filters): _("Variance ") + " " + str(year[0]) ] for label in labels: - columns.append(label + ":Float:150") + columns.append({ + 'label': label, + 'fieldtype': 'Float', + 'fieldname': frappe.scrub(label), + 'width': 150 + }) else: for label in [ _("Budget") + " (%s)" + " " + str(year[0]), @@ -95,14 +112,23 @@ def get_columns(filters): else: label = label % formatdate(from_date, format_string="MMM") - columns.append(label + ":Float:150") + columns.append({ + 'label': label, + 'fieldtype': 'Float', + 'fieldname': frappe.scrub(label), + 'width': 150 + }) if filters["period"] != "Yearly": - return columns + [ - _("Total Budget") + ":Float:150", - _("Total Actual") + ":Float:150", - _("Total Variance") + ":Float:150" - ] + for label in [_("Total Budget"), _("Total Actual"), _("Total Variance")]: + columns.append({ + 'label': label, + 'fieldtype': 'Float', + 'fieldname': frappe.scrub(label), + 'width': 150 + }) + + return columns else: return columns @@ -173,7 +199,7 @@ def get_dimension_target_details(filters): filters.budget_against, filters.company, ] - + filters.get("budget_against_filter") + + (filters.get("budget_against_filter") or []) ), as_dict=True) @@ -305,3 +331,49 @@ def get_fiscal_years(filters): }) return fiscal_year + +def get_chart_data(filters, columns, data): + + if not data: + return None + + labels = [] + + fiscal_year = get_fiscal_years(filters) + group_months = False if filters["period"] == "Monthly" else True + + for year in fiscal_year: + for from_date, to_date in get_period_date_ranges(filters["period"], year[0]): + if filters['period'] == 'Yearly': + labels.append(year[0]) + else: + if group_months: + label = formatdate(from_date, format_string="MMM") + "-" \ + + formatdate(to_date, format_string="MMM") + labels.append(label) + else: + label = formatdate(from_date, format_string="MMM") + labels.append(label) + + no_of_columns = len(labels) + + budget_values, actual_values = [0] * no_of_columns, [0] * no_of_columns + for d in data: + values = d[2:] + index = 0 + + for i in range(no_of_columns): + budget_values[i] += values[index] + actual_values[i] += values[index+1] + index += 3 + + return { + 'data': { + 'labels': labels, + 'datasets': [ + {'name': 'Budget', 'chartType': 'bar', 'values': budget_values}, + {'name': 'Actual Expense', 'chartType': 'bar', 'values': actual_values} + ] + } + } + From bb95cd5fd83ea5572940eed4ffde94891065589d Mon Sep 17 00:00:00 2001 From: marination Date: Fri, 15 May 2020 01:33:43 +0530 Subject: [PATCH 070/608] chore: Added Dynamic Charts to DN and PR Trends Reports --- erpnext/public/js/purchase_trends_filters.js | 5 ++- erpnext/public/js/sales_trends_filters.js | 5 ++- .../warehouse_wise_stock_value.py | 2 +- .../delivery_note_trends.py | 31 +++++++++++++++++-- .../purchase_receipt_trends.py | 30 +++++++++++++++++- 5 files changed, 67 insertions(+), 6 deletions(-) diff --git a/erpnext/public/js/purchase_trends_filters.js b/erpnext/public/js/purchase_trends_filters.js index cd767f5d167..d95507b7d1b 100644 --- a/erpnext/public/js/purchase_trends_filters.js +++ b/erpnext/public/js/purchase_trends_filters.js @@ -51,7 +51,10 @@ erpnext.get_purchase_trends_filters = function() { { "value": "Supplier Group", "label": __("Supplier Group") }, { "value": "Project", "label": __("Project") } ], - "default": "Item" + "default": "Item", + "dashboard_config": { + "read_only": 1, + } }, { "fieldname":"group_by", diff --git a/erpnext/public/js/sales_trends_filters.js b/erpnext/public/js/sales_trends_filters.js index b272fdd5fb0..b9c4dca9130 100644 --- a/erpnext/public/js/sales_trends_filters.js +++ b/erpnext/public/js/sales_trends_filters.js @@ -27,7 +27,10 @@ erpnext.get_sales_trends_filters = function() { { "value": "Territory", "label": __("Territory") }, { "value": "Project", "label": __("Project") } ], - "default": "Item" + "default": "Item", + "dashboard_config": { + "read_only": 1, + } }, { "fieldname":"group_by", diff --git a/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py b/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py index 0ee0a6cc77b..da2f9350c6d 100644 --- a/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py +++ b/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py @@ -10,7 +10,7 @@ from erpnext.stock.utils import get_stock_value_from_bin @frappe.whitelist() @cache_source def get(chart_name = None, chart = None, no_cache = None, filters = None, from_date = None, - to_date = None, timespan = None, time_interval = None): + to_date = None, timespan = None, time_interval = None, heatmap_year = None): labels, datapoints = [], [] filters = frappe.parse_json(filters) diff --git a/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py b/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py index 27cf6b66ccb..2108b51afc8 100644 --- a/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py +++ b/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py @@ -3,6 +3,7 @@ from __future__ import unicode_literals import frappe +from frappe import _ from erpnext.controllers.trends import get_columns,get_data def execute(filters=None): @@ -10,5 +11,31 @@ def execute(filters=None): data = [] conditions = get_columns(filters, "Delivery Note") data = get_data(filters, conditions) - - return conditions["columns"], data \ No newline at end of file + + chart_data = get_chart_data(data) + + return conditions["columns"], data, None, chart_data + +def get_chart_data(data): + labels, datapoints = [], [] + + if len(data) > 10: + data = sorted(data, key = lambda i: i[-1],reverse=True) + data = data[:10] + + for row in data: + labels.append(row[0]) + datapoints.append(row[-1]) + + return { + "data": { + "labels" : labels, + "datasets" : [ + { + "name": _("Total Revenue"), + "values": datapoints + } + ] + }, + "type" : "bar" + } \ No newline at end of file diff --git a/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.py b/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.py index 0e58920725e..ac235f74e1c 100644 --- a/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.py +++ b/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.py @@ -3,6 +3,7 @@ from __future__ import unicode_literals import frappe +from frappe import _ from erpnext.controllers.trends import get_columns,get_data def execute(filters=None): @@ -11,4 +12,31 @@ def execute(filters=None): conditions = get_columns(filters, "Purchase Receipt") data = get_data(filters, conditions) - return conditions["columns"], data \ No newline at end of file + chart_data = get_chart_data(data) + + return conditions["columns"], data, None, chart_data + +def get_chart_data(data): + labels, datapoints = [], [] + + if len(data) > 10: + data = sorted(data, key = lambda i: i[-1],reverse=True) + data = data[:10] + + for row in data: + labels.append(row[0]) + datapoints.append(row[-1]) + + return { + "data": { + "labels" : labels, + "datasets" : [ + { + "name": _("Total Expenditure"), + "values": datapoints + } + ] + }, + "type" : "bar", + "colors":["#5e64ff"] + } \ No newline at end of file From 3f7678416f76cb86157c1df4a744b064d55190de Mon Sep 17 00:00:00 2001 From: marination Date: Fri, 15 May 2020 03:58:09 +0530 Subject: [PATCH 071/608] chore: Stock Onboarding --- erpnext/stock/desk_page/stock/stock.json | 54 ++++++++++++++----- .../doctype/stock_settings/stock_settings.js | 34 ++++++++++++ .../stock/module_onboarding/stock/stock.json | 54 +++++++++++++++++++ .../buying_settings/buying_settings.json | 19 +++++++ .../create_a_price_list.json | 19 +++++++ .../create_a_product/create_a_product.json | 19 +++++++ .../create_a_stock_entry.json | 19 +++++++ .../create_a_warehouse.json | 19 +++++++ ...oduction_to_price_list_and_item_price.json | 19 +++++++ .../introduction_to_stock_entry.json | 19 +++++++ .../stock_settings/stock_settings.json | 19 +++++++ 11 files changed, 281 insertions(+), 13 deletions(-) create mode 100644 erpnext/stock/module_onboarding/stock/stock.json create mode 100644 erpnext/stock/onboarding_step/buying_settings/buying_settings.json create mode 100644 erpnext/stock/onboarding_step/create_a_price_list/create_a_price_list.json create mode 100644 erpnext/stock/onboarding_step/create_a_product/create_a_product.json create mode 100644 erpnext/stock/onboarding_step/create_a_stock_entry/create_a_stock_entry.json create mode 100644 erpnext/stock/onboarding_step/create_a_warehouse/create_a_warehouse.json create mode 100644 erpnext/stock/onboarding_step/introduction_to_price_list_and_item_price/introduction_to_price_list_and_item_price.json create mode 100644 erpnext/stock/onboarding_step/introduction_to_stock_entry/introduction_to_stock_entry.json create mode 100644 erpnext/stock/onboarding_step/stock_settings/stock_settings.json diff --git a/erpnext/stock/desk_page/stock/stock.json b/erpnext/stock/desk_page/stock/stock.json index 38475a6f269..6b93449ecd4 100644 --- a/erpnext/stock/desk_page/stock/stock.json +++ b/erpnext/stock/desk_page/stock/stock.json @@ -1,9 +1,14 @@ { "cards": [ + { + "hidden": 0, + "label": "Items and Pricing", + "links": "[\n {\n \"label\": \"Item\",\n \"name\": \"Item\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Item Group\",\n \"link\": \"Tree/Item Group\",\n \"name\": \"Item Group\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Product Bundle\",\n \"name\": \"Product Bundle\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Price List\",\n \"name\": \"Price List\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Item Price\",\n \"name\": \"Item Price\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Shipping Rule\",\n \"name\": \"Shipping Rule\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Pricing Rule\",\n \"name\": \"Pricing Rule\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Item Alternative\",\n \"name\": \"Item Alternative\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Item Manufacturer\",\n \"name\": \"Item Manufacturer\",\n \"type\": \"doctype\"\n }\n]" + }, { "hidden": 0, "label": "Stock Transactions", - "links": "[\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"label\": \"Stock Entry\",\n \"name\": \"Stock Entry\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"label\": \"Delivery Note\",\n \"name\": \"Delivery Note\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Supplier\"\n ],\n \"label\": \"Purchase Receipt\",\n \"name\": \"Purchase Receipt\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"label\": \"Material Request\",\n \"name\": \"Material Request\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"label\": \"Pick List\",\n \"name\": \"Pick List\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Delivery Trip\",\n \"name\": \"Delivery Trip\",\n \"type\": \"doctype\"\n }\n]" + "links": "[\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"label\": \"Material Request\",\n \"name\": \"Material Request\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"label\": \"Stock Entry\",\n \"name\": \"Stock Entry\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"label\": \"Delivery Note\",\n \"name\": \"Delivery Note\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Supplier\"\n ],\n \"label\": \"Purchase Receipt\",\n \"name\": \"Purchase Receipt\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"label\": \"Pick List\",\n \"name\": \"Pick List\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Delivery Trip\",\n \"name\": \"Delivery Trip\",\n \"type\": \"doctype\"\n }\n]" }, { "hidden": 0, @@ -13,12 +18,7 @@ { "hidden": 0, "label": "Settings", - "links": "[\n {\n \"label\": \"Stock Settings\",\n \"name\": \"Stock Settings\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Warehouse\",\n \"name\": \"Warehouse\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Unit of Measure (UOM)\",\n \"name\": \"UOM\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Brand\",\n \"name\": \"Brand\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Item Attribute\",\n \"name\": \"Item Attribute\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Item Variant Settings\",\n \"name\": \"Item Variant Settings\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Items and Pricing", - "links": "[\n {\n \"label\": \"Item\",\n \"name\": \"Item\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Product Bundle\",\n \"name\": \"Product Bundle\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Item Group\",\n \"link\": \"Tree/Item Group\",\n \"name\": \"Item Group\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Price List\",\n \"name\": \"Price List\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Item Price\",\n \"name\": \"Item Price\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Shipping Rule\",\n \"name\": \"Shipping Rule\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Pricing Rule\",\n \"name\": \"Pricing Rule\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Item Alternative\",\n \"name\": \"Item Alternative\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Item Manufacturer\",\n \"name\": \"Item Manufacturer\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Item Variant Settings\",\n \"name\": \"Item Variant Settings\",\n \"type\": \"doctype\"\n }\n]" + "links": "[\n {\n \"label\": \"Stock Settings\",\n \"name\": \"Stock Settings\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Warehouse\",\n \"name\": \"Warehouse\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Unit of Measure (UOM)\",\n \"name\": \"UOM\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Item Variant Settings\",\n \"name\": \"Item Variant Settings\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Brand\",\n \"name\": \"Brand\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Item Attribute\",\n \"name\": \"Item Attribute\",\n \"type\": \"doctype\"\n }\n]" }, { "hidden": 0, @@ -41,34 +41,45 @@ "links": "[\n {\n \"dependencies\": [\n \"Material Request\"\n ],\n \"doctype\": \"Material Request\",\n \"is_query_report\": true,\n \"label\": \"Requested Items To Be Transferred\",\n \"name\": \"Requested Items To Be Transferred\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Stock Ledger Entry\"\n ],\n \"doctype\": \"Stock Ledger Entry\",\n \"is_query_report\": true,\n \"label\": \"Batch Item Expiry Status\",\n \"name\": \"Batch Item Expiry Status\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Price List\"\n ],\n \"doctype\": \"Price List\",\n \"is_query_report\": true,\n \"label\": \"Item Prices\",\n \"name\": \"Item Prices\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"doctype\": \"Item\",\n \"is_query_report\": true,\n \"label\": \"Itemwise Recommended Reorder Level\",\n \"name\": \"Itemwise Recommended Reorder Level\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"doctype\": \"Item\",\n \"is_query_report\": true,\n \"label\": \"Item Variant Details\",\n \"name\": \"Item Variant Details\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Order\"\n ],\n \"doctype\": \"Purchase Order\",\n \"is_query_report\": true,\n \"label\": \"Subcontracted Raw Materials To Be Transferred\",\n \"name\": \"Subcontracted Raw Materials To Be Transferred\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Order\"\n ],\n \"doctype\": \"Purchase Order\",\n \"is_query_report\": true,\n \"label\": \"Subcontracted Item To Be Received\",\n \"name\": \"Subcontracted Item To Be Received\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Stock Ledger Entry\"\n ],\n \"doctype\": \"Stock Ledger Entry\",\n \"is_query_report\": true,\n \"label\": \"Stock and Account Value Comparison\",\n \"name\": \"Stock and Account Value Comparison\",\n \"type\": \"report\"\n }\n]" } ], + "cards_label": "Masters & Reports", "category": "Modules", - "charts": [], + "charts": [ + { + "chart_name": "Warehouse wise Stock Value" + } + ], "creation": "2020-03-02 15:43:10.096528", "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, - "icon": "", "idx": 0, "is_standard": 1, "label": "Stock", - "modified": "2020-04-01 11:28:51.148421", + "modified": "2020-05-15 04:11:36.326013", "modified_by": "Administrator", "module": "Stock", "name": "Stock", + "onboarding": "Stock", "owner": "Administrator", "pin_to_bottom": 0, "pin_to_top": 0, "shortcuts": [ { + "color": "#cef6d1", + "format": "{} available", "label": "Item", "link_to": "Item", + "stats_filter": "{\n \"disabled\" : 0\n}", "type": "DocType" }, { - "label": "Pricing Rule", - "link_to": "Pricing Rule", + "color": "#ffe8cd", + "format": "{} Pending", + "label": "Material Request", + "link_to": "Material Request", + "stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\": \"Pending\"\n}", "type": "DocType" }, { @@ -76,6 +87,22 @@ "link_to": "Stock Entry", "type": "DocType" }, + { + "color": "#ffe8cd", + "format": "{} to Bill", + "label": "Purchase Receipt", + "link_to": "Purchase Receipt", + "stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\": \"To Bill\"\n}", + "type": "DocType" + }, + { + "color": "#ffe8cd", + "format": "{} to Bill", + "label": "Delivery Note", + "link_to": "Delivery Note", + "stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\": \"To Bill\"\n}", + "type": "DocType" + }, { "label": "Stock Ledger", "link_to": "Stock Ledger", @@ -86,5 +113,6 @@ "link_to": "Stock Balance", "type": "Report" } - ] + ], + "shortcuts_label": "Quick Access" } \ No newline at end of file diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.js b/erpnext/stock/doctype/stock_settings/stock_settings.js index cc0e2cfc425..81c60679467 100644 --- a/erpnext/stock/doctype/stock_settings/stock_settings.js +++ b/erpnext/stock/doctype/stock_settings/stock_settings.js @@ -15,3 +15,37 @@ frappe.ui.form.on('Stock Settings', { frm.set_query("sample_retention_warehouse", filters); } }); + +frappe.tour['Stock Settings'] = [ + { + fieldname: "item_naming_by", + title: __("Item Naming By"), + description: __("By default, the Item Name is set as per the Item Code entered. If you want Items to be named by a set Naming Series choose the 'Naming Series' option.") + }, + { + fieldname: "valuation_method", + title: __("Valuation Method"), + description: __("Choose between FIFO and Moving Average Valuation Methods. Click") + "here" + __("to understand them") + }, + { + fieldname: "show_barcode_field", + title: __("Show Barcode Field"), + description: __("Show 'Scan Barcode' field above every child table to insert Items with ease.") + }, + { + fieldname: "default_warehouse", + title: __("Default Warehouse"), + description: __("Set a Default Warehouse for Inventory Transactions. This will be fetched into the Default Warehouse in the Item master:") + }, + { + fieldname: "allow_negative_stock", + title: __("Allow Negative Stock"), + description: __("This will allow stock items to be displayed in negative values. Using this option depends on your use case. With this option unchecked, the system warns before obstructing a transaction that is causing negative stock.") + + }, + { + fieldname: "automatically_set_serial_nos_based_on_fifo", + title: __("Automatically Set Serial Nos based on FIFO"), + description: __("Serial numbers for stock will be set automatically based on the Items entered based on first in first out in transactions like Purchase/Sales Invoices, Delivery Notes, etc.") + } +]; diff --git a/erpnext/stock/module_onboarding/stock/stock.json b/erpnext/stock/module_onboarding/stock/stock.json new file mode 100644 index 00000000000..28d8f672850 --- /dev/null +++ b/erpnext/stock/module_onboarding/stock/stock.json @@ -0,0 +1,54 @@ +{ + "allow_roles": [ + { + "role": "Manufacturing Manager" + }, + { + "role": "Stock Manager" + }, + { + "role": "Manufacturing User" + }, + { + "role": "Stock User" + } + ], + "creation": "2020-05-15 03:18:44.400108", + "docstatus": 0, + "doctype": "Module Onboarding", + "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/stock", + "idx": 0, + "is_complete": 0, + "modified": "2020-05-15 04:02:23.634655", + "modified_by": "Administrator", + "module": "Stock", + "name": "Stock", + "owner": "Administrator", + "steps": [ + { + "step": "Create a Warehouse" + }, + { + "step": "Create a Product" + }, + { + "step": "Stock Settings" + }, + { + "step": "Introduction to Stock Entry" + }, + { + "step": "Create a Stock Entry" + }, + { + "step": "Introduction to Price List and Item Price" + }, + { + "step": "Create a Price List" + } + ], + "subtitle": "Inventory, Warehouses, Analysis and more.", + "success_message": "The Stock Module is all set up!", + "title": "Let's Setup the Stock Module.", + "user_can_dismiss": 1 +} \ No newline at end of file diff --git a/erpnext/stock/onboarding_step/buying_settings/buying_settings.json b/erpnext/stock/onboarding_step/buying_settings/buying_settings.json new file mode 100644 index 00000000000..a788ccd4cc9 --- /dev/null +++ b/erpnext/stock/onboarding_step/buying_settings/buying_settings.json @@ -0,0 +1,19 @@ +{ + "action": "Update Settings", + "creation": "2020-05-06 15:53:44.667414", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-12 18:30:06.323797", + "modified_by": "Administrator", + "name": "Buying Settings", + "owner": "Administrator", + "reference_document": "Buying Settings", + "show_full_form": 0, + "title": "Configure Buying Settings.", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/stock/onboarding_step/create_a_price_list/create_a_price_list.json b/erpnext/stock/onboarding_step/create_a_price_list/create_a_price_list.json new file mode 100644 index 00000000000..ce5b5ecf86e --- /dev/null +++ b/erpnext/stock/onboarding_step/create_a_price_list/create_a_price_list.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-05-15 03:26:41.917046", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-15 03:33:09.305991", + "modified_by": "Administrator", + "name": "Create a Price List", + "owner": "Administrator", + "reference_document": "Price List", + "show_full_form": 1, + "title": "Create a Price List", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/stock/onboarding_step/create_a_product/create_a_product.json b/erpnext/stock/onboarding_step/create_a_product/create_a_product.json new file mode 100644 index 00000000000..d2068e167b7 --- /dev/null +++ b/erpnext/stock/onboarding_step/create_a_product/create_a_product.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-05-12 18:16:06.624554", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-12 18:30:02.489949", + "modified_by": "Administrator", + "name": "Create a Product", + "owner": "Administrator", + "reference_document": "Item", + "show_full_form": 0, + "title": "Create a Product", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/stock/onboarding_step/create_a_stock_entry/create_a_stock_entry.json b/erpnext/stock/onboarding_step/create_a_stock_entry/create_a_stock_entry.json new file mode 100644 index 00000000000..2b83f657d6e --- /dev/null +++ b/erpnext/stock/onboarding_step/create_a_stock_entry/create_a_stock_entry.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-05-15 03:20:16.277043", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-15 03:30:58.047696", + "modified_by": "Administrator", + "name": "Create a Stock Entry", + "owner": "Administrator", + "reference_document": "Stock Entry", + "show_full_form": 1, + "title": "Create a Stock Entry", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/stock/onboarding_step/create_a_warehouse/create_a_warehouse.json b/erpnext/stock/onboarding_step/create_a_warehouse/create_a_warehouse.json new file mode 100644 index 00000000000..3269125efc2 --- /dev/null +++ b/erpnext/stock/onboarding_step/create_a_warehouse/create_a_warehouse.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-05-12 18:00:03.027704", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-12 18:27:44.128737", + "modified_by": "Administrator", + "name": "Create a Warehouse", + "owner": "Administrator", + "reference_document": "Warehouse", + "show_full_form": 0, + "title": "Setup your Purchase Warehouse", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/stock/onboarding_step/introduction_to_price_list_and_item_price/introduction_to_price_list_and_item_price.json b/erpnext/stock/onboarding_step/introduction_to_price_list_and_item_price/introduction_to_price_list_and_item_price.json new file mode 100644 index 00000000000..f75523ab298 --- /dev/null +++ b/erpnext/stock/onboarding_step/introduction_to_price_list_and_item_price/introduction_to_price_list_and_item_price.json @@ -0,0 +1,19 @@ +{ + "action": "Watch Video", + "creation": "2020-05-15 03:26:01.386069", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-15 03:31:01.267728", + "modified_by": "Administrator", + "name": "Introduction to Price List and Item Price", + "owner": "Administrator", + "show_full_form": 0, + "title": "Let's take a brief look at Price List and Item Price", + "validate_action": 1, + "video_url": "https://www.youtube.com/watch?v=lY6hAQM1I28" +} \ No newline at end of file diff --git a/erpnext/stock/onboarding_step/introduction_to_stock_entry/introduction_to_stock_entry.json b/erpnext/stock/onboarding_step/introduction_to_stock_entry/introduction_to_stock_entry.json new file mode 100644 index 00000000000..229bcd49cf8 --- /dev/null +++ b/erpnext/stock/onboarding_step/introduction_to_stock_entry/introduction_to_stock_entry.json @@ -0,0 +1,19 @@ +{ + "action": "Watch Video", + "creation": "2020-05-15 02:47:17.958806", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-15 03:30:39.606147", + "modified_by": "Administrator", + "name": "Introduction to Stock Entry", + "owner": "Administrator", + "show_full_form": 0, + "title": "Introduction to the backbone of Stock, Stock Entry.", + "validate_action": 1, + "video_url": "https://www.youtube.com/watch?v=Njt107hlY3I" +} \ No newline at end of file diff --git a/erpnext/stock/onboarding_step/stock_settings/stock_settings.json b/erpnext/stock/onboarding_step/stock_settings/stock_settings.json new file mode 100644 index 00000000000..7591bff5386 --- /dev/null +++ b/erpnext/stock/onboarding_step/stock_settings/stock_settings.json @@ -0,0 +1,19 @@ +{ + "action": "Show Form Tour", + "creation": "2020-05-15 02:53:57.209967", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 1, + "is_skipped": 0, + "modified": "2020-05-15 03:55:15.444151", + "modified_by": "Administrator", + "name": "Stock Settings", + "owner": "Administrator", + "reference_document": "Stock Settings", + "show_full_form": 0, + "title": "Explore Stock Settings", + "validate_action": 1 +} \ No newline at end of file From 3373dad708faced41801b20a97674a7449ea9e81 Mon Sep 17 00:00:00 2001 From: theopen-institute Date: Fri, 15 May 2020 08:51:39 +0545 Subject: [PATCH 072/608] Added a posting_date field to Fee Schedule, which carries forward to created Fees --- .../education/doctype/fee_schedule/fee_schedule.json | 12 +++++++++++- .../education/doctype/fee_schedule/fee_schedule.py | 1 + 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/erpnext/education/doctype/fee_schedule/fee_schedule.json b/erpnext/education/doctype/fee_schedule/fee_schedule.json index 1e98709b2b9..791831810ae 100644 --- a/erpnext/education/doctype/fee_schedule/fee_schedule.json +++ b/erpnext/education/doctype/fee_schedule/fee_schedule.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_import": 1, "autoname": "naming_series:", "creation": "2017-07-18 15:21:21.527136", @@ -7,6 +8,7 @@ "engine": "InnoDB", "field_order": [ "fee_structure", + "posting_date", "due_date", "naming_series", "fee_creation_status", @@ -259,10 +261,18 @@ { "fieldname": "dimension_col_break", "fieldtype": "Column Break" + }, + { + "default": "Today", + "fieldname": "posting_date", + "fieldtype": "Date", + "label": "Posting Date", + "reqd": 1 } ], "is_submittable": 1, - "modified": "2019-05-26 09:10:34.522409", + "links": [], + "modified": "2020-05-15 08:39:20.682837", "modified_by": "Administrator", "module": "Education", "name": "Fee Schedule", diff --git a/erpnext/education/doctype/fee_schedule/fee_schedule.py b/erpnext/education/doctype/fee_schedule/fee_schedule.py index a42800a80d5..1543acdca98 100644 --- a/erpnext/education/doctype/fee_schedule/fee_schedule.py +++ b/erpnext/education/doctype/fee_schedule/fee_schedule.py @@ -87,6 +87,7 @@ def generate_fee(fee_schedule): } } }) + fees_doc.posting_date = doc.posting_date fees_doc.student = student.student fees_doc.student_name = student.student_name fees_doc.program = student.program From be5c45a9ae1fc0ddc48de5107e624a2ba144f63c Mon Sep 17 00:00:00 2001 From: marination Date: Fri, 15 May 2020 13:21:58 +0530 Subject: [PATCH 073/608] chore: Fixtures and empty state handling in chart widgets --- .../warehouse_wise_stock_value.py | 4 + erpnext/stock/dashboard_fixtures.py | 207 ++++++++++++++++++ .../delivery_note_trends.py | 3 + .../item_shortage_report.py | 2 +- .../purchase_receipt_trends.py | 3 + .../stock/report/stock_ageing/stock_ageing.py | 3 + 6 files changed, 221 insertions(+), 1 deletion(-) create mode 100644 erpnext/stock/dashboard_fixtures.py diff --git a/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py b/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py index da2f9350c6d..05a50687524 100644 --- a/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py +++ b/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py @@ -25,6 +25,10 @@ def get(chart_name = None, chart = None, no_cache = None, filters = None, from_d wh["balance"] = balance[0][0] warehouses = [x for x in warehouses if not (x.get('balance') == None)] + + if not warehouses: + return [] + sorted_warehouse_map = sorted(warehouses, key = lambda i: i['balance'],reverse=True) if len(sorted_warehouse_map) > 10: diff --git a/erpnext/stock/dashboard_fixtures.py b/erpnext/stock/dashboard_fixtures.py new file mode 100644 index 00000000000..db955c39c95 --- /dev/null +++ b/erpnext/stock/dashboard_fixtures.py @@ -0,0 +1,207 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +import frappe +import json +from frappe.utils import nowdate +from erpnext.accounts.utils import get_fiscal_year + +def get_data(): + return frappe._dict({ + "dashboards": get_dashboards(), + "charts": get_charts(), + "number_cards": get_number_cards(), + }) + +def get_company_for_dashboards(): + company = frappe.defaults.get_defaults().company + if company: + return company + else: + company_list = frappe.get_list("Company") + if company_list: + return company_list[0].name + return None + +company = frappe.get_doc("Company", get_company_for_dashboards()) +fiscal_year = get_fiscal_year(nowdate(), as_dict=1).get("name") + +def get_dashboards(): + return [{ + "name": "Stock", + "dashboard_name": "Stock", + "charts": [ + { "chart": "Item Shortage Summary", "width": "Half"}, + { "chart": "Stock Ageing", "width": "Half"}, + { "chart": "Item Wise Annual Revenue", "width": "Half"}, + { "chart": "Item Wise Annual Expenditure", "width": "Half"}, + { "chart": "Warehouse wise Stock Value", "width": "Full"} + ], + "cards": [ + { "card": "Purchase Receipts to Bill"}, + { "card": "Amount Payable against Receipt"}, + { "card": "Delivery Notes to Bill"}, + { "card": "Amount Receivable against Delivery"} + ] + }] + +def get_charts(): + return [ + { + "name": "Item Shortage Summary", + "chart_name": "Item Shortage Summary", + "chart_type": "Report", + "doctype": "Dashboard Chart", + "filters_json": json.dumps({ + "company": company.name + }), + "is_custom": 1, + "is_public": 1, + "owner": "Administrator", + "report_name": "Item Shortage Report", + "type": "Bar" + }, + { + "name": "Stock Ageing", + "chart_name": "Stock Ageing", + "chart_type": "Report", + "custom_options": json.dumps({ + "colors": ["#5e64ff"] + }), + "doctype": "Dashboard Chart", + "filters_json": json.dumps({ + "company": company.name, + "to_date": nowdate(), + "show_warehouse_wise_stock": 0 + }), + "is_custom": 1, + "is_public": 1, + "owner": "Administrator", + "report_name": "Stock Ageing", + "type": "Bar" + }, + { + "name": "Item Wise Annual Revenue", + "chart_name": "Item Wise Annual Revenue", + "chart_type": "Report", + "custom_options": json.dumps({ + "axisOptions": {"shortenYAxisNumbers": 1}, + "tooltipOptions": {}, + "colors":["#5e64ff"] + }), + "doctype": "Dashboard Chart", + "filters_json": json.dumps({ + "period": "Monthly", + "based_on": "Item", + "fiscal_year": fiscal_year, + "company": company.name + }), + "is_custom": 1, + "is_public": 1, + "owner": "Administrator", + "report_name": "Delivery Note Trends", + "type": "Bar" + }, + { + "name": "Item Wise Annual Expenditure", + "chart_name": "Item Wise Annual Expenditure", + "chart_type": "Report", + "custom_options": json.dumps({ + "axisOptions": {"shortenYAxisNumbers": 1}, + "tooltipOptions": {}, + "colors":["#5e64ff"] + }), + "doctype": "Dashboard Chart", + "filters_json": json.dumps({ + "period": "Monthly", + "based_on": "Item", + "fiscal_year": fiscal_year, + "company": company.name, + "period_based_on": "posting_date" + }), + "is_custom": 1, + "is_public": 1, + "owner": "Administrator", + "report_name": "Purchase Receipt Trends", + "type": "Bar" + }, + { + "name": "Warehouse wise Stock Value", + "chart_name": "Warehouse wise Stock Value", + "chart_type": "Custom", + "doctype": "Dashboard Chart", + "filters_json": json.dumps({}), + "is_custom": 0, + "is_public": 1, + "owner": "Administrator", + "source": "Warehouse wise Stock Value", + "type": "Bar" + } + + ] + +def get_number_cards(): + return [ + { + "name": "Amount Payable against Receipt", + "label": "Amount Payable against Receipt", + "function": "Sum", + "aggregate_function_based_on": "base_grand_total", + "doctype": "Number Card", + "document_type": "Purchase Receipt", + "filters_json": json.dumps( + [["Purchase Receipt","status","=","To Bill",False], + ["Purchase Receipt","company","=", company.name, False]] + ), + "is_public": 1, + "owner": "Administrator", + "show_percentage_stats": 1, + "stats_time_interval": "Daily" + }, + { + "name": "Amount Receivable against Delivery", + "label": "Amount Receivable against Delivery", + "function": "Sum", + "aggregate_function_based_on": "base_grand_total", + "doctype": "Number Card", + "document_type": "Delivery Note", + "filters_json": json.dumps( + [["Delivery Note","company","=",company.name,False], + ["Delivery Note","status","=","To Bill",False]] + ), + "is_public": 1, + "owner": "Administrator", + "show_percentage_stats": 1, + "stats_time_interval": "Daily" + }, + { + "name": "Purchase Receipts to Bill", + "label": "Purchase Receipts to Bill", + "function": "Count", + "doctype": "Number Card", + "document_type": "Purchase Receipt", + "filters_json": json.dumps( + [["Purchase Receipt","status","=","To Bill",False], + ["Purchase Receipt","company","=", company.name, False]] + ), + "is_public": 1, + "owner": "Administrator", + "show_percentage_stats": 1, + "stats_time_interval": "Daily" + }, + { + "name": "Delivery Notes to Bill", + "label": "Delivery Notes to Bill", + "function": "Count", + "doctype": "Number Card", + "document_type": "Delivery Note", + "filters_json": json.dumps( + [["Delivery Note","company","=",company.name,False], + ["Delivery Note","status","=","To Bill",False]] + ), + "is_public": 1, + "owner": "Administrator", + "show_percentage_stats": 1, + "stats_time_interval": "Daily" + } + ] \ No newline at end of file diff --git a/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py b/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py index 2108b51afc8..d088b0020d8 100644 --- a/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py +++ b/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py @@ -17,6 +17,9 @@ def execute(filters=None): return conditions["columns"], data, None, chart_data def get_chart_data(data): + if not data: + return [] + labels, datapoints = [], [] if len(data) > 10: diff --git a/erpnext/stock/report/item_shortage_report/item_shortage_report.py b/erpnext/stock/report/item_shortage_report/item_shortage_report.py index 07749ebec57..086d833bbc4 100644 --- a/erpnext/stock/report/item_shortage_report/item_shortage_report.py +++ b/erpnext/stock/report/item_shortage_report/item_shortage_report.py @@ -11,7 +11,7 @@ def execute(filters=None): data = get_data(conditions, filters) if not data: - return [], [] + return [], [], None, [] chart_data = get_chart_data(data) diff --git a/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.py b/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.py index ac235f74e1c..627c23b1051 100644 --- a/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.py +++ b/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.py @@ -17,6 +17,9 @@ def execute(filters=None): return conditions["columns"], data, None, chart_data def get_chart_data(data): + if not data: + return [] + labels, datapoints = [], [] if len(data) > 10: diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.py b/erpnext/stock/report/stock_ageing/stock_ageing.py index 48f22c2c24d..c5b8f43f968 100644 --- a/erpnext/stock/report/stock_ageing/stock_ageing.py +++ b/erpnext/stock/report/stock_ageing/stock_ageing.py @@ -234,6 +234,9 @@ def get_sle_conditions(filters): return "and {}".format(" and ".join(conditions)) if conditions else "" def get_chart_data(data, filters): + if not data: + return [] + labels, datapoints = [], [] if filters.get("show_warehouse_wise_stock"): From ec1f9594fa452059231f7aa87c76a5e563edd58d Mon Sep 17 00:00:00 2001 From: marination Date: Fri, 15 May 2020 15:17:11 +0530 Subject: [PATCH 074/608] fix: Refined Onboarding, desk cards and renamed field in Buying Settings --- erpnext/buying/desk_page/buying/buying.json | 43 ++++++--------- .../buying_settings/buying_settings.js | 23 ++++++++ .../buying_settings/buying_settings.json | 13 +++-- .../module_onboarding/buying/buying.json | 54 +++++++++++++++++++ .../buying_settings/buying_settings.json | 11 ++-- .../create_a_material_request.json | 19 +++++++ .../create_a_product/create_a_product.json | 5 +- .../create_a_supplier/create_a_supplier.json | 5 +- .../create_a_warehouse.json | 7 ++- .../create_your_first_purchase_order.json | 5 +- .../introduction_to_buying.json | 3 ++ 11 files changed, 148 insertions(+), 40 deletions(-) create mode 100644 erpnext/buying/module_onboarding/buying/buying.json create mode 100644 erpnext/buying/onboarding_step/create_a_material_request/create_a_material_request.json diff --git a/erpnext/buying/desk_page/buying/buying.json b/erpnext/buying/desk_page/buying/buying.json index 9749f3c97fc..e00db0e86d7 100644 --- a/erpnext/buying/desk_page/buying/buying.json +++ b/erpnext/buying/desk_page/buying/buying.json @@ -33,7 +33,7 @@ { "hidden": 0, "label": "Other Reports", - "links": "[\n {\n \"is_query_report\": true,\n \"label\": \"Items To Be Requested\",\n \"name\": \"Items To Be Requested\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Item-wise Purchase History\",\n \"name\": \"Item-wise Purchase History\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Subcontracted Raw Materials To Be Transferred\",\n \"name\": \"Subcontracted Raw Materials To Be Transferred\",\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Subcontracted Item To Be Received\",\n \"name\": \"Subcontracted Item To Be Received\",\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Quoted Item Comparison\",\n \"name\": \"Quoted Item Comparison\",\n \"reference_doctype\": \"Supplier Quotation\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Material Requests for which Supplier Quotations are not created\",\n \"name\": \"Material Requests for which Supplier Quotations are not created\",\n \"reference_doctype\": \"Material Request\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Supplier Addresses And Contacts\",\n \"name\": \"Address And Contacts\",\n \"reference_doctype\": \"Address\",\n \"route_options\": {\n \"party_type\": \"Supplier\"\n },\n \"type\": \"report\"\n }\n]" + "links": "[\n {\n \"is_query_report\": true,\n \"label\": \"Items To Be Requested\",\n \"name\": \"Items To Be Requested\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Item-wise Purchase History\",\n \"name\": \"Item-wise Purchase History\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Subcontracted Raw Materials To Be Transferred\",\n \"name\": \"Subcontracted Raw Materials To Be Transferred\",\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Subcontracted Item To Be Received\",\n \"name\": \"Subcontracted Item To Be Received\",\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Quoted Item Comparison\",\n \"name\": \"Quoted Item Comparison\",\n \"onboard\": 1,\n \"reference_doctype\": \"Supplier Quotation\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Material Requests for which Supplier Quotations are not created\",\n \"name\": \"Material Requests for which Supplier Quotations are not created\",\n \"reference_doctype\": \"Material Request\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Supplier Addresses And Contacts\",\n \"name\": \"Address And Contacts\",\n \"reference_doctype\": \"Address\",\n \"route_options\": {\n \"party_type\": \"Supplier\"\n },\n \"type\": \"report\"\n }\n]" } ], "cards_label": "Masters & Reports ", @@ -54,7 +54,7 @@ "idx": 0, "is_standard": 1, "label": "Buying", - "modified": "2020-05-06 18:10:12.760321", + "modified": "2020-05-15 14:26:42.505702", "modified_by": "Administrator", "module": "Buying", "name": "Buying", @@ -64,11 +64,11 @@ "pin_to_top": 0, "shortcuts": [ { - "color": "#ffe8cd", - "format": "{} to Receive", - "label": "Purchase Order", - "link_to": "Purchase Order", - "stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\":[\"in\", [\"To Receive\", \"To Receive and Bill\"]]\n}", + "color": "#cef6d1", + "format": "{} available", + "label": "Item", + "link_to": "Item", + "stats_filter": "{\n \"disabled\": 0\n}", "type": "DocType" }, { @@ -81,31 +81,20 @@ }, { "color": "#ffe8cd", - "format": "{} to Bill ", - "label": "Purchase Receipt", - "link_to": "Purchase Receipt", - "stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\": \"To Bill\"\n}", + "format": "{} to Receive", + "label": "Purchase Order", + "link_to": "Purchase Order", + "stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\":[\"in\", [\"To Receive\", \"To Receive and Bill\"]]\n}", "type": "DocType" }, { - "color": "#ffe8cd", - "format": "{} Unpaid / Overdue", - "label": "Purchase Invoice", - "link_to": "Purchase Invoice", - "stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\": [\"in\", [\"Unpaid\", \"Overdue\"]]\n}", - "type": "DocType" + "label": "Purchase Analytics", + "link_to": "Purchase Analytics", + "type": "Report" }, { - "color": "#cef6d1", - "format": "{} Active", - "label": "Supplier Quotation", - "link_to": "Supplier Quotation", - "stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\":[\"!=\", \"Expired\"]\n}", - "type": "DocType" - }, - { - "label": "Item-wise Purchase Register", - "link_to": "Item-wise Purchase Register", + "label": "Purchase Order Analysis", + "link_to": "Purchase Order Analysis", "type": "Report" } ], diff --git a/erpnext/buying/doctype/buying_settings/buying_settings.js b/erpnext/buying/doctype/buying_settings/buying_settings.js index 403b1c95e74..5a5afa41fcd 100644 --- a/erpnext/buying/doctype/buying_settings/buying_settings.js +++ b/erpnext/buying/doctype/buying_settings/buying_settings.js @@ -6,3 +6,26 @@ frappe.ui.form.on('Buying Settings', { // } }); + +frappe.tour['Buying Settings'] = [ + { + fieldname: "supp_master_name", + title: "Supplier Naming By", + description: __("By default, the Item Name is set as per the Item Code entered. If you want Items to be named by a set ") + "Naming Series" + __(" choose the 'Naming Series' option."), + }, + { + fieldname: "buying_price_list", + title: "Default Buying Price List", + description: __("Configure the default Price List when creating a new Buying transaction, the default is set as 'Standard Buying'. Item prices will be fetched from this Price List. You can modify the 'Price List' by using the arrow at the right-end of the field to change the currency and country.") + }, + { + fieldname: "po_required", + title: "Purchase Order Required for Purchase Invoice & Receipt Creation", + description: __("If this option is configured 'Yes', ERPNext will prevent you from creating a Purchase Invoice or Receipt without creating a Purchase Order first. This configuration can be overridden for a particular supplier by enabling the 'Allow Purchase Invoice Creation Without Purchase Order' checkbox in supplier master.") + }, + { + fieldname: "pr_required", + title: "Purchase Receipt Required for Purchase Invoice Creation", + description: __("If this option is configured 'Yes', ERPNext will prevent you from creating a Purchase Invoice without creating a Purchase Receipt first. This configuration can be overridden for a particular supplier by enabling the 'Allow Purchase Invoice Creation Without Purchase Receipt' checkbox in supplier master.") + } +]; \ No newline at end of file diff --git a/erpnext/buying/doctype/buying_settings/buying_settings.json b/erpnext/buying/doctype/buying_settings/buying_settings.json index a492519591b..a0ab2a00f99 100644 --- a/erpnext/buying/doctype/buying_settings/buying_settings.json +++ b/erpnext/buying/doctype/buying_settings/buying_settings.json @@ -1,8 +1,10 @@ { + "actions": [], "creation": "2013-06-25 11:04:03", "description": "Settings for Buying Module", "doctype": "DocType", "document_type": "Other", + "engine": "InnoDB", "field_order": [ "supp_master_name", "supplier_group", @@ -44,13 +46,13 @@ { "fieldname": "po_required", "fieldtype": "Select", - "label": "Purchase Order Required", + "label": "Purchase Order Required for Purchase Invoice & Receipt Creation", "options": "No\nYes" }, { "fieldname": "pr_required", "fieldtype": "Select", - "label": "Purchase Receipt Required", + "label": "Purchase Receipt Required for Purchase Invoice Creation", "options": "No\nYes" }, { @@ -92,7 +94,8 @@ "icon": "fa fa-cog", "idx": 1, "issingle": 1, - "modified": "2019-08-20 13:13:09.055189", + "links": [], + "modified": "2020-05-15 14:49:32.513611", "modified_by": "Administrator", "module": "Buying", "name": "Buying Settings", @@ -107,5 +110,7 @@ "share": 1, "write": 1 } - ] + ], + "sort_field": "modified", + "sort_order": "DESC" } \ No newline at end of file diff --git a/erpnext/buying/module_onboarding/buying/buying.json b/erpnext/buying/module_onboarding/buying/buying.json new file mode 100644 index 00000000000..fc956baaa3b --- /dev/null +++ b/erpnext/buying/module_onboarding/buying/buying.json @@ -0,0 +1,54 @@ +{ + "allow_roles": [ + { + "role": "Purchase Manager" + }, + { + "role": "Purchase User" + }, + { + "role": "Stock Manager" + }, + { + "role": "Stock User" + } + ], + "creation": "2020-05-06 15:56:35.049205", + "docstatus": 0, + "doctype": "Module Onboarding", + "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/buying", + "idx": 0, + "is_complete": 0, + "modified": "2020-05-15 14:39:16.514904", + "modified_by": "Administrator", + "module": "Buying", + "name": "Buying", + "owner": "Administrator", + "steps": [ + { + "step": "Introduction to Buying" + }, + { + "step": "Create a Supplier" + }, + { + "step": "Create a Warehouse" + }, + { + "step": "Create a Product" + }, + { + "step": "Buying Settings" + }, + { + "step": "Create your first Purchase Order" + }, + { + "step": "Create a Material Request" + } + ], + "subtitle": "Products, Purchases, Analysis and more.", + "success_message": "The Buying Module is all set up!", + "title": "Let's Setup the Buying Module.", + "user_can_dismiss": 1 +} \ No newline at end of file diff --git a/erpnext/buying/onboarding_step/buying_settings/buying_settings.json b/erpnext/buying/onboarding_step/buying_settings/buying_settings.json index 3b3208f5f04..45a19fb4a18 100644 --- a/erpnext/buying/onboarding_step/buying_settings/buying_settings.json +++ b/erpnext/buying/onboarding_step/buying_settings/buying_settings.json @@ -1,16 +1,19 @@ { - "action": "Update Settings", + "action": "Show Form Tour", "creation": "2020-05-06 15:53:44.667414", "docstatus": 0, "doctype": "Onboarding Step", "idx": 0, "is_complete": 0, - "is_mandatory": 0, + "is_mandatory": 1, + "is_single": 1, "is_skipped": 0, - "modified": "2020-05-12 18:30:06.323797", + "modified": "2020-05-15 14:38:01.142256", "modified_by": "Administrator", "name": "Buying Settings", "owner": "Administrator", "reference_document": "Buying Settings", - "title": "Configure Buying Settings." + "show_full_form": 0, + "title": "Explore Buying Settings.", + "validate_action": 0 } \ No newline at end of file diff --git a/erpnext/buying/onboarding_step/create_a_material_request/create_a_material_request.json b/erpnext/buying/onboarding_step/create_a_material_request/create_a_material_request.json new file mode 100644 index 00000000000..9dc493dd499 --- /dev/null +++ b/erpnext/buying/onboarding_step/create_a_material_request/create_a_material_request.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-05-15 14:39:09.818764", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 1, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-15 14:39:09.818764", + "modified_by": "Administrator", + "name": "Create a Material Request", + "owner": "Administrator", + "reference_document": "Material Request", + "show_full_form": 1, + "title": "Create a Material Request", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/buying/onboarding_step/create_a_product/create_a_product.json b/erpnext/buying/onboarding_step/create_a_product/create_a_product.json index dce1a215053..d2068e167b7 100644 --- a/erpnext/buying/onboarding_step/create_a_product/create_a_product.json +++ b/erpnext/buying/onboarding_step/create_a_product/create_a_product.json @@ -6,11 +6,14 @@ "idx": 0, "is_complete": 0, "is_mandatory": 0, + "is_single": 0, "is_skipped": 0, "modified": "2020-05-12 18:30:02.489949", "modified_by": "Administrator", "name": "Create a Product", "owner": "Administrator", "reference_document": "Item", - "title": "Create a Product" + "show_full_form": 0, + "title": "Create a Product", + "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/buying/onboarding_step/create_a_supplier/create_a_supplier.json b/erpnext/buying/onboarding_step/create_a_supplier/create_a_supplier.json index f87a43dbc39..7bbbc928418 100644 --- a/erpnext/buying/onboarding_step/create_a_supplier/create_a_supplier.json +++ b/erpnext/buying/onboarding_step/create_a_supplier/create_a_supplier.json @@ -6,11 +6,14 @@ "idx": 0, "is_complete": 0, "is_mandatory": 0, + "is_single": 0, "is_skipped": 0, "modified": "2020-05-12 18:25:29.121647", "modified_by": "Administrator", "name": "Create a Supplier", "owner": "Administrator", "reference_document": "Supplier", - "title": "Create a Supplier" + "show_full_form": 0, + "title": "Create a Supplier", + "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/buying/onboarding_step/create_a_warehouse/create_a_warehouse.json b/erpnext/buying/onboarding_step/create_a_warehouse/create_a_warehouse.json index 20744f6624b..8aac6d4cf48 100644 --- a/erpnext/buying/onboarding_step/create_a_warehouse/create_a_warehouse.json +++ b/erpnext/buying/onboarding_step/create_a_warehouse/create_a_warehouse.json @@ -6,11 +6,14 @@ "idx": 0, "is_complete": 0, "is_mandatory": 0, + "is_single": 0, "is_skipped": 0, - "modified": "2020-05-12 18:27:44.128737", + "modified": "2020-05-15 14:32:17.072731", "modified_by": "Administrator", "name": "Create a Warehouse", "owner": "Administrator", "reference_document": "Warehouse", - "title": "Setup your Purchase Warehouse" + "show_full_form": 0, + "title": "Setup your Warehouse", + "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/buying/onboarding_step/create_your_first_purchase_order/create_your_first_purchase_order.json b/erpnext/buying/onboarding_step/create_your_first_purchase_order/create_your_first_purchase_order.json index 0efadbd7887..9dbed239789 100644 --- a/erpnext/buying/onboarding_step/create_your_first_purchase_order/create_your_first_purchase_order.json +++ b/erpnext/buying/onboarding_step/create_your_first_purchase_order/create_your_first_purchase_order.json @@ -6,11 +6,14 @@ "idx": 0, "is_complete": 0, "is_mandatory": 0, + "is_single": 0, "is_skipped": 0, "modified": "2020-05-12 18:31:56.856112", "modified_by": "Administrator", "name": "Create your first Purchase Order", "owner": "Administrator", "reference_document": "Purchase Order", - "title": "Create your first Purchase Order" + "show_full_form": 0, + "title": "Create your first Purchase Order", + "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/buying/onboarding_step/introduction_to_buying/introduction_to_buying.json b/erpnext/buying/onboarding_step/introduction_to_buying/introduction_to_buying.json index 73f22df48c1..fd98fddafae 100644 --- a/erpnext/buying/onboarding_step/introduction_to_buying/introduction_to_buying.json +++ b/erpnext/buying/onboarding_step/introduction_to_buying/introduction_to_buying.json @@ -6,11 +6,14 @@ "idx": 0, "is_complete": 0, "is_mandatory": 0, + "is_single": 0, "is_skipped": 0, "modified": "2020-05-12 18:25:08.509900", "modified_by": "Administrator", "name": "Introduction to Buying", "owner": "Administrator", + "show_full_form": 0, "title": "Introduction to Buying", + "validate_action": 1, "video_url": "https://youtu.be/efFajTTQBa8" } \ No newline at end of file From a74d433cd81661120b4bcb5f9099ce2d3b25fbde Mon Sep 17 00:00:00 2001 From: marination Date: Fri, 15 May 2020 23:37:48 +0530 Subject: [PATCH 075/608] fix: Fixed Dashboard Charts, modified fixtures and minor changes --- erpnext/buying/dashboard_fixtures.py | 217 ++++++++++++------ .../buying_settings/buying_settings.js | 2 +- .../supplier_quotation.json | 4 +- .../purchase_order_analysis.js | 8 - .../purchase_order_analysis.json | 4 +- .../purchase_order_analysis.py | 13 +- .../purchase_order_trends.py | 43 +++- erpnext/public/js/purchase_trends_filters.js | 5 +- 8 files changed, 198 insertions(+), 98 deletions(-) diff --git a/erpnext/buying/dashboard_fixtures.py b/erpnext/buying/dashboard_fixtures.py index 0bd9a1fd488..291abb87b04 100644 --- a/erpnext/buying/dashboard_fixtures.py +++ b/erpnext/buying/dashboard_fixtures.py @@ -3,7 +3,8 @@ import frappe import json - +from frappe.utils import nowdate +from erpnext.accounts.utils import get_fiscal_year def get_data(): return frappe._dict({ @@ -23,108 +24,172 @@ def get_company_for_dashboards(): return None company = frappe.get_doc("Company", get_company_for_dashboards()) +fiscal_year = get_fiscal_year(nowdate(), as_dict=1) +fiscal_year_name = fiscal_year.get("name") +start_date = str(fiscal_year.get("year_start_date")) +end_date = str(fiscal_year.get("year_end_date")) def get_dashboards(): return [{ "name": "Buying", "dashboard_name": "Buying", "charts": [ - { "chart": "Purchase Analytics", "width": "Full"}, - { "chart": "Material Request Purchase Analysis", "width": "Half"}, + { "chart": "Top Suppliers", "width": "Full"}, + { "chart": "Material Request Analysis", "width": "Half"}, { "chart": "Purchase Order Analysis", "width": "Half"}, - { "chart": "Requested Items to Order", "width": "Full"} + { "chart": "Purchase Order Trends", "width": "Full"} + ], + "cards": [ + { "card": "Purchase Orders to Receive"}, + { "card": "Purchase Order Expenses"}, + { "card": "Active Suppliers"}, + { "card": "Active Supplier Quotations"} ] }] def get_charts(): return [ - { - "name": "Purchase Analytics", - "doctype": "Dashboard Chart", - "owner": "Administrator", - "report_name": "Purchase Analytics", - "filters_json": json.dumps({ - "tree_type": "Item", - "doc_type": "Purchase Receipt", - "value_quantity": "Value", - "from_date": "2020-03-01", - "to_date": "2020-07-31", - "company": company.name, - "range": "Weekly" - }), - "x_field": "entity", - "type": "Bar", - 'timeseries': 0, - "chart_type": "Report", - "chart_name": "Purchase Analytics", - "custom_options": json.dumps({ - "x_field": "entity", - "chart_type": "Bar", - "y_axis_fields": [{"idx": 1, "__islocal": "true", "y_field": "total"}], - "y_fields": ["total"] - }) - }, - { - "name": "Material Request Purchase Analysis", - "doctype": "Dashboard Chart", - "owner": "Administrator", - "document_type": "Material Request", - "filters_json": '[["Material Request","status","not in",["Draft","Cancelled","Stopped",null],false],["Material Request","material_request_type","=","Purchase",false],["Material Request","company","=", "{company}", false]]'.format(company=company.name), - "is_custom": 0, - "type": "Donut", - "timeseries": 0, - "chart_type": "Group By", - "group_by_based_on": "status", - "chart_name": "Material Request Purchase Analysis", - "group_by_type": "Count", - "custom_options": json.dumps({"height": 300}) - - }, { "name": "Purchase Order Analysis", - "doctype": "Dashboard Chart", - "owner": "Administrator", - "report_name": "Purchase Order Analysis", - "filters_json": json.dumps({ - "company": company.name, - "from_date": "2020-04-04", - "to_date": "2020-07-04", - "chart_based_on": "Quantity" - }), - "is_custom": 1, - "type": "Donut", - "timeseries": 0, - "chart_type": "Report", "chart_name": "Purchase Order Analysis", + "chart_type": "Report", "custom_options": json.dumps({ "type": "donut", "height": 300, "axisOptions": {"shortenYAxisNumbers": 1} - }) - }, - { - "name": "Requested Items to Order", + }), "doctype": "Dashboard Chart", - "owner": "Administrator", - "report_name": "Requested Items to Order", "filters_json": json.dumps({ "company": company.name, - "from_date": "2020-04-01", - "to_date": "2020-07-01", - "group_by_mr": 0 + "from_date": start_date, + "to_date": end_date }), "is_custom": 1, - "type": "Bar", - "timeseries": 0, + "is_public": 1, + "owner": "Administrator", + "report_name": "Purchase Order Analysis", + "type": "Donut" + }, + { + "name": "Material Request Analysis", + "chart_name": "Material Request Analysis", + "chart_type": "Group By", + "custom_options": json.dumps({"height": 300}), + "doctype": "Dashboard Chart", + "document_type": "Material Request", + "filters_json": json.dumps( + [["Material Request", "status", "not in", ["Draft", "Cancelled", "Stopped", None], False], + ["Material Request", "material_request_type", "=", "Purchase", False], + ["Material Request", "company", "=", company.name, False]] + ), + "group_by_based_on": "status", + "group_by_type": "Count", + "is_custom": 0, + "is_public": 1, + "number_of_groups": 0, + "owner": "Administrator", + "type": "Donut" + }, + { + "name": "Purchase Order Trends", + "chart_name": "Purchase Order Trends", "chart_type": "Report", - "chart_name": "Requested Items to Order", "custom_options": json.dumps({ - "type": "bar", - "barOptions": {"stacked": 1}, - "axisOptions": {"shortenYAxisNumbers": 1} - }) + "type": "line", + "regionFill": 1, + "axisOptions": {"shortenYAxisNumbers": 1}, + "tooltipOptions": {} + }), + "doctype": "Dashboard Chart", + "filters_json": json.dumps({ + "company": company.name, + "period": "Monthly", + "fiscal_year": fiscal_year_name, + "period_based_on": "posting_date", + "based_on": "Item" + }), + "is_custom": 1, + "is_public": 1, + "owner": "Administrator", + "report_name": "Purchase Order Trends", + "type": "Line" + }, + { + "name": "Top Suppliers", + "chart_name": "Top Suppliers", + "chart_type": "Report", + "doctype": "Dashboard Chart", + "filters_json": json.dumps({ + "company": company.name, + "period": "Monthly", + "fiscal_year": fiscal_year_name, + "period_based_on": "posting_date", + "based_on": "Supplier" + }), + "is_custom": 1, + "is_public": 1, + "owner": "Administrator", + "report_name": "Purchase Receipt Trends", + "type": "Bar" } ] def get_number_cards(): - return [{}] \ No newline at end of file + return [ + { + "name": "Purchase Order Expenses", + "aggregate_function_based_on": "base_grand_total", + "doctype": "Number Card", + "document_type": "Purchase Order", + "filters_json": json.dumps( + [["Purchase Order", "transaction_date", "Between", [start_date,end_date], False], + ["Purchase Order", "status", "not in", ["Draft","On Hold","Cancelled","Closed", None], False], + ["Purchase Order", "company", "=", company.name, False]] + ), + "function": "Sum", + "is_public": 1, + "label": "Purchase Order Expenses", + "owner": "Administrator", + "show_percentage_stats": 1, + "stats_time_interval": "Monthly" + }, + { + "name": "Purchase Orders to Receive", + "doctype": "Number Card", + "document_type": "Purchase Order", + "filters_json": json.dumps( + [["Purchase Order", "status", "in", ["To Receive and Bill", "To Receive", None], False], + ["Purchase Order", "company", "=", company.name, False]] + ), + "function": "Count", + "is_public": 1, + "label": "Purchase Orders to Receive", + "owner": "Administrator", + "show_percentage_stats": 1, + "stats_time_interval": "Weekly" + }, + { + "name": "Active Suppliers", + "doctype": "Number Card", + "document_type": "Supplier", + "filters_json": json.dumps([["Supplier", "disabled", "=", "0"]]), + "function": "Count", + "is_public": 1, + "label": "Active Suppliers", + "owner": "Administrator", + "show_percentage_stats": 1, + "stats_time_interval": "Monthly" + }, + { + "name": "Active Supplier Quotations", + "doctype": "Number Card", + "document_type": "Supplier Quotation", + "filters_json": json.dumps([["Supplier Quotation", "status", "=", "Submitted", False]]), + "function": "Count", + "is_public": 1, + "label": "Active Supplier Quotations", + "owner": "Administrator", + "show_percentage_stats": 1, + "stats_time_interval": "Monthly" + } + ] \ No newline at end of file diff --git a/erpnext/buying/doctype/buying_settings/buying_settings.js b/erpnext/buying/doctype/buying_settings/buying_settings.js index 5a5afa41fcd..a27950a9414 100644 --- a/erpnext/buying/doctype/buying_settings/buying_settings.js +++ b/erpnext/buying/doctype/buying_settings/buying_settings.js @@ -16,7 +16,7 @@ frappe.tour['Buying Settings'] = [ { fieldname: "buying_price_list", title: "Default Buying Price List", - description: __("Configure the default Price List when creating a new Buying transaction, the default is set as 'Standard Buying'. Item prices will be fetched from this Price List. You can modify the 'Price List' by using the arrow at the right-end of the field to change the currency and country.") + description: __("Configure the default Price List when creating a new Buying transaction, the default is set as 'Standard Buying'. Item prices will be fetched from this Price List.") }, { fieldname: "po_required", diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json index 3bc441af6d1..7db1516ce1b 100644 --- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json +++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json @@ -761,7 +761,7 @@ "no_copy": 1, "oldfieldname": "status", "oldfieldtype": "Select", - "options": "\nDraft\nSubmitted\nStopped\nCancelled", + "options": "\nDraft\nSubmitted\nStopped\nCancelled\nExpired", "print_hide": 1, "read_only": 1, "reqd": 1, @@ -803,7 +803,7 @@ "idx": 29, "is_submittable": 1, "links": [], - "modified": "2020-04-15 11:44:52.958022", + "modified": "2020-05-15 21:24:12.639482", "modified_by": "Administrator", "module": "Buying", "name": "Supplier Quotation", diff --git a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js index 24abb6d44ab..701da4380aa 100644 --- a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js +++ b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js @@ -58,14 +58,6 @@ frappe.query_reports["Purchase Order Analysis"] = { return options } }, - { - "fieldname": "chart_based_on", - "label": __("Chart Based On"), - "fieldtype": "Select", - "width": "80", - "options": "Quantity\nAmount", - "default": "Quantity" - }, { "fieldname": "group_by_po", "label": __("Group by Purchase Order"), diff --git a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.json b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.json index 196aaaed224..5ba3101ec51 100644 --- a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.json +++ b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.json @@ -1,5 +1,5 @@ { - "add_total_row": 0, + "add_total_row": 1, "creation": "2020-05-04 18:41:28.625119", "disable_prepared_report": 0, "disabled": 0, @@ -7,7 +7,7 @@ "doctype": "Report", "idx": 0, "is_standard": "Yes", - "modified": "2020-05-04 18:41:28.625119", + "modified": "2020-05-15 20:57:52.623455", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order Analysis", diff --git a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py index 96e2fc8a209..497ce684fd6 100644 --- a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py +++ b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py @@ -82,10 +82,9 @@ def get_data(conditions, filters): return data def prepare_data(data, filters): - completed, pending = 0,0 - chart_based_on = filters.get("chart_based_on") - pending_field = "pending_qty" if chart_based_on == "Quantity" else "pending_amount" - completed_field = "received_qty" if chart_based_on == "Quantity" else "billed_amount" + completed, pending = 0, 0 + pending_field = "pending_amount" + completed_field = "billed_amount" if filters.get("group_by_po"): purchase_order_map = {} @@ -116,7 +115,7 @@ def prepare_data(data, filters): for field in fields: po_row[field] = flt(row[field]) + flt(po_row[field]) - chart_data = prepare_chart_data(chart_based_on, pending, completed) + chart_data = prepare_chart_data(pending, completed) if filters.get("group_by_po"): data = [] @@ -126,8 +125,8 @@ def prepare_data(data, filters): return data, chart_data -def prepare_chart_data(chart_based_on, pending, completed): - labels = ["Qty to Receive","Received Qty"] if chart_based_on == "Quantity" else ["Amount to Bill","Billed Amount"] +def prepare_chart_data(pending, completed): + labels = ["Amount to Bill", "Billed Amount"] return { "data" : { diff --git a/erpnext/buying/report/purchase_order_trends/purchase_order_trends.py b/erpnext/buying/report/purchase_order_trends/purchase_order_trends.py index 888676cf64f..011760dacc3 100644 --- a/erpnext/buying/report/purchase_order_trends/purchase_order_trends.py +++ b/erpnext/buying/report/purchase_order_trends/purchase_order_trends.py @@ -3,6 +3,7 @@ from __future__ import unicode_literals import frappe +from frappe import _ from erpnext.controllers.trends import get_columns,get_data def execute(filters=None): @@ -10,5 +11,45 @@ def execute(filters=None): data = [] conditions = get_columns(filters, "Purchase Order") data = get_data(filters, conditions) + chart_data = get_chart_data(data, conditions, filters) - return conditions["columns"], data \ No newline at end of file + return conditions["columns"], data, None, chart_data + +def get_chart_data(data, conditions, filters): + if not (data and conditions): + return [] + + datapoints = [] + + start = 2 if filters.get("based_on") in ["Item", "Supplier"] else 1 + if filters.get("group_by"): + start += 1 + + # fetch only periodic columns as labels + columns = conditions.get("columns")[start:-2][1::2] + labels = [column.split(':')[0] for column in columns] + datapoints = [0] * len(labels) + + for row in data: + # If group by filter, don't add first row of group (it's already summed) + if not row[start-1]: + continue + # Remove None values and compute only periodic data + row = [x if x else 0 for x in row[start:-2]] + row = row[1::2] + + for i in range(len(row)): + datapoints[i] += row[i] + + return { + "data" : { + "labels" : labels, + "datasets" : [ + { + "name" : _("{0}").format(filters.get("period")) + _(" Revenue"), + "values" : datapoints + } + ] + }, + "type" : "line" + } \ No newline at end of file diff --git a/erpnext/public/js/purchase_trends_filters.js b/erpnext/public/js/purchase_trends_filters.js index cd767f5d167..c786a8674e6 100644 --- a/erpnext/public/js/purchase_trends_filters.js +++ b/erpnext/public/js/purchase_trends_filters.js @@ -51,7 +51,10 @@ erpnext.get_purchase_trends_filters = function() { { "value": "Supplier Group", "label": __("Supplier Group") }, { "value": "Project", "label": __("Project") } ], - "default": "Item" + "default": "Item", + "dashboard_config": { + "read_only": 1 + } }, { "fieldname":"group_by", From 75c65fd1b1eb6ee79858ad75f3bd5a4aa8eec6bb Mon Sep 17 00:00:00 2001 From: marination Date: Fri, 15 May 2020 23:47:51 +0530 Subject: [PATCH 076/608] fix: Handle empty chart state and change desk dashboard --- erpnext/buying/desk_page/buying/buying.json | 6 +++--- erpnext/buying/module_onboarding/buying/buying.json | 2 +- .../purchase_order_analysis/purchase_order_analysis.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/buying/desk_page/buying/buying.json b/erpnext/buying/desk_page/buying/buying.json index e00db0e86d7..1a69f422695 100644 --- a/erpnext/buying/desk_page/buying/buying.json +++ b/erpnext/buying/desk_page/buying/buying.json @@ -40,8 +40,8 @@ "category": "Modules", "charts": [ { - "chart_name": "Purchase Analytics", - "label": "Buying Analytics" + "chart_name": "Purchase Order Trends", + "label": "Purchase Order Trends" } ], "charts_label": "Buying Dashboard", @@ -54,7 +54,7 @@ "idx": 0, "is_standard": 1, "label": "Buying", - "modified": "2020-05-15 14:26:42.505702", + "modified": "2020-05-15 23:41:09.307288", "modified_by": "Administrator", "module": "Buying", "name": "Buying", diff --git a/erpnext/buying/module_onboarding/buying/buying.json b/erpnext/buying/module_onboarding/buying/buying.json index fc956baaa3b..70d6cbb77a4 100644 --- a/erpnext/buying/module_onboarding/buying/buying.json +++ b/erpnext/buying/module_onboarding/buying/buying.json @@ -19,7 +19,7 @@ "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/buying", "idx": 0, "is_complete": 0, - "modified": "2020-05-15 14:39:16.514904", + "modified": "2020-05-15 23:42:35.625736", "modified_by": "Administrator", "module": "Buying", "name": "Buying", diff --git a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py index 497ce684fd6..89be62231b9 100644 --- a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py +++ b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py @@ -19,7 +19,7 @@ def execute(filters=None): data = get_data(conditions, filters) if not data: - return [], [] + return [], [], None, [] data, chart_data = prepare_data(data, filters) From fc514ba940f4017fdf569ee558f076571143a9de Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Sat, 16 May 2020 19:00:00 +0530 Subject: [PATCH 077/608] feat: save contact to woocommerce --- .../connectors/woocommerce_connection.py | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py index 54fa6085d03..e4593a02b9f 100644 --- a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py +++ b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py @@ -78,11 +78,39 @@ def link_customer_and_address(raw_billing_data, raw_shipping_data, customer_name else: billing_address = create_address(raw_billing_data, customer, "Billing") shipping_address = create_address(raw_shipping_data, customer, "Shipping") + contact = create_contact(raw_billing_data, customer) if customer_exists: rename_address(billing_address, customer) rename_address(shipping_address, customer) +def create_contact(data, customer): + email = data.get("email", None) + phone = data.get("phone", None) + + if not email and not phone: + return + + contact = frappe.new_doc("Contact") + contact.first_name = data.get("first_name") + contact.last_name = data.get("last_name") + contact.is_primary_contact = 1 + contact.is_billing_contact = 1 + + if phone: + contact.add_phone(phone, is_primary_mobile_no=1, is_primary_phone=1) + + if email: + contact.add_email(email, is_primary=1) + + contact.append("links", { + "link_doctype": "Customer", + "link_name": customer.customer_name + }) + + contact.flags.ignore_mandatory = True + contact.save() + def create_address(raw_data, customer, address_type): address = frappe.new_doc("Address") @@ -102,7 +130,7 @@ def create_address(raw_data, customer, address_type): }) address.flags.ignore_mandatory = True - address = address.save() + address.save() def rename_address(address, customer): old_address_title = address.name From 180bda76186a65ab68c70abd06d66d1f586d0f6a Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Sat, 16 May 2020 19:20:55 +0530 Subject: [PATCH 078/608] refactor: return address and contact objects --- .../erpnext_integrations/connectors/woocommerce_connection.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py index e4593a02b9f..1e422db828b 100644 --- a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py +++ b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py @@ -111,6 +111,8 @@ def create_contact(data, customer): contact.flags.ignore_mandatory = True contact.save() + return contact + def create_address(raw_data, customer, address_type): address = frappe.new_doc("Address") @@ -132,6 +134,8 @@ def create_address(raw_data, customer, address_type): address.flags.ignore_mandatory = True address.save() + return address + def rename_address(address, customer): old_address_title = address.name new_address_title = customer.customer_name + "-" + address.address_type From d69e3eb5d79bd3276c83713c25d88e35c38f038f Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Sat, 16 May 2020 19:37:03 +0530 Subject: [PATCH 079/608] feat: use name instead of customer name to link address and contact --- .../connectors/woocommerce_connection.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py index 1e422db828b..6d379f6ed5f 100644 --- a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py +++ b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py @@ -105,7 +105,7 @@ def create_contact(data, customer): contact.append("links", { "link_doctype": "Customer", - "link_name": customer.customer_name + "link_name": customer.name }) contact.flags.ignore_mandatory = True @@ -128,7 +128,7 @@ def create_address(raw_data, customer, address_type): address.email_id = customer.woocommerce_email address.append("links", { "link_doctype": "Customer", - "link_name": customer.customer_name + "link_name": customer.name }) address.flags.ignore_mandatory = True @@ -138,7 +138,7 @@ def create_address(raw_data, customer, address_type): def rename_address(address, customer): old_address_title = address.name - new_address_title = customer.customer_name + "-" + address.address_type + new_address_title = customer.name + "-" + address.address_type address.address_title = customer.customer_name address.save() From 34f4a2398b7734fac40f8290ade81d6c914c38b1 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Sat, 18 Apr 2020 18:59:32 +0530 Subject: [PATCH 080/608] feat: manufacturing dashboards --- erpnext/config/manufacturing.py | 14 +- .../manufacturing/manufacturing.json | 31 ++- .../doctype/downtime_entry/__init__.py | 0 .../doctype/downtime_entry/downtime_entry.js | 8 + .../downtime_entry/downtime_entry.json | 120 +++++++++ .../doctype/downtime_entry/downtime_entry.py | 13 + .../downtime_entry/test_downtime_entry.py | 10 + .../doctype/job_card/job_card.json | 13 +- .../doctype/work_order/work_order.json | 25 +- .../doctype/work_order/work_order.py | 27 +- .../report/downtime_analysis/__init__.py | 0 .../downtime_analysis/downtime_analysis.js | 28 ++ .../downtime_analysis/downtime_analysis.json | 31 +++ .../downtime_analysis/downtime_analysis.py | 96 +++++++ .../report/job_card_summary/__init__.py | 0 .../job_card_summary/job_card_summary.js | 52 ++++ .../job_card_summary/job_card_summary.json | 34 +++ .../job_card_summary/job_card_summary.py | 197 ++++++++++++++ .../production_analytics.py | 13 +- .../quality_inspection_summary/__init__.py | 0 .../quality_inspection_summary.js | 40 +++ .../quality_inspection_summary.json | 32 +++ .../quality_inspection_summary.py | 133 ++++++++++ .../report/work_order_summary/__init__.py | 0 .../work_order_summary/work_order_summary.js | 58 ++++ .../work_order_summary.json | 31 +++ .../work_order_summary/work_order_summary.py | 173 ++++++++++++ erpnext/patches.txt | 1 + erpnext/patches/v13_0/__init__.py | 1 + .../update_actual_start_and_end_date_in_wo.py | 43 +++ .../setup_wizard/data/dashboard_charts.py | 247 ++++++++++++++++++ .../quality_inspection.json | 16 +- .../stock/doctype/stock_entry/stock_entry.py | 4 + 33 files changed, 1444 insertions(+), 47 deletions(-) create mode 100644 erpnext/manufacturing/doctype/downtime_entry/__init__.py create mode 100644 erpnext/manufacturing/doctype/downtime_entry/downtime_entry.js create mode 100644 erpnext/manufacturing/doctype/downtime_entry/downtime_entry.json create mode 100644 erpnext/manufacturing/doctype/downtime_entry/downtime_entry.py create mode 100644 erpnext/manufacturing/doctype/downtime_entry/test_downtime_entry.py create mode 100644 erpnext/manufacturing/report/downtime_analysis/__init__.py create mode 100644 erpnext/manufacturing/report/downtime_analysis/downtime_analysis.js create mode 100644 erpnext/manufacturing/report/downtime_analysis/downtime_analysis.json create mode 100644 erpnext/manufacturing/report/downtime_analysis/downtime_analysis.py create mode 100644 erpnext/manufacturing/report/job_card_summary/__init__.py create mode 100644 erpnext/manufacturing/report/job_card_summary/job_card_summary.js create mode 100644 erpnext/manufacturing/report/job_card_summary/job_card_summary.json create mode 100644 erpnext/manufacturing/report/job_card_summary/job_card_summary.py create mode 100644 erpnext/manufacturing/report/quality_inspection_summary/__init__.py create mode 100644 erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.js create mode 100644 erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.json create mode 100644 erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.py create mode 100644 erpnext/manufacturing/report/work_order_summary/__init__.py create mode 100644 erpnext/manufacturing/report/work_order_summary/work_order_summary.js create mode 100644 erpnext/manufacturing/report/work_order_summary/work_order_summary.json create mode 100644 erpnext/manufacturing/report/work_order_summary/work_order_summary.py create mode 100644 erpnext/patches/v13_0/update_actual_start_and_end_date_in_wo.py create mode 100644 erpnext/setup/setup_wizard/data/dashboard_charts.py diff --git a/erpnext/config/manufacturing.py b/erpnext/config/manufacturing.py index 2c18eeb83a1..012f1cad0ad 100644 --- a/erpnext/config/manufacturing.py +++ b/erpnext/config/manufacturing.py @@ -120,13 +120,7 @@ def get_data(): { "type": "report", "is_query_report": True, - "name": "Open Work Orders", - "doctype": "Work Order" - }, - { - "type": "report", - "is_query_report": True, - "name": "Work Orders in Progress", + "name": "Work Order Summary", "doctype": "Work Order" }, { @@ -135,12 +129,6 @@ def get_data(): "name": "Issued Items Against Work Order", "doctype": "Work Order" }, - { - "type": "report", - "is_query_report": True, - "name": "Completed Work Orders", - "doctype": "Work Order" - }, { "type": "report", "is_query_report": True, diff --git a/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json b/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json index 18604e283ab..0464b763c0e 100644 --- a/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json +++ b/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json @@ -3,7 +3,7 @@ { "hidden": 0, "label": "Production", - "links": "[\n {\n \"dependencies\": [\n \"Item\",\n \"BOM\"\n ],\n \"description\": \"Orders released for production.\",\n \"label\": \"Work Order\",\n \"name\": \"Work Order\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"BOM\"\n ],\n \"description\": \"Generate Material Requests (MRP) and Work Orders.\",\n \"label\": \"Production Plan\",\n \"name\": \"Production Plan\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"label\": \"Stock Entry\",\n \"name\": \"Stock Entry\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Activity Type\"\n ],\n \"description\": \"Time Sheet for manufacturing.\",\n \"label\": \"Timesheet\",\n \"name\": \"Timesheet\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Job Card\",\n \"name\": \"Job Card\",\n \"type\": \"doctype\"\n }\n]" + "links": "[\n {\n \"dependencies\": [\n \"Item\",\n \"BOM\"\n ],\n \"description\": \"Orders released for production.\",\n \"label\": \"Work Order\",\n \"name\": \"Work Order\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"BOM\"\n ],\n \"description\": \"Generate Material Requests (MRP) and Work Orders.\",\n \"label\": \"Production Plan\",\n \"name\": \"Production Plan\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"label\": \"Stock Entry\",\n \"name\": \"Stock Entry\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Job Card\",\n \"name\": \"Job Card\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Downtime Entry\",\n \"name\": \"Downtime Entry\",\n \"type\": \"doctype\"\n }\n]" }, { "hidden": 0, @@ -13,7 +13,7 @@ { "hidden": 0, "label": "Reports", - "links": "[\n {\n \"dependencies\": [\n \"Work Order\"\n ],\n \"doctype\": \"Work Order\",\n \"is_query_report\": true,\n \"label\": \"Open Work Orders\",\n \"name\": \"Open Work Orders\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Work Order\"\n ],\n \"doctype\": \"Work Order\",\n \"is_query_report\": true,\n \"label\": \"Work Orders in Progress\",\n \"name\": \"Work Orders in Progress\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Work Order\"\n ],\n \"doctype\": \"Work Order\",\n \"is_query_report\": true,\n \"label\": \"Issued Items Against Work Order\",\n \"name\": \"Issued Items Against Work Order\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Work Order\"\n ],\n \"doctype\": \"Work Order\",\n \"is_query_report\": true,\n \"label\": \"Completed Work Orders\",\n \"name\": \"Completed Work Orders\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Work Order\"\n ],\n \"doctype\": \"Work Order\",\n \"is_query_report\": true,\n \"label\": \"Production Analytics\",\n \"name\": \"Production Analytics\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"BOM\"\n ],\n \"doctype\": \"BOM\",\n \"is_query_report\": true,\n \"label\": \"BOM Search\",\n \"name\": \"BOM Search\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"BOM\"\n ],\n \"doctype\": \"BOM\",\n \"is_query_report\": true,\n \"label\": \"BOM Stock Report\",\n \"name\": \"BOM Stock Report\",\n \"type\": \"report\"\n }\n]" + "links": "[\n {\n \"dependencies\": [\n \"Work Order\"\n ],\n \"doctype\": \"Work Order\",\n \"is_query_report\": true,\n \"label\": \"Work Order Summary\",\n \"name\": \"Work Order Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Work Order\"\n ],\n \"doctype\": \"Work Order\",\n \"is_query_report\": true,\n \"label\": \"Issued Items Against Work Order\",\n \"name\": \"Issued Items Against Work Order\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Work Order\"\n ],\n \"doctype\": \"Work Order\",\n \"is_query_report\": true,\n \"label\": \"Production Analytics\",\n \"name\": \"Production Analytics\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Quality Inspection\"\n ],\n \"doctype\": \"Quality Inspection\",\n \"is_query_report\": true,\n \"label\": \"Quality Inspection Summary\",\n \"name\": \"Quality Inspection Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Downtime Entry\"\n ],\n \"doctype\": \"Downtime Entry\",\n \"is_query_report\": true,\n \"label\": \"Downtime Analysis\",\n \"name\": \"Downtime Analysis\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Job Card\"\n ],\n \"doctype\": \"Job Card\",\n \"is_query_report\": true,\n \"label\": \"Job Card Summary\",\n \"name\": \"Job Card Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"BOM\"\n ],\n \"doctype\": \"BOM\",\n \"is_query_report\": true,\n \"label\": \"BOM Search\",\n \"name\": \"BOM Search\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"BOM\"\n ],\n \"doctype\": \"BOM\",\n \"is_query_report\": true,\n \"label\": \"BOM Stock Report\",\n \"name\": \"BOM Stock Report\",\n \"type\": \"report\"\n }\n]" }, { "hidden": 0, @@ -32,7 +32,12 @@ } ], "category": "Domains", - "charts": [], + "charts": [ + { + "chart_name": "Production Analysis", + "label": "Production Analysis" + } + ], "creation": "2020-03-02 17:11:37.032604", "developer_mode_only": 0, "disable_user_customization": 0, @@ -42,7 +47,7 @@ "idx": 0, "is_standard": 1, "label": "Manufacturing", - "modified": "2020-04-01 11:28:50.979358", + "modified": "2020-04-27 00:17:26.323677", "modified_by": "Administrator", "module": "Manufacturing", "name": "Manufacturing", @@ -50,5 +55,21 @@ "pin_to_bottom": 0, "pin_to_top": 0, "restrict_to_domain": "Manufacturing", - "shortcuts": [] + "shortcuts": [ + { + "label": "Item", + "link_to": "Item", + "type": "DocType" + }, + { + "label": "BOM", + "link_to": "BOM", + "type": "DocType" + }, + { + "label": "Work Order", + "link_to": "Work Order", + "type": "DocType" + } + ] } \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/downtime_entry/__init__.py b/erpnext/manufacturing/doctype/downtime_entry/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/manufacturing/doctype/downtime_entry/downtime_entry.js b/erpnext/manufacturing/doctype/downtime_entry/downtime_entry.js new file mode 100644 index 00000000000..3b7f5ba8d7f --- /dev/null +++ b/erpnext/manufacturing/doctype/downtime_entry/downtime_entry.js @@ -0,0 +1,8 @@ +// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Downtime Entry', { + // refresh: function(frm) { + + // } +}); diff --git a/erpnext/manufacturing/doctype/downtime_entry/downtime_entry.json b/erpnext/manufacturing/doctype/downtime_entry/downtime_entry.json new file mode 100644 index 00000000000..6ec088ad9e7 --- /dev/null +++ b/erpnext/manufacturing/doctype/downtime_entry/downtime_entry.json @@ -0,0 +1,120 @@ +{ + "actions": [], + "allow_import": 1, + "creation": "2020-04-18 04:50:46.187638", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "workstation", + "operator", + "column_break_4", + "from_time", + "to_time", + "downtime", + "downtime_reason_section", + "reason" + ], + "fields": [ + { + "fieldname": "workstation", + "fieldtype": "Link", + "label": "Workstation / Machine", + "options": "Workstation", + "reqd": 1 + }, + { + "fieldname": "from_time", + "fieldtype": "Datetime", + "in_list_view": 1, + "label": "From Time", + "reqd": 1 + }, + { + "fieldname": "to_time", + "fieldtype": "Datetime", + "in_list_view": 1, + "label": "To Time", + "reqd": 1 + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "fieldname": "reason", + "fieldtype": "Text", + "label": "Reason", + "reqd": 1 + }, + { + "fieldname": "operator", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Operator", + "options": "Employee", + "reqd": 1 + }, + { + "fieldname": "downtime_reason_section", + "fieldtype": "Section Break", + "label": "Downtime Reason" + }, + { + "description": "In Mins", + "fieldname": "downtime", + "fieldtype": "Float", + "label": "Downtime", + "read_only": 1 + } + ], + "links": [], + "modified": "2020-04-20 17:34:51.299607", + "modified_by": "Administrator", + "module": "Manufacturing", + "name": "Downtime Entry", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Manufacturing User", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Manufacturing Manager", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "workstation", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/downtime_entry/downtime_entry.py b/erpnext/manufacturing/doctype/downtime_entry/downtime_entry.py new file mode 100644 index 00000000000..56ec4356af4 --- /dev/null +++ b/erpnext/manufacturing/doctype/downtime_entry/downtime_entry.py @@ -0,0 +1,13 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.utils import time_diff_in_hours +from frappe.model.document import Document + +class DowntimeEntry(Document): + def validate(self): + if self.from_time and self.to_time: + self.downtime = time_diff_in_hours(self.to_time, self.from_time) * 60 diff --git a/erpnext/manufacturing/doctype/downtime_entry/test_downtime_entry.py b/erpnext/manufacturing/doctype/downtime_entry/test_downtime_entry.py new file mode 100644 index 00000000000..8b2a8d36c12 --- /dev/null +++ b/erpnext/manufacturing/doctype/downtime_entry/test_downtime_entry.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +# import frappe +import unittest + +class TestDowntimeEntry(unittest.TestCase): + pass diff --git a/erpnext/manufacturing/doctype/job_card/job_card.json b/erpnext/manufacturing/doctype/job_card/job_card.json index 7661fffa864..fba670c1c15 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.json +++ b/erpnext/manufacturing/doctype/job_card/job_card.json @@ -1,4 +1,5 @@ { + "actions": [], "autoname": "naming_series:", "creation": "2018-07-09 17:23:29.518745", "doctype": "DocType", @@ -264,8 +265,10 @@ { "fetch_from": "work_order.production_item", "fieldname": "production_item", - "fieldtype": "Read Only", - "label": "Production Item" + "fieldtype": "Link", + "label": "Production Item", + "options": "Item", + "read_only": 1 }, { "fieldname": "barcode", @@ -274,7 +277,8 @@ "read_only": 1 }, { - "fetch_from": "work_order.item_name", + "fetch_from": "production_item.item_name", + "fetch_if_empty": 1, "fieldname": "item_name", "fieldtype": "Read Only", "label": "Item Name" @@ -290,7 +294,8 @@ } ], "is_submittable": 1, - "modified": "2020-03-27 13:36:35.417502", + "links": [], + "modified": "2020-04-20 15:14:00.273441", "modified_by": "Administrator", "module": "Manufacturing", "name": "Job Card", diff --git a/erpnext/manufacturing/doctype/work_order/work_order.json b/erpnext/manufacturing/doctype/work_order/work_order.json index 00a67a03d67..8be22875a31 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.json +++ b/erpnext/manufacturing/doctype/work_order/work_order.json @@ -38,11 +38,12 @@ "required_items", "time", "planned_start_date", - "actual_start_date", - "column_break_13", "planned_end_date", - "actual_end_date", "expected_delivery_date", + "column_break_13", + "actual_start_date", + "actual_end_date", + "lead_time", "operations_section", "transfer_material_against", "operations", @@ -108,6 +109,8 @@ }, { "depends_on": "eval:doc.production_item", + "fetch_from": "production_item.item_name", + "fetch_if_empty": 1, "fieldname": "item_name", "fieldtype": "Data", "label": "Item Name", @@ -281,27 +284,30 @@ "reqd": 1 }, { + "allow_on_submit": 1, "fieldname": "actual_start_date", "fieldtype": "Datetime", "label": "Actual Start Date", - "read_only": 1 + "read_only_depends_on": "eval:doc.operations && doc.operations.length > 0" }, { "fieldname": "column_break_13", "fieldtype": "Column Break" }, { + "allow_on_submit": 1, "fieldname": "planned_end_date", "fieldtype": "Datetime", "label": "Planned End Date", "no_copy": 1, - "read_only": 1 + "read_only_depends_on": "eval:doc.operations && doc.operations.length > 0" }, { + "allow_on_submit": 1, "fieldname": "actual_end_date", "fieldtype": "Datetime", "label": "Actual End Date", - "read_only": 1 + "read_only_depends_on": "eval:doc.operations && doc.operations.length > 0" }, { "allow_on_submit": 1, @@ -476,6 +482,13 @@ "fieldtype": "Link", "label": "Source Warehouse", "options": "Warehouse" + }, + { + "description": "In Mins", + "fieldname": "lead_time", + "fieldtype": "Float", + "label": "Lead Time", + "read_only": 1 } ], "icon": "fa fa-cogs", diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 8301f30d837..c2789559b07 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -6,7 +6,7 @@ import frappe import json import math from frappe import _ -from frappe.utils import flt, get_datetime, getdate, date_diff, cint, nowdate, get_link_to_form +from frappe.utils import flt, get_datetime, getdate, date_diff, cint, nowdate, get_link_to_form, time_diff_in_hours from frappe.model.document import Document from erpnext.manufacturing.doctype.bom.bom import validate_bom_no, get_bom_items_as_dict from dateutil.relativedelta import relativedelta @@ -279,7 +279,7 @@ class WorkOrder(Document): if enable_capacity_planning and job_card_doc: row.planned_start_time = job_card_doc.time_logs[-1].from_time row.planned_end_time = job_card_doc.time_logs[-1].to_time - print(row.planned_start_time, original_start_time, plan_days) + if date_diff(row.planned_start_time, original_start_time) > plan_days: frappe.message_log.pop() frappe.throw(_("Unable to find the time slot in the next {0} days for the operation {1}.") @@ -437,8 +437,6 @@ class WorkOrder(Document): frappe.throw(_("Completed Qty can not be greater than 'Qty to Manufacture'")) def set_actual_dates(self): - self.actual_start_date = None - self.actual_end_date = None if self.get("operations"): actual_start_dates = [d.actual_start_time for d in self.get("operations") if d.actual_start_time] if actual_start_dates: @@ -447,6 +445,27 @@ class WorkOrder(Document): actual_end_dates = [d.actual_end_time for d in self.get("operations") if d.actual_end_time] if actual_end_dates: self.actual_end_date = max(actual_end_dates) + else: + data = frappe.get_all("Stock Entry", + fields = ["timestamp(posting_date, posting_time) as posting_datetime"], + filters = { + "work_order": self.name, + "purpose": ("in", ["Material Transfer for Manufacture", "Manufacture"]) + } + ) + + if data and len(data): + dates = [d.posting_datetime for d in data] + self.actual_start_date = min(dates) + + if self.status == "Completed": + self.actual_end_date = max(dates) + + self.set_lead_time() + + def set_lead_time(self): + if self.actual_start_date and self.actual_end_date: + self.lead_time = flt(time_diff_in_hours(self.actual_end_date, self.actual_start_date) * 60) def delete_job_card(self): for d in frappe.get_all("Job Card", ["name"], {"work_order": self.name}): diff --git a/erpnext/manufacturing/report/downtime_analysis/__init__.py b/erpnext/manufacturing/report/downtime_analysis/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.js b/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.js new file mode 100644 index 00000000000..e20342792f4 --- /dev/null +++ b/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.js @@ -0,0 +1,28 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Downtime Analysis"] = { + "filters": [ + { + label: __("From Date"), + fieldname:"from_date", + fieldtype: "Date", + default: frappe.datetime.add_months(frappe.datetime.get_today(), -1), + reqd: 1 + }, + { + label: __("To Date"), + fieldname:"to_date", + fieldtype: "Date", + default: frappe.datetime.get_today(), + reqd: 1, + }, + { + label: __("Machine"), + fieldname: "workstation", + fieldtype: "Link", + options: "Workstation" + } + ] +}; diff --git a/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.json b/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.json new file mode 100644 index 00000000000..5edc7781a2e --- /dev/null +++ b/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.json @@ -0,0 +1,31 @@ +{ + "add_total_row": 1, + "creation": "2020-04-20 18:26:04.345289", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "letter_head": "Gadgets International", + "modified": "2020-04-20 18:26:04.345289", + "modified_by": "Administrator", + "module": "Manufacturing", + "name": "Downtime Analysis", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Downtime Entry", + "report_name": "Downtime Analysis", + "report_type": "Script Report", + "roles": [ + { + "role": "System Manager" + }, + { + "role": "Manufacturing User" + }, + { + "role": "Manufacturing Manager" + } + ] +} \ No newline at end of file diff --git a/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.py b/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.py new file mode 100644 index 00000000000..d69ec189baa --- /dev/null +++ b/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.py @@ -0,0 +1,96 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.utils import flt +from frappe import _ + +def execute(filters=None): + columns, data = [], [] + data = get_data(filters) + columns = get_columns(filters) + chart_data = get_chart_data(data, filters) + return columns, data, None, chart_data + +def get_data(filters): + query_filters = {} + + fields = ["workstation", "operator", "from_time", "to_time", "downtime", "reason"] + + query_filters["from_time"] = (">=", filters.get("from_date")) + query_filters["to_time"] = ("<=", filters.get("to_date")) + + if filters.get("workstation"): + query_filters["workstation"] = filters.get("workstation") + + return frappe.get_all("Downtime Entry", fields= fields, filters=query_filters) + +def get_chart_data(data, columns): + labels = sorted(list(set([d.workstation for d in data]))) + + workstation_wise_data = {} + for d in data: + if d.workstation not in workstation_wise_data: + workstation_wise_data[d.workstation] = 0 + + workstation_wise_data[d.workstation] += flt(d.downtime, 2) + + datasets = [] + for label in labels: + datasets.append(workstation_wise_data.get(label, 0)) + + chart = { + "data": { + "labels": labels, + "datasets": [ + {"name": "Dataset 1", "values": datasets} + ] + }, + "type": "bar", + "colors": ["#ff5858"] + } + + return chart + +def get_columns(filters): + return [ + { + "label": _("Machine"), + "fieldname": "workstation", + "fieldtype": "Link", + "options": "Workstation", + "width": 100 + }, + { + "label": _("Operator"), + "fieldname": "operator", + "fieldtype": "Link", + "options": "Employee", + "width": 130 + }, + { + "label": _("From Time"), + "fieldname": "from_time", + "fieldtype": "Datetime", + "width": 160 + }, + { + "label": _("To Time"), + "fieldname": "to_time", + "fieldtype": "Datetime", + "width": 160 + }, + { + "label": _("Downtime (In Mins)"), + "fieldname": "downtime", + "fieldtype": "Float", + "width": 150 + }, + { + "label": _("Reason"), + "fieldname": "reason", + "fieldtype": "Text", + "width": 180 + } + ] \ No newline at end of file diff --git a/erpnext/manufacturing/report/job_card_summary/__init__.py b/erpnext/manufacturing/report/job_card_summary/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/manufacturing/report/job_card_summary/job_card_summary.js b/erpnext/manufacturing/report/job_card_summary/job_card_summary.js new file mode 100644 index 00000000000..b7e307183f8 --- /dev/null +++ b/erpnext/manufacturing/report/job_card_summary/job_card_summary.js @@ -0,0 +1,52 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Job Card Summary"] = { + "filters": [ + { + label: __("Company"), + fieldname: "company", + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1 + }, + { + label: __("From Date"), + fieldname:"from_date", + fieldtype: "Date", + default: frappe.datetime.add_months(frappe.datetime.get_today(), -12), + reqd: 1 + }, + { + label: __("To Date"), + fieldname:"to_date", + fieldtype: "Date", + default: frappe.datetime.get_today(), + reqd: 1, + }, + { + label: __("Status"), + fieldname: "status", + fieldtype: "Select", + options: ["", "Open", "Work In Progress", "Completed", "On Hold"] + }, + { + label: __("Sales Orders"), + fieldname: "sales_order", + fieldtype: "MultiSelectList", + get_data: function(txt) { + return frappe.db.get_link_options('Sales Order', txt); + } + }, + { + label: __("Production Item"), + fieldname: "production_item", + fieldtype: "MultiSelectList", + get_data: function(txt) { + return frappe.db.get_link_options('Item', txt); + } + } + ] +}; diff --git a/erpnext/manufacturing/report/job_card_summary/job_card_summary.json b/erpnext/manufacturing/report/job_card_summary/job_card_summary.json new file mode 100644 index 00000000000..9f08fc34cb8 --- /dev/null +++ b/erpnext/manufacturing/report/job_card_summary/job_card_summary.json @@ -0,0 +1,34 @@ +{ + "add_total_row": 0, + "creation": "2020-04-20 12:00:21.436619", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "letter_head": "Gadgets International", + "modified": "2020-04-20 12:00:21.436619", + "modified_by": "Administrator", + "module": "Manufacturing", + "name": "Job Card Summary", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Job Card", + "report_name": "Job Card Summary", + "report_type": "Script Report", + "roles": [ + { + "role": "Manufacturing User" + }, + { + "role": "Stock User" + }, + { + "role": "Manufacturing Manager" + }, + { + "role": "Stock Manager" + } + ] +} \ No newline at end of file diff --git a/erpnext/manufacturing/report/job_card_summary/job_card_summary.py b/erpnext/manufacturing/report/job_card_summary/job_card_summary.py new file mode 100644 index 00000000000..0f60c132ec8 --- /dev/null +++ b/erpnext/manufacturing/report/job_card_summary/job_card_summary.py @@ -0,0 +1,197 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe import _ +from frappe.utils import getdate, flt +from erpnext.stock.report.stock_analytics.stock_analytics import (get_period_date_ranges, get_period) + +def execute(filters=None): + columns, data = [], [] + data = get_data(filters) + columns = get_columns(filters) + chart_data = get_chart_data(data, filters) + return columns, data, None, chart_data + +def get_data(filters): + query_filters = {"docstatus": ("<", 2)} + + fields = ["name", "status", "work_order", "production_item", "item_name", + "total_completed_qty", "workstation", "operation", "employee_name", "total_time_in_mins"] + + for field in ["work_order", "workstation", "operation", "company"]: + if filters.get(field): + query_filters[field] = ("in", filters.get(field)) + + data = frappe.get_all("Job Card", + fields= fields, filters=query_filters) + + if not data: return [] + + job_cards = [d.name for d in data] + job_card_time_details = {} + for job_card_data in frappe.get_all("Job Card Time Log", + fields=["min(from_time) as from_time", "max(to_time) as to_time", "parent"], + filters={"docstatus": ("<", 2), "parent": ("in", job_cards)}, group_by="parent"): + job_card_time_details[job_card_data.parent] = job_card_data + + for d in data: + if d.status == "Material Transferred": + d.status = "Open" + + if job_card_time_details.get(d.name): + d.from_time = job_card_time_details.get(d.name).from_time + d.to_time = job_card_time_details.get(d.name).to_time + + return data + +def get_chart_data(job_card_details, filters): + labels, periodic_data = prepare_chart_data(job_card_details, filters) + + not_start, in_progress, on_hold, completed = [], [], [] , [] + datasets = [] + + for d in labels: + not_start.append(periodic_data.get("Open").get(d)) + in_progress.append(periodic_data.get("Work In Progress").get(d)) + on_hold.append(periodic_data.get("On Hold").get(d)) + completed.append(periodic_data.get("Completed").get(d)) + + datasets.append({'name':'Open', 'values': not_start}) + datasets.append({'name':'Work In Progress', 'values': in_progress}) + datasets.append({'name':'On Hold', 'values': on_hold}) + datasets.append({'name':'Completed', 'values': completed}) + + chart = { + "data": { + 'labels': labels, + 'datasets': datasets + }, + "type": "bar", + "colors": ["#ff5858", "#ffa00a", "#5e64ff", "#98d85b"], + "axisOptions": { + "xAxisMode": "tick" + }, + "barOptions": { + "stacked": 1 + } + } + + return chart + +def prepare_chart_data(job_card_details, filters): + labels = [] + + periodic_data = { + "Open": {}, + "Work In Progress": {}, + "On Hold": {}, + "Completed": {} + } + + filters.range = "Monthly" + + ranges = get_period_date_ranges(filters) + for from_date, end_date in ranges: + period = get_period(end_date, filters) + if period not in labels: + labels.append(period) + + for d in job_card_details: + if getdate(d.from_time) >= from_date and getdate(d.to_time) <= end_date: + if periodic_data.get(d.status) and periodic_data.get(d.status).get(period): + periodic_data[d.status][period] += 1 + else: + periodic_data[d.status][period] = 1 + + return labels, periodic_data + +def get_columns(filters): + columns = [ + { + "label": _("Id"), + "fieldname": "name", + "fieldtype": "Link", + "options": "Job Card", + "width": 100 + }, + ] + + if not filters.get("status"): + columns.append( + { + "label": _("Status"), + "fieldname": "status", + "width": 100 + }, + ) + + columns.extend([ + { + "label": _("Work Order"), + "fieldname": "work_order", + "fieldtype": "Link", + "options": "Work Order", + "width": 100 + }, + { + "label": _("Production Item"), + "fieldname": "production_item", + "fieldtype": "Link", + "options": "Item", + "width": 110 + }, + { + "label": _("Item Name"), + "fieldname": "item_name", + "fieldtype": "Data", + "width": 100 + }, + { + "label": _("Workstation"), + "fieldname": "workstation", + "fieldtype": "Link", + "options": "Workstation", + "width": 110 + }, + { + "label": _("Operation"), + "fieldname": "operation", + "fieldtype": "Link", + "options": "Operation", + "width": 110 + }, + { + "label": _("Employee Name"), + "fieldname": "employee_name", + "fieldtype": "Data", + "width": 110 + }, + { + "label": _("Total Completed Qty"), + "fieldname": "total_completed_qty", + "fieldtype": "Float", + "width": 120 + }, + { + "label": _("From Time"), + "fieldname": "from_time", + "fieldtype": "Datetime", + "width": 120 + }, + { + "label": _("To Time"), + "fieldname": "to_time", + "fieldtype": "Datetime", + "width": 120 + }, + { + "label": _("Time Required (In Mins)"), + "fieldname": "total_time_in_mins", + "fieldtype": "Float", + "width": 100 + } + ]) + + return columns \ No newline at end of file diff --git a/erpnext/manufacturing/report/production_analytics/production_analytics.py b/erpnext/manufacturing/report/production_analytics/production_analytics.py index 7447a1f6705..f0bdfeda21c 100644 --- a/erpnext/manufacturing/report/production_analytics/production_analytics.py +++ b/erpnext/manufacturing/report/production_analytics/production_analytics.py @@ -38,7 +38,6 @@ def get_columns(filters): def get_periodic_data(filters, entry): periodic_data = { - "All Work Orders": {}, "Not Started": {}, "Overdue": {}, "Pending": {}, @@ -51,7 +50,6 @@ def get_periodic_data(filters, entry): period = get_period(end_date, filters) for d in entry: if getdate(d.creation) <= getdate(from_date) or getdate(d.creation) <= getdate(end_date) : - periodic_data = update_periodic_data(periodic_data, "All Work Orders", period) if d.status == 'Completed': if getdate(d.actual_end_date) < getdate(from_date) or getdate(d.modified) < getdate(from_date): periodic_data = update_periodic_data(periodic_data, "Completed", period) @@ -61,7 +59,7 @@ def get_periodic_data(filters, entry): elif getdate(d.planned_start_date) < getdate(from_date) : periodic_data = update_periodic_data(periodic_data, "Overdue", period) - + else: periodic_data = update_periodic_data(periodic_data, "Not Started", period) @@ -99,7 +97,7 @@ def get_data(filters, columns): periodic_data = get_periodic_data(filters,entry) - labels = ["All Work Orders", "Not Started", "Overdue", "Pending", "Completed"] + labels = ["Not Started", "Overdue", "Pending", "Completed"] chart_data = get_chart_data(periodic_data,columns) ranges = get_period_date_ranges(filters) @@ -123,13 +121,11 @@ def get_chart_data(periodic_data, columns): datasets = [] for d in labels: - all_data.append(periodic_data.get("All Work Orders").get(d)) not_start.append(periodic_data.get("Not Started").get(d)) overdue.append(periodic_data.get("Overdue").get(d)) pending.append(periodic_data.get("Pending").get(d)) completed.append(periodic_data.get("Completed").get(d)) - datasets.append({'name':'All Work Orders', 'values': all_data}) datasets.append({'name':'Not Started', 'values': not_start}) datasets.append({'name':'Overdue', 'values': overdue}) datasets.append({'name':'Pending', 'values': pending}) @@ -139,9 +135,10 @@ def get_chart_data(periodic_data, columns): "data": { 'labels': labels, 'datasets': datasets - } + }, + "type": "bar", + "colors": ["#5e64ff", "#ff5858", "#ffa00a", "#98d85b"] } - chart["type"] = "line" return chart diff --git a/erpnext/manufacturing/report/quality_inspection_summary/__init__.py b/erpnext/manufacturing/report/quality_inspection_summary/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.js b/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.js new file mode 100644 index 00000000000..d4587aa6619 --- /dev/null +++ b/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.js @@ -0,0 +1,40 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Quality Inspection Summary"] = { + "filters": [ + { + label: __("From Date"), + fieldname:"from_date", + fieldtype: "Date", + default: frappe.datetime.add_months(frappe.datetime.get_today(), -12), + reqd: 1 + }, + { + label: __("To Date"), + fieldname:"to_date", + fieldtype: "Date", + default: frappe.datetime.get_today(), + reqd: 1, + }, + { + label: __("Status"), + fieldname: "status", + fieldtype: "Select", + options: ["", "Accepted", "Rejected"] + }, + { + label: __("Item Code"), + fieldname: "item_code", + fieldtype: "Link", + options: "Item" + }, + { + label: __("Inspected By"), + fieldname: "inspected_by", + fieldtype: "Link", + options: "User" + } + ] +}; diff --git a/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.json b/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.json new file mode 100644 index 00000000000..48226e6b21d --- /dev/null +++ b/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.json @@ -0,0 +1,32 @@ +{ + "add_total_row": 0, + "creation": "2020-04-26 18:23:53.475110", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "json": "{}", + "letter_head": "Gadgets International", + "modified": "2020-04-26 18:24:50.529940", + "modified_by": "Administrator", + "module": "Manufacturing", + "name": "Quality Inspection Summary", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Quality Inspection", + "report_name": "Quality Inspection Summary", + "report_type": "Script Report", + "roles": [ + { + "role": "Quality Manager" + }, + { + "role": "Stock User" + }, + { + "role": "Stock Manager" + } + ] +} \ No newline at end of file diff --git a/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.py b/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.py new file mode 100644 index 00000000000..9c1bfa46301 --- /dev/null +++ b/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.py @@ -0,0 +1,133 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe import _ + +def execute(filters=None): + columns, data = [], [] + data = get_data(filters) + columns = get_columns(filters) + chart_data = get_chart_data(data, filters) + return columns, data , None, chart_data + +def get_data(filters): + query_filters = {"docstatus": ("<", 2)} + + fields = ["name", "status", "report_date", "item_code", "item_name", "sample_size", + "inspection_type", "reference_type", "reference_name", "inspected_by"] + + for field in ["status", "item_code", "status", "inspected_by"]: + if filters.get(field): + query_filters[field] = ("in", filters.get(field)) + + query_filters["report_date"] = (">=", filters.get("from_date")) + query_filters["report_date"] = ("<=", filters.get("to_date")) + + return frappe.get_all("Quality Inspection", + fields= fields, filters=query_filters, order_by="report_date asc") + +def get_chart_data(periodic_data, columns): + labels = ["Rejected", "Accepted"] + + status_wise_data = { + "Accepted": 0, + "Rejected": 0 + } + + datasets = [] + + for d in periodic_data: + status_wise_data[d.status] += 1 + + datasets.append({'name':'Qty Wise Chart', + 'values': [status_wise_data.get("Rejected"), status_wise_data.get("Accepted")]}) + + chart = { + "data": { + 'labels': labels, + 'datasets': datasets + }, + "type": "donut", + "height": 300, + "colors": ["#ff5858", "#98d85b"] + } + + return chart + +def get_columns(filters): + columns = [ + { + "label": _("Id"), + "fieldname": "name", + "fieldtype": "Link", + "options": "Work Order", + "width": 100 + }, + { + "label": _("Report Date"), + "fieldname": "report_date", + "fieldtype": "Date", + "width": 150 + } + ] + + if not filters.get("status"): + columns.append( + { + "label": _("Status"), + "fieldname": "status", + "width": 100 + }, + ) + + columns.extend([ + { + "label": _("Item Code"), + "fieldname": "item_code", + "fieldtype": "Link", + "options": "Item", + "width": 130 + }, + { + "label": _("Item Name"), + "fieldname": "item_name", + "fieldtype": "Data", + "width": 130 + }, + { + "label": _("Sample Size"), + "fieldname": "sample_size", + "fieldtype": "Float", + "width": 110 + }, + { + "label": _("Inspection Type"), + "fieldname": "inspection_type", + "fieldtype": "Data", + "width": 110 + }, + { + "label": _("Document Type"), + "fieldname": "reference_type", + "fieldtype": "Data", + "width": 90 + }, + { + "label": _("Document Name"), + "fieldname": "reference_name", + "fieldtype": "Dynamic Link", + "options": "reference_type", + "width": 150 + }, + { + "label": _("Inspected By"), + "fieldname": "inspected_by", + "fieldtype": "Link", + "options": "User", + "width": 150 + } + ]) + + return columns \ No newline at end of file diff --git a/erpnext/manufacturing/report/work_order_summary/__init__.py b/erpnext/manufacturing/report/work_order_summary/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/manufacturing/report/work_order_summary/work_order_summary.js b/erpnext/manufacturing/report/work_order_summary/work_order_summary.js new file mode 100644 index 00000000000..33b147df49a --- /dev/null +++ b/erpnext/manufacturing/report/work_order_summary/work_order_summary.js @@ -0,0 +1,58 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Work Order Summary"] = { + "filters": [ + { + label: __("Company"), + fieldname: "company", + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1 + }, + { + label: __("From Date"), + fieldname:"from_date", + fieldtype: "Date", + default: frappe.datetime.add_months(frappe.datetime.get_today(), -12), + reqd: 1 + }, + { + label: __("To Date"), + fieldname:"to_date", + fieldtype: "Date", + default: frappe.datetime.get_today(), + reqd: 1, + }, + { + label: __("Status"), + fieldname: "status", + fieldtype: "Select", + options: ["", "Not Started", "In Process", "Completed", "Stopped"] + }, + { + label: __("Sales Orders"), + fieldname: "sales_order", + fieldtype: "MultiSelectList", + get_data: function(txt) { + return frappe.db.get_link_options('Sales Order', txt); + } + }, + { + label: __("Production Item"), + fieldname: "production_item", + fieldtype: "MultiSelectList", + get_data: function(txt) { + return frappe.db.get_link_options('Item', txt); + } + }, + { + label: __("Age"), + fieldname:"age", + fieldtype: "Int", + default: "0" + }, + ] +}; diff --git a/erpnext/manufacturing/report/work_order_summary/work_order_summary.json b/erpnext/manufacturing/report/work_order_summary/work_order_summary.json new file mode 100644 index 00000000000..0d093e22e94 --- /dev/null +++ b/erpnext/manufacturing/report/work_order_summary/work_order_summary.json @@ -0,0 +1,31 @@ +{ + "add_total_row": 0, + "creation": "2020-04-17 17:07:56.830358", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "letter_head": "Gadgets International", + "modified": "2020-04-19 16:59:47.979278", + "modified_by": "Administrator", + "module": "Manufacturing", + "name": "Work Order Summary", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Work Order", + "report_name": "Work Order Summary", + "report_type": "Script Report", + "roles": [ + { + "role": "Manufacturing User" + }, + { + "role": "Stock User" + }, + { + "role": "Manufacturing Manager" + } + ] +} \ No newline at end of file diff --git a/erpnext/manufacturing/report/work_order_summary/work_order_summary.py b/erpnext/manufacturing/report/work_order_summary/work_order_summary.py new file mode 100644 index 00000000000..92c31ab382e --- /dev/null +++ b/erpnext/manufacturing/report/work_order_summary/work_order_summary.py @@ -0,0 +1,173 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.utils import date_diff, today +from frappe import _ + +def execute(filters=None): + columns, data = [], [] + + if not filters.get("age"): + filters["age"] = 0 + + data = get_data(filters) + columns = get_columns(filters) + chart_data = get_chart_data(data, filters) + return columns, data, None, chart_data + +def get_data(filters): + query_filters = {"docstatus": 1} + + fields = ["name", "status", "sales_order", "production_item", "qty", "produced_qty", + "planned_start_date", "planned_end_date", "actual_start_date", "actual_end_date", "lead_time"] + + for field in ["sales_order", "production_item", "status", "company"]: + if filters.get(field): + query_filters[field] = ("in", filters.get(field)) + + query_filters["planned_start_date"] = (">=", filters.get("from_date")) + query_filters["planned_end_date"] = ("<=", filters.get("to_date")) + + data = frappe.get_all("Work Order", + fields= fields, filters=query_filters, order_by="planned_start_date asc") + + res = [] + for d in data: + start_date = d.actual_start_date or d.planned_start_date + d.age = 0 + + if d.status != 'Completed': + d.age = date_diff(today(), start_date) + + if filters.get("age") <= d.age: + res.append(d) + + return res + +def get_chart_data(periodic_data, columns): + labels = ["Not Started", "In Process", "Stopped", "Completed"] + + status_wise_data = { + "Not Started": 0, + "In Process": 0, + "Stopped": 0, + "Completed": 0 + } + + for d in periodic_data: + if d.status == "In Process" and d.produced_qty: + status_wise_data["Completed"] += d.produced_qty + + status_wise_data[d.status] += d.qty + + values = [status_wise_data["Not Started"], status_wise_data["In Process"], + status_wise_data["Stopped"], status_wise_data["Completed"]] + + chart = { + "data": { + 'labels': labels, + 'datasets': [{'name':'Qty Wise Chart', 'values': values}] + }, + "type": "donut", + "height": 300, + "colors": ["#ff5858", "#ffa00a", "#5e64ff", "#98d85b"] + } + + return chart + +def get_columns(filters): + columns = [ + { + "label": _("Id"), + "fieldname": "name", + "fieldtype": "Link", + "options": "Work Order", + "width": 100 + }, + ] + + if not filters.get("status"): + columns.append( + { + "label": _("Status"), + "fieldname": "status", + "width": 100 + }, + ) + + columns.extend([ + { + "label": _("Production Item"), + "fieldname": "production_item", + "fieldtype": "Link", + "options": "Item", + "width": 130 + }, + { + "label": _("Produce Qty"), + "fieldname": "qty", + "fieldtype": "Float", + "width": 110 + }, + { + "label": _("Produced Qty"), + "fieldname": "produced_qty", + "fieldtype": "Float", + "width": 110 + }, + { + "label": _("Sales Order"), + "fieldname": "sales_order", + "fieldtype": "Link", + "options": "Sales Order", + "width": 90 + }, + { + "label": _("Planned Start Date"), + "fieldname": "planned_start_date", + "fieldtype": "Date", + "width": 150 + }, + { + "label": _("Planned End Date"), + "fieldname": "planned_end_date", + "fieldtype": "Date", + "width": 150 + } + ]) + + if filters.get("status") != 'Not Started': + columns.extend([ + { + "label": _("Actual Start Date"), + "fieldname": "actual_start_date", + "fieldtype": "Date", + "width": 100 + }, + { + "label": _("Actual End Date"), + "fieldname": "actual_end_date", + "fieldtype": "Date", + "width": 100 + }, + { + "label": _("Age"), + "fieldname": "age", + "fieldtype": "Float", + "width": 110 + }, + ]) + + if filters.get("status") == 'Completed': + columns.extend([ + { + "label": _("Lead Time (in mins)"), + "fieldname": "lead_time", + "fieldtype": "Float", + "width": 110 + }, + ]) + + return columns \ No newline at end of file diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 4ae591b54b5..b920d0334f5 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -685,3 +685,4 @@ execute:frappe.rename_doc("Desk Page", "Getting Started", "Home", force=True) erpnext.patches.v12_0.unset_customer_supplier_based_on_type_of_item_price erpnext.patches.v12_0.set_valid_till_date_in_supplier_quotation erpnext.patches.v12_0.set_serial_no_status +erpnext.patches.v13_0.update_actual_start_and_end_date_in_wo diff --git a/erpnext/patches/v13_0/__init__.py b/erpnext/patches/v13_0/__init__.py index e69de29bb2d..baffc488252 100644 --- a/erpnext/patches/v13_0/__init__.py +++ b/erpnext/patches/v13_0/__init__.py @@ -0,0 +1 @@ +from __future__ import unicode_literals diff --git a/erpnext/patches/v13_0/update_actual_start_and_end_date_in_wo.py b/erpnext/patches/v13_0/update_actual_start_and_end_date_in_wo.py new file mode 100644 index 00000000000..3fab0040fb1 --- /dev/null +++ b/erpnext/patches/v13_0/update_actual_start_and_end_date_in_wo.py @@ -0,0 +1,43 @@ + +# Copyright (c) 2019, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals + +import frappe +from frappe.utils import add_to_date + +def execute(): + frappe.reload_doc("manufacturing", "doctype", "work_order") + frappe.reload_doc("manufacturing", "doctype", "work_order_item") + frappe.reload_doc("manufacturing", "doctype", "job_card") + + data = frappe.get_all("Work Order", + filters = { + "docstatus": 1, + "status": ("in", ["In Process", "Completed"]) + }) + + for d in data: + doc = frappe.get_doc("Work Order", d.name) + doc.set_actual_dates() + doc.db_set("actual_start_date", doc.actual_start_date, update_modified=False) + + if doc.status == "Completed": + frappe.db.set_value("Work Order", d.name, { + "actual_end_date": doc.actual_end_date, + "lead_time": doc.lead_time + }, update_modified=False) + + if not doc.planned_end_date: + planned_end_date = add_to_date(doc.planned_start_date, minutes=doc.lead_time) + doc.db_set("planned_end_date", doc.actual_start_date, update_modified=False) + + + frappe.db.sql(""" UPDATE `tabJob Card` as jc, `tabWork Order` as wo + SET + jc.production_item = wo.production_item, jc.item_name = wo.item_name + WHERE + jc.work_order = wo.name and IFNULL(jc.production_item, "") = "" + """) + diff --git a/erpnext/setup/setup_wizard/data/dashboard_charts.py b/erpnext/setup/setup_wizard/data/dashboard_charts.py new file mode 100644 index 00000000000..f5b66817672 --- /dev/null +++ b/erpnext/setup/setup_wizard/data/dashboard_charts.py @@ -0,0 +1,247 @@ +from __future__ import unicode_literals +from frappe import _ +import frappe +import json + +def get_company_for_dashboards(): + company = frappe.defaults.get_defaults().company + if company: + return company + else: + company_list = frappe.get_list("Company") + if company_list: + return company_list[0].name + return None + +def get_default_dashboards(): + company = frappe.get_doc("Company", get_company_for_dashboards()) + income_account = company.default_income_account or get_account("Income Account", company.name) + expense_account = company.default_expense_account or get_account("Expense Account", company.name) + bank_account = company.default_bank_account or get_account("Bank", company.name) + + return { + "Dashboards": [ + { + "doctype": "Dashboard", + "dashboard_name": "Accounts", + "charts": [ + { "chart": "Outgoing Bills (Sales Invoice)" }, + { "chart": "Incoming Bills (Purchase Invoice)" }, + { "chart": "Bank Balance" }, + { "chart": "Income" }, + { "chart": "Expenses" }, + { "chart": "Patient Appointments" } + ] + }, + { + "doctype": "Dashboard", + "dashboard_name": "Manufacturing", + "charts": [ + { "chart": "Work Order Analysis", "width": "Half" }, + { "chart": "Quality Inspection Analysis", "width": "Half" }, + { "chart": "Long Time Pending Work Orders", "width": "Half" }, + { "chart": "Downtime Analysis", "width": "Half" }, + { "chart": "Production Analysis", "width": "Full" }, + { "chart": "Job Card Analysis", "width": "Full" } + ] + } + ], + "Charts": [ + { + "doctype": "Dashboard Chart", + "time_interval": "Quarterly", + "chart_name": "Income", + "timespan": "Last Year", + "color": None, + "filters_json": json.dumps({"company": company.name, "account": income_account}), + "source": "Account Balance Timeline", + "chart_type": "Custom", + "timeseries": 1, + "owner": "Administrator", + "type": "Line", + "width": "Half" + }, + { + "doctype": "Dashboard Chart", + "time_interval": "Quarterly", + "chart_name": "Expenses", + "timespan": "Last Year", + "color": None, + "filters_json": json.dumps({"company": company.name, "account": expense_account}), + "source": "Account Balance Timeline", + "chart_type": "Custom", + "timeseries": 1, + "owner": "Administrator", + "type": "Line", + "width": "Half" + }, + { + "doctype": "Dashboard Chart", + "time_interval": "Quarterly", + "chart_name": "Bank Balance", + "timespan": "Last Year", + "color": "#ffb868", + "filters_json": json.dumps({"company": company.name, "account": bank_account}), + "source": "Account Balance Timeline", + "chart_type": "Custom", + "timeseries": 1, + "owner": "Administrator", + "type": "Line", + "width": "Half" + }, + { + "doctype": "Dashboard Chart", + "time_interval": "Monthly", + "chart_name": "Incoming Bills (Purchase Invoice)", + "timespan": "Last Year", + "color": "#a83333", + "value_based_on": "base_grand_total", + "filters_json": json.dumps({}), + "chart_type": "Sum", + "timeseries": 1, + "based_on": "posting_date", + "owner": "Administrator", + "document_type": "Purchase Invoice", + "type": "Bar", + "width": "Half" + }, + { + "doctype": "Dashboard Chart", + "time_interval": "Monthly", + "chart_name": "Outgoing Bills (Sales Invoice)", + "timespan": "Last Year", + "color": "#7b933d", + "value_based_on": "base_grand_total", + "filters_json": json.dumps({}), + "chart_type": "Sum", + "timeseries": 1, + "based_on": "posting_date", + "owner": "Administrator", + "document_type": "Sales Invoice", + "type": "Bar", + "width": "Half" + }, + { + "doctype": "Dashboard Chart", + "time_interval": "Daily", + "chart_name": "Patient Appointments", + "timespan": "Last Month", + "color": "#77ecca", + "filters_json": json.dumps({}), + "chart_type": "Count", + "timeseries": 1, + "based_on": "appointment_datetime", + "owner": "Administrator", + "document_type": "Patient Appointment", + "type": "Line", + "width": "Half" + } + { + "doctype": "Dashboard Chart", + "time_interval": "Yearly", + "chart_type": "Report", + "chart_name": "Work Order Analysis", + "timespan": "Last Year", + "report_name": "Work Order Summary", + "owner": "Administrator", + "filters_json": json.dumps({"company": company.name}), + "bar": "Donut", + "custom_options": json.dumps({ + "axisOptions": { + "shortenYAxisNumbers": 1 + }, + "height": 300, + "colors": ["#ff5858", "#ffa00a", "#5e64ff", "#98d85b"] + }), + }, + { + "doctype": "Dashboard Chart", + "time_interval": "Yearly", + "chart_type": "Report", + "chart_name": "Quality Inspection Analysis", + "timespan": "Last Year", + "report_name": "Quality Inspection Summary", + "owner": "Administrator", + "filters_json": json.dumps({}), + "bar": "Donut", + "custom_options": json.dumps({ + "axisOptions": { + "shortenYAxisNumbers": 1 + }, + "height": 300, + "colors": ["#ff5858", "#98d85b"] + }), + }, + { + "doctype": "Dashboard Chart", + "time_interval": "Yearly", + "chart_type": "Report", + "chart_name": "Long Time Pending Work Orders", + "timespan": "Last Year", + "report_name": "Work Order Summary", + "filters_json": json.dumps({"company": company.name, "age":180}), + "owner": "Administrator", + "bar": "Bar", + "custom_options": json.dumps({ + "colors": ["#ff5858"], + "x_field": "name", + "y_fields": ["age"], + "y_axis_fields": [{"__islocal": "true", "idx": 1, "y_field": "age"}], + "chart_type": "Bar" + }), + }, + { + "doctype": "Dashboard Chart", + "time_interval": "Yearly", + "chart_type": "Report", + "chart_name": "Downtime Analysis", + "timespan": "Last Year", + "filters_json": json.dumps({}), + "report_name": "Downtime Analysis", + "owner": "Administrator", + "bar": "Bar", + "custom_options": json.dumps({ + "colors": ["#ff5858"] + }), + }, + { + "doctype": "Dashboard Chart", + "time_interval": "Yearly", + "chart_type": "Report", + "chart_name": "Production Analysis", + "timespan": "Last Year", + "report_name": "Production Analytics", + "owner": "Administrator", + "filters_json": json.dumps({"company": company.name, "range":"Monthly"}), + "bar": "Bar", + "custom_options": json.dumps({ + "colors": ["#7cd6fd", "#ff5858", "#ffa00a", "#98d85b"] + }), + }, + { + "doctype": "Dashboard Chart", + "time_interval": "Yearly", + "chart_type": "Report", + "chart_name": "Job Card Analysis", + "timespan": "Last Year", + "report_name": "Job Card Summary", + "owner": "Administrator", + "filters_json": json.dumps({"company": company.name, "range":"Monthly"}), + "bar": "Bar", + "custom_options": json.dumps({ + "axisOptions": { + "xAxisMode": "tick" + }, + "barOptions": { + "stacked": 1 + }, + "colors": ["#ff5858", "#ffa00a", "#5e64ff", "#98d85b"] + }), + }, + ] + } + +def get_account(account_type, company): + accounts = frappe.get_list("Account", filters={"account_type": account_type, "company": company}) + if accounts: + return accounts[0].name diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.json b/erpnext/stock/doctype/quality_inspection/quality_inspection.json index a9f3cd09ef5..c951066aa83 100644 --- a/erpnext/stock/doctype/quality_inspection/quality_inspection.json +++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.json @@ -1,4 +1,5 @@ { + "actions": [], "autoname": "naming_series:", "creation": "2013-04-30 13:13:03", "doctype": "DocType", @@ -8,6 +9,7 @@ "field_order": [ "naming_series", "report_date", + "status", "column_break_4", "inspection_type", "reference_type", @@ -20,17 +22,16 @@ "column_break1", "item_name", "description", - "status", + "bom_no", + "specification_details", + "quality_inspection_template", + "readings", "section_break_14", "inspected_by", "verified_by", - "bom_no", "column_break_17", "remarks", - "amended_from", - "specification_details", - "quality_inspection_template", - "readings" + "amended_from" ], "fields": [ { @@ -231,7 +232,8 @@ "icon": "fa fa-search", "idx": 1, "is_submittable": 1, - "modified": "2019-07-12 12:07:23.153698", + "links": [], + "modified": "2020-04-26 17:50:25.068222", "modified_by": "Administrator", "module": "Stock", "name": "Quality Inspection", diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index be2dd526a65..18d68539daa 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -732,11 +732,15 @@ class StockEntry(StockController): pro_doc = frappe.get_doc("Work Order", self.work_order) _validate_work_order(pro_doc) pro_doc.run_method("update_status") + if self.fg_completed_qty: pro_doc.run_method("update_work_order_qty") if self.purpose == "Manufacture": pro_doc.run_method("update_planned_qty") + if not pro_doc.operations: + pro_doc.set_actual_dates() + def get_item_details(self, args=None, for_update=False): item = frappe.db.sql("""select i.name, i.stock_uom, i.description, i.image, i.item_name, i.item_group, i.has_batch_no, i.sample_quantity, i.has_serial_no, From 366bce678f0b3754dfa00202530f135178fc78a9 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 6 May 2020 02:38:27 +0530 Subject: [PATCH 081/608] added onboarding for manufacturing --- erpnext/manufacturing/dashboard_fixtures.py | 216 +++++++++++++++ .../manufacturing/manufacturing.json | 43 ++- .../doctype/work_order/work_order.json | 2 +- .../manufacturing/manufacturing.json | 54 ++++ .../create_bom/create_bom.json | 16 ++ .../create_product/create_product.json | 16 ++ .../introduction_to_manufacturing.json | 17 ++ .../onboarding_step/operation/operation.json | 15 ++ .../onboarding_step/warehouse/warehouse.json | 15 ++ .../work_order/work_order.json | 15 ++ .../workstation/workstation.json | 15 ++ .../downtime_analysis/downtime_analysis.py | 3 +- .../job_card_summary/job_card_summary.py | 33 +-- .../production_analytics.py | 12 +- .../quality_inspection_summary.py | 3 +- .../work_order_summary/work_order_summary.js | 7 + .../work_order_summary/work_order_summary.py | 107 +++++++- .../update_actual_start_and_end_date_in_wo.py | 5 +- .../setup_wizard/data/dashboard_charts.py | 247 ------------------ 19 files changed, 545 insertions(+), 296 deletions(-) create mode 100644 erpnext/manufacturing/dashboard_fixtures.py create mode 100644 erpnext/manufacturing/onboarding/manufacturing/manufacturing.json create mode 100644 erpnext/manufacturing/onboarding_step/create_bom/create_bom.json create mode 100644 erpnext/manufacturing/onboarding_step/create_product/create_product.json create mode 100644 erpnext/manufacturing/onboarding_step/introduction_to_manufacturing/introduction_to_manufacturing.json create mode 100644 erpnext/manufacturing/onboarding_step/operation/operation.json create mode 100644 erpnext/manufacturing/onboarding_step/warehouse/warehouse.json create mode 100644 erpnext/manufacturing/onboarding_step/work_order/work_order.json create mode 100644 erpnext/manufacturing/onboarding_step/workstation/workstation.json delete mode 100644 erpnext/setup/setup_wizard/data/dashboard_charts.py diff --git a/erpnext/manufacturing/dashboard_fixtures.py b/erpnext/manufacturing/dashboard_fixtures.py new file mode 100644 index 00000000000..de3775f52c6 --- /dev/null +++ b/erpnext/manufacturing/dashboard_fixtures.py @@ -0,0 +1,216 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +import frappe, erpnext, json +from frappe import _ + +def get_data(): + return frappe._dict({ + "dashboards": get_dashboards(), + "charts": get_charts(), + "number_cards": get_number_cards(), + }) + +def get_dashboards(): + return [{ + "name": "Manufacturing Dashboard", + "dashboard_name": "Manufacturing Dashboard", + "charts": [ + { "chart": "Produced Quantity", "width": "Half" }, + { "chart": "Completed Operation", "width": "Half" }, + { "chart": "Work Order Analysis", "width": "Half" }, + { "chart": "Quality Inspection Analysis", "width": "Half" }, + { "chart": "Pending Work Order", "width": "Half" }, + { "chart": "Last Month Downtime Analysis", "width": "Half" }, + { "chart": "Work Order Qty Analysis", "width": "Full" }, + { "chart": "Job Card Analysis", "width": "Full" } + ], + "cards": [ + { "card": "Total Work Order" }, + { "card": "Completed Work Order" }, + { "card": "Ongoing Job Card" }, + { "card": "Total Quality Inspection"} + ] + }] + +def get_charts(): + company = erpnext.get_default_company() + + if not company: + company = frappe.db.get_value("Company", {"is_group": 0}, "name") + + return [{ + "doctype": "Dashboard Chart", + "based_on": "creation", + "time_interval": "Yearly", + "chart_type": "Sum", + "chart_name": "Produced Quantity", + "document_type": "Work Order", + "filters_json": json.dumps({}), + "group_by_type": "Count", + "time_interval": "Quarterly", + "timespan": "Last Year", + "owner": "Administrator", + "type": "Line", + "value_based_on": "qty", + "is_public": 1, + "timeseries": 1 + }, { + "doctype": "Dashboard Chart", + "based_on": "creation", + "time_interval": "Yearly", + "chart_type": "Sum", + "chart_name": "Completed Operation", + "document_type": "Work Order Operation", + "filters_json": json.dumps({}), + "group_by_type": "Count", + "time_interval": "Quarterly", + "timespan": "Last Year", + "owner": "Administrator", + "type": "Line", + "value_based_on": "completed_qty", + "is_public": 1, + "timeseries": 1 + }, { + "doctype": "Dashboard Chart", + "time_interval": "Yearly", + "chart_type": "Report", + "chart_name": "Work Order Analysis", + "timespan": "Last Year", + "report_name": "Work Order Summary", + "owner": "Administrator", + "filters_json": json.dumps({"company": company, "charts_based_on": "Status"}), + "type": "Donut", + "is_public": 1, + "is_custom": 1, + "custom_options": json.dumps({ + "axisOptions": { + "shortenYAxisNumbers": 1 + }, + "height": 300 + }), + }, { + "doctype": "Dashboard Chart", + "time_interval": "Yearly", + "chart_type": "Report", + "chart_name": "Quality Inspection Analysis", + "timespan": "Last Year", + "report_name": "Quality Inspection Summary", + "owner": "Administrator", + "filters_json": json.dumps({}), + "type": "Donut", + "is_public": 1, + "is_custom": 1, + "custom_options": json.dumps({ + "axisOptions": { + "shortenYAxisNumbers": 1 + }, + "height": 300 + }), + }, { + "doctype": "Dashboard Chart", + "time_interval": "Yearly", + "chart_type": "Report", + "chart_name": "Pending Work Order", + "timespan": "Last Year", + "report_name": "Work Order Summary", + "filters_json": json.dumps({"company": company, "charts_based_on": "Age"}), + "owner": "Administrator", + "type": "Donut", + "is_public": 1, + "is_custom": 1, + "custom_options": json.dumps({ + "axisOptions": { + "shortenYAxisNumbers": 1 + }, + "height": 300 + }), + }, { + "doctype": "Dashboard Chart", + "time_interval": "Yearly", + "chart_type": "Report", + "chart_name": "Last Month Downtime Analysis", + "timespan": "Last Year", + "filters_json": json.dumps({}), + "report_name": "Downtime Analysis", + "owner": "Administrator", + "is_public": 1, + "is_custom": 1, + "type": "Bar" + }, { + "doctype": "Dashboard Chart", + "time_interval": "Yearly", + "chart_type": "Report", + "chart_name": _("Work Order Qty Analysis"), + "timespan": "Last Year", + "report_name": "Work Order Summary", + "filters_json": json.dumps({"company": company, "charts_based_on": "Quantity"}), + "owner": "Administrator", + "type": "Bar", + "is_public": 1, + "is_custom": 1, + "custom_options": json.dumps({ + "barOptions": { "stacked": 1 } + }), + }, { + "doctype": "Dashboard Chart", + "time_interval": "Yearly", + "chart_type": "Report", + "chart_name": "Job Card Analysis", + "timespan": "Last Year", + "report_name": "Job Card Summary", + "owner": "Administrator", + "is_public": 1, + "is_custom": 1, + "filters_json": json.dumps({"company": company, "range":"Monthly"}), + "custom_options": json.dumps({ + "barOptions": { "stacked": 1 } + }), + "type": "Bar" + }] + +def get_number_cards(): + return [{ + "doctype": "Number Card", + "document_type": "Work Order", + "name": "Total Work Order", + "filters_json": json.dumps([]), + "function": "Count", + "is_public": 1, + "label": _("Total Work Order"), + "show_percentage_stats": 1, + "stats_time_interval": "Daily" + }, + { + "doctype": "Number Card", + "document_type": "Work Order", + "name": "Completed Work Order", + "filters_json": json.dumps([['Work Order', 'status','=','Completed', False]]), + "function": "Count", + "is_public": 1, + "label": _("Completed Work Order"), + "show_percentage_stats": 1, + "stats_time_interval": "Daily" + }, + { + "doctype": "Number Card", + "document_type": "Job Card", + "name": "Ongoing Job Card", + "filters_json": json.dumps([['Job Card', 'status','!=','Completed', False]]), + "function": "Count", + "is_public": 1, + "label": _("Ongoing Job Card"), + "show_percentage_stats": 1, + "stats_time_interval": "Daily" + }, + { + "doctype": "Number Card", + "document_type": "Quality Inspection", + "name": "Total Quality Inspection", + "filters_json": json.dumps([]), + "function": "Count", + "is_public": 1, + "label": _("Total Quality Inspection"), + "show_percentage_stats": 1, + "stats_time_interval": "Daily" + }] \ No newline at end of file diff --git a/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json b/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json index 0464b763c0e..f6bbf3db85c 100644 --- a/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json +++ b/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json @@ -3,7 +3,7 @@ { "hidden": 0, "label": "Production", - "links": "[\n {\n \"dependencies\": [\n \"Item\",\n \"BOM\"\n ],\n \"description\": \"Orders released for production.\",\n \"label\": \"Work Order\",\n \"name\": \"Work Order\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"BOM\"\n ],\n \"description\": \"Generate Material Requests (MRP) and Work Orders.\",\n \"label\": \"Production Plan\",\n \"name\": \"Production Plan\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"label\": \"Stock Entry\",\n \"name\": \"Stock Entry\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Job Card\",\n \"name\": \"Job Card\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Downtime Entry\",\n \"name\": \"Downtime Entry\",\n \"type\": \"doctype\"\n }\n]" + "links": "[\n {\n \"dependencies\": [\n \"Item\",\n \"BOM\"\n ],\n \"description\": \"Orders released for production.\",\n \"label\": \"Work Order\",\n \"name\": \"Work Order\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"BOM\"\n ],\n \"description\": \"Generate Material Requests (MRP) and Work Orders.\",\n \"label\": \"Production Plan\",\n \"name\": \"Production Plan\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"label\": \"Stock Entry\",\n \"name\": \"Stock Entry\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Activity Type\"\n ],\n \"description\": \"Time Sheet for manufacturing.\",\n \"label\": \"Timesheet\",\n \"name\": \"Timesheet\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Job Card\",\n \"name\": \"Job Card\",\n \"type\": \"doctype\"\n }\n]" }, { "hidden": 0, @@ -13,7 +13,7 @@ { "hidden": 0, "label": "Reports", - "links": "[\n {\n \"dependencies\": [\n \"Work Order\"\n ],\n \"doctype\": \"Work Order\",\n \"is_query_report\": true,\n \"label\": \"Work Order Summary\",\n \"name\": \"Work Order Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Work Order\"\n ],\n \"doctype\": \"Work Order\",\n \"is_query_report\": true,\n \"label\": \"Issued Items Against Work Order\",\n \"name\": \"Issued Items Against Work Order\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Work Order\"\n ],\n \"doctype\": \"Work Order\",\n \"is_query_report\": true,\n \"label\": \"Production Analytics\",\n \"name\": \"Production Analytics\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Quality Inspection\"\n ],\n \"doctype\": \"Quality Inspection\",\n \"is_query_report\": true,\n \"label\": \"Quality Inspection Summary\",\n \"name\": \"Quality Inspection Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Downtime Entry\"\n ],\n \"doctype\": \"Downtime Entry\",\n \"is_query_report\": true,\n \"label\": \"Downtime Analysis\",\n \"name\": \"Downtime Analysis\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Job Card\"\n ],\n \"doctype\": \"Job Card\",\n \"is_query_report\": true,\n \"label\": \"Job Card Summary\",\n \"name\": \"Job Card Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"BOM\"\n ],\n \"doctype\": \"BOM\",\n \"is_query_report\": true,\n \"label\": \"BOM Search\",\n \"name\": \"BOM Search\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"BOM\"\n ],\n \"doctype\": \"BOM\",\n \"is_query_report\": true,\n \"label\": \"BOM Stock Report\",\n \"name\": \"BOM Stock Report\",\n \"type\": \"report\"\n }\n]" + "links": "[{\n\t\"dependencies\": [\"Work Order\"],\n\t\"name\": \"Work Order Summary\",\n\t\"is_query_report\": true,\n\t\"type\": \"report\",\n\t\"doctype\": \"Work Order\",\n\t\"label\": \"Work Order Summary\"\n}, {\n\t\"dependencies\": [\"Work Order\"],\n\t\"name\": \"Production Analytics\",\n\t\"is_query_report\": true,\n\t\"type\": \"report\",\n\t\"doctype\": \"Work Order\",\n\t\"label\": \"Production Analytics\"\n}, {\n\t\"dependencies\": [\"Quality Inspection\"],\n\t\"name\": \"Quality Inspection Summary\",\n\t\"is_query_report\": true,\n\t\"type\": \"report\",\n\t\"doctype\": \"Quality Inspection\",\n\t\"label\": \"Quality Inspection Summary\"\n}, {\n\t\"dependencies\": [\"Downtime Entry\"],\n\t\"name\": \"Downtime Analysis\",\n\t\"is_query_report\": true,\n\t\"type\": \"report\",\n\t\"doctype\": \"Downtime Entry\",\n\t\"label\": \"Downtime Analysis\"\n}, {\n\t\"dependencies\": [\"Job Card\"],\n\t\"name\": \"Job Card Summary\",\n\t\"is_query_report\": true,\n\t\"type\": \"report\",\n\t\"doctype\": \"Job Card\",\n\t\"label\": \"Job Card Summary\"\n}, {\n\t\"dependencies\": [\"BOM\"],\n\t\"name\": \"BOM Search\",\n\t\"is_query_report\": true,\n\t\"type\": \"report\",\n\t\"doctype\": \"BOM\",\n\t\"label\": \"BOM Search\"\n}, {\n\t\"dependencies\": [\"BOM\"],\n\t\"name\": \"BOM Stock Report\",\n\t\"is_query_report\": true,\n\t\"type\": \"report\",\n\t\"doctype\": \"BOM\",\n\t\"label\": \"BOM Stock Report\"\n}]" }, { "hidden": 0, @@ -32,12 +32,7 @@ } ], "category": "Domains", - "charts": [ - { - "chart_name": "Production Analysis", - "label": "Production Analysis" - } - ], + "charts": [], "creation": "2020-03-02 17:11:37.032604", "developer_mode_only": 0, "disable_user_customization": 0, @@ -47,29 +42,59 @@ "idx": 0, "is_standard": 1, "label": "Manufacturing", - "modified": "2020-04-27 00:17:26.323677", + "modified": "2020-05-14 15:06:45.963942", "modified_by": "Administrator", "module": "Manufacturing", "name": "Manufacturing", + "onboarding": "Manufacturing", "owner": "Administrator", "pin_to_bottom": 0, "pin_to_top": 0, "restrict_to_domain": "Manufacturing", "shortcuts": [ { + "format": "{} Active", "label": "Item", "link_to": "Item", + "restrict_to_domain": "Manufacturing", + "stats_filter": "{\n \"disabled\": 0\n}", "type": "DocType" }, { + "format": "{} Active", "label": "BOM", "link_to": "BOM", + "restrict_to_domain": "Manufacturing", + "stats_filter": "{\n \"is_active\": 1\n}", "type": "DocType" }, { + "format": "{} Open", "label": "Work Order", "link_to": "Work Order", + "restrict_to_domain": "Manufacturing", + "stats_filter": "{ \"status\": \n (\n \"in\", [\"Draft\", \"Not Started\", \"In Process\"]\n )\n}", "type": "DocType" + }, + { + "format": "{} Open", + "label": "Production Plan", + "link_to": "Production Plan", + "restrict_to_domain": "Manufacturing", + "stats_filter": "{ \"status\": \n (\n \"!=\", \"Completed\"\n )\n}", + "type": "DocType" + }, + { + "label": "Work Order Summary", + "link_to": "Work Order Summary", + "restrict_to_domain": "Manufacturing", + "type": "Report" + }, + { + "label": "BOM Stock Report", + "link_to": "BOM Stock Report", + "restrict_to_domain": "Manufacturing", + "type": "Report" } ] } \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/work_order/work_order.json b/erpnext/manufacturing/doctype/work_order/work_order.json index 8be22875a31..585a09db2bf 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.json +++ b/erpnext/manufacturing/doctype/work_order/work_order.json @@ -496,7 +496,7 @@ "image_field": "image", "is_submittable": 1, "links": [], - "modified": "2020-04-24 19:32:43.323054", + "modified": "2020-05-05 19:32:43.323054", "modified_by": "Administrator", "module": "Manufacturing", "name": "Work Order", diff --git a/erpnext/manufacturing/onboarding/manufacturing/manufacturing.json b/erpnext/manufacturing/onboarding/manufacturing/manufacturing.json new file mode 100644 index 00000000000..50584e19cac --- /dev/null +++ b/erpnext/manufacturing/onboarding/manufacturing/manufacturing.json @@ -0,0 +1,54 @@ +{ + "allow_roles": [ + { + "role": "Manufacturing User" + }, + { + "role": "Manufacturing Manager" + }, + { + "role": "Item Manager" + }, + { + "role": "Stock User" + } + ], + "creation": "2020-05-05 16:37:08.238935", + "docstatus": 0, + "doctype": "Onboarding", + "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/manufacturing", + "idx": 0, + "is_complete": 0, + "modified": "2020-05-12 16:22:07.050224", + "modified_by": "Administrator", + "module": "Manufacturing", + "name": "Manufacturing", + "owner": "Administrator", + "steps": [ + { + "step": "Introduction to Manufacturing" + }, + { + "step": "Warehouse" + }, + { + "step": "Workstation" + }, + { + "step": "Operation" + }, + { + "step": "Create Product" + }, + { + "step": "Create BOM" + }, + { + "step": "Work Order" + } + ], + "subtitle": "Products, Raw Materials, BOM, Work Order and more.", + "success_message": "Manufacturing module is all setup!", + "title": "Let's Setup Manufacturing Module", + "user_can_dismiss": 1 +} \ No newline at end of file diff --git a/erpnext/manufacturing/onboarding_step/create_bom/create_bom.json b/erpnext/manufacturing/onboarding_step/create_bom/create_bom.json new file mode 100644 index 00000000000..969c2fa5fe4 --- /dev/null +++ b/erpnext/manufacturing/onboarding_step/create_bom/create_bom.json @@ -0,0 +1,16 @@ +{ + "action": "Create Entry", + "creation": "2020-05-05 16:41:20.239696", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 1, + "is_skipped": 0, + "modified": "2020-05-05 16:41:20.239696", + "modified_by": "Administrator", + "name": "Create BOM", + "owner": "Administrator", + "reference_document": "BOM", + "title": "Create BOM" +} \ No newline at end of file diff --git a/erpnext/manufacturing/onboarding_step/create_product/create_product.json b/erpnext/manufacturing/onboarding_step/create_product/create_product.json new file mode 100644 index 00000000000..082f31ed6e7 --- /dev/null +++ b/erpnext/manufacturing/onboarding_step/create_product/create_product.json @@ -0,0 +1,16 @@ +{ + "action": "Create Entry", + "creation": "2020-05-05 16:42:31.476275", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 1, + "is_skipped": 0, + "modified": "2020-05-05 16:42:31.476275", + "modified_by": "Administrator", + "name": "Create Product", + "owner": "Administrator", + "reference_document": "Item", + "title": "Create Product" +} \ No newline at end of file diff --git a/erpnext/manufacturing/onboarding_step/introduction_to_manufacturing/introduction_to_manufacturing.json b/erpnext/manufacturing/onboarding_step/introduction_to_manufacturing/introduction_to_manufacturing.json new file mode 100644 index 00000000000..6e5c3a9f45c --- /dev/null +++ b/erpnext/manufacturing/onboarding_step/introduction_to_manufacturing/introduction_to_manufacturing.json @@ -0,0 +1,17 @@ +{ + "action": "Update Settings", + "creation": "2020-05-05 16:40:23.676406", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_skipped": 0, + "modified": "2020-05-05 16:40:23.676406", + "modified_by": "Administrator", + "name": "Introduction to Manufacturing", + "owner": "Administrator", + "reference_document": "Manufacturing Settings", + "title": "Manufacturing Settings", + "video_url": "https://www.youtube.com/watch?v=UVGfzwOOZC4" +} \ No newline at end of file diff --git a/erpnext/manufacturing/onboarding_step/operation/operation.json b/erpnext/manufacturing/onboarding_step/operation/operation.json new file mode 100644 index 00000000000..6f84db13ec8 --- /dev/null +++ b/erpnext/manufacturing/onboarding_step/operation/operation.json @@ -0,0 +1,15 @@ +{ + "action": "Create Entry", + "creation": "2020-05-12 16:15:31.706756", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_skipped": 0, + "modified": "2020-05-12 16:17:06.943067", + "modified_by": "Administrator", + "name": "Operation", + "owner": "Administrator", + "title": "Create Operation" +} \ No newline at end of file diff --git a/erpnext/manufacturing/onboarding_step/warehouse/warehouse.json b/erpnext/manufacturing/onboarding_step/warehouse/warehouse.json new file mode 100644 index 00000000000..36f5204e0b6 --- /dev/null +++ b/erpnext/manufacturing/onboarding_step/warehouse/warehouse.json @@ -0,0 +1,15 @@ +{ + "action": "Create Entry", + "creation": "2020-05-12 16:13:34.014554", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_skipped": 0, + "modified": "2020-05-12 16:16:48.345846", + "modified_by": "Administrator", + "name": "Warehouse", + "owner": "Administrator", + "title": "Create Warehouse" +} \ No newline at end of file diff --git a/erpnext/manufacturing/onboarding_step/work_order/work_order.json b/erpnext/manufacturing/onboarding_step/work_order/work_order.json new file mode 100644 index 00000000000..57e4e7b3bc7 --- /dev/null +++ b/erpnext/manufacturing/onboarding_step/work_order/work_order.json @@ -0,0 +1,15 @@ +{ + "action": "Create Entry", + "creation": "2020-05-12 16:15:56.084682", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_skipped": 0, + "modified": "2020-05-12 16:17:33.675304", + "modified_by": "Administrator", + "name": "Work Order", + "owner": "Administrator", + "title": "Create Work Order" +} \ No newline at end of file diff --git a/erpnext/manufacturing/onboarding_step/workstation/workstation.json b/erpnext/manufacturing/onboarding_step/workstation/workstation.json new file mode 100644 index 00000000000..6c1a06938b4 --- /dev/null +++ b/erpnext/manufacturing/onboarding_step/workstation/workstation.json @@ -0,0 +1,15 @@ +{ + "action": "Create Entry", + "creation": "2020-05-12 16:14:14.930214", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_skipped": 0, + "modified": "2020-05-12 16:16:58.808906", + "modified_by": "Administrator", + "name": "Workstation", + "owner": "Administrator", + "title": "Create Workstation" +} \ No newline at end of file diff --git a/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.py b/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.py index d69ec189baa..dfc6b02b84f 100644 --- a/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.py +++ b/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.py @@ -47,8 +47,7 @@ def get_chart_data(data, columns): {"name": "Dataset 1", "values": datasets} ] }, - "type": "bar", - "colors": ["#ff5858"] + "type": "bar" } return chart diff --git a/erpnext/manufacturing/report/job_card_summary/job_card_summary.py b/erpnext/manufacturing/report/job_card_summary/job_card_summary.py index 0f60c132ec8..ae1e4f30469 100644 --- a/erpnext/manufacturing/report/job_card_summary/job_card_summary.py +++ b/erpnext/manufacturing/report/job_card_summary/job_card_summary.py @@ -49,33 +49,22 @@ def get_data(filters): def get_chart_data(job_card_details, filters): labels, periodic_data = prepare_chart_data(job_card_details, filters) - not_start, in_progress, on_hold, completed = [], [], [] , [] + pending, completed = [], [] datasets = [] for d in labels: - not_start.append(periodic_data.get("Open").get(d)) - in_progress.append(periodic_data.get("Work In Progress").get(d)) - on_hold.append(periodic_data.get("On Hold").get(d)) + pending.append(periodic_data.get("Pending").get(d)) completed.append(periodic_data.get("Completed").get(d)) - datasets.append({'name':'Open', 'values': not_start}) - datasets.append({'name':'Work In Progress', 'values': in_progress}) - datasets.append({'name':'On Hold', 'values': on_hold}) - datasets.append({'name':'Completed', 'values': completed}) + datasets.append({"name": "Pending", "values": pending}) + datasets.append({"name": "Completed", "values": completed}) chart = { "data": { 'labels': labels, 'datasets': datasets }, - "type": "bar", - "colors": ["#ff5858", "#ffa00a", "#5e64ff", "#98d85b"], - "axisOptions": { - "xAxisMode": "tick" - }, - "barOptions": { - "stacked": 1 - } + "type": "bar" } return chart @@ -84,9 +73,7 @@ def prepare_chart_data(job_card_details, filters): labels = [] periodic_data = { - "Open": {}, - "Work In Progress": {}, - "On Hold": {}, + "Pending": {}, "Completed": {} } @@ -100,10 +87,12 @@ def prepare_chart_data(job_card_details, filters): for d in job_card_details: if getdate(d.from_time) >= from_date and getdate(d.to_time) <= end_date: - if periodic_data.get(d.status) and periodic_data.get(d.status).get(period): - periodic_data[d.status][period] += 1 + status = "Completed" if d.status == "Completed" else "Pending" + + if periodic_data.get(status) and periodic_data.get(status).get(period): + periodic_data[status][period] += 1 else: - periodic_data[d.status][period] = 1 + periodic_data[status][period] = 1 return labels, periodic_data diff --git a/erpnext/manufacturing/report/production_analytics/production_analytics.py b/erpnext/manufacturing/report/production_analytics/production_analytics.py index f0bdfeda21c..f62cd255b5e 100644 --- a/erpnext/manufacturing/report/production_analytics/production_analytics.py +++ b/erpnext/manufacturing/report/production_analytics/production_analytics.py @@ -38,6 +38,7 @@ def get_columns(filters): def get_periodic_data(filters, entry): periodic_data = { + "All Work Orders": {}, "Not Started": {}, "Overdue": {}, "Pending": {}, @@ -50,6 +51,7 @@ def get_periodic_data(filters, entry): period = get_period(end_date, filters) for d in entry: if getdate(d.creation) <= getdate(from_date) or getdate(d.creation) <= getdate(end_date) : + periodic_data = update_periodic_data(periodic_data, "All Work Orders", period) if d.status == 'Completed': if getdate(d.actual_end_date) < getdate(from_date) or getdate(d.modified) < getdate(from_date): periodic_data = update_periodic_data(periodic_data, "Completed", period) @@ -97,7 +99,7 @@ def get_data(filters, columns): periodic_data = get_periodic_data(filters,entry) - labels = ["Not Started", "Overdue", "Pending", "Completed"] + labels = ["All Work Orders", "Not Started", "Overdue", "Pending", "Completed"] chart_data = get_chart_data(periodic_data,columns) ranges = get_period_date_ranges(filters) @@ -121,11 +123,13 @@ def get_chart_data(periodic_data, columns): datasets = [] for d in labels: + all_data.append(periodic_data.get("All Work Orders").get(d)) not_start.append(periodic_data.get("Not Started").get(d)) overdue.append(periodic_data.get("Overdue").get(d)) pending.append(periodic_data.get("Pending").get(d)) completed.append(periodic_data.get("Completed").get(d)) + datasets.append({'name':'All Work Orders', 'values': all_data}) datasets.append({'name':'Not Started', 'values': not_start}) datasets.append({'name':'Overdue', 'values': overdue}) datasets.append({'name':'Pending', 'values': pending}) @@ -135,14 +139,12 @@ def get_chart_data(periodic_data, columns): "data": { 'labels': labels, 'datasets': datasets - }, - "type": "bar", - "colors": ["#5e64ff", "#ff5858", "#ffa00a", "#98d85b"] + } } + chart["type"] = "line" return chart - diff --git a/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.py b/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.py index 9c1bfa46301..6192632bda6 100644 --- a/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.py +++ b/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.py @@ -50,8 +50,7 @@ def get_chart_data(periodic_data, columns): 'datasets': datasets }, "type": "donut", - "height": 300, - "colors": ["#ff5858", "#98d85b"] + "height": 300 } return chart diff --git a/erpnext/manufacturing/report/work_order_summary/work_order_summary.js b/erpnext/manufacturing/report/work_order_summary/work_order_summary.js index 33b147df49a..ec9fe35d63a 100644 --- a/erpnext/manufacturing/report/work_order_summary/work_order_summary.js +++ b/erpnext/manufacturing/report/work_order_summary/work_order_summary.js @@ -54,5 +54,12 @@ frappe.query_reports["Work Order Summary"] = { fieldtype: "Int", default: "0" }, + { + label: __("Charts Based On"), + fieldname:"charts_based_on", + fieldtype: "Select", + options: ["Status", "Age", "Quantity"], + default: "Status" + }, ] }; diff --git a/erpnext/manufacturing/report/work_order_summary/work_order_summary.py b/erpnext/manufacturing/report/work_order_summary/work_order_summary.py index 92c31ab382e..bc09ed43354 100644 --- a/erpnext/manufacturing/report/work_order_summary/work_order_summary.py +++ b/erpnext/manufacturing/report/work_order_summary/work_order_summary.py @@ -3,8 +3,9 @@ from __future__ import unicode_literals import frappe -from frappe.utils import date_diff, today +from frappe.utils import date_diff, today, getdate, flt from frappe import _ +from erpnext.stock.report.stock_analytics.stock_analytics import (get_period_date_ranges, get_period) def execute(filters=None): columns, data = [], [] @@ -46,7 +47,15 @@ def get_data(filters): return res -def get_chart_data(periodic_data, columns): +def get_chart_data(data, filters): + if filters.get("charts_based_on") == "Status": + return get_chart_based_on_status(data) + elif filters.get("charts_based_on") == "Age": + return get_chart_based_on_age(data) + else: + return get_chart_based_on_qty(data, filters) + +def get_chart_based_on_status(data): labels = ["Not Started", "In Process", "Stopped", "Completed"] status_wise_data = { @@ -56,7 +65,7 @@ def get_chart_data(periodic_data, columns): "Completed": 0 } - for d in periodic_data: + for d in data: if d.status == "In Process" and d.produced_qty: status_wise_data["Completed"] += d.produced_qty @@ -71,12 +80,100 @@ def get_chart_data(periodic_data, columns): 'datasets': [{'name':'Qty Wise Chart', 'values': values}] }, "type": "donut", - "height": 300, - "colors": ["#ff5858", "#ffa00a", "#5e64ff", "#98d85b"] + "height": 300 } return chart +def get_chart_based_on_age(data): + labels = ["0-30 Days", "30-60 Days", "60-90 Days", "90 Above"] + + age_wise_data = { + "0-30 Days": 0, + "30-60 Days": 0, + "60-90 Days": 0, + "90 Above": 0 + } + + for d in data: + if d.age > 0 and d.age <= 30: + age_wise_data["0-30 Days"] += 1 + elif d.age > 30 and d.age <= 60: + age_wise_data["30-60 Days"] += 1 + elif d.age > 60 and d.age <= 90: + age_wise_data["60-90 Days"] += 1 + else: + age_wise_data["90 Above"] += 1 + + values = [age_wise_data["0-30 Days"], age_wise_data["30-60 Days"], + age_wise_data["60-90 Days"], age_wise_data["90 Above"]] + + chart = { + "data": { + 'labels': labels, + 'datasets': [{'name':'Qty Wise Chart', 'values': values}] + }, + "type": "donut", + "height": 300 + } + + return chart + +def get_chart_based_on_qty(data, filters): + labels, periodic_data = prepare_chart_data(data, filters) + + pending, completed = [], [] + datasets = [] + + for d in labels: + pending.append(periodic_data.get("Pending").get(d)) + completed.append(periodic_data.get("Completed").get(d)) + + datasets.append({"name": "Pending", "values": pending}) + datasets.append({"name": "Completed", "values": completed}) + + chart = { + "data": { + 'labels': labels, + 'datasets': datasets + }, + "type": "bar", + "barOptions": { + "stacked": 1 + } + } + + return chart + +def prepare_chart_data(data, filters): + labels = [] + + periodic_data = { + "Pending": {}, + "Completed": {} + } + + filters.range = "Monthly" + + ranges = get_period_date_ranges(filters) + for from_date, end_date in ranges: + period = get_period(end_date, filters) + if period not in labels: + labels.append(period) + + if period not in periodic_data["Pending"]: + periodic_data["Pending"][period] = 0 + + if period not in periodic_data["Completed"]: + periodic_data["Completed"][period] = 0 + + for d in data: + if getdate(d.planned_start_date) >= from_date and getdate(d.planned_start_date) <= end_date: + periodic_data["Pending"][period] += (flt(d.qty) - flt(d.produced_qty)) + periodic_data["Completed"][period] += flt(d.produced_qty) + + return labels, periodic_data + def get_columns(filters): columns = [ { diff --git a/erpnext/patches/v13_0/update_actual_start_and_end_date_in_wo.py b/erpnext/patches/v13_0/update_actual_start_and_end_date_in_wo.py index 3fab0040fb1..331c5590e58 100644 --- a/erpnext/patches/v13_0/update_actual_start_and_end_date_in_wo.py +++ b/erpnext/patches/v13_0/update_actual_start_and_end_date_in_wo.py @@ -6,6 +6,7 @@ from __future__ import unicode_literals import frappe from frappe.utils import add_to_date +from frappe.utils.dashboard import get_config, make_records def execute(): frappe.reload_doc("manufacturing", "doctype", "work_order") @@ -33,11 +34,9 @@ def execute(): planned_end_date = add_to_date(doc.planned_start_date, minutes=doc.lead_time) doc.db_set("planned_end_date", doc.actual_start_date, update_modified=False) - frappe.db.sql(""" UPDATE `tabJob Card` as jc, `tabWork Order` as wo SET jc.production_item = wo.production_item, jc.item_name = wo.item_name WHERE jc.work_order = wo.name and IFNULL(jc.production_item, "") = "" - """) - + """) \ No newline at end of file diff --git a/erpnext/setup/setup_wizard/data/dashboard_charts.py b/erpnext/setup/setup_wizard/data/dashboard_charts.py deleted file mode 100644 index f5b66817672..00000000000 --- a/erpnext/setup/setup_wizard/data/dashboard_charts.py +++ /dev/null @@ -1,247 +0,0 @@ -from __future__ import unicode_literals -from frappe import _ -import frappe -import json - -def get_company_for_dashboards(): - company = frappe.defaults.get_defaults().company - if company: - return company - else: - company_list = frappe.get_list("Company") - if company_list: - return company_list[0].name - return None - -def get_default_dashboards(): - company = frappe.get_doc("Company", get_company_for_dashboards()) - income_account = company.default_income_account or get_account("Income Account", company.name) - expense_account = company.default_expense_account or get_account("Expense Account", company.name) - bank_account = company.default_bank_account or get_account("Bank", company.name) - - return { - "Dashboards": [ - { - "doctype": "Dashboard", - "dashboard_name": "Accounts", - "charts": [ - { "chart": "Outgoing Bills (Sales Invoice)" }, - { "chart": "Incoming Bills (Purchase Invoice)" }, - { "chart": "Bank Balance" }, - { "chart": "Income" }, - { "chart": "Expenses" }, - { "chart": "Patient Appointments" } - ] - }, - { - "doctype": "Dashboard", - "dashboard_name": "Manufacturing", - "charts": [ - { "chart": "Work Order Analysis", "width": "Half" }, - { "chart": "Quality Inspection Analysis", "width": "Half" }, - { "chart": "Long Time Pending Work Orders", "width": "Half" }, - { "chart": "Downtime Analysis", "width": "Half" }, - { "chart": "Production Analysis", "width": "Full" }, - { "chart": "Job Card Analysis", "width": "Full" } - ] - } - ], - "Charts": [ - { - "doctype": "Dashboard Chart", - "time_interval": "Quarterly", - "chart_name": "Income", - "timespan": "Last Year", - "color": None, - "filters_json": json.dumps({"company": company.name, "account": income_account}), - "source": "Account Balance Timeline", - "chart_type": "Custom", - "timeseries": 1, - "owner": "Administrator", - "type": "Line", - "width": "Half" - }, - { - "doctype": "Dashboard Chart", - "time_interval": "Quarterly", - "chart_name": "Expenses", - "timespan": "Last Year", - "color": None, - "filters_json": json.dumps({"company": company.name, "account": expense_account}), - "source": "Account Balance Timeline", - "chart_type": "Custom", - "timeseries": 1, - "owner": "Administrator", - "type": "Line", - "width": "Half" - }, - { - "doctype": "Dashboard Chart", - "time_interval": "Quarterly", - "chart_name": "Bank Balance", - "timespan": "Last Year", - "color": "#ffb868", - "filters_json": json.dumps({"company": company.name, "account": bank_account}), - "source": "Account Balance Timeline", - "chart_type": "Custom", - "timeseries": 1, - "owner": "Administrator", - "type": "Line", - "width": "Half" - }, - { - "doctype": "Dashboard Chart", - "time_interval": "Monthly", - "chart_name": "Incoming Bills (Purchase Invoice)", - "timespan": "Last Year", - "color": "#a83333", - "value_based_on": "base_grand_total", - "filters_json": json.dumps({}), - "chart_type": "Sum", - "timeseries": 1, - "based_on": "posting_date", - "owner": "Administrator", - "document_type": "Purchase Invoice", - "type": "Bar", - "width": "Half" - }, - { - "doctype": "Dashboard Chart", - "time_interval": "Monthly", - "chart_name": "Outgoing Bills (Sales Invoice)", - "timespan": "Last Year", - "color": "#7b933d", - "value_based_on": "base_grand_total", - "filters_json": json.dumps({}), - "chart_type": "Sum", - "timeseries": 1, - "based_on": "posting_date", - "owner": "Administrator", - "document_type": "Sales Invoice", - "type": "Bar", - "width": "Half" - }, - { - "doctype": "Dashboard Chart", - "time_interval": "Daily", - "chart_name": "Patient Appointments", - "timespan": "Last Month", - "color": "#77ecca", - "filters_json": json.dumps({}), - "chart_type": "Count", - "timeseries": 1, - "based_on": "appointment_datetime", - "owner": "Administrator", - "document_type": "Patient Appointment", - "type": "Line", - "width": "Half" - } - { - "doctype": "Dashboard Chart", - "time_interval": "Yearly", - "chart_type": "Report", - "chart_name": "Work Order Analysis", - "timespan": "Last Year", - "report_name": "Work Order Summary", - "owner": "Administrator", - "filters_json": json.dumps({"company": company.name}), - "bar": "Donut", - "custom_options": json.dumps({ - "axisOptions": { - "shortenYAxisNumbers": 1 - }, - "height": 300, - "colors": ["#ff5858", "#ffa00a", "#5e64ff", "#98d85b"] - }), - }, - { - "doctype": "Dashboard Chart", - "time_interval": "Yearly", - "chart_type": "Report", - "chart_name": "Quality Inspection Analysis", - "timespan": "Last Year", - "report_name": "Quality Inspection Summary", - "owner": "Administrator", - "filters_json": json.dumps({}), - "bar": "Donut", - "custom_options": json.dumps({ - "axisOptions": { - "shortenYAxisNumbers": 1 - }, - "height": 300, - "colors": ["#ff5858", "#98d85b"] - }), - }, - { - "doctype": "Dashboard Chart", - "time_interval": "Yearly", - "chart_type": "Report", - "chart_name": "Long Time Pending Work Orders", - "timespan": "Last Year", - "report_name": "Work Order Summary", - "filters_json": json.dumps({"company": company.name, "age":180}), - "owner": "Administrator", - "bar": "Bar", - "custom_options": json.dumps({ - "colors": ["#ff5858"], - "x_field": "name", - "y_fields": ["age"], - "y_axis_fields": [{"__islocal": "true", "idx": 1, "y_field": "age"}], - "chart_type": "Bar" - }), - }, - { - "doctype": "Dashboard Chart", - "time_interval": "Yearly", - "chart_type": "Report", - "chart_name": "Downtime Analysis", - "timespan": "Last Year", - "filters_json": json.dumps({}), - "report_name": "Downtime Analysis", - "owner": "Administrator", - "bar": "Bar", - "custom_options": json.dumps({ - "colors": ["#ff5858"] - }), - }, - { - "doctype": "Dashboard Chart", - "time_interval": "Yearly", - "chart_type": "Report", - "chart_name": "Production Analysis", - "timespan": "Last Year", - "report_name": "Production Analytics", - "owner": "Administrator", - "filters_json": json.dumps({"company": company.name, "range":"Monthly"}), - "bar": "Bar", - "custom_options": json.dumps({ - "colors": ["#7cd6fd", "#ff5858", "#ffa00a", "#98d85b"] - }), - }, - { - "doctype": "Dashboard Chart", - "time_interval": "Yearly", - "chart_type": "Report", - "chart_name": "Job Card Analysis", - "timespan": "Last Year", - "report_name": "Job Card Summary", - "owner": "Administrator", - "filters_json": json.dumps({"company": company.name, "range":"Monthly"}), - "bar": "Bar", - "custom_options": json.dumps({ - "axisOptions": { - "xAxisMode": "tick" - }, - "barOptions": { - "stacked": 1 - }, - "colors": ["#ff5858", "#ffa00a", "#5e64ff", "#98d85b"] - }), - }, - ] - } - -def get_account(account_type, company): - accounts = frappe.get_list("Account", filters={"account_type": account_type, "company": company}) - if accounts: - return accounts[0].name From 1c9210b303df927e0b214c17d9fe1423176e682a Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 14 May 2020 18:58:25 +0530 Subject: [PATCH 082/608] made changes in the dashboards, renamed number cards --- erpnext/manufacturing/dashboard_fixtures.py | 8 +++ .../manufacturing/manufacturing.json | 8 ++- .../manufacturing/manufacturing.json | 54 +++++++++++++++++++ .../create_bom/create_bom.json | 7 ++- .../create_product/create_product.json | 7 ++- .../introduction_to_manufacturing.json | 5 +- .../onboarding_step/operation/operation.json | 8 ++- .../onboarding_step/warehouse/warehouse.json | 8 ++- .../work_order/work_order.json | 8 ++- .../workstation/workstation.json | 8 ++- 10 files changed, 106 insertions(+), 15 deletions(-) create mode 100644 erpnext/manufacturing/module_onboarding/manufacturing/manufacturing.json diff --git a/erpnext/manufacturing/dashboard_fixtures.py b/erpnext/manufacturing/dashboard_fixtures.py index de3775f52c6..3029f488dcb 100644 --- a/erpnext/manufacturing/dashboard_fixtures.py +++ b/erpnext/manufacturing/dashboard_fixtures.py @@ -45,6 +45,7 @@ def get_charts(): "time_interval": "Yearly", "chart_type": "Sum", "chart_name": "Produced Quantity", + "name": "Produced Quantity", "document_type": "Work Order", "filters_json": json.dumps({}), "group_by_type": "Count", @@ -61,6 +62,7 @@ def get_charts(): "time_interval": "Yearly", "chart_type": "Sum", "chart_name": "Completed Operation", + "name": "Completed Operation", "document_type": "Work Order Operation", "filters_json": json.dumps({}), "group_by_type": "Count", @@ -76,6 +78,7 @@ def get_charts(): "time_interval": "Yearly", "chart_type": "Report", "chart_name": "Work Order Analysis", + "name": "Work Order Analysis", "timespan": "Last Year", "report_name": "Work Order Summary", "owner": "Administrator", @@ -94,6 +97,7 @@ def get_charts(): "time_interval": "Yearly", "chart_type": "Report", "chart_name": "Quality Inspection Analysis", + "name": "Quality Inspection Analysis", "timespan": "Last Year", "report_name": "Quality Inspection Summary", "owner": "Administrator", @@ -112,6 +116,7 @@ def get_charts(): "time_interval": "Yearly", "chart_type": "Report", "chart_name": "Pending Work Order", + "name": "Pending Work Order", "timespan": "Last Year", "report_name": "Work Order Summary", "filters_json": json.dumps({"company": company, "charts_based_on": "Age"}), @@ -130,6 +135,7 @@ def get_charts(): "time_interval": "Yearly", "chart_type": "Report", "chart_name": "Last Month Downtime Analysis", + "name": "Last Month Downtime Analysis", "timespan": "Last Year", "filters_json": json.dumps({}), "report_name": "Downtime Analysis", @@ -142,6 +148,7 @@ def get_charts(): "time_interval": "Yearly", "chart_type": "Report", "chart_name": _("Work Order Qty Analysis"), + "name": _("Work Order Qty Analysis"), "timespan": "Last Year", "report_name": "Work Order Summary", "filters_json": json.dumps({"company": company, "charts_based_on": "Quantity"}), @@ -157,6 +164,7 @@ def get_charts(): "time_interval": "Yearly", "chart_type": "Report", "chart_name": "Job Card Analysis", + "name": "Job Card Analysis", "timespan": "Last Year", "report_name": "Job Card Summary", "owner": "Administrator", diff --git a/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json b/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json index f6bbf3db85c..6288169ee2b 100644 --- a/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json +++ b/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json @@ -32,7 +32,11 @@ } ], "category": "Domains", - "charts": [], + "charts": [ + { + "chart_name": "Produced Quantity" + } + ], "creation": "2020-03-02 17:11:37.032604", "developer_mode_only": 0, "disable_user_customization": 0, @@ -42,7 +46,7 @@ "idx": 0, "is_standard": 1, "label": "Manufacturing", - "modified": "2020-05-14 15:06:45.963942", + "modified": "2020-05-14 18:57:26.810652", "modified_by": "Administrator", "module": "Manufacturing", "name": "Manufacturing", diff --git a/erpnext/manufacturing/module_onboarding/manufacturing/manufacturing.json b/erpnext/manufacturing/module_onboarding/manufacturing/manufacturing.json new file mode 100644 index 00000000000..4092e9d87cb --- /dev/null +++ b/erpnext/manufacturing/module_onboarding/manufacturing/manufacturing.json @@ -0,0 +1,54 @@ +{ + "allow_roles": [ + { + "role": "Manufacturing User" + }, + { + "role": "Manufacturing Manager" + }, + { + "role": "Item Manager" + }, + { + "role": "Stock User" + } + ], + "creation": "2020-05-05 16:37:08.238935", + "docstatus": 0, + "doctype": "Module Onboarding", + "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/manufacturing", + "idx": 0, + "is_complete": 0, + "modified": "2020-05-14 19:12:17.289867", + "modified_by": "Administrator", + "module": "Manufacturing", + "name": "Manufacturing", + "owner": "Administrator", + "steps": [ + { + "step": "Warehouse" + }, + { + "step": "Workstation" + }, + { + "step": "Operation" + }, + { + "step": "Create Product" + }, + { + "step": "Create BOM" + }, + { + "step": "Work Order" + }, + { + "step": "Introduction to Manufacturing" + } + ], + "subtitle": "Products, Raw Materials, BOM, Work Order and more.", + "success_message": "Manufacturing module is all setup!", + "title": "Let's Setup Manufacturing Module", + "user_can_dismiss": 1 +} \ No newline at end of file diff --git a/erpnext/manufacturing/onboarding_step/create_bom/create_bom.json b/erpnext/manufacturing/onboarding_step/create_bom/create_bom.json index 969c2fa5fe4..866b871f2b7 100644 --- a/erpnext/manufacturing/onboarding_step/create_bom/create_bom.json +++ b/erpnext/manufacturing/onboarding_step/create_bom/create_bom.json @@ -6,11 +6,14 @@ "idx": 0, "is_complete": 0, "is_mandatory": 1, + "is_single": 0, "is_skipped": 0, - "modified": "2020-05-05 16:41:20.239696", + "modified": "2020-05-14 19:11:57.153679", "modified_by": "Administrator", "name": "Create BOM", "owner": "Administrator", "reference_document": "BOM", - "title": "Create BOM" + "show_full_form": 1, + "title": "Create BOM (Bill of Material)", + "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/manufacturing/onboarding_step/create_product/create_product.json b/erpnext/manufacturing/onboarding_step/create_product/create_product.json index 082f31ed6e7..dd2fcb4ce7f 100644 --- a/erpnext/manufacturing/onboarding_step/create_product/create_product.json +++ b/erpnext/manufacturing/onboarding_step/create_product/create_product.json @@ -6,11 +6,14 @@ "idx": 0, "is_complete": 0, "is_mandatory": 1, + "is_single": 0, "is_skipped": 0, - "modified": "2020-05-05 16:42:31.476275", + "modified": "2020-05-14 19:10:39.974370", "modified_by": "Administrator", "name": "Create Product", "owner": "Administrator", "reference_document": "Item", - "title": "Create Product" + "show_full_form": 0, + "title": "Create Product (Raw Materials / Finished Good)", + "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/manufacturing/onboarding_step/introduction_to_manufacturing/introduction_to_manufacturing.json b/erpnext/manufacturing/onboarding_step/introduction_to_manufacturing/introduction_to_manufacturing.json index 6e5c3a9f45c..eb7ab3a175c 100644 --- a/erpnext/manufacturing/onboarding_step/introduction_to_manufacturing/introduction_to_manufacturing.json +++ b/erpnext/manufacturing/onboarding_step/introduction_to_manufacturing/introduction_to_manufacturing.json @@ -6,12 +6,15 @@ "idx": 0, "is_complete": 0, "is_mandatory": 0, + "is_single": 0, "is_skipped": 0, - "modified": "2020-05-05 16:40:23.676406", + "modified": "2020-05-14 19:11:57.152883", "modified_by": "Administrator", "name": "Introduction to Manufacturing", "owner": "Administrator", "reference_document": "Manufacturing Settings", + "show_full_form": 0, "title": "Manufacturing Settings", + "validate_action": 1, "video_url": "https://www.youtube.com/watch?v=UVGfzwOOZC4" } \ No newline at end of file diff --git a/erpnext/manufacturing/onboarding_step/operation/operation.json b/erpnext/manufacturing/onboarding_step/operation/operation.json index 6f84db13ec8..86e62e74111 100644 --- a/erpnext/manufacturing/onboarding_step/operation/operation.json +++ b/erpnext/manufacturing/onboarding_step/operation/operation.json @@ -6,10 +6,14 @@ "idx": 0, "is_complete": 0, "is_mandatory": 0, + "is_single": 0, "is_skipped": 0, - "modified": "2020-05-12 16:17:06.943067", + "modified": "2020-05-14 19:10:27.258157", "modified_by": "Administrator", "name": "Operation", "owner": "Administrator", - "title": "Create Operation" + "reference_document": "Operation", + "show_full_form": 0, + "title": "Create Operation", + "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/manufacturing/onboarding_step/warehouse/warehouse.json b/erpnext/manufacturing/onboarding_step/warehouse/warehouse.json index 36f5204e0b6..f187f57414d 100644 --- a/erpnext/manufacturing/onboarding_step/warehouse/warehouse.json +++ b/erpnext/manufacturing/onboarding_step/warehouse/warehouse.json @@ -6,10 +6,14 @@ "idx": 0, "is_complete": 0, "is_mandatory": 0, + "is_single": 0, "is_skipped": 0, - "modified": "2020-05-12 16:16:48.345846", + "modified": "2020-05-14 19:10:07.439037", "modified_by": "Administrator", "name": "Warehouse", "owner": "Administrator", - "title": "Create Warehouse" + "reference_document": "Warehouse", + "show_full_form": 0, + "title": "Create Warehouse", + "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/manufacturing/onboarding_step/work_order/work_order.json b/erpnext/manufacturing/onboarding_step/work_order/work_order.json index 57e4e7b3bc7..e757c23d122 100644 --- a/erpnext/manufacturing/onboarding_step/work_order/work_order.json +++ b/erpnext/manufacturing/onboarding_step/work_order/work_order.json @@ -6,10 +6,14 @@ "idx": 0, "is_complete": 0, "is_mandatory": 0, + "is_single": 0, "is_skipped": 0, - "modified": "2020-05-12 16:17:33.675304", + "modified": "2020-05-14 19:11:57.145924", "modified_by": "Administrator", "name": "Work Order", "owner": "Administrator", - "title": "Create Work Order" + "reference_document": "Work Order", + "show_full_form": 1, + "title": "Create Work Order", + "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/manufacturing/onboarding_step/workstation/workstation.json b/erpnext/manufacturing/onboarding_step/workstation/workstation.json index 6c1a06938b4..b786443cbc7 100644 --- a/erpnext/manufacturing/onboarding_step/workstation/workstation.json +++ b/erpnext/manufacturing/onboarding_step/workstation/workstation.json @@ -6,10 +6,14 @@ "idx": 0, "is_complete": 0, "is_mandatory": 0, + "is_single": 0, "is_skipped": 0, - "modified": "2020-05-12 16:16:58.808906", + "modified": "2020-05-14 19:10:20.930139", "modified_by": "Administrator", "name": "Workstation", "owner": "Administrator", - "title": "Create Workstation" + "reference_document": "Workstation", + "show_full_form": 0, + "title": "Create Workstation / Machine", + "validate_action": 1 } \ No newline at end of file From 1ac2512c7302096a23f7a7e8a6aaf4fbf6fbd4c1 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 18 May 2020 12:55:05 +0530 Subject: [PATCH 083/608] refactor: desk page colors and filters --- erpnext/healthcare/dashboard_fixtures.py | 1 - erpnext/healthcare/desk_page/healthcare/healthcare.json | 9 ++++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/erpnext/healthcare/dashboard_fixtures.py b/erpnext/healthcare/dashboard_fixtures.py index fc3d62f2d84..8b86ac39ace 100644 --- a/erpnext/healthcare/dashboard_fixtures.py +++ b/erpnext/healthcare/dashboard_fixtures.py @@ -28,7 +28,6 @@ def get_charts(): "name": "Patient Appointments", "chart_name": "Patient Appointments", "timespan": "Last Month", - "color": "#77ecca", "filters_json": json.dumps({}), "chart_type": "Count", "timeseries": 1, diff --git a/erpnext/healthcare/desk_page/healthcare/healthcare.json b/erpnext/healthcare/desk_page/healthcare/healthcare.json index 5cf09b34b24..4559606a24c 100644 --- a/erpnext/healthcare/desk_page/healthcare/healthcare.json +++ b/erpnext/healthcare/desk_page/healthcare/healthcare.json @@ -63,7 +63,7 @@ "idx": 0, "is_standard": 1, "label": "Healthcare", - "modified": "2020-04-25 22:31:36.576444", + "modified": "2020-05-18 12:41:04.288871", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare", @@ -73,13 +73,15 @@ "restrict_to_domain": "Healthcare", "shortcuts": [ { + "color": "#ffe8cd", "format": "{} Open", "label": "Patient Appointment", "link_to": "Patient Appointment", - "stats_filter": "{\n \"status\": \"Open\"\n}", + "stats_filter": "{\n \"status\": \"Open\",\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%']\n}", "type": "DocType" }, { + "color": "#ffe8cd", "format": "{} Active", "label": "Patient", "link_to": "Patient", @@ -87,10 +89,11 @@ "type": "DocType" }, { + "color": "#cef6d1", "format": "{} Vacant", "label": "Healthcare Service Unit", "link_to": "Healthcare Service Unit", - "stats_filter": "{\n \"occupancy_status\": \"Vacant\",\n \"is_group\": 0\n}", + "stats_filter": "{\n \"occupancy_status\": \"Vacant\",\n \"is_group\": 0,\n \"company\": [\"like\", \"%\" + frappe.defaults.get_global_default(\"company\") + \"%\"]\n}", "type": "DocType" }, { From 57bfee8ea923455bc591da169a1c6a6df003169a Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Wed, 13 May 2020 21:47:52 +0530 Subject: [PATCH 084/608] fix: dashboard changes --- erpnext/hr/dashboard_fixtures.py | 94 ++++++++++++++----- erpnext/hr/desk_page/hr/hr.json | 41 ++++---- .../leave_application/leave_application.json | 11 ++- .../human_resource/human_resource.json | 48 ++++++++++ .../create_department/create_department.json | 16 ++++ .../create_designation.json | 16 ++++ .../create_employee/create_employee.json | 16 ++++ .../create_holiday_list.json | 16 ++++ .../create_leave_allocation.json | 16 ++++ .../create_leave_application.json | 16 ++++ .../hr_settings/hr_settings.json | 16 ++++ .../employee_analytics/employee_analytics.py | 2 +- .../monthly_attendance_sheet.py | 16 ++-- 13 files changed, 265 insertions(+), 59 deletions(-) create mode 100644 erpnext/hr/onboarding/human_resource/human_resource.json create mode 100644 erpnext/hr/onboarding_step/create_department/create_department.json create mode 100644 erpnext/hr/onboarding_step/create_designation/create_designation.json create mode 100644 erpnext/hr/onboarding_step/create_employee/create_employee.json create mode 100644 erpnext/hr/onboarding_step/create_holiday_list/create_holiday_list.json create mode 100644 erpnext/hr/onboarding_step/create_leave_allocation/create_leave_allocation.json create mode 100644 erpnext/hr/onboarding_step/create_leave_application/create_leave_application.json create mode 100644 erpnext/hr/onboarding_step/hr_settings/hr_settings.json diff --git a/erpnext/hr/dashboard_fixtures.py b/erpnext/hr/dashboard_fixtures.py index dafacaada41..c608e2bb298 100644 --- a/erpnext/hr/dashboard_fixtures.py +++ b/erpnext/hr/dashboard_fixtures.py @@ -24,33 +24,36 @@ def get_human_resource_dashboard(): "is_default": 1, "charts": [ { "chart": "Gender Diversity Ratio", "width": "Half"}, - { "chart": "Employee Count", "width": "Half"}, + { "chart": "Job Application Status", "width": "Half"}, + { "chart": 'Designation Wise Employee Count', "width": "Half"}, + { "chart": 'Department Wise Employee Count', "width": "Half"}, + { "chart": 'Designation Wise Openings', "width": "Half"}, + { "chart": 'Department Wise Openings', "width": "Half"}, { "chart": "Outgoing Salary", "width": "Full"}, { "chart": "Attendance Count", "width": "Full"} ], "cards": [ {"card": "Total Employees"}, {"card": "New Joinees"}, - {'card': "Job Applicants"}, - {'card': "Employees Left"} + {'card': "Employees Left"}, + {'card': "Total Job Openings"}, + {'card': "Total Applicants"}, + {'card': "Short Listed Candidates"}, + {'card': "Rejected Candidates"}, + {'card': "Total Job Offered"}, ] } def get_recruitment_dashboard(): pass - # return { - # "name": "Human Resource", - # "dashboard_name": "Human Resource", - # "is_default": 1, - # "charts": [ - # ], - # "cards": [ - # ] - # } def get_charts(): company = erpnext.get_default_company() + date = frappe.utils.get_datetime() + + month_map = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov","Dec"] + if not company: company = frappe.db.get_value("Company", {"is_group": 0}, "name") @@ -60,24 +63,34 @@ def get_charts(): ] dashboard_charts.append( - get_dashboards_chart_doc('Outgoing salary', "Sum", "Line",document_type = "Salary Slip", group_by_type="Count", based_on="end_date", value_based_on = "rounded_total", time_interval = "Monthly", timeseries = 1 , filters_json = json.dumps([["Salary Slip","docstatus","=","1"]])) + get_dashboards_chart_doc('Job Application Status', "Group By", "Donut",document_type = "Job Applicant", group_by_type="Count", group_by_based_on="status", filters_json = json.dumps([["Job Applicant","creation","Previous","1 month"]]), time_interval = "Monthly") ) - custom_options = '''{"type": "bar", "axisOptions": {"shortenYAxisNumbers": 1}, "tooltipOptions": {}, "barOptions":{"stacked": 1}}''' - filters_json = json.dumps({"month":"May","year":"2020","company":company}) - dashboard_charts.append( - get_dashboards_chart_doc('Attendance Count', "Report", "Bar",report_name = "Monthly Attendance Sheet", is_custom =1,group_by_type="Count", timeseries = 1 , filters_json = filters_json, custom_options=custom_options) + get_dashboards_chart_doc('Outgoing Salary', "Sum", "Line",document_type = "Salary Slip", group_by_type="Count", based_on="end_date", value_based_on = "rounded_total", time_interval = "Monthly", timeseries = 1 , filters_json = json.dumps([["Salary Slip","docstatus","=","1"]])) ) - - custom_options = """{"type": "donut", "axisOptions": {"shortenYAxisNumbers": 1}}""" - filters_json = json.dumps({"company":company ,"parameter":"Department"}) + custom_options = '''{"type": "line", "lineOptions": {"dotSize": 6}, "axisOptions": {"shortenYAxisNumbers": 1}, "tooltipOptions": {}}''' + filters_json = json.dumps({"month": month_map[date.month - 1], "year": str(date.year), "company":company}) dashboard_charts.append( - get_dashboards_chart_doc('Employee Count', "Report", "Donut",report_name = "Employee Analytics", is_custom =1, group_by_type="Count", timeseries = 1 , filters_json = filters_json, custom_options=custom_options) + get_dashboards_chart_doc('Attendance Count', "Report", "Line",report_name = "Monthly Attendance Sheet", is_custom =1,group_by_type="Count", filters_json = filters_json, custom_options=custom_options) ) + dashboard_charts.append( + get_dashboards_chart_doc('Department Wise Employee Count', "Group By", "Donut",document_type = "Employee", group_by_type="Count", group_by_based_on="department", filters_json = json.dumps([["Employee","status","=","Active"]]), time_interval = "Monthly") + ) + dashboard_charts.append( + get_dashboards_chart_doc('Designation Wise Employee Count', "Group By", "Donut",document_type = "Employee", group_by_type="Count", group_by_based_on="designation", filters_json = json.dumps([["Employee","status","=","Active"]]), time_interval = "Monthly") + ) + + dashboard_charts.append( + get_dashboards_chart_doc('Designation Wise Openings', "Group By", "Bar",document_type = "Job Opening", group_by_type="Sum", group_by_based_on="designation", time_interval = "Monthly", aggregate_function_based_on = "planned_vacancies") + ) + dashboard_charts.append( + get_dashboards_chart_doc('Department Wise Openings', "Group By", "Bar",document_type = "Job Opening", group_by_type="Sum", group_by_based_on="department", time_interval = "Monthly", aggregate_function_based_on = "planned_vacancies") + ) + return dashboard_charts def get_number_cards(): @@ -89,26 +102,53 @@ def get_number_cards(): ]) ) ] + number_cards.append( get_number_cards_doc("Employee", "New Joinees", filters_json = json.dumps([ - ["Employee","date_of_joining","Previous","6 months"], + ["Employee","date_of_joining","Previous","1 year"], ["Employee","status","=","Active"] - ]), - stats_time_interval = "Daily") + ]) ) + ) + number_cards.append( get_number_cards_doc("Employee", "Employees Left", filters_json = json.dumps([ + ["Employee","modified","Previous","1 year"], ["Employee","status","=","Left"] ]) ) ) + number_cards.append( - get_number_cards_doc("Job Applicant", "Job Applicants", filters_json = json.dumps([ - ["Job Applicant","status","!=","Rejected"] + get_number_cards_doc("Job Applicant", "Total Applicants", filters_json = json.dumps([ + ["Job Applicant","creation","Previous","1 month"] ]) ) ) + number_cards.append( + get_number_cards_doc("Job Opening", "Total Job Openings", func = "Sum",aggregate_function_based_on = "planned_vacancies", filters_json = json.dumps([["Job Opening","creation","Previous","1 month"]]) + ) + ) + number_cards.append( + get_number_cards_doc("Job Applicant", "Short Listed Candidates", filters_json = json.dumps([ + ["Job Applicant","status","=","Accepted"], + ["Job Applicant","creation","Previous","1 month"] + ]) + ) + ) + number_cards.append( + get_number_cards_doc("Job Applicant", "Rejected Candidates", filters_json = json.dumps([ + ["Job Applicant","status","=","Rejected"], + ["Job Applicant","creation","Previous","1 month"] + ]) + ) + ) + number_cards.append( + get_number_cards_doc("Job Offer", "Total Job Offered", filters_json = json.dumps([["Job Offer","creation","Previous","1 month"]]) + ) + ) + return number_cards @@ -125,6 +165,7 @@ def get_number_cards_doc(document_type, label, **args): "show_percentage_stats": args.show_percentage_stats or 1, "stats_time_interval": args.stats_time_interval or 'Monthly', "filters_json": args.filters_json or '[]', + "aggregate_function_based_on": args.aggregate_function_based_on or None } def get_dashboards_chart_doc(name, chart_type, graph_type, **args): @@ -151,4 +192,5 @@ def get_dashboards_chart_doc(name, chart_type, graph_type, **args): "type": graph_type, "custom_options": args.custom_options or '', "doctype": "Dashboard Chart", + "aggregate_function_based_on": args.aggregate_function_based_on or None } \ No newline at end of file diff --git a/erpnext/hr/desk_page/hr/hr.json b/erpnext/hr/desk_page/hr/hr.json index 22aa170744c..570596eb5b9 100644 --- a/erpnext/hr/desk_page/hr/hr.json +++ b/erpnext/hr/desk_page/hr/hr.json @@ -77,21 +77,26 @@ } ], "category": "Modules", - "charts": [], + "charts": [ + { + "chart_name": "Outgoing Salary", + "label": "Outgoing Salary" + } + ], "creation": "2020-03-02 15:48:58.322521", "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, - "icon": "", "idx": 0, "is_standard": 1, "label": "HR", - "modified": "2020-04-29 20:29:22.114309", + "modified": "2020-05-14 12:14:21.682026", "modified_by": "Administrator", "module": "HR", "name": "HR", + "onboarding": "Human Resource", "owner": "Administrator", "pin_to_bottom": 0, "pin_to_top": 0, @@ -104,10 +109,20 @@ "type": "DocType" }, { - "format": "{} Unpaid", - "label": "Expense Claim", - "link_to": "Expense Claim", - "stats_filter": "{\"approval_status\":\"Draft\"}", + "label": "Attendance", + "link_to": "Attendance", + "stats_filter": "", + "type": "DocType" + }, + { + "label": "Leave Application", + "link_to": "Leave Application", + "stats_filter": "{\"status\":\"Open\"}", + "type": "DocType" + }, + { + "label": "Salary Structure", + "link_to": "Payroll Entry", "type": "DocType" }, { @@ -117,19 +132,9 @@ "stats_filter": "{\n \"status\": \"Open\"\n}", "type": "DocType" }, - { - "label": "Salary Structure", - "link_to": "Salary Structure", - "type": "DocType" - }, - { - "label": "Leave Application", - "link_to": "Leave Application", - "type": "DocType" - }, { "label": "Salary Register", - "link_to": "Salary Register", + "link_to": "Monthly Attendance Sheet", "type": "Report" } ] diff --git a/erpnext/hr/doctype/leave_application/leave_application.json b/erpnext/hr/doctype/leave_application/leave_application.json index 74707a24b86..7f50ace7663 100644 --- a/erpnext/hr/doctype/leave_application/leave_application.json +++ b/erpnext/hr/doctype/leave_application/leave_application.json @@ -174,7 +174,8 @@ "label": "Status", "no_copy": 1, "options": "Open\nApproved\nRejected\nCancelled", - "permlevel": 1 + "permlevel": 1, + "reqd": 1 }, { "fieldname": "sb10", @@ -189,14 +190,14 @@ "reqd": 1 }, { + "fetch_from": "employee.company", "fieldname": "company", "fieldtype": "Link", "label": "Company", "options": "Company", "read_only": 1, "remember_last_selected_value": 1, - "reqd": 1, - "fetch_from": "employee.company" + "reqd": 1 }, { "allow_on_submit": 1, @@ -249,7 +250,7 @@ "is_submittable": 1, "links": [], "max_attachments": 3, - "modified": "2020-03-10 22:40:43.487721", + "modified": "2020-05-18 13:00:41.577327", "modified_by": "Administrator", "module": "HR", "name": "Leave Application", @@ -334,4 +335,4 @@ "sort_order": "DESC", "timeline_field": "employee", "title_field": "employee_name" -} +} \ No newline at end of file diff --git a/erpnext/hr/onboarding/human_resource/human_resource.json b/erpnext/hr/onboarding/human_resource/human_resource.json new file mode 100644 index 00000000000..45dd50d8bb0 --- /dev/null +++ b/erpnext/hr/onboarding/human_resource/human_resource.json @@ -0,0 +1,48 @@ +{ + "allow_roles": [ + { + "role": "HR Manager" + }, + { + "role": "HR User" + } + ], + "creation": "2020-05-14 11:51:45.050242", + "docstatus": 0, + "doctype": "Onboarding", + "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/human-resources", + "idx": 0, + "is_complete": 0, + "modified": "2020-05-14 14:44:09.069921", + "modified_by": "Administrator", + "module": "HR", + "name": "Human Resource", + "owner": "Administrator", + "steps": [ + { + "step": "Create Department" + }, + { + "step": "Create Designation" + }, + { + "step": "Create Holiday list" + }, + { + "step": "Create Employee" + }, + { + "step": "Create Leave Allocation" + }, + { + "step": "Create Leave Application" + }, + { + "step": "HR Settings" + } + ], + "subtitle": "Employee, Recruitment, Payroll and more.", + "success_message": "The HR Module is all set up!", + "title": "Let's Setup the Human Resource Module. ", + "user_can_dismiss": 0 +} \ No newline at end of file diff --git a/erpnext/hr/onboarding_step/create_department/create_department.json b/erpnext/hr/onboarding_step/create_department/create_department.json new file mode 100644 index 00000000000..c3e461550a4 --- /dev/null +++ b/erpnext/hr/onboarding_step/create_department/create_department.json @@ -0,0 +1,16 @@ +{ + "action": "Create Entry", + "creation": "2020-05-14 11:44:34.682115", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_skipped": 0, + "modified": "2020-05-14 12:22:26.448420", + "modified_by": "Administrator", + "name": "Create Department", + "owner": "Administrator", + "reference_document": "Department", + "title": "Create Department" +} \ No newline at end of file diff --git a/erpnext/hr/onboarding_step/create_designation/create_designation.json b/erpnext/hr/onboarding_step/create_designation/create_designation.json new file mode 100644 index 00000000000..b58c06c3a38 --- /dev/null +++ b/erpnext/hr/onboarding_step/create_designation/create_designation.json @@ -0,0 +1,16 @@ +{ + "action": "Create Entry", + "creation": "2020-05-14 11:45:07.514193", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_skipped": 0, + "modified": "2020-05-14 12:22:41.500795", + "modified_by": "Administrator", + "name": "Create Designation", + "owner": "Administrator", + "reference_document": "Designation", + "title": "Create Designation" +} \ No newline at end of file diff --git a/erpnext/hr/onboarding_step/create_employee/create_employee.json b/erpnext/hr/onboarding_step/create_employee/create_employee.json new file mode 100644 index 00000000000..b4988a5ce54 --- /dev/null +++ b/erpnext/hr/onboarding_step/create_employee/create_employee.json @@ -0,0 +1,16 @@ +{ + "action": "Create Entry", + "creation": "2020-05-14 11:43:25.561152", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 1, + "is_skipped": 0, + "modified": "2020-05-14 12:26:28.629074", + "modified_by": "Administrator", + "name": "Create Employee", + "owner": "Administrator", + "reference_document": "Employee", + "title": "Create Employee" +} \ No newline at end of file diff --git a/erpnext/hr/onboarding_step/create_holiday_list/create_holiday_list.json b/erpnext/hr/onboarding_step/create_holiday_list/create_holiday_list.json new file mode 100644 index 00000000000..0db67d67639 --- /dev/null +++ b/erpnext/hr/onboarding_step/create_holiday_list/create_holiday_list.json @@ -0,0 +1,16 @@ +{ + "action": "Create Entry", + "creation": "2020-05-14 11:47:34.700174", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 1, + "is_skipped": 0, + "modified": "2020-05-14 12:25:38.068582", + "modified_by": "Administrator", + "name": "Create Holiday list", + "owner": "Administrator", + "reference_document": "Holiday List", + "title": "Create Holiday list" +} \ No newline at end of file diff --git a/erpnext/hr/onboarding_step/create_leave_allocation/create_leave_allocation.json b/erpnext/hr/onboarding_step/create_leave_allocation/create_leave_allocation.json new file mode 100644 index 00000000000..287dd6c0a6f --- /dev/null +++ b/erpnext/hr/onboarding_step/create_leave_allocation/create_leave_allocation.json @@ -0,0 +1,16 @@ +{ + "action": "Create Entry", + "creation": "2020-05-14 11:48:56.123718", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 1, + "is_skipped": 0, + "modified": "2020-05-14 11:48:56.123718", + "modified_by": "Administrator", + "name": "Create Leave Allocation", + "owner": "Administrator", + "reference_document": "Leave Allocation", + "title": "Create Leave Allocation" +} \ No newline at end of file diff --git a/erpnext/hr/onboarding_step/create_leave_application/create_leave_application.json b/erpnext/hr/onboarding_step/create_leave_application/create_leave_application.json new file mode 100644 index 00000000000..7b70a7f5bd8 --- /dev/null +++ b/erpnext/hr/onboarding_step/create_leave_application/create_leave_application.json @@ -0,0 +1,16 @@ +{ + "action": "Create Entry", + "creation": "2020-05-14 11:49:45.400764", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 1, + "is_skipped": 0, + "modified": "2020-05-14 11:49:45.400764", + "modified_by": "Administrator", + "name": "Create Leave Application", + "owner": "Administrator", + "reference_document": "Leave Application", + "title": "Create Leave Application" +} \ No newline at end of file diff --git a/erpnext/hr/onboarding_step/hr_settings/hr_settings.json b/erpnext/hr/onboarding_step/hr_settings/hr_settings.json new file mode 100644 index 00000000000..1f4f1bfe002 --- /dev/null +++ b/erpnext/hr/onboarding_step/hr_settings/hr_settings.json @@ -0,0 +1,16 @@ +{ + "action": "Update Settings", + "creation": "2020-05-14 13:13:52.427711", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_skipped": 0, + "modified": "2020-05-14 13:13:52.427711", + "modified_by": "Administrator", + "name": "HR Settings", + "owner": "Administrator", + "reference_document": "HR Settings", + "title": "HR settings" +} \ No newline at end of file diff --git a/erpnext/hr/report/employee_analytics/employee_analytics.py b/erpnext/hr/report/employee_analytics/employee_analytics.py index 6f8f161c3db..df64006c1be 100644 --- a/erpnext/hr/report/employee_analytics/employee_analytics.py +++ b/erpnext/hr/report/employee_analytics/employee_analytics.py @@ -65,7 +65,7 @@ def get_chart_data(parameters,employees, filters): total_employee = frappe.db.count('Employee', {'status':'Active'}) others = total_employee - sum(values) - label.append(["Others"]) + label.append(["Not Set"]) values.append(others) chart = { diff --git a/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py b/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py index a6509766c7c..096e3c58101 100644 --- a/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py +++ b/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py @@ -30,6 +30,9 @@ day_abbr = [ def execute(filters=None): if not filters: filters = {} + if filters.hide_year_field == 1: + filters.year = 2020 + conditions, filters = get_conditions(filters) columns, days = get_columns(filters) att_map = get_attendance_list(conditions, filters) @@ -75,14 +78,11 @@ def execute(filters=None): return columns, data, None, chart_data def get_chart_data(emp_att_map, days): - from pprint import pprint - pprint(emp_att_map) labels = [] datasets = [ {"name": "Absent", "values": []}, {"name": "Present", "values": []}, {"name": "Leave", "values": []}, - {"name": "Unmarked", "values": []} ] for idx, day in enumerate(days, start=0): p = day.replace("::65", "") @@ -91,7 +91,6 @@ def get_chart_data(emp_att_map, days): total_leave_on_day = 0 total_present_on_day = 0 total_holiday = 0 - total_unmarked_on_day = 0 for emp in emp_att_map.keys(): if emp_att_map[emp][idx]: if emp_att_map[emp][idx] == "A": @@ -104,12 +103,10 @@ def get_chart_data(emp_att_map, days): if emp_att_map[emp][idx] == "L": total_leave_on_day += 1 - total_marked = total_absent_on_day + total_present_on_day + total_leave_on_day datasets[0]["values"].append(total_absent_on_day) datasets[1]["values"].append(total_present_on_day) datasets[2]["values"].append(total_leave_on_day) - datasets[3]["values"].append(frappe.db.count('Employee', {'status':'Active'}) - total_marked) chart = { @@ -119,10 +116,11 @@ def get_chart_data(emp_att_map, days): } } - chart["type"] = "bar" - # chart["spaceRatio"] = 0.1 - + chart["type"] = "line" + chart['lineOptions'] = { + "dotSize": 6 + } return chart From be750096ed782bd03108f3a35bd203b171bc516d Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Mon, 18 May 2020 14:23:30 +0530 Subject: [PATCH 085/608] fix: bom incorrect price list rate for raw material if price list currency is different from company currency (#21585) * fix: bom incorrect price list rate for raw material if price list currency is different from company currency * fixed test cases * fixed base_rate calculation and added plc_conversion_rate trigger --- erpnext/manufacturing/doctype/bom/bom.js | 21 ++++++++- erpnext/manufacturing/doctype/bom/bom.json | 44 ++++++++++++++----- erpnext/manufacturing/doctype/bom/bom.py | 20 +++++++-- erpnext/manufacturing/doctype/bom/test_bom.py | 8 ++-- erpnext/patches.txt | 1 + .../update_price_list_currency_in_bom.py | 31 +++++++++++++ 6 files changed, 105 insertions(+), 20 deletions(-) create mode 100644 erpnext/patches/v12_0/update_price_list_currency_in_bom.py diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js index ebfb7626407..44da9cae355 100644 --- a/erpnext/manufacturing/doctype/bom/bom.js +++ b/erpnext/manufacturing/doctype/bom/bom.js @@ -212,6 +212,12 @@ frappe.ui.form.on("BOM", { }); }, + rm_cost_as_per: function(frm) { + if (in_list(["Valuation Rate", "Last Purchase Rate"], frm.doc.rm_cost_as_per)) { + frm.set_value("plc_conversion_rate", 1.0); + } + }, + routing: function(frm) { if (frm.doc.routing) { frappe.call({ @@ -242,7 +248,7 @@ erpnext.bom.BomController = erpnext.TransactionController.extend({ item_code: function(doc, cdt, cdn){ var scrap_items = false; var child = locals[cdt][cdn]; - if(child.doctype == 'BOM Scrap Item') { + if (child.doctype == 'BOM Scrap Item') { scrap_items = true; } @@ -252,8 +258,19 @@ erpnext.bom.BomController = erpnext.TransactionController.extend({ get_bom_material_detail(doc, cdt, cdn, scrap_items); }, + + buying_price_list: function(doc) { + this.apply_price_list(); + }, + + plc_conversion_rate: function(doc) { + if (!this.in_apply_price_list) { + this.apply_price_list(); + } + }, + conversion_factor: function(doc, cdt, cdn) { - if(frappe.meta.get_docfield(cdt, "stock_qty", cdn)) { + if (frappe.meta.get_docfield(cdt, "stock_qty", cdn)) { var item = frappe.get_doc(cdt, cdn); frappe.model.round_floats_in(item, ["qty", "conversion_factor"]); item.stock_qty = flt(item.qty * item.conversion_factor, precision("stock_qty", item)); diff --git a/erpnext/manufacturing/doctype/bom/bom.json b/erpnext/manufacturing/doctype/bom/bom.json index 63f4f977c59..4ce0ecf3f27 100644 --- a/erpnext/manufacturing/doctype/bom/bom.json +++ b/erpnext/manufacturing/doctype/bom/bom.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_import": 1, "creation": "2013-01-22 15:11:38", "doctype": "DocType", @@ -6,23 +7,25 @@ "engine": "InnoDB", "field_order": [ "item", - "quantity", - "set_rate_of_sub_assembly_item_based_on_bom", + "company", + "item_name", + "uom", "cb0", "is_active", "is_default", "allow_alternative_item", - "image", - "item_name", - "uom", - "currency_detail", - "company", + "set_rate_of_sub_assembly_item_based_on_bom", "project", + "quantity", + "image", + "currency_detail", + "currency", "conversion_rate", "column_break_12", - "currency", "rm_cost_as_per", "buying_price_list", + "price_list_currency", + "plc_conversion_rate", "section_break_21", "with_operations", "column_break_23", @@ -176,7 +179,8 @@ }, { "fieldname": "currency_detail", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "label": "Currency and Price List" }, { "fieldname": "company", @@ -324,7 +328,7 @@ }, { "fieldname": "base_scrap_material_cost", - "fieldtype": "Data", + "fieldtype": "Currency", "label": "Scrap Material Cost(Company Currency)", "no_copy": 1, "options": "Company:company:default_currency", @@ -477,13 +481,31 @@ { "fieldname": "column_break_52", "fieldtype": "Column Break" + }, + { + "allow_on_submit": 1, + "depends_on": "eval:doc.rm_cost_as_per=='Price List'", + "fieldname": "plc_conversion_rate", + "fieldtype": "Float", + "label": "Price List Exchange Rate" + }, + { + "allow_on_submit": 1, + "depends_on": "eval:doc.rm_cost_as_per=='Price List'", + "fieldname": "price_list_currency", + "fieldtype": "Link", + "label": "Price List Currency", + "options": "Currency", + "print_hide": 1, + "read_only": 1 } ], "icon": "fa fa-sitemap", "idx": 1, "image_field": "image", "is_submittable": 1, - "modified": "2019-11-22 14:35:12.142150", + "links": [], + "modified": "2020-05-05 14:29:32.634952", "modified_by": "Administrator", "module": "Manufacturing", "name": "BOM", diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index b1fc4deae91..6ac653e37a4 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -70,11 +70,13 @@ class BOM(WebsiteGenerator): self.validate_main_item() self.validate_currency() self.set_conversion_rate() + self.set_plc_conversion_rate() self.validate_uom_is_interger() self.set_bom_material_details() self.validate_materials() self.validate_operations() self.calculate_cost() + self.update_cost(update_parent=False, from_child_bom=True, save=False) def get_context(self, context): context.parents = [{'name': 'boms', 'title': _('All BOMs') }] @@ -165,7 +167,7 @@ class BOM(WebsiteGenerator): 'rate' : rate, 'qty' : args.get("qty") or args.get("stock_qty") or 1, 'stock_qty' : args.get("qty") or args.get("stock_qty") or 1, - 'base_rate' : rate, + 'base_rate' : flt(rate) * (flt(self.conversion_rate) or 1), 'include_item_in_manufacturing': cint(args['transfer_for_manufacture']) or 0 } @@ -226,7 +228,7 @@ class BOM(WebsiteGenerator): frappe.msgprint(_("{0} not found for item {1}") .format(self.rm_cost_as_per, arg["item_code"]), alert=True) - return flt(rate) / (self.conversion_rate or 1) + return flt(rate) * flt(self.plc_conversion_rate or 1) / (self.conversion_rate or 1) def update_cost(self, update_parent=True, from_child_bom=False, save=True): if self.docstatus == 2: @@ -243,10 +245,15 @@ class BOM(WebsiteGenerator): "stock_uom": d.stock_uom, "conversion_factor": d.conversion_factor }) + if rate: d.rate = rate d.amount = flt(d.rate) * flt(d.qty) - d.db_update() + d.base_rate = flt(d.rate) * flt(self.conversion_rate) + d.base_amount = flt(d.amount) * flt(self.conversion_rate) + + if save: + d.db_update() if self.docstatus == 1: self.flags.ignore_validate_update_after_submit = True @@ -372,6 +379,13 @@ class BOM(WebsiteGenerator): elif self.conversion_rate == 1 or flt(self.conversion_rate) <= 0: self.conversion_rate = get_exchange_rate(self.currency, self.company_currency(), args="for_buying") + def set_plc_conversion_rate(self): + if self.rm_cost_as_per in ["Valuation Rate", "Last Purchase Rate"]: + self.plc_conversion_rate = 1 + elif not self.plc_conversion_rate and self.price_list_currency: + self.plc_conversion_rate = get_exchange_rate(self.price_list_currency, + self.company_currency(), args="for_buying") + def validate_materials(self): """ Validate raw material entries """ diff --git a/erpnext/manufacturing/doctype/bom/test_bom.py b/erpnext/manufacturing/doctype/bom/test_bom.py index 45a7b935d38..3dfd03b1395 100644 --- a/erpnext/manufacturing/doctype/bom/test_bom.py +++ b/erpnext/manufacturing/doctype/bom/test_bom.py @@ -81,13 +81,13 @@ class TestBOM(unittest.TestCase): # test amounts in selected currency self.assertEqual(bom.operating_cost, 100) - self.assertEqual(bom.raw_material_cost, 8000) - self.assertEqual(bom.total_cost, 8100) + self.assertEqual(bom.raw_material_cost, 351.68) + self.assertEqual(bom.total_cost, 451.68) # test amounts in selected currency self.assertEqual(bom.base_operating_cost, 6000) - self.assertEqual(bom.base_raw_material_cost, 480000) - self.assertEqual(bom.base_total_cost, 486000) + self.assertEqual(bom.base_raw_material_cost, 21100.80) + self.assertEqual(bom.base_total_cost, 27100.80) def test_bom_cost_multi_uom_multi_currency_based_on_price_list(self): frappe.db.set_value("Price List", "_Test Price List", "price_not_uom_dependent", 1) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 4ae591b54b5..28cd21536f6 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -685,3 +685,4 @@ execute:frappe.rename_doc("Desk Page", "Getting Started", "Home", force=True) erpnext.patches.v12_0.unset_customer_supplier_based_on_type_of_item_price erpnext.patches.v12_0.set_valid_till_date_in_supplier_quotation erpnext.patches.v12_0.set_serial_no_status +erpnext.patches.v12_0.update_price_list_currency_in_bom diff --git a/erpnext/patches/v12_0/update_price_list_currency_in_bom.py b/erpnext/patches/v12_0/update_price_list_currency_in_bom.py new file mode 100644 index 00000000000..f5e7b947c23 --- /dev/null +++ b/erpnext/patches/v12_0/update_price_list_currency_in_bom.py @@ -0,0 +1,31 @@ +from __future__ import unicode_literals +import frappe +from frappe.utils import getdate, flt +from erpnext.setup.utils import get_exchange_rate + +def execute(): + frappe.reload_doc("manufacturing", "doctype", "bom") + frappe.reload_doc("manufacturing", "doctype", "bom_item") + + frappe.db.sql(""" UPDATE `tabBOM`, `tabPrice List` + SET + `tabBOM`.price_list_currency = `tabPrice List`.currency, + `tabBOM`.plc_conversion_rate = 1.0 + WHERE + `tabBOM`.buying_price_list = `tabPrice List`.name AND `tabBOM`.docstatus < 2 + AND `tabBOM`.rm_cost_as_per = 'Price List' + """) + + for d in frappe.db.sql(""" + SELECT + bom.creation, bom.name, bom.price_list_currency as currency, + company.default_currency as company_currency + FROM + `tabBOM` as bom, `tabCompany` as company + WHERE + bom.company = company.name AND bom.rm_cost_as_per = 'Price List' AND + bom.price_list_currency != company.default_currency AND bom.docstatus < 2""", as_dict=1): + plc_conversion_rate = get_exchange_rate(d.currency, + d.company_currency, getdate(d.creation), "for_buying") + + frappe.db.set_value("BOM", d.name, "plc_conversion_rate", plc_conversion_rate) \ No newline at end of file From d94a38eb51bdf9ec20f4c6b0fb7926e6ac052740 Mon Sep 17 00:00:00 2001 From: Himanshu Date: Mon, 18 May 2020 14:26:26 +0530 Subject: [PATCH 086/608] fix: make queries show searchfields (#21685) * fix: make queries show searchfields * Update erpnext/controllers/queries.py Co-authored-by: Nabin Hait * Update queries.py * Update erpnext/controllers/queries.py Co-authored-by: Nabin Hait * Update erpnext/controllers/queries.py Co-authored-by: Nabin Hait * fix: make fields string for sql Co-authored-by: Nabin Hait --- erpnext/controllers/queries.py | 66 +++++++++++++++----- erpnext/selling/doctype/customer/customer.py | 21 ++++--- 2 files changed, 65 insertions(+), 22 deletions(-) diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index 5febfd6bf28..5fbc460a973 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -8,11 +8,14 @@ from frappe.desk.reportview import get_match_cond, get_filters_cond from frappe.utils import nowdate, getdate from collections import defaultdict from erpnext.stock.get_item_details import _get_item_tax_template +from frappe.utils import unique # searches for active employees def employee_query(doctype, txt, searchfield, start, page_len, filters): conditions = [] - return frappe.db.sql("""select name, employee_name from `tabEmployee` + fields = get_fields("Employee", ["name", "employee_name"]) + + return frappe.db.sql("""select {fields} from `tabEmployee` where status = 'Active' and docstatus < 2 and ({key} like %(txt)s @@ -24,6 +27,7 @@ def employee_query(doctype, txt, searchfield, start, page_len, filters): idx desc, name, employee_name limit %(start)s, %(page_len)s""".format(**{ + 'fields': ", ".join(fields), 'key': searchfield, 'fcond': get_filters_cond(doctype, filters, conditions), 'mcond': get_match_cond(doctype) @@ -34,9 +38,12 @@ def employee_query(doctype, txt, searchfield, start, page_len, filters): 'page_len': page_len }) - # searches for leads which are not converted + +# searches for leads which are not converted def lead_query(doctype, txt, searchfield, start, page_len, filters): - return frappe.db.sql("""select name, lead_name, company_name from `tabLead` + fields = get_fields("Lead", ["name", "lead_name", "company_name"]) + + return frappe.db.sql("""select {fields} from `tabLead` where docstatus < 2 and ifnull(status, '') != 'Converted' and ({key} like %(txt)s @@ -50,6 +57,7 @@ def lead_query(doctype, txt, searchfield, start, page_len, filters): idx desc, name, lead_name limit %(start)s, %(page_len)s""".format(**{ + 'fields': ", ".join(fields), 'key': searchfield, 'mcond':get_match_cond(doctype) }), { @@ -59,6 +67,7 @@ def lead_query(doctype, txt, searchfield, start, page_len, filters): 'page_len': page_len }) + # searches for customer def customer_query(doctype, txt, searchfield, start, page_len, filters): conditions = [] @@ -69,13 +78,9 @@ def customer_query(doctype, txt, searchfield, start, page_len, filters): else: fields = ["name", "customer_name", "customer_group", "territory"] - meta = frappe.get_meta("Customer") - searchfields = meta.get_search_fields() - searchfields = searchfields + [f for f in [searchfield or "name", "customer_name"] \ - if not f in searchfields] - fields = fields + [f for f in searchfields if not f in fields] + fields = get_fields("Customer", fields) - fields = ", ".join(fields) + searchfields = frappe.get_meta("Customer").get_search_fields() searchfields = " or ".join([field + " like %(txt)s" for field in searchfields]) return frappe.db.sql("""select {fields} from `tabCustomer` @@ -88,7 +93,7 @@ def customer_query(doctype, txt, searchfield, start, page_len, filters): idx desc, name, customer_name limit %(start)s, %(page_len)s""".format(**{ - "fields": fields, + "fields": ", ".join(fields), "scond": searchfields, "mcond": get_match_cond(doctype), "fcond": get_filters_cond(doctype, filters, conditions).replace('%', '%%'), @@ -99,6 +104,7 @@ def customer_query(doctype, txt, searchfield, start, page_len, filters): 'page_len': page_len }) + # searches for supplier def supplier_query(doctype, txt, searchfield, start, page_len, filters): supp_master_name = frappe.defaults.get_user_default("supp_master_name") @@ -106,7 +112,8 @@ def supplier_query(doctype, txt, searchfield, start, page_len, filters): fields = ["name", "supplier_group"] else: fields = ["name", "supplier_name", "supplier_group"] - fields = ", ".join(fields) + + fields = get_fields("Supplier", fields) return frappe.db.sql("""select {field} from `tabSupplier` where docstatus < 2 @@ -119,7 +126,7 @@ def supplier_query(doctype, txt, searchfield, start, page_len, filters): idx desc, name, supplier_name limit %(start)s, %(page_len)s """.format(**{ - 'field': fields, + 'field': ', '.join(fields), 'key': searchfield, 'mcond':get_match_cond(doctype) }), { @@ -129,6 +136,7 @@ def supplier_query(doctype, txt, searchfield, start, page_len, filters): 'page_len': page_len }) + def tax_account_query(doctype, txt, searchfield, start, page_len, filters): company_currency = erpnext.get_company_currency(filters.get('company')) @@ -153,6 +161,7 @@ def tax_account_query(doctype, txt, searchfield, start, page_len, filters): return tax_accounts + def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=False): conditions = [] @@ -221,10 +230,12 @@ def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=Fals "page_len": page_len }, as_dict=as_dict) + def bom(doctype, txt, searchfield, start, page_len, filters): conditions = [] + fields = get_fields("BOM", ["name", "item"]) - return frappe.db.sql("""select tabBOM.name, tabBOM.item + return frappe.db.sql("""select {fields} from tabBOM where tabBOM.docstatus=1 and tabBOM.is_active=1 @@ -234,6 +245,7 @@ def bom(doctype, txt, searchfield, start, page_len, filters): if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999), idx desc, name limit %(start)s, %(page_len)s """.format( + fields=", ".join(fields), fcond=get_filters_cond(doctype, filters, conditions).replace('%', '%%'), mcond=get_match_cond(doctype).replace('%', '%%'), key=searchfield), @@ -244,13 +256,16 @@ def bom(doctype, txt, searchfield, start, page_len, filters): 'page_len': page_len or 20 }) + def get_project_name(doctype, txt, searchfield, start, page_len, filters): cond = '' if filters.get('customer'): cond = """(`tabProject`.customer = %s or ifnull(`tabProject`.customer,"")="") and""" %(frappe.db.escape(filters.get("customer"))) - return frappe.db.sql("""select `tabProject`.name from `tabProject` + fields = get_fields("Project", ["name"]) + + return frappe.db.sql("""select {fields} from `tabProject` where `tabProject`.status not in ("Completed", "Cancelled") and {cond} `tabProject`.name like %(txt)s {match_cond} order by @@ -258,6 +273,7 @@ def get_project_name(doctype, txt, searchfield, start, page_len, filters): idx desc, `tabProject`.name asc limit {start}, {page_len}""".format( + fields=", ".join(['`tabProject`.{0}'.format(f) for f in fields]), cond=cond, match_cond=get_match_cond(doctype), start=start, @@ -268,8 +284,10 @@ def get_project_name(doctype, txt, searchfield, start, page_len, filters): def get_delivery_notes_to_be_billed(doctype, txt, searchfield, start, page_len, filters, as_dict): + fields = get_fields("Delivery Note", ["name", "customer", "posting_date"]) + return frappe.db.sql(""" - select `tabDelivery Note`.name, `tabDelivery Note`.customer, `tabDelivery Note`.posting_date + select %(fields)s from `tabDelivery Note` where `tabDelivery Note`.`%(key)s` like %(txt)s and `tabDelivery Note`.docstatus = 1 @@ -284,6 +302,7 @@ def get_delivery_notes_to_be_billed(doctype, txt, searchfield, start, page_len, ) %(mcond)s order by `tabDelivery Note`.`%(key)s` asc limit %(start)s, %(page_len)s """ % { + "fields": ", ".join(["`tabDelivery Note`.{0}".format(f) for f in fields]), "key": searchfield, "fcond": get_filters_cond(doctype, filters, []), "mcond": get_match_cond(doctype), @@ -349,6 +368,7 @@ def get_batch_no(doctype, txt, searchfield, start, page_len, filters): order by expiry_date, name desc limit %(start)s, %(page_len)s""".format(cond, match_conditions=get_match_cond(doctype)), args) + def get_account_list(doctype, txt, searchfield, start, page_len, filters): filter_list = [] @@ -371,6 +391,7 @@ def get_account_list(doctype, txt, searchfield, start, page_len, filters): fields = ["name", "parent_account"], limit_start=start, limit_page_length=page_len, as_list=True) + def get_blanket_orders(doctype, txt, searchfield, start, page_len, filters): return frappe.db.sql("""select distinct bo.name, bo.blanket_order_type, bo.to_date from `tabBlanket Order` bo, `tabBlanket Order Item` boi @@ -385,6 +406,7 @@ def get_blanket_orders(doctype, txt, searchfield, start, page_len, filters): company = frappe.db.escape(filters.get("company")) )) + @frappe.whitelist() def get_income_account(doctype, txt, searchfield, start, page_len, filters): from erpnext.controllers.queries import get_match_cond @@ -490,6 +512,7 @@ def get_batch_numbers(doctype, txt, searchfield, start, page_len, filters): return frappe.db.sql(query, filters) + @frappe.whitelist() def item_manufacturer_query(doctype, txt, searchfield, start, page_len, filters): item_filters = [ @@ -507,6 +530,7 @@ def item_manufacturer_query(doctype, txt, searchfield, start, page_len, filters) ) return item_manufacturers + @frappe.whitelist() def get_purchase_receipts(doctype, txt, searchfield, start, page_len, filters): query = """ @@ -520,6 +544,7 @@ def get_purchase_receipts(doctype, txt, searchfield, start, page_len, filters): return frappe.db.sql(query, filters) + @frappe.whitelist() def get_purchase_invoices(doctype, txt, searchfield, start, page_len, filters): query = """ @@ -533,6 +558,7 @@ def get_purchase_invoices(doctype, txt, searchfield, start, page_len, filters): return frappe.db.sql(query, filters) + @frappe.whitelist() def get_tax_template(doctype, txt, searchfield, start, page_len, filters): @@ -556,3 +582,13 @@ def get_tax_template(doctype, txt, searchfield, start, page_len, filters): taxes = _get_item_tax_template(args, taxes, for_validate=True) return [(d,) for d in set(taxes)] + + +def get_fields(doctype, fields=[]): + meta = frappe.get_meta(doctype) + fields.extend(meta.get_search_fields()) + + if meta.title_field and not meta.title_field.strip() in fields: + fields.insert(1, meta.title_field.strip()) + + return unique(fields) diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index 3d172ac7a22..a6889e080d9 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -337,11 +337,15 @@ def get_loyalty_programs(doc): return lp_details def get_customer_list(doctype, txt, searchfield, start, page_len, filters=None): + from erpnext.controllers.queries import get_fields + if frappe.db.get_default("cust_master_name") == "Customer Name": fields = ["name", "customer_group", "territory"] else: fields = ["name", "customer_name", "customer_group", "territory"] + fields = get_fields("Customer", fields) + match_conditions = build_match_conditions("Customer") match_conditions = "and {}".format(match_conditions) if match_conditions else "" @@ -349,14 +353,17 @@ def get_customer_list(doctype, txt, searchfield, start, page_len, filters=None): filter_conditions = get_filters_cond(doctype, filters, []) match_conditions += "{}".format(filter_conditions) - return frappe.db.sql("""select %s from `tabCustomer` where docstatus < 2 - and (%s like %s or customer_name like %s) - {match_conditions} + return frappe.db.sql(""" + select %s + from `tabCustomer` + where docstatus < 2 + and (%s like %s or customer_name like %s) + {match_conditions} order by - case when name like %s then 0 else 1 end, - case when customer_name like %s then 0 else 1 end, - name, customer_name limit %s, %s""".format(match_conditions=match_conditions) % - (", ".join(fields), searchfield, "%s", "%s", "%s", "%s", "%s", "%s"), + case when name like %s then 0 else 1 end, + case when customer_name like %s then 0 else 1 end, + name, customer_name limit %s, %s + """.format(match_conditions=match_conditions) % (", ".join(fields), searchfield, "%s", "%s", "%s", "%s", "%s", "%s"), ("%%%s%%" % txt, "%%%s%%" % txt, "%%%s%%" % txt, "%%%s%%" % txt, start, page_len)) From c734db5d4505fc08c2a55d1c5be53a10c669e8cb Mon Sep 17 00:00:00 2001 From: Marica Date: Mon, 18 May 2020 14:38:57 +0530 Subject: [PATCH 087/608] fix: Validate Filters in Sales Funnel. (#21761) * fix: Validate Filters in Sales Funnel. * fix: Style fixes --- .../selling/page/sales_funnel/sales_funnel.js | 4 ++++ .../selling/page/sales_funnel/sales_funnel.py | 17 +++++++++++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/erpnext/selling/page/sales_funnel/sales_funnel.js b/erpnext/selling/page/sales_funnel/sales_funnel.js index 85c0cd8bf0c..e3d0a55c3a0 100644 --- a/erpnext/selling/page/sales_funnel/sales_funnel.js +++ b/erpnext/selling/page/sales_funnel/sales_funnel.js @@ -90,6 +90,10 @@ erpnext.SalesFunnel = class SalesFunnel { get_data(btn) { var me = this; + if (!this.company) { + frappe.throw(__("Please Select a Company.")); + } + const method_map = { "sales_funnel": "erpnext.selling.page.sales_funnel.sales_funnel.get_funnel_data", "opp_by_lead_source": "erpnext.selling.page.sales_funnel.sales_funnel.get_opp_by_lead_source", diff --git a/erpnext/selling/page/sales_funnel/sales_funnel.py b/erpnext/selling/page/sales_funnel/sales_funnel.py index d62e2093c69..dba24ef5b00 100644 --- a/erpnext/selling/page/sales_funnel/sales_funnel.py +++ b/erpnext/selling/page/sales_funnel/sales_funnel.py @@ -8,14 +8,23 @@ from frappe import _ from erpnext.accounts.report.utils import convert import pandas as pd +def validate_filters(from_date, to_date, company): + if from_date and to_date and (from_date >= to_date): + frappe.throw(_("To Date must be greater than From Date")) + + if not company: + frappe.throw(_("Please Select a Company")) + @frappe.whitelist() def get_funnel_data(from_date, to_date, company): + validate_filters(from_date, to_date, company) + active_leads = frappe.db.sql("""select count(*) from `tabLead` where (date(`modified`) between %s and %s) and status != "Do Not Contact" and company=%s""", (from_date, to_date, company))[0][0] active_leads += frappe.db.sql("""select count(distinct contact.name) from `tabContact` contact - left join `tabDynamic Link` dl on (dl.parent=contact.name) where dl.link_doctype='Customer' + left join `tabDynamic Link` dl on (dl.parent=contact.name) where dl.link_doctype='Customer' and (date(contact.modified) between %s and %s) and status != "Passive" """, (from_date, to_date))[0][0] opportunities = frappe.db.sql("""select count(*) from `tabOpportunity` @@ -38,6 +47,8 @@ def get_funnel_data(from_date, to_date, company): @frappe.whitelist() def get_opp_by_lead_source(from_date, to_date, company): + validate_filters(from_date, to_date, company) + opportunities = frappe.get_all("Opportunity", filters=[['status', 'in', ['Open', 'Quotation', 'Replied']], ['company', '=', company], ['transaction_date', 'Between', [from_date, to_date]]], fields=['currency', 'sales_stage', 'opportunity_amount', 'probability', 'source']) if opportunities: @@ -68,11 +79,13 @@ def get_opp_by_lead_source(from_date, to_date, company): @frappe.whitelist() def get_pipeline_data(from_date, to_date, company): + validate_filters(from_date, to_date, company) + opportunities = frappe.get_all("Opportunity", filters=[['status', 'in', ['Open', 'Quotation', 'Replied']], ['company', '=', company], ['transaction_date', 'Between', [from_date, to_date]]], fields=['currency', 'sales_stage', 'opportunity_amount', 'probability']) if opportunities: default_currency = frappe.get_cached_value('Global Defaults', 'None', 'default_currency') - + cp_opportunities = [dict(x, **{'compound_amount': (convert(x['opportunity_amount'], x['currency'], default_currency, to_date) * x['probability']/100)}) for x in opportunities] df = pd.DataFrame(cp_opportunities).groupby(['sales_stage'], as_index=True).agg({'compound_amount': 'sum'}).to_dict() From fb29714911490bee40d2c5b9342a3f9a4a84d46d Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 18 May 2020 16:28:11 +0530 Subject: [PATCH 088/608] feat: Healthcare Dashboard --- erpnext/healthcare/dashboard_fixtures.py | 88 +++++++++++++++++++++++- 1 file changed, 87 insertions(+), 1 deletion(-) diff --git a/erpnext/healthcare/dashboard_fixtures.py b/erpnext/healthcare/dashboard_fixtures.py index 8b86ac39ace..0f5728de9aa 100644 --- a/erpnext/healthcare/dashboard_fixtures.py +++ b/erpnext/healthcare/dashboard_fixtures.py @@ -16,7 +16,13 @@ def get_dashboards(): "name": "Healthcare", "dashboard_name": "Healthcare", "charts": [ - { "chart": "Patient Appointments" } + { "chart": "Patient Appointments", "width": "Full"}, + { "chart": "In-Patient Status", "width": "Half"}, + { "chart": "Clinical Procedures Status", "width": "Half"}, + { "chart": "Symptoms", "width": "Half"}, + { "chart": "Diagnoses", "width": "Half"}, + { "chart": "Department wise Patient Appointments", "width": "Full"}, + { "chart": "Lab Tests", "width": "Full"}, ] }] @@ -36,5 +42,85 @@ def get_charts(): "document_type": "Patient Appointment", "type": "Line", "width": "Half" + }, + { + "doctype": "Dashboard Chart", + "name": "Department wise Patient Appointments", + "chart_name": "Department wise Patient Appointments", + "chart_type": "Group By", + "document_type": "Patient Appointment", + "group_by_type": "Count", + "group_by_based_on": "department", + 'is_public': 1, + "owner": "Administrator", + "type": "Bar", + "width": "Full", + "color": "#5F62F6" + }, + { + "doctype": "Dashboard Chart", + "name": "Lab Tests", + "chart_name": "Lab Tests", + "chart_type": "Group By", + "document_type": "Lab Test", + "group_by_type": "Count", + "group_by_based_on": "template", + 'is_public': 1, + "owner": "Administrator", + "type": "Bar", + "width": "Full", + "color": "#8548EB" + }, + { + "doctype": "Dashboard Chart", + "name": "In-Patient Status", + "chart_name": "In-Patient Status", + "chart_type": "Group By", + "document_type": "Inpatient Record", + "group_by_type": "Count", + "group_by_based_on": "status", + 'is_public': 1, + "owner": "Administrator", + "type": "Bar", + "width": "Half", + }, + { + "doctype": "Dashboard Chart", + "name": "Clinical Procedures Status", + "chart_name": "Clinical Procedure Status", + "chart_type": "Group By", + "document_type": "Clinical Procedure", + "group_by_type": "Count", + "group_by_based_on": "status", + 'is_public': 1, + "owner": "Administrator", + "type": "Pie", + "width": "Half", + }, + { + "doctype": "Dashboard Chart", + "name": "Symptoms", + "chart_name": "Symptoms", + "chart_type": "Group By", + "document_type": "Patient Encounter Symptom", + "group_by_type": "Count", + "group_by_based_on": "complaint", + 'is_public': 1, + "owner": "Administrator", + "type": "Percentage", + "width": "Half", + }, + { + "doctype": "Dashboard Chart", + "name": "Diagnoses", + "chart_name": "Diagnoses", + "chart_type": "Group By", + "document_type": "Patient Encounter Diagnosis", + "group_by_type": "Count", + "group_by_based_on": "diagnosis", + 'is_public': 1, + "owner": "Administrator", + "type": "Percentage", + "width": "Half", } ] From 9aae6f479398847b18bb231cee4ce35dc036420d Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 18 May 2020 18:03:11 +0530 Subject: [PATCH 089/608] feat: accounting dashboard + desk --- erpnext/accounts/dashboard_fixtures.py | 131 +++++++++++++----- .../desk_page/accounting/accounting.json | 9 +- erpnext/patches.txt | 1 + .../selling/desk_page/selling/selling.json | 85 ------------ 4 files changed, 108 insertions(+), 118 deletions(-) delete mode 100644 erpnext/selling/desk_page/selling/selling.json diff --git a/erpnext/accounts/dashboard_fixtures.py b/erpnext/accounts/dashboard_fixtures.py index 8b9eca5b2d3..214e467b5c0 100644 --- a/erpnext/accounts/dashboard_fixtures.py +++ b/erpnext/accounts/dashboard_fixtures.py @@ -3,9 +3,9 @@ import frappe import json -from frappe.utils import nowdate, add_months +from frappe.utils import nowdate, add_months, get_date_str from frappe import _ -from erpnext.accounts.utils import get_fiscal_year +from erpnext.accounts.utils import get_fiscal_year, get_account_name def get_company_for_dashboards(): company = frappe.defaults.get_defaults().company @@ -27,25 +27,28 @@ def get_data(): def get_dashboards(): return [{ "name": "Accounts Dashboard", - "dashboard_name": "Accounts", + "dashboard_name": "Accounts Dashboard", "doctype": "Dashboard", "charts": [ { "chart": "Profit and Loss" , "width": "Full"}, - { "chart": "Incoming Bills"}, - { "chart": "Outgoing Bills"}, - { "chart": "Accounts Receivable Ageing"}, - { "chart": "Accounts Payable Ageing"}, + { "chart": "Incoming Bills (Purchase Invoice)", "width": "Half"}, + { "chart": "Outgoing Bills (Sales Invoice)", "width": "Half"}, + { "chart": "Accounts Receivable Ageing", "width": "Half"}, + { "chart": "Accounts Payable Ageing", "width": "Half"}, { "chart": "Budget Variance", "width": "Full"}, - { "chart": "Bank Balance", "width": "Full"}, + { "chart": "Bank Balance", "width": "Full"} ], "cards": [ - {"card": "Total Payment Received"} + {"card": "Total Outgoing Bills"}, + {"card": "Total Incoming Bills"}, + {"card": "Total Incoming Payment"}, + {"card": "Total Outgoing Payment"} ] }] def get_charts(): company = frappe.get_doc("Company", get_company_for_dashboards()) - bank_account = company.default_bank_account or get_account("Bank", company.name) + bank_account = company.default_bank_account or get_account_name("Bank", company=company.name) fiscal_year = get_fiscal_year(date=nowdate()) default_cost_center = company.cost_center @@ -58,50 +61,53 @@ def get_charts(): "filters_json": json.dumps({ "company": company.name, "filter_based_on": "Date Range", - "period_start_date": add_months(nowdate(), -4), - "period_end_date": nowdate(), + "period_start_date": get_date_str(fiscal_year[1]), + "period_end_date": get_date_str(fiscal_year[2]), "periodicity": "Monthly", "include_default_book_entries": 1 - }), + }), "type": "Bar", 'timeseries': 0, "chart_type": "Report", "chart_name": _("Profit and Loss"), - "is_custom": 1 + "is_custom": 1, + "is_public": 1 }, { "doctype": "Dashboard Chart", "time_interval": "Monthly", - "name": "Incoming Bills", + "name": "Incoming Bills (Purchase Invoice)", "chart_name": _("Incoming Bills (Purchase Invoice)"), "timespan": "Last Year", "color": "#a83333", - "value_based_on": "base_grand_total", - "filters_json": json.dumps({}), + "value_based_on": "base_net_total", + "filters_json": json.dumps({"docstatus": 1}), "chart_type": "Sum", "timeseries": 1, "based_on": "posting_date", "owner": "Administrator", "document_type": "Purchase Invoice", "type": "Bar", - "width": "Half" + "width": "Half", + "is_public": 1 }, { "doctype": "Dashboard Chart", - "name": "Outgoing Bills", + "name": "Outgoing Bills (Sales Invoice)", "time_interval": "Monthly", "chart_name": _("Outgoing Bills (Sales Invoice)"), "timespan": "Last Year", "color": "#7b933d", - "value_based_on": "base_grand_total", - "filters_json": json.dumps({}), + "value_based_on": "base_net_total", + "filters_json": json.dumps({"docstatus": 1}), "chart_type": "Sum", "timeseries": 1, "based_on": "posting_date", "owner": "Administrator", "document_type": "Sales Invoice", "type": "Bar", - "width": "Half" + "width": "Half", + "is_public": 1 }, { "doctype": "Dashboard Charts", @@ -121,7 +127,8 @@ def get_charts(): 'timeseries': 0, "chart_type": "Report", "chart_name": _("Accounts Receivable Ageing"), - "is_custom": 1 + "is_custom": 1, + "is_public": 1 }, { "doctype": "Dashboard Charts", @@ -141,7 +148,8 @@ def get_charts(): 'timeseries': 0, "chart_type": "Report", "chart_name": _("Accounts Payable Ageing"), - "is_custom": 1 + "is_custom": 1, + "is_public": 1 }, { "doctype": "Dashboard Charts", @@ -159,7 +167,8 @@ def get_charts(): "timeseries": 0, "chart_type": "Report", "chart_name": _("Budget Variance"), - "is_custom": 1 + "is_custom": 1, + "is_public": 1 }, { "doctype": "Dashboard Charts", @@ -167,29 +176,89 @@ def get_charts(): "time_interval": "Quarterly", "chart_name": "Bank Balance", "timespan": "Last Year", - "filters_json": json.dumps({"company": company.name, "account": bank_account}), + "filters_json": json.dumps({ + "company": company.name, + "account": bank_account + }), "source": "Account Balance Timeline", "chart_type": "Custom", "timeseries": 1, "owner": "Administrator", "type": "Line", - "width": "Half" + "width": "Half", + "is_public": 1 }, ] def get_number_cards(): + fiscal_year = get_fiscal_year(date=nowdate()) + year_start_date = get_date_str(fiscal_year[1]) + year_end_date = get_date_str(fiscal_year[2]) return [ { "doctype": "Number Card", "document_type": "Payment Entry", - "name": "Total Payment Received", - "filters_json": json.dumps([]), - "label": _("Total Payment Received"), + "name": "Total Incoming Payment", + "filters_json": json.dumps([ + ['Payment Entry', 'docstatus', '=', 1], + ['Payment Entry', 'posting_date', 'between', [year_start_date, year_end_date]], + ['Payment Entry', 'payment_type', '=', 'Receive'] + ]), + "label": _("Total Incoming Payment"), "function": "Sum", "aggregate_function_based_on": "base_received_amount", "is_public": 1, "is_custom": 1, "show_percentage_stats": 1, - "stats_time_interval": "Daily" + "stats_time_interval": "Monthly" + }, + { + "doctype": "Number Card", + "document_type": "Payment Entry", + "name": "Total Outgoing Payment", + "filters_json": json.dumps([ + ['Payment Entry', 'docstatus', '=', 1], + ['Payment Entry', 'posting_date', 'between', [year_start_date, year_end_date]], + ['Payment Entry', 'payment_type', '=', 'Pay'] + ]), + "label": _("Total Outgoing Payment"), + "function": "Sum", + "aggregate_function_based_on": "base_paid_amount", + "is_public": 1, + "is_custom": 1, + "show_percentage_stats": 1, + "stats_time_interval": "Monthly" + }, + { + "doctype": "Number Card", + "document_type": "Sales Invoice", + "name": "Total Outgoing Bills", + "filters_json": json.dumps([ + ['Sales Invoice', 'docstatus', '=', 1], + ['Sales Invoice', 'posting_date', 'between', [year_start_date, year_end_date]] + ]), + "label": _("Total Outgoing Bills"), + "function": "Sum", + "aggregate_function_based_on": "base_net_total", + "is_public": 1, + "is_custom": 1, + "show_percentage_stats": 1, + "stats_time_interval": "Monthly" + }, + { + "doctype": "Number Card", + "document_type": "Purchase Invoice", + "name": "Total Incoming Bills", + "filters_json": json.dumps([ + ['Purchase Invoice', 'docstatus', '=', 1], + ['Purchase Invoice', 'posting_date', 'between', [year_start_date, year_end_date]] + ]), + "label": _("Total Incoming Bills"), + "function": "Sum", + "aggregate_function_based_on": "base_net_total", + "is_public": 1, + "is_custom": 1, + "show_percentage_stats": 1, + "stats_time_interval": "Monthly" } ] diff --git a/erpnext/accounts/desk_page/accounting/accounting.json b/erpnext/accounts/desk_page/accounting/accounting.json index 682eb8fcede..a783b1d0dbb 100644 --- a/erpnext/accounts/desk_page/accounting/accounting.json +++ b/erpnext/accounts/desk_page/accounting/accounting.json @@ -82,7 +82,12 @@ } ], "category": "Modules", - "charts": [], + "charts": [ + { + "chart_name": "Profit and Loss", + "label": "Profit and Loss" + } + ], "creation": "2020-03-02 15:41:59.515192", "developer_mode_only": 0, "disable_user_customization": 0, @@ -92,7 +97,7 @@ "idx": 0, "is_standard": 1, "label": "Accounting", - "modified": "2020-05-14 22:28:25.262409", + "modified": "2020-05-18 17:27:26.882340", "modified_by": "Administrator", "module": "Accounts", "name": "Accounting", diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 4ae591b54b5..e88e05dd30b 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -685,3 +685,4 @@ execute:frappe.rename_doc("Desk Page", "Getting Started", "Home", force=True) erpnext.patches.v12_0.unset_customer_supplier_based_on_type_of_item_price erpnext.patches.v12_0.set_valid_till_date_in_supplier_quotation erpnext.patches.v12_0.set_serial_no_status +execute:frappe.delete_doc_if_exists('Dashboard', 'Accounts') \ No newline at end of file diff --git a/erpnext/selling/desk_page/selling/selling.json b/erpnext/selling/desk_page/selling/selling.json deleted file mode 100644 index a20806b264e..00000000000 --- a/erpnext/selling/desk_page/selling/selling.json +++ /dev/null @@ -1,85 +0,0 @@ -{ - "cards": [ - { - "hidden": 0, - "label": "Items and Pricing", - "links": "[\n {\n \"description\": \"All Products or Services.\",\n \"label\": \"Item\",\n \"name\": \"Item\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Price List\"\n ],\n \"description\": \"Multiple Item prices.\",\n \"label\": \"Item Price\",\n \"name\": \"Item Price\",\n \"onboard\": 1,\n \"route\": \"#Report/Item Price\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Price List master.\",\n \"label\": \"Price List\",\n \"name\": \"Price List\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tree of Item Groups.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Item Group\",\n \"link\": \"Tree/Item Group\",\n \"name\": \"Item Group\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"description\": \"Bundle items at time of sale.\",\n \"label\": \"Product Bundle\",\n \"name\": \"Product Bundle\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Rules for applying different promotional schemes.\",\n \"label\": \"Promotional Scheme\",\n \"name\": \"Promotional Scheme\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"description\": \"Rules for applying pricing and discount.\",\n \"label\": \"Pricing Rule\",\n \"name\": \"Pricing Rule\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Rules for adding shipping costs.\",\n \"label\": \"Shipping Rule\",\n \"name\": \"Shipping Rule\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Define coupon codes.\",\n \"label\": \"Coupon Code\",\n \"name\": \"Coupon Code\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Settings", - "links": "[\n {\n \"description\": \"Default settings for selling transactions.\",\n \"label\": \"Selling Settings\",\n \"name\": \"Selling Settings\",\n \"settings\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Template of terms or contract.\",\n \"label\": \"Terms and Conditions Template\",\n \"name\": \"Terms and Conditions\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tax template for selling transactions.\",\n \"label\": \"Sales Taxes and Charges Template\",\n \"name\": \"Sales Taxes and Charges Template\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Track Leads by Lead Source.\",\n \"label\": \"Lead Source\",\n \"name\": \"Lead Source\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Manage Customer Group Tree.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Customer Group\",\n \"link\": \"Tree/Customer Group\",\n \"name\": \"Customer Group\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"All Contacts.\",\n \"label\": \"Contact\",\n \"name\": \"Contact\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"All Addresses.\",\n \"label\": \"Address\",\n \"name\": \"Address\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Manage Territory Tree.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Territory\",\n \"link\": \"Tree/Territory\",\n \"name\": \"Territory\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Sales campaigns.\",\n \"label\": \"Campaign\",\n \"name\": \"Campaign\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Other Reports", - "links": "[\n {\n \"dependencies\": [\n \"Lead\"\n ],\n \"doctype\": \"Lead\",\n \"is_query_report\": true,\n \"label\": \"Lead Details\",\n \"name\": \"Lead Details\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Address\"\n ],\n \"doctype\": \"Address\",\n \"is_query_report\": true,\n \"label\": \"Customer Addresses And Contacts\",\n \"name\": \"Address And Contacts\",\n \"route_options\": {\n \"party_type\": \"Customer\"\n },\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"BOM\"\n ],\n \"doctype\": \"BOM\",\n \"is_query_report\": true,\n \"label\": \"BOM Search\",\n \"name\": \"BOM Search\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"doctype\": \"Item\",\n \"is_query_report\": true,\n \"label\": \"Available Stock for Packing Items\",\n \"name\": \"Available Stock for Packing Items\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Pending SO Items For Purchase Request\",\n \"name\": \"Pending SO Items For Purchase Request\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Customer Credit Balance\",\n \"name\": \"Customer Credit Balance\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Customers Without Any Sales Transactions\",\n \"name\": \"Customers Without Any Sales Transactions\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Sales Partners Commission\",\n \"name\": \"Sales Partners Commission\",\n \"type\": \"report\"\n }\n]" - }, - { - "hidden": 0, - "label": "Sales", - "links": "[\n {\n \"description\": \"Customer Database.\",\n \"label\": \"Customer\",\n \"name\": \"Customer\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Quotes to Leads or Customers.\",\n \"label\": \"Quotation\",\n \"name\": \"Quotation\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Confirmed orders from Customers.\",\n \"label\": \"Sales Order\",\n \"name\": \"Sales Order\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Invoices for Costumers.\",\n \"label\": \"Sales Invoice\",\n \"name\": \"Sales Invoice\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Blanket Orders from Costumers.\",\n \"label\": \"Blanket Order\",\n \"name\": \"Blanket Order\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"description\": \"Manage Sales Partners.\",\n \"label\": \"Sales Partner\",\n \"name\": \"Sales Partner\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Manage Sales Person Tree.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Sales Person\",\n \"link\": \"Tree/Sales Person\",\n \"name\": \"Sales Person\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Key Reports", - "links": "[\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Analytics\",\n \"name\": \"Sales Analytics\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"icon\": \"fa fa-bar-chart\",\n \"label\": \"Sales Funnel\",\n \"name\": \"sales-funnel\",\n \"onboard\": 1,\n \"type\": \"page\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"icon\": \"fa fa-bar-chart\",\n \"is_query_report\": true,\n \"label\": \"Customer Acquisition and Loyalty\",\n \"name\": \"Customer Acquisition and Loyalty\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Inactive Customers\",\n \"name\": \"Inactive Customers\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Ordered Items To Be Delivered\",\n \"name\": \"Ordered Items To Be Delivered\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Person-wise Transaction Summary\",\n \"name\": \"Sales Person-wise Transaction Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"doctype\": \"Item\",\n \"is_query_report\": true,\n \"label\": \"Item-wise Sales History\",\n \"name\": \"Item-wise Sales History\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Quotation\"\n ],\n \"doctype\": \"Quotation\",\n \"is_query_report\": true,\n \"label\": \"Quotation Trends\",\n \"name\": \"Quotation Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Order Trends\",\n \"name\": \"Sales Order Trends\",\n \"type\": \"report\"\n }\n]" - } - ], - "category": "Modules", - "charts": [ - { - "chart_name": "Income", - "label": "Income" - } - ], - "creation": "2020-01-28 11:49:12.092882", - "developer_mode_only": 0, - "disable_user_customization": 0, - "docstatus": 0, - "doctype": "Desk Page", - "extends_another_page": 0, - "icon": "", - "idx": 0, - "is_standard": 1, - "label": "Selling", - "modified": "2020-04-01 11:28:51.047373", - "modified_by": "Administrator", - "module": "Selling", - "name": "Selling", - "owner": "Administrator", - "pin_to_bottom": 0, - "pin_to_top": 0, - "shortcuts": [ - { - "label": "Sales Invoice", - "link_to": "Sales Invoice", - "type": "DocType" - }, - { - "label": "Sales Order", - "link_to": "Sales Order", - "type": "DocType" - }, - { - "label": "Quotation", - "link_to": "Quotation", - "type": "DocType" - }, - { - "label": "Delivery Note", - "link_to": "Delivery Note", - "type": "DocType" - }, - { - "label": "Accounts Receivable", - "link_to": "Accounts Receivable", - "type": "Report" - }, - { - "label": "Sales Register", - "link_to": "Sales Register", - "type": "Report" - } - ] -} \ No newline at end of file From 24477d5d0639f0ab824f045d66a58a81b860ab40 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 18 May 2020 18:15:20 +0530 Subject: [PATCH 090/608] feat: added Number Cards to Healthcare Dashboard --- erpnext/healthcare/dashboard_fixtures.py | 79 ++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/erpnext/healthcare/dashboard_fixtures.py b/erpnext/healthcare/dashboard_fixtures.py index 0f5728de9aa..b3c6723be0f 100644 --- a/erpnext/healthcare/dashboard_fixtures.py +++ b/erpnext/healthcare/dashboard_fixtures.py @@ -9,8 +9,19 @@ def get_data(): return frappe._dict({ "dashboards": get_dashboards(), "charts": get_charts(), + "number_cards": get_number_cards(), }) +def get_company(): + company = frappe.defaults.get_defaults().company + if company: + return company + else: + company = frappe.get_list("Company", limit=1) + if company: + return company.name + return None + def get_dashboards(): return [{ "name": "Healthcare", @@ -23,6 +34,12 @@ def get_dashboards(): { "chart": "Diagnoses", "width": "Half"}, { "chart": "Department wise Patient Appointments", "width": "Full"}, { "chart": "Lab Tests", "width": "Full"}, + ], + "cards": [ + { "card": "Total Patients" }, + { "card": "Total Patient Admitted" }, + { "card": "Open Appointments" }, + { "card": "Appointments to Bill" } ] }] @@ -124,3 +141,65 @@ def get_charts(): "width": "Half", } ] + +def get_number_cards(): + return [ + { + "name": "Total Patients", + "label": "Total Patients", + "function": "Count", + "doctype": "Number Card", + "document_type": "Patient", + "filters_json": json.dumps( + [["Patient","status","=","Active",False]] + ), + "is_public": 1, + "owner": "Administrator", + "show_percentage_stats": 1, + "stats_time_interval": "Daily" + }, + { + "name": "Total Patients Admitted", + "label": "Total Patients Admitted", + "function": "Count", + "doctype": "Number Card", + "document_type": "Patient", + "filters_json": json.dumps( + [["Patient","inpatient_status","=","Admitted",False]] + ), + "is_public": 1, + "owner": "Administrator", + "show_percentage_stats": 1, + "stats_time_interval": "Daily" + }, + { + "name": "Open Appointments", + "label": "Open Appointments", + "function": "Count", + "doctype": "Number Card", + "document_type": "Patient Appointment", + "filters_json": json.dumps( + [["Patient Appointment","company","=",get_company(),False], + ["Patient Appointment","status","=","Open",False]] + ), + "is_public": 1, + "owner": "Administrator", + "show_percentage_stats": 1, + "stats_time_interval": "Daily" + }, + { + "name": "Appointments to Bill", + "label": "Appointments to Bill", + "function": "Count", + "doctype": "Number Card", + "document_type": "Patient Appointment", + "filters_json": json.dumps( + [["Patient Appointment","company","=",get_company(),False], + ["Patient Appointment","invoiced","=",0,False]] + ), + "is_public": 1, + "owner": "Administrator", + "show_percentage_stats": 1, + "stats_time_interval": "Daily" + } + ] \ No newline at end of file From 5162faa7df623da56f87883ea29de635ef9697a4 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 18 May 2020 18:59:59 +0530 Subject: [PATCH 091/608] fix: add default filters in Dashboard Charts --- erpnext/healthcare/dashboard_fixtures.py | 28 +++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/erpnext/healthcare/dashboard_fixtures.py b/erpnext/healthcare/dashboard_fixtures.py index b3c6723be0f..647d91ab604 100644 --- a/erpnext/healthcare/dashboard_fixtures.py +++ b/erpnext/healthcare/dashboard_fixtures.py @@ -44,6 +44,7 @@ def get_dashboards(): }] def get_charts(): + company = get_company() return [ { "doctype": "Dashboard Chart", @@ -51,7 +52,10 @@ def get_charts(): "name": "Patient Appointments", "chart_name": "Patient Appointments", "timespan": "Last Month", - "filters_json": json.dumps({}), + "filters_json": json.dumps([ + ["Patient Appointment", "company", "=", company, False], + ["Patient Appointment", "status", "!=", "Cancelled"] + ]), "chart_type": "Count", "timeseries": 1, "based_on": "appointment_datetime", @@ -68,6 +72,10 @@ def get_charts(): "document_type": "Patient Appointment", "group_by_type": "Count", "group_by_based_on": "department", + "filters_json": json.dumps([ + ["Patient Appointment", "company", "=", company, False], + ["Patient Appointment", "status", "!=", "Cancelled"] + ]), 'is_public': 1, "owner": "Administrator", "type": "Bar", @@ -82,6 +90,10 @@ def get_charts(): "document_type": "Lab Test", "group_by_type": "Count", "group_by_based_on": "template", + "filters_json": json.dumps([ + ["Lab Test", "company", "=", company, False], + ["Lab Test", "docstatus", "=", 1] + ]), 'is_public': 1, "owner": "Administrator", "type": "Bar", @@ -96,6 +108,9 @@ def get_charts(): "document_type": "Inpatient Record", "group_by_type": "Count", "group_by_based_on": "status", + "filters_json": json.dumps([ + ["Inpatient Record", "company", "=", company, False] + ]), 'is_public': 1, "owner": "Administrator", "type": "Bar", @@ -109,6 +124,10 @@ def get_charts(): "document_type": "Clinical Procedure", "group_by_type": "Count", "group_by_based_on": "status", + "filters_json": json.dumps([ + ["Clinical Procedure", "company", "=", company, False], + ["Clinical Procedure", "docstatus", "=", 1] + ]), 'is_public': 1, "owner": "Administrator", "type": "Pie", @@ -122,6 +141,7 @@ def get_charts(): "document_type": "Patient Encounter Symptom", "group_by_type": "Count", "group_by_based_on": "complaint", + "filters_json": json.dumps({}), 'is_public': 1, "owner": "Administrator", "type": "Percentage", @@ -135,6 +155,7 @@ def get_charts(): "document_type": "Patient Encounter Diagnosis", "group_by_type": "Count", "group_by_based_on": "diagnosis", + "filters_json": json.dumps({}), 'is_public': 1, "owner": "Administrator", "type": "Percentage", @@ -143,6 +164,7 @@ def get_charts(): ] def get_number_cards(): + company = get_company() return [ { "name": "Total Patients", @@ -179,7 +201,7 @@ def get_number_cards(): "doctype": "Number Card", "document_type": "Patient Appointment", "filters_json": json.dumps( - [["Patient Appointment","company","=",get_company(),False], + [["Patient Appointment","company","=",company,False], ["Patient Appointment","status","=","Open",False]] ), "is_public": 1, @@ -194,7 +216,7 @@ def get_number_cards(): "doctype": "Number Card", "document_type": "Patient Appointment", "filters_json": json.dumps( - [["Patient Appointment","company","=",get_company(),False], + [["Patient Appointment","company","=",company,False], ["Patient Appointment","invoiced","=",0,False]] ), "is_public": 1, From de3751387a27deff9f9dce21fd9aab3b5c99e125 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 18 May 2020 19:15:08 +0530 Subject: [PATCH 092/608] refactor: Lab Tests and Clinical Procedures charts to Percentage type --- erpnext/healthcare/dashboard_fixtures.py | 27 +++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/erpnext/healthcare/dashboard_fixtures.py b/erpnext/healthcare/dashboard_fixtures.py index 647d91ab604..faa83cccf3d 100644 --- a/erpnext/healthcare/dashboard_fixtures.py +++ b/erpnext/healthcare/dashboard_fixtures.py @@ -30,10 +30,11 @@ def get_dashboards(): { "chart": "Patient Appointments", "width": "Full"}, { "chart": "In-Patient Status", "width": "Half"}, { "chart": "Clinical Procedures Status", "width": "Half"}, + { "chart": "Lab Tests", "width": "Half"}, + { "chart": "Clinical Procedures", "width": "Half"}, { "chart": "Symptoms", "width": "Half"}, { "chart": "Diagnoses", "width": "Half"}, - { "chart": "Department wise Patient Appointments", "width": "Full"}, - { "chart": "Lab Tests", "width": "Full"}, + { "chart": "Department wise Patient Appointments", "width": "Full"} ], "cards": [ { "card": "Total Patients" }, @@ -96,9 +97,25 @@ def get_charts(): ]), 'is_public': 1, "owner": "Administrator", - "type": "Bar", - "width": "Full", - "color": "#8548EB" + "type": "Percentage", + "width": "Half", + }, + { + "doctype": "Dashboard Chart", + "name": "Clinical Procedures", + "chart_name": "Clinical Procedures", + "chart_type": "Group By", + "document_type": "Clinical Procedure", + "group_by_type": "Count", + "group_by_based_on": "procedure_template", + "filters_json": json.dumps([ + ["Clinical Procedure", "company", "=", company, False], + ["Clinical Procedure", "docstatus", "=", 1] + ]), + 'is_public': 1, + "owner": "Administrator", + "type": "Percentage", + "width": "Half", }, { "doctype": "Dashboard Chart", From b8db275a357e4d8f8d730e5ff177ae73ba898bab Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 19 May 2020 01:46:48 +0530 Subject: [PATCH 093/608] feat: Department wise Patient Appointments custom chart --- .../dashboard_chart_source/__init__.py | 0 .../__init__.py | 0 .../department_wise_patient_appointments.js | 14 ++++ .../department_wise_patient_appointments.json | 13 ++++ .../department_wise_patient_appointments.py | 75 +++++++++++++++++++ erpnext/healthcare/dashboard_fixtures.py | 19 ++--- 6 files changed, 112 insertions(+), 9 deletions(-) create mode 100644 erpnext/healthcare/dashboard_chart_source/__init__.py create mode 100644 erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/__init__.py create mode 100644 erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.js create mode 100644 erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.json create mode 100644 erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.py diff --git a/erpnext/healthcare/dashboard_chart_source/__init__.py b/erpnext/healthcare/dashboard_chart_source/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/__init__.py b/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.js b/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.js new file mode 100644 index 00000000000..dd6dc666d23 --- /dev/null +++ b/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.js @@ -0,0 +1,14 @@ +frappe.provide('frappe.dashboards.chart_sources'); + +frappe.dashboards.chart_sources["Department wise Patient Appointments"] = { + method: "erpnext.healthcare.dashboard_chart_source.department_wise_patient_appointments.department_wise_patient_appointments.get", + filters: [ + { + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company") + } + ] +}; \ No newline at end of file diff --git a/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.json b/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.json new file mode 100644 index 00000000000..00301ef2c36 --- /dev/null +++ b/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.json @@ -0,0 +1,13 @@ +{ + "creation": "2020-05-18 19:18:42.571045", + "docstatus": 0, + "doctype": "Dashboard Chart Source", + "idx": 0, + "modified": "2020-05-18 19:18:42.571045", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Department wise Patient Appointments", + "owner": "Administrator", + "source_name": "Department wise Patient Appointments", + "timeseries": 0 +} \ No newline at end of file diff --git a/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.py b/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.py new file mode 100644 index 00000000000..3cd987b8b4e --- /dev/null +++ b/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.py @@ -0,0 +1,75 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe, json +from frappe import _ +from frappe.utils.dashboard import cache_source +from erpnext.stock.utils import get_stock_value_from_bin + +@frappe.whitelist() +@cache_source +def get(chart_name = None, chart = None, no_cache = None, filters = None, from_date = None, + to_date = None, timespan = None, time_interval = None, heatmap_year = None): + if chart_name: + chart = frappe.get_doc('Dashboard Chart', chart_name) + else: + chart = frappe._dict(frappe.parse_json(chart)) + + labels, datapoints = [], [] + filters = frappe.parse_json(filters) + + data = frappe.db.get_list('Medical Department', fields=['name']) + if not filters: + filters = {} + + status = ['Open', 'Scheduled', 'Closed', 'Cancelled'] + for department in data: + filters['department'] = department.name + department['total_appointments'] = frappe.db.count('Patient Appointment', filters=filters) + + for entry in status: + filters['status'] = entry + department[frappe.scrub(entry)] = frappe.db.count('Patient Appointment', filters=filters) + filters.pop('status') + + sorted_department_map = sorted(data, key = lambda i: i['total_appointments'],reverse=True) + + if len(sorted_department_map) > 10: + sorted_department_map = sorted_department_map[:10] + + labels = [] + open_appointments = [] + scheduled = [] + closed = [] + cancelled = [] + + for department in sorted_department_map: + labels.append(department.name) + open_appointments.append(department.open) + scheduled.append(department.scheduled) + closed.append(department.closed) + cancelled.append(department.cancelled) + + return { + 'labels': labels, + 'datasets': [ + { + 'name': 'Open', + 'values': open_appointments + }, + { + 'name': 'Scheduled', + 'values': scheduled + }, + { + 'name': 'Closed', + 'values': closed + }, + { + 'name': 'Cancelled', + 'values': cancelled + } + ], + 'type': 'bar' + } \ No newline at end of file diff --git a/erpnext/healthcare/dashboard_fixtures.py b/erpnext/healthcare/dashboard_fixtures.py index faa83cccf3d..4fb05a8f81f 100644 --- a/erpnext/healthcare/dashboard_fixtures.py +++ b/erpnext/healthcare/dashboard_fixtures.py @@ -69,19 +69,20 @@ def get_charts(): "doctype": "Dashboard Chart", "name": "Department wise Patient Appointments", "chart_name": "Department wise Patient Appointments", - "chart_type": "Group By", - "document_type": "Patient Appointment", - "group_by_type": "Count", - "group_by_based_on": "department", - "filters_json": json.dumps([ - ["Patient Appointment", "company", "=", company, False], - ["Patient Appointment", "status", "!=", "Cancelled"] - ]), + "chart_type": "Custom", + "source": "Department wise Patient Appointments", + "filters_json": json.dumps({}), 'is_public': 1, "owner": "Administrator", "type": "Bar", "width": "Full", - "color": "#5F62F6" + "custom_options": json.dumps({ + "colors": ["#7CD5FA", "#5F62F6", "#7544E2", "#EE5555"], + "barOptions":{ + "stacked":1 + }, + "height": 300 + }) }, { "doctype": "Dashboard Chart", From d3d3836943304137dafc792a1cb842f0934f5779 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 19 May 2020 11:53:28 +0530 Subject: [PATCH 094/608] style: cleaner conditions and returns --- .../connectors/woocommerce_connection.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py index 6d379f6ed5f..44f87e0462e 100644 --- a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py +++ b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py @@ -75,14 +75,12 @@ def link_customer_and_address(raw_billing_data, raw_shipping_data, customer_name frappe.rename_doc("Customer", old_name, customer_name) billing_address = frappe.get_doc("Address", {"woocommerce_email": customer_woo_com_email, "address_type": "Billing"}) shipping_address = frappe.get_doc("Address", {"woocommerce_email": customer_woo_com_email, "address_type": "Shipping"}) - else: - billing_address = create_address(raw_billing_data, customer, "Billing") - shipping_address = create_address(raw_shipping_data, customer, "Shipping") - contact = create_contact(raw_billing_data, customer) - - if customer_exists: rename_address(billing_address, customer) rename_address(shipping_address, customer) + else: + create_address(raw_billing_data, customer, "Billing") + create_address(raw_shipping_data, customer, "Shipping") + create_contact(raw_billing_data, customer) def create_contact(data, customer): email = data.get("email", None) @@ -111,8 +109,6 @@ def create_contact(data, customer): contact.flags.ignore_mandatory = True contact.save() - return contact - def create_address(raw_data, customer, address_type): address = frappe.new_doc("Address") @@ -134,8 +130,6 @@ def create_address(raw_data, customer, address_type): address.flags.ignore_mandatory = True address.save() - return address - def rename_address(address, customer): old_address_title = address.name new_address_title = customer.name + "-" + address.address_type From 0799c679d42bb7d3cffdd48a0fe7bacd07453a77 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 19 May 2020 12:07:23 +0530 Subject: [PATCH 095/608] feat: Healthcare Onboarding --- .../desk_page/healthcare/healthcare.json | 3 +- .../clinical_procedure_template.js | 35 +++++++++++++++ .../healthcare_practitioner.js | 35 +++++++++++++++ .../healthcare_settings.js | 34 ++++++++++++++ .../healthcare/healthcare.json | 45 +++++++++++++++++++ .../book_a_patient_appointment.json | 19 ++++++++ .../create_patient/create_patient.json | 19 ++++++++ .../create_practitioner.json | 19 ++++++++ .../create_practitioner_schedule.json | 19 ++++++++ .../explore_clinical_procedure_templates.json | 19 ++++++++ .../explore_healthcare_settings.json | 19 ++++++++ .../explore_lab_test_template.json | 19 ++++++++ ..._employee_for_healthcare_practitioner.json | 20 +++++++++ 13 files changed, 304 insertions(+), 1 deletion(-) create mode 100644 erpnext/healthcare/module_onboarding/healthcare/healthcare.json create mode 100644 erpnext/healthcare/onboarding_step/book_a_patient_appointment/book_a_patient_appointment.json create mode 100644 erpnext/healthcare/onboarding_step/create_patient/create_patient.json create mode 100644 erpnext/healthcare/onboarding_step/create_practitioner/create_practitioner.json create mode 100644 erpnext/healthcare/onboarding_step/create_practitioner_schedule/create_practitioner_schedule.json create mode 100644 erpnext/healthcare/onboarding_step/explore_clinical_procedure_templates/explore_clinical_procedure_templates.json create mode 100644 erpnext/healthcare/onboarding_step/explore_healthcare_settings/explore_healthcare_settings.json create mode 100644 erpnext/healthcare/onboarding_step/explore_lab_test_template/explore_lab_test_template.json create mode 100644 erpnext/healthcare/onboarding_step/setup_schedule_and_employee_for_healthcare_practitioner/setup_schedule_and_employee_for_healthcare_practitioner.json diff --git a/erpnext/healthcare/desk_page/healthcare/healthcare.json b/erpnext/healthcare/desk_page/healthcare/healthcare.json index 4559606a24c..81ba61b682c 100644 --- a/erpnext/healthcare/desk_page/healthcare/healthcare.json +++ b/erpnext/healthcare/desk_page/healthcare/healthcare.json @@ -63,10 +63,11 @@ "idx": 0, "is_standard": 1, "label": "Healthcare", - "modified": "2020-05-18 12:41:04.288871", + "modified": "2020-05-19 11:47:42.118464", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare", + "onboarding": "Healthcare", "owner": "Administrator", "pin_to_bottom": 0, "pin_to_top": 0, diff --git a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.js b/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.js index 57f4cdf3b2f..3f3d606ef81 100644 --- a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.js +++ b/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.js @@ -144,3 +144,38 @@ cur_frm.set_query('item_code', 'items', function() { } }; }); + +frappe.tour['Clinical Procedure Template'] = [ + { + fieldname: 'template', + title: __('Template Name'), + description: __('Enter a name for the Clinical Procedure Template') + }, + { + fieldname: 'item_code', + title: __('Item Code'), + description: __('Set the Item Code which will be used for billing the Clinical Procedure.') + }, + { + fieldname: 'item_group', + title: __('Item Group'), + description: __('Select an Item Group for the Clinical Procedure Item.') + }, + { + fieldname: 'is_billable', + title: __('Clinical Procedure Rate'), + description: __('Check this if the Clinical Procedure is billable and also set the rate.') + }, + { + fieldname: 'consume_stock', + title: __('Consume Stock'), + description: __('Check this if the Clinical Procedure utilises consumables. Click ') + "here" + __(' to know more') + + }, + { + fieldname: 'medical_department', + title: __('Medical Department'), + description: __('You can also set the Medical Department for the template. After saving the document, an Item will automatically be created for billing this Clinical Procedure. You can then use this template while creating Clinical Procedures for Patients. Templates save you from filling up redundant data every single time. You can also create templates for other operations like Lab Tests, Therapy Sessions, etc.') + } +]; + diff --git a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.js b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.js index 4ab3b6e9c1b..fc0b24122ae 100644 --- a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.js +++ b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.js @@ -108,3 +108,38 @@ frappe.ui.form.on('Healthcare Practitioner', 'employee', function(frm) { }); } }); + +frappe.tour['Healthcare Practitioner'] = [ + { + fieldname: 'employee', + title: __('Employee'), + description: __('If you want to track Payroll and other HRMS operations for a Practitoner, create an Employee and link it here.') + }, + { + fieldname: 'practitioner_schedules', + title: __('Practitioner Schedules'), + description: __('Set the Practitioner Schedule you just created. This will be used while booking appointments.') + }, + { + fieldname: 'op_consulting_charge_item', + title: __('Out Patient Consulting Charge Item'), + description: __('Create a service item for Out Patient Consulting.') + }, + { + fieldname: 'inpatient_visit_charge_item', + title: __('Inpatient Visit Charge Item'), + description: __('If this Healthcare Practitioner works for the In-Patient Department, create a service item for Inpatient Visits.') + }, + { + fieldname: 'op_consulting_charge', + title: __('Out Patient Consulting Charge'), + description: __('Set the Out Patient Consulting Charge for this Practitioner.') + + }, + { + fieldname: 'inpatient_visit_charge', + title: __('Inpatient Visit Charge'), + description: __('If this Healthcare Practitioner also works for the In-Patient Department, set the inpatient visit charge for this Practitioner.') + } +]; + diff --git a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.js b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.js index 22fbf5019a6..310ba2e5108 100644 --- a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.js +++ b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.js @@ -39,3 +39,37 @@ var set_query_service_item = function(frm, service_item_field) { }; }); }; + +frappe.tour['Healthcare Settings'] = [ + { + fieldname: 'link_customer_to_patient', + title: __('Link Customer to Patient'), + description: __('If checked, a customer will be created for every Patient. Patient Invoices will be created against this Customer. You can also select existing Customer while creating a Patient. This field is checked by default.') + }, + { + fieldname: 'collect_registration_fee', + title: __('Collect Registration Fee'), + description: __('If your Healthcare facility bills registrations of Patients, you can check this and set the Registration Fee in the field below. Checking this will create new Patients with a Disabled status by default and will only be enabled after invoicing the Registration Fee.') + }, + { + fieldname: 'automate_appointment_invoicing', + title: __('Automate Appointment Invoicing'), + description: __('Checking this will automatically create a Sales Invoice whenever an appointment is booked for a Patient.') + }, + { + fieldname: 'healthcare_service_items', + title: __('Healthcare Service Items'), + description: __('Set up the Healthcare Service Items for billing. Click ') + "here" + __(' to know more') + }, + { + fieldname: 'sb_in_ac', + title: __('Set up default Accounts for the Healthcare Facility'), + description: __('If you wish to override default accounts settings and configure the Income and Receivable accounts for Healthcare, you can do so here.') + + }, + { + fieldname: 'out_patient_sms_alerts', + title: __('Out Patient SMS alerts'), + description: __('You can set up Out Patient SMS alerts here. Click ') + "here" + __(' to know more') + } +]; diff --git a/erpnext/healthcare/module_onboarding/healthcare/healthcare.json b/erpnext/healthcare/module_onboarding/healthcare/healthcare.json new file mode 100644 index 00000000000..fc8e70230e2 --- /dev/null +++ b/erpnext/healthcare/module_onboarding/healthcare/healthcare.json @@ -0,0 +1,45 @@ +{ + "allow_roles": [ + { + "role": "Healthcare Administrator" + } + ], + "creation": "2020-05-19 10:32:43.025852", + "docstatus": 0, + "doctype": "Module Onboarding", + "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/healthcare", + "idx": 0, + "is_complete": 0, + "modified": "2020-05-19 11:48:08.746918", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Healthcare", + "owner": "Administrator", + "steps": [ + { + "step": "Create Patient" + }, + { + "step": "Create Practitioner" + }, + { + "step": "Create Practitioner Schedule" + }, + { + "step": "Setup Schedule and Employee for Healthcare Practitioner" + }, + { + "step": "Explore Healthcare Settings" + }, + { + "step": "Book a Patient Appointment" + }, + { + "step": "Explore Clinical Procedure Templates" + } + ], + "subtitle": "Patients, Practitioner Schedules, Appointments and more.", + "success_message": "Yayy! The Healthcare Module is all set up!", + "title": "Let's Setup the Healthcare Module", + "user_can_dismiss": 1 +} \ No newline at end of file diff --git a/erpnext/healthcare/onboarding_step/book_a_patient_appointment/book_a_patient_appointment.json b/erpnext/healthcare/onboarding_step/book_a_patient_appointment/book_a_patient_appointment.json new file mode 100644 index 00000000000..5f0990931f3 --- /dev/null +++ b/erpnext/healthcare/onboarding_step/book_a_patient_appointment/book_a_patient_appointment.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-05-19 11:39:54.975372", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-19 11:39:54.975372", + "modified_by": "Administrator", + "name": "Book a Patient Appointment", + "owner": "Administrator", + "reference_document": "Patient Appointment", + "show_full_form": 1, + "title": "Book a Patient Appointment", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/healthcare/onboarding_step/create_patient/create_patient.json b/erpnext/healthcare/onboarding_step/create_patient/create_patient.json new file mode 100644 index 00000000000..3bfa144ba28 --- /dev/null +++ b/erpnext/healthcare/onboarding_step/create_patient/create_patient.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-05-19 10:32:27.648902", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-19 10:32:27.648902", + "modified_by": "Administrator", + "name": "Create Patient", + "owner": "Administrator", + "reference_document": "Patient", + "show_full_form": 1, + "title": "Create Patient", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/healthcare/onboarding_step/create_practitioner/create_practitioner.json b/erpnext/healthcare/onboarding_step/create_practitioner/create_practitioner.json new file mode 100644 index 00000000000..3ad60ed0949 --- /dev/null +++ b/erpnext/healthcare/onboarding_step/create_practitioner/create_practitioner.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-05-19 10:39:55.728057", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-19 10:39:55.728057", + "modified_by": "Administrator", + "name": "Create Practitioner", + "owner": "Administrator", + "reference_document": "Healthcare Practitioner", + "show_full_form": 0, + "title": "Create Practitioner", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/healthcare/onboarding_step/create_practitioner_schedule/create_practitioner_schedule.json b/erpnext/healthcare/onboarding_step/create_practitioner_schedule/create_practitioner_schedule.json new file mode 100644 index 00000000000..361921710a3 --- /dev/null +++ b/erpnext/healthcare/onboarding_step/create_practitioner_schedule/create_practitioner_schedule.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-05-19 10:41:19.065753", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-19 10:41:19.065753", + "modified_by": "Administrator", + "name": "Create Practitioner Schedule", + "owner": "Administrator", + "reference_document": "Practitioner Schedule", + "show_full_form": 1, + "title": "Create Practitioner Schedule", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/healthcare/onboarding_step/explore_clinical_procedure_templates/explore_clinical_procedure_templates.json b/erpnext/healthcare/onboarding_step/explore_clinical_procedure_templates/explore_clinical_procedure_templates.json new file mode 100644 index 00000000000..f0c0f612e18 --- /dev/null +++ b/erpnext/healthcare/onboarding_step/explore_clinical_procedure_templates/explore_clinical_procedure_templates.json @@ -0,0 +1,19 @@ +{ + "action": "Show Form Tour", + "creation": "2020-05-19 11:40:51.963741", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-19 11:46:35.085270", + "modified_by": "Administrator", + "name": "Explore Clinical Procedure Templates", + "owner": "Administrator", + "reference_document": "Clinical Procedure Template", + "show_full_form": 0, + "title": "Explore Clinical Procedure Templates", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/healthcare/onboarding_step/explore_healthcare_settings/explore_healthcare_settings.json b/erpnext/healthcare/onboarding_step/explore_healthcare_settings/explore_healthcare_settings.json new file mode 100644 index 00000000000..0bdadade6fd --- /dev/null +++ b/erpnext/healthcare/onboarding_step/explore_healthcare_settings/explore_healthcare_settings.json @@ -0,0 +1,19 @@ +{ + "action": "Show Form Tour", + "creation": "2020-05-19 11:14:33.044989", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 1, + "is_skipped": 0, + "modified": "2020-05-19 11:14:33.044989", + "modified_by": "Administrator", + "name": "Explore Healthcare Settings", + "owner": "Administrator", + "reference_document": "Healthcare Settings", + "show_full_form": 0, + "title": "Explore Healthcare Settings", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/healthcare/onboarding_step/explore_lab_test_template/explore_lab_test_template.json b/erpnext/healthcare/onboarding_step/explore_lab_test_template/explore_lab_test_template.json new file mode 100644 index 00000000000..179c24ec776 --- /dev/null +++ b/erpnext/healthcare/onboarding_step/explore_lab_test_template/explore_lab_test_template.json @@ -0,0 +1,19 @@ +{ + "action": "Show Form Tour", + "creation": "2020-05-19 11:44:35.766626", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-19 11:44:35.766626", + "modified_by": "Administrator", + "name": "Explore Lab Test Template", + "owner": "Administrator", + "reference_document": "Lab Test Template", + "show_full_form": 1, + "title": "Explore Lab Test Template", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/healthcare/onboarding_step/setup_schedule_and_employee_for_healthcare_practitioner/setup_schedule_and_employee_for_healthcare_practitioner.json b/erpnext/healthcare/onboarding_step/setup_schedule_and_employee_for_healthcare_practitioner/setup_schedule_and_employee_for_healthcare_practitioner.json new file mode 100644 index 00000000000..6b78db38f73 --- /dev/null +++ b/erpnext/healthcare/onboarding_step/setup_schedule_and_employee_for_healthcare_practitioner/setup_schedule_and_employee_for_healthcare_practitioner.json @@ -0,0 +1,20 @@ +{ + "action": "Show Form Tour", + "creation": "2020-05-19 10:43:56.231679", + "docstatus": 0, + "doctype": "Onboarding Step", + "field": "schedule", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-19 10:43:56.231679", + "modified_by": "Administrator", + "name": "Setup Schedule and Employee for Healthcare Practitioner", + "owner": "Administrator", + "reference_document": "Healthcare Practitioner", + "show_full_form": 0, + "title": "Setup Schedule and Employee for Healthcare Practitioner", + "validate_action": 0 +} \ No newline at end of file From 84011b79d1c04d1bca4ac971e273f891af892edf Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 19 May 2020 13:00:54 +0530 Subject: [PATCH 096/608] fix: updated manufacturing onboarding and desk --- .../manufacturing/manufacturing.json | 9 +++--- .../downtime_entry/downtime_entry.json | 28 +++++++++++++------ .../manufacturing/manufacturing.json | 7 +++-- .../create_bom/create_bom.json | 4 +-- .../create_product/create_product.json | 4 +-- .../create_raw_materials.json | 19 +++++++++++++ .../explore_manufacturing_settings.json | 20 +++++++++++++ .../onboarding_step/operation/operation.json | 4 +-- .../onboarding_step/warehouse/warehouse.json | 4 +-- .../work_order/work_order.json | 4 +-- .../workstation/workstation.json | 4 +-- .../downtime_analysis/downtime_analysis.js | 8 +++--- .../downtime_analysis/downtime_analysis.py | 21 +++++++++++--- 13 files changed, 101 insertions(+), 35 deletions(-) create mode 100644 erpnext/manufacturing/onboarding_step/create_raw_materials/create_raw_materials.json create mode 100644 erpnext/manufacturing/onboarding_step/explore_manufacturing_settings/explore_manufacturing_settings.json diff --git a/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json b/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json index c181d5d5361..e35f1fb4ead 100644 --- a/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json +++ b/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json @@ -3,7 +3,7 @@ { "hidden": 0, "label": "Production", - "links": "[\n {\n \"dependencies\": [\n \"Item\",\n \"BOM\"\n ],\n \"description\": \"Orders released for production.\",\n \"label\": \"Work Order\",\n \"name\": \"Work Order\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"BOM\"\n ],\n \"description\": \"Generate Material Requests (MRP) and Work Orders.\",\n \"label\": \"Production Plan\",\n \"name\": \"Production Plan\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"label\": \"Stock Entry\",\n \"name\": \"Stock Entry\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Activity Type\"\n ],\n \"description\": \"Time Sheet for manufacturing.\",\n \"label\": \"Timesheet\",\n \"name\": \"Timesheet\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Job Card\",\n \"name\": \"Job Card\",\n \"type\": \"doctype\"\n }\n]" + "links": "[\n {\n \"dependencies\": [\n \"Item\",\n \"BOM\"\n ],\n \"description\": \"Orders released for production.\",\n \"label\": \"Work Order\",\n \"name\": \"Work Order\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"BOM\"\n ],\n \"description\": \"Generate Material Requests (MRP) and Work Orders.\",\n \"label\": \"Production Plan\",\n \"name\": \"Production Plan\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"label\": \"Stock Entry\",\n \"name\": \"Stock Entry\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Job Card\",\n \"name\": \"Job Card\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Downtime Entry\",\n \"name\": \"Downtime Entry\",\n \"type\": \"doctype\"\n }\n]" }, { "hidden": 0, @@ -43,11 +43,10 @@ "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, - "hide_custom": 0, "idx": 0, "is_standard": 1, "label": "Manufacturing", - "modified": "2020-05-19 12:06:25.047481", + "modified": "2020-05-19 12:54:04.104444", "modified_by": "Administrator", "module": "Manufacturing", "name": "Manufacturing", @@ -78,7 +77,7 @@ "label": "Work Order", "link_to": "Work Order", "restrict_to_domain": "Manufacturing", - "stats_filter": "{ \"status\": \n (\n \"in\", [\"Draft\", \"Not Started\", \"In Process\"]\n )\n}", + "stats_filter": "{ \n \"status\": [\"in\", \n [\"Draft\", \"Not Started\", \"In Process\"]\n ]\n}", "type": "DocType" }, { @@ -86,7 +85,7 @@ "label": "Production Plan", "link_to": "Production Plan", "restrict_to_domain": "Manufacturing", - "stats_filter": "{ \"status\": \n (\n \"!=\", \"Completed\"\n )\n}", + "stats_filter": "{ \n \"status\": [\"not in\", [\"Completed\"]]\n}", "type": "DocType" }, { diff --git a/erpnext/manufacturing/doctype/downtime_entry/downtime_entry.json b/erpnext/manufacturing/doctype/downtime_entry/downtime_entry.json index 6ec088ad9e7..9acb4f05133 100644 --- a/erpnext/manufacturing/doctype/downtime_entry/downtime_entry.json +++ b/erpnext/manufacturing/doctype/downtime_entry/downtime_entry.json @@ -13,7 +13,9 @@ "to_time", "downtime", "downtime_reason_section", - "reason" + "stop_reason", + "column_break_9", + "remarks" ], "fields": [ { @@ -41,12 +43,6 @@ "fieldname": "column_break_4", "fieldtype": "Column Break" }, - { - "fieldname": "reason", - "fieldtype": "Text", - "label": "Reason", - "reqd": 1 - }, { "fieldname": "operator", "fieldtype": "Link", @@ -66,10 +62,26 @@ "fieldtype": "Float", "label": "Downtime", "read_only": 1 + }, + { + "fieldname": "stop_reason", + "fieldtype": "Select", + "label": "Stop Reason", + "options": "\nExcessive machine set up time\nUnplanned machine maintenance\nOn-machine press checks\nMachine operator errors\nMachine malfunction\nElectricity down\nOther", + "reqd": 1 + }, + { + "fieldname": "column_break_9", + "fieldtype": "Column Break" + }, + { + "fieldname": "remarks", + "fieldtype": "Text", + "label": "Remarks" } ], "links": [], - "modified": "2020-04-20 17:34:51.299607", + "modified": "2020-05-19 12:59:37.358483", "modified_by": "Administrator", "module": "Manufacturing", "name": "Downtime Entry", diff --git a/erpnext/manufacturing/module_onboarding/manufacturing/manufacturing.json b/erpnext/manufacturing/module_onboarding/manufacturing/manufacturing.json index 4092e9d87cb..952d1f0e071 100644 --- a/erpnext/manufacturing/module_onboarding/manufacturing/manufacturing.json +++ b/erpnext/manufacturing/module_onboarding/manufacturing/manufacturing.json @@ -19,7 +19,7 @@ "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/manufacturing", "idx": 0, "is_complete": 0, - "modified": "2020-05-14 19:12:17.289867", + "modified": "2020-05-19 12:51:42.744570", "modified_by": "Administrator", "module": "Manufacturing", "name": "Manufacturing", @@ -37,6 +37,9 @@ { "step": "Create Product" }, + { + "step": "Create Raw Materials" + }, { "step": "Create BOM" }, @@ -44,7 +47,7 @@ "step": "Work Order" }, { - "step": "Introduction to Manufacturing" + "step": "Explore Manufacturing Settings" } ], "subtitle": "Products, Raw Materials, BOM, Work Order and more.", diff --git a/erpnext/manufacturing/onboarding_step/create_bom/create_bom.json b/erpnext/manufacturing/onboarding_step/create_bom/create_bom.json index 866b871f2b7..84b4088f233 100644 --- a/erpnext/manufacturing/onboarding_step/create_bom/create_bom.json +++ b/erpnext/manufacturing/onboarding_step/create_bom/create_bom.json @@ -8,12 +8,12 @@ "is_mandatory": 1, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-14 19:11:57.153679", + "modified": "2020-05-19 12:51:31.315686", "modified_by": "Administrator", "name": "Create BOM", "owner": "Administrator", "reference_document": "BOM", "show_full_form": 1, - "title": "Create BOM (Bill of Material)", + "title": "Create a BOM (Bill of Material)", "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/manufacturing/onboarding_step/create_product/create_product.json b/erpnext/manufacturing/onboarding_step/create_product/create_product.json index dd2fcb4ce7f..0ffa30158b0 100644 --- a/erpnext/manufacturing/onboarding_step/create_product/create_product.json +++ b/erpnext/manufacturing/onboarding_step/create_product/create_product.json @@ -8,12 +8,12 @@ "is_mandatory": 1, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-14 19:10:39.974370", + "modified": "2020-05-19 12:50:59.010439", "modified_by": "Administrator", "name": "Create Product", "owner": "Administrator", "reference_document": "Item", "show_full_form": 0, - "title": "Create Product (Raw Materials / Finished Good)", + "title": "Create a Finished Good", "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/manufacturing/onboarding_step/create_raw_materials/create_raw_materials.json b/erpnext/manufacturing/onboarding_step/create_raw_materials/create_raw_materials.json new file mode 100644 index 00000000000..0764f2e44a8 --- /dev/null +++ b/erpnext/manufacturing/onboarding_step/create_raw_materials/create_raw_materials.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-05-19 11:53:17.295372", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-19 11:53:25.147837", + "modified_by": "Administrator", + "name": "Create Raw Materials", + "owner": "Administrator", + "reference_document": "Item", + "show_full_form": 0, + "title": "Create Raw Materials", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/manufacturing/onboarding_step/explore_manufacturing_settings/explore_manufacturing_settings.json b/erpnext/manufacturing/onboarding_step/explore_manufacturing_settings/explore_manufacturing_settings.json new file mode 100644 index 00000000000..582aba40d68 --- /dev/null +++ b/erpnext/manufacturing/onboarding_step/explore_manufacturing_settings/explore_manufacturing_settings.json @@ -0,0 +1,20 @@ +{ + "action": "Update Settings", + "creation": "2020-05-19 11:55:11.378374", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 1, + "is_skipped": 0, + "modified": "2020-05-19 12:12:28.145366", + "modified_by": "Administrator", + "name": "Explore Manufacturing Settings", + "owner": "Administrator", + "reference_document": "Manufacturing Settings", + "show_full_form": 0, + "title": "Explore Manufacturing Settings", + "validate_action": 0, + "video_url": "https://www.youtube.com/watch?v=UVGfzwOOZC4" +} \ No newline at end of file diff --git a/erpnext/manufacturing/onboarding_step/operation/operation.json b/erpnext/manufacturing/onboarding_step/operation/operation.json index 86e62e74111..b532e6778c4 100644 --- a/erpnext/manufacturing/onboarding_step/operation/operation.json +++ b/erpnext/manufacturing/onboarding_step/operation/operation.json @@ -8,12 +8,12 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-14 19:10:27.258157", + "modified": "2020-05-19 12:50:41.642754", "modified_by": "Administrator", "name": "Operation", "owner": "Administrator", "reference_document": "Operation", "show_full_form": 0, - "title": "Create Operation", + "title": "Create a Operation", "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/manufacturing/onboarding_step/warehouse/warehouse.json b/erpnext/manufacturing/onboarding_step/warehouse/warehouse.json index f187f57414d..e23bd33b787 100644 --- a/erpnext/manufacturing/onboarding_step/warehouse/warehouse.json +++ b/erpnext/manufacturing/onboarding_step/warehouse/warehouse.json @@ -8,12 +8,12 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-14 19:10:07.439037", + "modified": "2020-05-19 12:50:13.766712", "modified_by": "Administrator", "name": "Warehouse", "owner": "Administrator", "reference_document": "Warehouse", "show_full_form": 0, - "title": "Create Warehouse", + "title": "Create a Warehouse", "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/manufacturing/onboarding_step/work_order/work_order.json b/erpnext/manufacturing/onboarding_step/work_order/work_order.json index e757c23d122..c63363e7cb2 100644 --- a/erpnext/manufacturing/onboarding_step/work_order/work_order.json +++ b/erpnext/manufacturing/onboarding_step/work_order/work_order.json @@ -8,12 +8,12 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-14 19:11:57.145924", + "modified": "2020-05-19 12:51:38.133150", "modified_by": "Administrator", "name": "Work Order", "owner": "Administrator", "reference_document": "Work Order", "show_full_form": 1, - "title": "Create Work Order", + "title": "Create a Work Order", "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/manufacturing/onboarding_step/workstation/workstation.json b/erpnext/manufacturing/onboarding_step/workstation/workstation.json index b786443cbc7..df244bb494a 100644 --- a/erpnext/manufacturing/onboarding_step/workstation/workstation.json +++ b/erpnext/manufacturing/onboarding_step/workstation/workstation.json @@ -8,12 +8,12 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-14 19:10:20.930139", + "modified": "2020-05-19 12:50:33.938176", "modified_by": "Administrator", "name": "Workstation", "owner": "Administrator", "reference_document": "Workstation", "show_full_form": 0, - "title": "Create Workstation / Machine", + "title": "Create a Workstation / Machine", "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.js b/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.js index e20342792f4..ff32dbed98a 100644 --- a/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.js +++ b/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.js @@ -7,15 +7,15 @@ frappe.query_reports["Downtime Analysis"] = { { label: __("From Date"), fieldname:"from_date", - fieldtype: "Date", - default: frappe.datetime.add_months(frappe.datetime.get_today(), -1), + fieldtype: "Datetime", + default: frappe.datetime.add_months(frappe.datetime.now_datetime(), -1), reqd: 1 }, { label: __("To Date"), fieldname:"to_date", - fieldtype: "Date", - default: frappe.datetime.get_today(), + fieldtype: "Datetime", + default: frappe.datetime.now_datetime(), reqd: 1, }, { diff --git a/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.py b/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.py index dfc6b02b84f..2b2be4faa3b 100644 --- a/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.py +++ b/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.py @@ -16,7 +16,7 @@ def execute(filters=None): def get_data(filters): query_filters = {} - fields = ["workstation", "operator", "from_time", "to_time", "downtime", "reason"] + fields = ["name", "workstation", "operator", "from_time", "to_time", "downtime", "stop_reason", "remarks"] query_filters["from_time"] = (">=", filters.get("from_date")) query_filters["to_time"] = ("<=", filters.get("to_date")) @@ -54,6 +54,13 @@ def get_chart_data(data, columns): def get_columns(filters): return [ + { + "label": _("ID"), + "fieldname": "name", + "fieldtype": "Link", + "options": "Downtime Entry", + "width": 100 + }, { "label": _("Machine"), "fieldname": "workstation", @@ -87,9 +94,15 @@ def get_columns(filters): "width": 150 }, { - "label": _("Reason"), - "fieldname": "reason", + "label": _("Stop Reason"), + "fieldname": "stop_reason", + "fieldtype": "Data", + "width": 220 + }, + { + "label": _("Remarks"), + "fieldname": "remarks", "fieldtype": "Text", - "width": 180 + "width": 100 } ] \ No newline at end of file From f472a19889a2853d716b31770539f813837a893e Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 19 May 2020 13:03:22 +0530 Subject: [PATCH 097/608] fix: mark onboarding steps as mandatory --- .../healthcare/healthcare.json | 7 ++----- .../book_a_patient_appointment.json | 19 ------------------- .../create_patient/create_patient.json | 4 ++-- .../create_practitioner.json | 6 +++--- .../create_practitioner_schedule.json | 4 ++-- .../explore_healthcare_settings.json | 4 ++-- .../explore_lab_test_template.json | 19 ------------------- ..._employee_for_healthcare_practitioner.json | 4 ++-- 8 files changed, 13 insertions(+), 54 deletions(-) delete mode 100644 erpnext/healthcare/onboarding_step/book_a_patient_appointment/book_a_patient_appointment.json delete mode 100644 erpnext/healthcare/onboarding_step/explore_lab_test_template/explore_lab_test_template.json diff --git a/erpnext/healthcare/module_onboarding/healthcare/healthcare.json b/erpnext/healthcare/module_onboarding/healthcare/healthcare.json index fc8e70230e2..db35149f876 100644 --- a/erpnext/healthcare/module_onboarding/healthcare/healthcare.json +++ b/erpnext/healthcare/module_onboarding/healthcare/healthcare.json @@ -10,7 +10,7 @@ "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/healthcare", "idx": 0, "is_complete": 0, - "modified": "2020-05-19 11:48:08.746918", + "modified": "2020-05-19 12:52:09.757729", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare", @@ -31,14 +31,11 @@ { "step": "Explore Healthcare Settings" }, - { - "step": "Book a Patient Appointment" - }, { "step": "Explore Clinical Procedure Templates" } ], - "subtitle": "Patients, Practitioner Schedules, Appointments and more.", + "subtitle": "Patients, Practitioner Schedules, Settings and more.", "success_message": "Yayy! The Healthcare Module is all set up!", "title": "Let's Setup the Healthcare Module", "user_can_dismiss": 1 diff --git a/erpnext/healthcare/onboarding_step/book_a_patient_appointment/book_a_patient_appointment.json b/erpnext/healthcare/onboarding_step/book_a_patient_appointment/book_a_patient_appointment.json deleted file mode 100644 index 5f0990931f3..00000000000 --- a/erpnext/healthcare/onboarding_step/book_a_patient_appointment/book_a_patient_appointment.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "action": "Create Entry", - "creation": "2020-05-19 11:39:54.975372", - "docstatus": 0, - "doctype": "Onboarding Step", - "idx": 0, - "is_complete": 0, - "is_mandatory": 0, - "is_single": 0, - "is_skipped": 0, - "modified": "2020-05-19 11:39:54.975372", - "modified_by": "Administrator", - "name": "Book a Patient Appointment", - "owner": "Administrator", - "reference_document": "Patient Appointment", - "show_full_form": 1, - "title": "Book a Patient Appointment", - "validate_action": 1 -} \ No newline at end of file diff --git a/erpnext/healthcare/onboarding_step/create_patient/create_patient.json b/erpnext/healthcare/onboarding_step/create_patient/create_patient.json index 3bfa144ba28..77bc5bd7adf 100644 --- a/erpnext/healthcare/onboarding_step/create_patient/create_patient.json +++ b/erpnext/healthcare/onboarding_step/create_patient/create_patient.json @@ -5,10 +5,10 @@ "doctype": "Onboarding Step", "idx": 0, "is_complete": 0, - "is_mandatory": 0, + "is_mandatory": 1, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-19 10:32:27.648902", + "modified": "2020-05-19 12:26:24.023418", "modified_by": "Administrator", "name": "Create Patient", "owner": "Administrator", diff --git a/erpnext/healthcare/onboarding_step/create_practitioner/create_practitioner.json b/erpnext/healthcare/onboarding_step/create_practitioner/create_practitioner.json index 3ad60ed0949..614b201a58f 100644 --- a/erpnext/healthcare/onboarding_step/create_practitioner/create_practitioner.json +++ b/erpnext/healthcare/onboarding_step/create_practitioner/create_practitioner.json @@ -5,15 +5,15 @@ "doctype": "Onboarding Step", "idx": 0, "is_complete": 0, - "is_mandatory": 0, + "is_mandatory": 1, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-19 10:39:55.728057", + "modified": "2020-05-19 12:27:39.851375", "modified_by": "Administrator", "name": "Create Practitioner", "owner": "Administrator", "reference_document": "Healthcare Practitioner", - "show_full_form": 0, + "show_full_form": 1, "title": "Create Practitioner", "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/healthcare/onboarding_step/create_practitioner_schedule/create_practitioner_schedule.json b/erpnext/healthcare/onboarding_step/create_practitioner_schedule/create_practitioner_schedule.json index 361921710a3..65980ef6687 100644 --- a/erpnext/healthcare/onboarding_step/create_practitioner_schedule/create_practitioner_schedule.json +++ b/erpnext/healthcare/onboarding_step/create_practitioner_schedule/create_practitioner_schedule.json @@ -5,10 +5,10 @@ "doctype": "Onboarding Step", "idx": 0, "is_complete": 0, - "is_mandatory": 0, + "is_mandatory": 1, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-19 10:41:19.065753", + "modified": "2020-05-19 12:27:09.437825", "modified_by": "Administrator", "name": "Create Practitioner Schedule", "owner": "Administrator", diff --git a/erpnext/healthcare/onboarding_step/explore_healthcare_settings/explore_healthcare_settings.json b/erpnext/healthcare/onboarding_step/explore_healthcare_settings/explore_healthcare_settings.json index 0bdadade6fd..2bdab69faa3 100644 --- a/erpnext/healthcare/onboarding_step/explore_healthcare_settings/explore_healthcare_settings.json +++ b/erpnext/healthcare/onboarding_step/explore_healthcare_settings/explore_healthcare_settings.json @@ -5,10 +5,10 @@ "doctype": "Onboarding Step", "idx": 0, "is_complete": 0, - "is_mandatory": 0, + "is_mandatory": 1, "is_single": 1, "is_skipped": 0, - "modified": "2020-05-19 11:14:33.044989", + "modified": "2020-05-19 12:26:48.682673", "modified_by": "Administrator", "name": "Explore Healthcare Settings", "owner": "Administrator", diff --git a/erpnext/healthcare/onboarding_step/explore_lab_test_template/explore_lab_test_template.json b/erpnext/healthcare/onboarding_step/explore_lab_test_template/explore_lab_test_template.json deleted file mode 100644 index 179c24ec776..00000000000 --- a/erpnext/healthcare/onboarding_step/explore_lab_test_template/explore_lab_test_template.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "action": "Show Form Tour", - "creation": "2020-05-19 11:44:35.766626", - "docstatus": 0, - "doctype": "Onboarding Step", - "idx": 0, - "is_complete": 0, - "is_mandatory": 0, - "is_single": 0, - "is_skipped": 0, - "modified": "2020-05-19 11:44:35.766626", - "modified_by": "Administrator", - "name": "Explore Lab Test Template", - "owner": "Administrator", - "reference_document": "Lab Test Template", - "show_full_form": 1, - "title": "Explore Lab Test Template", - "validate_action": 1 -} \ No newline at end of file diff --git a/erpnext/healthcare/onboarding_step/setup_schedule_and_employee_for_healthcare_practitioner/setup_schedule_and_employee_for_healthcare_practitioner.json b/erpnext/healthcare/onboarding_step/setup_schedule_and_employee_for_healthcare_practitioner/setup_schedule_and_employee_for_healthcare_practitioner.json index 6b78db38f73..c5af177e347 100644 --- a/erpnext/healthcare/onboarding_step/setup_schedule_and_employee_for_healthcare_practitioner/setup_schedule_and_employee_for_healthcare_practitioner.json +++ b/erpnext/healthcare/onboarding_step/setup_schedule_and_employee_for_healthcare_practitioner/setup_schedule_and_employee_for_healthcare_practitioner.json @@ -6,10 +6,10 @@ "field": "schedule", "idx": 0, "is_complete": 0, - "is_mandatory": 0, + "is_mandatory": 1, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-19 10:43:56.231679", + "modified": "2020-05-19 12:26:42.492734", "modified_by": "Administrator", "name": "Setup Schedule and Employee for Healthcare Practitioner", "owner": "Administrator", From a5fcaf66f9e6a8a9935af5da680bf1f48e1eca9e Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 19 May 2020 13:05:22 +0530 Subject: [PATCH 098/608] fix: Added filters in number Cards --- erpnext/stock/dashboard_fixtures.py | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/erpnext/stock/dashboard_fixtures.py b/erpnext/stock/dashboard_fixtures.py index db955c39c95..d1a3a146a23 100644 --- a/erpnext/stock/dashboard_fixtures.py +++ b/erpnext/stock/dashboard_fixtures.py @@ -24,7 +24,10 @@ def get_company_for_dashboards(): return None company = frappe.get_doc("Company", get_company_for_dashboards()) -fiscal_year = get_fiscal_year(nowdate(), as_dict=1).get("name") +fiscal_year = get_fiscal_year(nowdate(), as_dict=1) +fiscal_year_name = fiscal_year.get("name") +start_date = str(fiscal_year.get("year_start_date")) +end_date = str(fiscal_year.get("year_end_date")) def get_dashboards(): return [{ @@ -93,7 +96,7 @@ def get_charts(): "filters_json": json.dumps({ "period": "Monthly", "based_on": "Item", - "fiscal_year": fiscal_year, + "fiscal_year": fiscal_year_name, "company": company.name }), "is_custom": 1, @@ -108,14 +111,13 @@ def get_charts(): "chart_type": "Report", "custom_options": json.dumps({ "axisOptions": {"shortenYAxisNumbers": 1}, - "tooltipOptions": {}, - "colors":["#5e64ff"] + "tooltipOptions": {} }), "doctype": "Dashboard Chart", "filters_json": json.dumps({ "period": "Monthly", "based_on": "Item", - "fiscal_year": fiscal_year, + "fiscal_year": fiscal_year_name, "company": company.name, "period_based_on": "posting_date" }), @@ -151,7 +153,8 @@ def get_number_cards(): "document_type": "Purchase Receipt", "filters_json": json.dumps( [["Purchase Receipt","status","=","To Bill",False], - ["Purchase Receipt","company","=", company.name, False]] + ["Purchase Receipt","company","=", company.name, False], + ["Purchase Receipt", "posting_date", "Between", [start_date,end_date], False]] ), "is_public": 1, "owner": "Administrator", @@ -167,7 +170,8 @@ def get_number_cards(): "document_type": "Delivery Note", "filters_json": json.dumps( [["Delivery Note","company","=",company.name,False], - ["Delivery Note","status","=","To Bill",False]] + ["Delivery Note","status","=","To Bill",False], + ["Delivery Note", "posting_date", "Between", [start_date,end_date], False]] ), "is_public": 1, "owner": "Administrator", @@ -182,7 +186,8 @@ def get_number_cards(): "document_type": "Purchase Receipt", "filters_json": json.dumps( [["Purchase Receipt","status","=","To Bill",False], - ["Purchase Receipt","company","=", company.name, False]] + ["Purchase Receipt","company","=", company.name, False], + ["Purchase Receipt", "posting_date", "Between", [start_date,end_date], False]] ), "is_public": 1, "owner": "Administrator", @@ -197,7 +202,8 @@ def get_number_cards(): "document_type": "Delivery Note", "filters_json": json.dumps( [["Delivery Note","company","=",company.name,False], - ["Delivery Note","status","=","To Bill",False]] + ["Delivery Note","status","=","To Bill",False], + ["Delivery Note", "posting_date", "Between", [start_date,end_date], False]] ), "is_public": 1, "owner": "Administrator", From 64b36dba4fef5e2db71f27fbf3b0f37e889443cb Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 19 May 2020 13:27:10 +0530 Subject: [PATCH 099/608] fix: Changed Form Tour order and grammar fixes. --- .../doctype/stock_settings/stock_settings.js | 22 +++++++++---------- .../stock/module_onboarding/stock/stock.json | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.js b/erpnext/stock/doctype/stock_settings/stock_settings.js index 81c60679467..6f9757274d1 100644 --- a/erpnext/stock/doctype/stock_settings/stock_settings.js +++ b/erpnext/stock/doctype/stock_settings/stock_settings.js @@ -22,20 +22,10 @@ frappe.tour['Stock Settings'] = [ title: __("Item Naming By"), description: __("By default, the Item Name is set as per the Item Code entered. If you want Items to be named by a set Naming Series choose the 'Naming Series' option.") }, - { - fieldname: "valuation_method", - title: __("Valuation Method"), - description: __("Choose between FIFO and Moving Average Valuation Methods. Click") + "here" + __("to understand them") - }, - { - fieldname: "show_barcode_field", - title: __("Show Barcode Field"), - description: __("Show 'Scan Barcode' field above every child table to insert Items with ease.") - }, { fieldname: "default_warehouse", title: __("Default Warehouse"), - description: __("Set a Default Warehouse for Inventory Transactions. This will be fetched into the Default Warehouse in the Item master:") + description: __("Set a Default Warehouse for Inventory Transactions. This will be fetched into the Default Warehouse in the Item master.") }, { fieldname: "allow_negative_stock", @@ -43,6 +33,16 @@ frappe.tour['Stock Settings'] = [ description: __("This will allow stock items to be displayed in negative values. Using this option depends on your use case. With this option unchecked, the system warns before obstructing a transaction that is causing negative stock.") }, + { + fieldname: "valuation_method", + title: __("Valuation Method"), + description: __("Choose between FIFO and Moving Average Valuation Methods. Click ") + "here" + __(" to know more about them.") + }, + { + fieldname: "show_barcode_field", + title: __("Show Barcode Field"), + description: __("Show 'Scan Barcode' field above every child table to insert Items with ease.") + }, { fieldname: "automatically_set_serial_nos_based_on_fifo", title: __("Automatically Set Serial Nos based on FIFO"), diff --git a/erpnext/stock/module_onboarding/stock/stock.json b/erpnext/stock/module_onboarding/stock/stock.json index 28d8f672850..5208dcbbf8e 100644 --- a/erpnext/stock/module_onboarding/stock/stock.json +++ b/erpnext/stock/module_onboarding/stock/stock.json @@ -19,7 +19,7 @@ "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/stock", "idx": 0, "is_complete": 0, - "modified": "2020-05-15 04:02:23.634655", + "modified": "2020-05-19 13:13:19.157316", "modified_by": "Administrator", "module": "Stock", "name": "Stock", From 89dab1bc25c093eb915844d11c4f938cba4ad019 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 19 May 2020 13:46:06 +0530 Subject: [PATCH 100/608] refactor: open links in new tab in form tour --- .../department_wise_patient_appointments.py | 3 +-- .../clinical_procedure_template.js | 4 ++-- .../doctype/healthcare_settings/healthcare_settings.js | 4 ++-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.py b/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.py index 3cd987b8b4e..ae3f340b1d6 100644 --- a/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.py +++ b/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.py @@ -16,7 +16,6 @@ def get(chart_name = None, chart = None, no_cache = None, filters = None, from_d else: chart = frappe._dict(frappe.parse_json(chart)) - labels, datapoints = [], [] filters = frappe.parse_json(filters) data = frappe.db.get_list('Medical Department', fields=['name']) @@ -33,7 +32,7 @@ def get(chart_name = None, chart = None, no_cache = None, filters = None, from_d department[frappe.scrub(entry)] = frappe.db.count('Patient Appointment', filters=filters) filters.pop('status') - sorted_department_map = sorted(data, key = lambda i: i['total_appointments'],reverse=True) + sorted_department_map = sorted(data, key = lambda i: i['total_appointments'], reverse=True) if len(sorted_department_map) > 10: sorted_department_map = sorted_department_map[:10] diff --git a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.js b/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.js index 3f3d606ef81..16d4540c7c5 100644 --- a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.js +++ b/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.js @@ -168,8 +168,8 @@ frappe.tour['Clinical Procedure Template'] = [ }, { fieldname: 'consume_stock', - title: __('Consume Stock'), - description: __('Check this if the Clinical Procedure utilises consumables. Click ') + "here" + __(' to know more') + title: __('Allow Stock Consumption'), + description: __('Check this if the Clinical Procedure utilises consumables. Click ') + "here" + __(' to know more') }, { diff --git a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.js b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.js index 310ba2e5108..c266ba86477 100644 --- a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.js +++ b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.js @@ -59,7 +59,7 @@ frappe.tour['Healthcare Settings'] = [ { fieldname: 'healthcare_service_items', title: __('Healthcare Service Items'), - description: __('Set up the Healthcare Service Items for billing. Click ') + "here" + __(' to know more') + description: __('Set up the Healthcare Service Items for billing. Click ') + "here" + __(' to know more') }, { fieldname: 'sb_in_ac', @@ -70,6 +70,6 @@ frappe.tour['Healthcare Settings'] = [ { fieldname: 'out_patient_sms_alerts', title: __('Out Patient SMS alerts'), - description: __('You can set up Out Patient SMS alerts here. Click ') + "here" + __(' to know more') + description: __('You can set up Out Patient SMS alerts here. Click ') + "here" + __(' to know more') } ]; From 658f29915a2bf70ec722aa84b9cebcb8b7875da8 Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 19 May 2020 13:58:13 +0530 Subject: [PATCH 101/608] fix: PR and DN trend report charts, group by fixes --- .../report/delivery_note_trends/delivery_note_trends.py | 9 +++++++-- .../purchase_receipt_trends/purchase_receipt_trends.py | 9 +++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py b/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py index d088b0020d8..40a639bc090 100644 --- a/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py +++ b/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py @@ -12,17 +12,22 @@ def execute(filters=None): conditions = get_columns(filters, "Delivery Note") data = get_data(filters, conditions) - chart_data = get_chart_data(data) + chart_data = get_chart_data(data, filters) return conditions["columns"], data, None, chart_data -def get_chart_data(data): +def get_chart_data(data, filters): if not data: return [] labels, datapoints = [], [] + if filters.get("group_by"): + # consider only consolidated row + data = [row for row in data if row[0]] + if len(data) > 10: + # get top 10 if data too long data = sorted(data, key = lambda i: i[-1],reverse=True) data = data[:10] diff --git a/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.py b/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.py index 627c23b1051..3b8d8d2dcdb 100644 --- a/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.py +++ b/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.py @@ -12,17 +12,22 @@ def execute(filters=None): conditions = get_columns(filters, "Purchase Receipt") data = get_data(filters, conditions) - chart_data = get_chart_data(data) + chart_data = get_chart_data(data, filters) return conditions["columns"], data, None, chart_data -def get_chart_data(data): +def get_chart_data(data, filters): if not data: return [] labels, datapoints = [], [] + if filters.get("group_by"): + # consider only consolidated row + data = [row for row in data if row[0]] + if len(data) > 10: + # get top 10 if data too long data = sorted(data, key = lambda i: i[-1],reverse=True) data = data[:10] From 19ecde21979b7503f937c40d3a069de56a0b21af Mon Sep 17 00:00:00 2001 From: Himanshu Date: Tue, 19 May 2020 14:30:42 +0530 Subject: [PATCH 102/608] fix: add naming series (#21769) --- .../doctype/quality_meeting/quality_meeting.json | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/erpnext/quality_management/doctype/quality_meeting/quality_meeting.json b/erpnext/quality_management/doctype/quality_meeting/quality_meeting.json index 0849fd7aeb0..7691fe35870 100644 --- a/erpnext/quality_management/doctype/quality_meeting/quality_meeting.json +++ b/erpnext/quality_management/doctype/quality_meeting/quality_meeting.json @@ -1,10 +1,12 @@ { - "autoname": "format:MTNG-{date}", + "actions": [], + "autoname": "naming_series:", "creation": "2018-10-15 16:25:41.548432", "doctype": "DocType", "editable_grid": 1, "engine": "InnoDB", "field_order": [ + "naming_series", "date", "cb_00", "status", @@ -53,9 +55,16 @@ "fieldname": "sb_01", "fieldtype": "Section Break", "label": "Minutes" + }, + { + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Naming Series", + "options": "MTNG-.YYYY.-.MM.-.DD.-" } ], - "modified": "2019-07-13 19:57:40.500541", + "links": [], + "modified": "2020-05-19 13:18:59.821740", "modified_by": "Administrator", "module": "Quality Management", "name": "Quality Meeting", From df1cd0ef7084ac44d42be0bb6fd8d7b6f2d994f3 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 19 May 2020 15:38:46 +0530 Subject: [PATCH 103/608] fix: update default dashboard in Healthcare Desk Page --- .../department_wise_patient_appointments.py | 4 +--- erpnext/healthcare/desk_page/healthcare/healthcare.json | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.py b/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.py index ae3f340b1d6..062da6e4654 100644 --- a/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.py +++ b/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.py @@ -2,10 +2,8 @@ # License: GNU General Public License v3. See license.txt from __future__ import unicode_literals -import frappe, json -from frappe import _ +import frappe from frappe.utils.dashboard import cache_source -from erpnext.stock.utils import get_stock_value_from_bin @frappe.whitelist() @cache_source diff --git a/erpnext/healthcare/desk_page/healthcare/healthcare.json b/erpnext/healthcare/desk_page/healthcare/healthcare.json index 81ba61b682c..14ad5e450a9 100644 --- a/erpnext/healthcare/desk_page/healthcare/healthcare.json +++ b/erpnext/healthcare/desk_page/healthcare/healthcare.json @@ -63,7 +63,7 @@ "idx": 0, "is_standard": 1, "label": "Healthcare", - "modified": "2020-05-19 11:47:42.118464", + "modified": "2020-05-19 14:05:10.520457", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare", From 65c90959d1b45ae712e15c94ebda7c9684c390a3 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Tue, 19 May 2020 15:54:24 +0530 Subject: [PATCH 104/608] fix: renamed manufacturing dashboards to manufacturing (#21776) --- erpnext/accounts/dashboard_fixtures.py | 4 ++-- erpnext/manufacturing/dashboard_fixtures.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/dashboard_fixtures.py b/erpnext/accounts/dashboard_fixtures.py index 214e467b5c0..cdd375802db 100644 --- a/erpnext/accounts/dashboard_fixtures.py +++ b/erpnext/accounts/dashboard_fixtures.py @@ -26,8 +26,8 @@ def get_data(): def get_dashboards(): return [{ - "name": "Accounts Dashboard", - "dashboard_name": "Accounts Dashboard", + "name": "Accounts", + "dashboard_name": "Accounts", "doctype": "Dashboard", "charts": [ { "chart": "Profit and Loss" , "width": "Full"}, diff --git a/erpnext/manufacturing/dashboard_fixtures.py b/erpnext/manufacturing/dashboard_fixtures.py index 587a0322415..ef61f230acd 100644 --- a/erpnext/manufacturing/dashboard_fixtures.py +++ b/erpnext/manufacturing/dashboard_fixtures.py @@ -15,8 +15,8 @@ def get_data(): def get_dashboards(): return [{ - "name": "Manufacturing Dashboard", - "dashboard_name": "Manufacturing Dashboard", + "name": "Manufacturing", + "dashboard_name": "Manufacturing", "charts": [ { "chart": "Produced Quantity", "width": "Half" }, { "chart": "Completed Operation", "width": "Half" }, From 31f00b605210d8038e70470319d09fa43d910ad2 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 19 May 2020 15:56:47 +0530 Subject: [PATCH 105/608] refactor: rename dashboards in desk page --- erpnext/accounts/desk_page/accounting/accounting.json | 4 ++-- .../manufacturing/desk_page/manufacturing/manufacturing.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/desk_page/accounting/accounting.json b/erpnext/accounts/desk_page/accounting/accounting.json index a783b1d0dbb..576d10c0247 100644 --- a/erpnext/accounts/desk_page/accounting/accounting.json +++ b/erpnext/accounts/desk_page/accounting/accounting.json @@ -122,8 +122,8 @@ "type": "DocType" }, { - "label": "Accounts Dashboard", - "link_to": "Accounts Dashboard", + "label": "Dashboard", + "link_to": "Accounts", "type": "Dashboard" }, { diff --git a/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json b/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json index e35f1fb4ead..bbc4c991df1 100644 --- a/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json +++ b/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json @@ -95,8 +95,8 @@ "type": "Report" }, { - "label": "Manufacturing Dashboard", - "link_to": "Manufacturing Dashboard", + "label": "Dashboard", + "link_to": "Manufacturing", "restrict_to_domain": "Manufacturing", "type": "Dashboard" } From 8caffe2c712bb7b2d7ab75e10538b9f22cf7d958 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 19 May 2020 16:37:34 +0530 Subject: [PATCH 106/608] refactor: use text editor in issue web form (#21767) --- erpnext/support/web_form/issues/issues.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/support/web_form/issues/issues.json b/erpnext/support/web_form/issues/issues.json index 0f15e4737fd..1df9fb79696 100644 --- a/erpnext/support/web_form/issues/issues.json +++ b/erpnext/support/web_form/issues/issues.json @@ -18,7 +18,7 @@ "is_standard": 1, "login_required": 1, "max_attachment_size": 0, - "modified": "2020-03-06 05:24:05.749664", + "modified": "2020-05-19 13:01:10.729088", "modified_by": "Administrator", "module": "Support", "name": "issues", @@ -76,7 +76,7 @@ { "allow_read_on_all_link_options": 0, "fieldname": "description", - "fieldtype": "Text", + "fieldtype": "Text Editor", "hidden": 0, "label": "Description", "max_length": 0, From 50d4bf578b584475399029f42c075e330a4e6dd3 Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 19 May 2020 17:02:47 +0530 Subject: [PATCH 107/608] fix: Date filters added to dashboards, number cards and minor fixes. --- erpnext/buying/dashboard_fixtures.py | 19 ++++++++++++------- .../module_onboarding/buying/buying.json | 2 +- .../purchase_order_trends.py | 7 +++++-- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/erpnext/buying/dashboard_fixtures.py b/erpnext/buying/dashboard_fixtures.py index 291abb87b04..abb858f1f66 100644 --- a/erpnext/buying/dashboard_fixtures.py +++ b/erpnext/buying/dashboard_fixtures.py @@ -34,10 +34,10 @@ def get_dashboards(): "name": "Buying", "dashboard_name": "Buying", "charts": [ - { "chart": "Top Suppliers", "width": "Full"}, + { "chart": "Purchase Order Trends", "width": "Full"}, { "chart": "Material Request Analysis", "width": "Half"}, { "chart": "Purchase Order Analysis", "width": "Half"}, - { "chart": "Purchase Order Trends", "width": "Full"} + { "chart": "Top Suppliers", "width": "Full"} ], "cards": [ { "card": "Purchase Orders to Receive"}, @@ -80,7 +80,8 @@ def get_charts(): "filters_json": json.dumps( [["Material Request", "status", "not in", ["Draft", "Cancelled", "Stopped", None], False], ["Material Request", "material_request_type", "=", "Purchase", False], - ["Material Request", "company", "=", company.name, False]] + ["Material Request", "company", "=", company.name, False], + ["Material Request", "transaction_date", "Between", [start_date,end_date], False]] ), "group_by_based_on": "status", "group_by_type": "Count", @@ -96,9 +97,11 @@ def get_charts(): "chart_type": "Report", "custom_options": json.dumps({ "type": "line", - "regionFill": 1, "axisOptions": {"shortenYAxisNumbers": 1}, - "tooltipOptions": {} + "tooltipOptions": {}, + "lineOptions": { + "regionFill": 1 + } }), "doctype": "Dashboard Chart", "filters_json": json.dumps({ @@ -144,7 +147,8 @@ def get_number_cards(): "filters_json": json.dumps( [["Purchase Order", "transaction_date", "Between", [start_date,end_date], False], ["Purchase Order", "status", "not in", ["Draft","On Hold","Cancelled","Closed", None], False], - ["Purchase Order", "company", "=", company.name, False]] + ["Purchase Order", "company", "=", company.name, False], + ["Purchase Order", "transaction_date", "Between", [start_date,end_date], False]] ), "function": "Sum", "is_public": 1, @@ -159,7 +163,8 @@ def get_number_cards(): "document_type": "Purchase Order", "filters_json": json.dumps( [["Purchase Order", "status", "in", ["To Receive and Bill", "To Receive", None], False], - ["Purchase Order", "company", "=", company.name, False]] + ["Purchase Order", "company", "=", company.name, False], + ["Purchase Order", "transaction_date", "Between", [start_date,end_date], False]] ), "function": "Count", "is_public": 1, diff --git a/erpnext/buying/module_onboarding/buying/buying.json b/erpnext/buying/module_onboarding/buying/buying.json index 70d6cbb77a4..7de44f087fc 100644 --- a/erpnext/buying/module_onboarding/buying/buying.json +++ b/erpnext/buying/module_onboarding/buying/buying.json @@ -19,7 +19,7 @@ "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/buying", "idx": 0, "is_complete": 0, - "modified": "2020-05-15 23:42:35.625736", + "modified": "2020-05-19 15:37:50.111851", "modified_by": "Administrator", "module": "Buying", "name": "Buying", diff --git a/erpnext/buying/report/purchase_order_trends/purchase_order_trends.py b/erpnext/buying/report/purchase_order_trends/purchase_order_trends.py index 011760dacc3..abe9af979ce 100644 --- a/erpnext/buying/report/purchase_order_trends/purchase_order_trends.py +++ b/erpnext/buying/report/purchase_order_trends/purchase_order_trends.py @@ -46,10 +46,13 @@ def get_chart_data(data, conditions, filters): "labels" : labels, "datasets" : [ { - "name" : _("{0}").format(filters.get("period")) + _(" Revenue"), + "name" : _("{0}").format(filters.get("period")) + _(" Expenditure"), "values" : datapoints } ] }, - "type" : "line" + "type" : "line", + "lineOptions": { + "regionFill": 1 + } } \ No newline at end of file From 007eda71645cf77534c0302cead4bce91b0e37a9 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 19 May 2020 18:53:08 +0530 Subject: [PATCH 108/608] fix: Instate Invoice not appearing in GSTR-1 report --- erpnext/regional/report/gstr_1/gstr_1.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/erpnext/regional/report/gstr_1/gstr_1.py b/erpnext/regional/report/gstr_1/gstr_1.py index fd1cc58c20a..dd5bb4ae9b0 100644 --- a/erpnext/regional/report/gstr_1/gstr_1.py +++ b/erpnext/regional/report/gstr_1/gstr_1.py @@ -60,6 +60,9 @@ class Gstr1Report(object): for inv, items_based_on_rate in self.items_based_on_tax_rate.items(): invoice_details = self.invoices.get(inv) for rate, items in items_based_on_rate.items(): + if inv in self.cgst_igst_invoices: + rate = rate/2 + row, taxable_value = self.get_row_data_for_invoice(inv, invoice_details, rate, items) if self.filters.get("type_of_business") == "CDNR": @@ -118,11 +121,11 @@ class Gstr1Report(object): row.append(invoice_details.get(fieldname)) taxable_value = 0 for item_code, net_amount in self.invoice_items.get(invoice).items(): - if item_code in items: - if self.item_tax_rate.get(invoice) and tax_rate in self.item_tax_rate.get(invoice, {}).get(item_code, []): - taxable_value += abs(net_amount) - elif not self.item_tax_rate.get(invoice): - taxable_value += abs(net_amount) + if item_code in items: + if self.item_tax_rate.get(invoice) and tax_rate in self.item_tax_rate.get(invoice, {}).get(item_code, []): + taxable_value += abs(net_amount) + elif not self.item_tax_rate.get(invoice): + taxable_value += abs(net_amount) row += [tax_rate or 0, taxable_value] @@ -196,7 +199,7 @@ class Gstr1Report(object): if d.item_code not in self.invoice_items.get(d.parent, {}): self.invoice_items.setdefault(d.parent, {}).setdefault(d.item_code, sum(i.get('base_net_amount', 0) for i in items - if i.item_code == d.item_code and i.parent == d.parent)) + if i.item_code == d.item_code and i.parent == d.parent)) item_tax_rate = {} @@ -221,6 +224,8 @@ class Gstr1Report(object): self.items_based_on_tax_rate = {} self.invoice_cess = frappe._dict() + self.cgst_igst_invoices = [] + unidentified_gst_accounts = [] for parent, account, item_wise_tax_detail, tax_amount in self.tax_details: if account in self.gst_accounts.cess_account: @@ -243,6 +248,8 @@ class Gstr1Report(object): tax_rate = tax_amounts[0] if cgst_or_sgst: tax_rate *= 2 + if parent not in self.cgst_igst_invoices: + self.cgst_igst_invoices.append(parent) rate_based_dict = self.items_based_on_tax_rate\ .setdefault(parent, {}).setdefault(tax_rate, []) From f128b1c0c56d8e66754d2138ba56ead60ce4889a Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Tue, 19 May 2020 19:05:42 +0530 Subject: [PATCH 109/608] refactor: changed the fieldtype from data to small text (#21782) --- .../accounts/doctype/sales_invoice/sales_invoice.json | 4 ++-- .../stock/doctype/delivery_note/delivery_note.json | 11 ++++------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index db205891444..63c34ed2056 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -398,7 +398,7 @@ { "allow_on_submit": 1, "fieldname": "po_no", - "fieldtype": "Data", + "fieldtype": "Small Text", "label": "Customer's Purchase Order", "no_copy": 1, "print_hide": 1 @@ -1579,7 +1579,7 @@ "idx": 181, "is_submittable": 1, "links": [], - "modified": "2020-04-29 13:37:09.355300", + "modified": "2020-05-19 17:00:57.208696", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice", diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json index 9f5dee901ce..84d2057f960 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.json +++ b/erpnext/stock/doctype/delivery_note/delivery_note.json @@ -24,10 +24,10 @@ "return_against", "customer_po_details", "po_no", - "section_break_18", - "pick_list", "column_break_17", "po_date", + "section_break_18", + "pick_list", "contact_info", "shipping_address_name", "shipping_address", @@ -296,7 +296,6 @@ }, { "collapsible": 1, - "collapsible_depends_on": "po_no", "fieldname": "customer_po_details", "fieldtype": "Section Break", "label": "Customer PO Details" @@ -304,7 +303,7 @@ { "allow_on_submit": 1, "fieldname": "po_no", - "fieldtype": "Data", + "fieldtype": "Small Text", "label": "Customer's Purchase Order No", "no_copy": 1, "oldfieldname": "po_no", @@ -318,7 +317,6 @@ "fieldtype": "Column Break" }, { - "depends_on": "eval:doc.po_no", "fieldname": "po_date", "fieldtype": "Date", "label": "Customer's Purchase Order Date", @@ -326,7 +324,6 @@ "oldfieldtype": "Data", "print_hide": 1, "print_width": "100px", - "read_only": 1, "width": "100px" }, { @@ -1256,7 +1253,7 @@ "idx": 146, "is_submittable": 1, "links": [], - "modified": "2020-04-17 12:51:41.288600", + "modified": "2020-05-19 17:03:45.880106", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note", From 8b686a5721b16d144737f280eb9c2db3a446e70c Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 19 May 2020 19:08:30 +0530 Subject: [PATCH 110/608] feat: stock module dashboard and onboarding --- erpnext/accounts/dashboard_fixtures.py | 4 +- .../warehouse_wise_stock_value.py | 2 +- erpnext/stock/dashboard_fixtures.py | 194 +++++++----------- erpnext/stock/desk_page/stock/stock.json | 8 +- .../stock/module_onboarding/stock/stock.json | 14 +- .../create_a_purchase_receipt.json} | 10 +- .../create_a_supplier.json} | 10 +- ...oduction_to_price_list_and_item_price.json | 19 -- .../introduction_to_stock_entry.json | 4 +- .../setup_your_warehouse.json | 20 ++ .../delivery_note_trends.py | 2 +- .../purchase_receipt_trends.py | 2 +- .../stock/report/stock_ageing/stock_ageing.py | 4 +- 13 files changed, 132 insertions(+), 161 deletions(-) rename erpnext/stock/onboarding_step/{create_a_price_list/create_a_price_list.json => create_a_purchase_receipt/create_a_purchase_receipt.json} (56%) rename erpnext/stock/onboarding_step/{create_a_warehouse/create_a_warehouse.json => create_a_supplier/create_a_supplier.json} (57%) delete mode 100644 erpnext/stock/onboarding_step/introduction_to_price_list_and_item_price/introduction_to_price_list_and_item_price.json create mode 100644 erpnext/stock/onboarding_step/setup_your_warehouse/setup_your_warehouse.json diff --git a/erpnext/accounts/dashboard_fixtures.py b/erpnext/accounts/dashboard_fixtures.py index cdd375802db..1eed5a0f9ca 100644 --- a/erpnext/accounts/dashboard_fixtures.py +++ b/erpnext/accounts/dashboard_fixtures.py @@ -81,7 +81,7 @@ def get_charts(): "timespan": "Last Year", "color": "#a83333", "value_based_on": "base_net_total", - "filters_json": json.dumps({"docstatus": 1}), + "filters_json": json.dumps([["Purchase Invoice", "docstatus", "=", 1]]), "chart_type": "Sum", "timeseries": 1, "based_on": "posting_date", @@ -99,7 +99,7 @@ def get_charts(): "timespan": "Last Year", "color": "#7b933d", "value_based_on": "base_net_total", - "filters_json": json.dumps({"docstatus": 1}), + "filters_json": json.dumps([["Sales Invoice", "docstatus", "=", 1]]), "chart_type": "Sum", "timeseries": 1, "based_on": "posting_date", diff --git a/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py b/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py index 05a50687524..374a34ea7ca 100644 --- a/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py +++ b/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py @@ -29,7 +29,7 @@ def get(chart_name = None, chart = None, no_cache = None, filters = None, from_d if not warehouses: return [] - sorted_warehouse_map = sorted(warehouses, key = lambda i: i['balance'],reverse=True) + sorted_warehouse_map = sorted(warehouses, key = lambda i: i['balance'], reverse=True) if len(sorted_warehouse_map) > 10: sorted_warehouse_map = sorted_warehouse_map[:10] diff --git a/erpnext/stock/dashboard_fixtures.py b/erpnext/stock/dashboard_fixtures.py index d1a3a146a23..0f1fd128f0b 100644 --- a/erpnext/stock/dashboard_fixtures.py +++ b/erpnext/stock/dashboard_fixtures.py @@ -3,6 +3,7 @@ import frappe import json +from frappe import _ from frappe.utils import nowdate from erpnext.accounts.utils import get_fiscal_year @@ -34,39 +35,72 @@ def get_dashboards(): "name": "Stock", "dashboard_name": "Stock", "charts": [ - { "chart": "Item Shortage Summary", "width": "Half"}, - { "chart": "Stock Ageing", "width": "Half"}, - { "chart": "Item Wise Annual Revenue", "width": "Half"}, - { "chart": "Item Wise Annual Expenditure", "width": "Half"}, - { "chart": "Warehouse wise Stock Value", "width": "Full"} + { "chart": "Warehouse wise Stock Value", "width": "Full"}, + { "chart": "Purchase Receipt Trends", "width": "Half"}, + { "chart": "Delivery Trends", "width": "Half"}, + { "chart": "Oldest Items", "width": "Half"}, + { "chart": "Item Shortage Summary", "width": "Half"} ], "cards": [ - { "card": "Purchase Receipts to Bill"}, - { "card": "Amount Payable against Receipt"}, - { "card": "Delivery Notes to Bill"}, - { "card": "Amount Receivable against Delivery"} + { "card": "Total Active Items"}, + { "card": "Total Warehouses"}, + { "card": "Total Stock Value"} ] }] def get_charts(): return [ { - "name": "Item Shortage Summary", - "chart_name": "Item Shortage Summary", - "chart_type": "Report", "doctype": "Dashboard Chart", - "filters_json": json.dumps({ - "company": company.name - }), - "is_custom": 1, + "name": "Purchase Receipt Trends", + "time_interval": "Monthly", + "chart_name": _("Purchase Receipt Trends"), + "timespan": "Last Year", + "color": "#7b933d", + "value_based_on": "base_net_total", + "filters_json": json.dumps([["Purchase Receipt", "docstatus", "=", 1]]), + "chart_type": "Sum", + "timeseries": 1, + "based_on": "posting_date", + "owner": "Administrator", + "document_type": "Purchase Receipt", + "type": "Bar", + "width": "Half", + "is_public": 1 + }, + { + "doctype": "Dashboard Chart", + "name": "Delivery Trends", + "time_interval": "Monthly", + "chart_name": _("Delivery Trends"), + "timespan": "Last Year", + "color": "#7b933d", + "value_based_on": "base_net_total", + "filters_json": json.dumps([["Delivery Note", "docstatus", "=", 1]]), + "chart_type": "Sum", + "timeseries": 1, + "based_on": "posting_date", + "owner": "Administrator", + "document_type": "Delivery Note", + "type": "Bar", + "width": "Half", + "is_public": 1 + }, + { + "name": "Warehouse wise Stock Value", + "chart_name": _("Warehouse wise Stock Value"), + "chart_type": "Custom", + "doctype": "Dashboard Chart", + "filters_json": json.dumps({}), + "is_custom": 0, "is_public": 1, "owner": "Administrator", - "report_name": "Item Shortage Report", + "source": "Warehouse wise Stock Value", "type": "Bar" }, { - "name": "Stock Ageing", - "chart_name": "Stock Ageing", + "name": "Oldest Items", + "chart_name": _("Oldest Items"), "chart_type": "Report", "custom_options": json.dumps({ "colors": ["#5e64ff"] @@ -84,127 +118,55 @@ def get_charts(): "type": "Bar" }, { - "name": "Item Wise Annual Revenue", - "chart_name": "Item Wise Annual Revenue", + "name": "Item Shortage Summary", + "chart_name": _("Item Shortage Summary"), "chart_type": "Report", - "custom_options": json.dumps({ - "axisOptions": {"shortenYAxisNumbers": 1}, - "tooltipOptions": {}, - "colors":["#5e64ff"] - }), "doctype": "Dashboard Chart", "filters_json": json.dumps({ - "period": "Monthly", - "based_on": "Item", - "fiscal_year": fiscal_year_name, "company": company.name }), "is_custom": 1, "is_public": 1, "owner": "Administrator", - "report_name": "Delivery Note Trends", - "type": "Bar" - }, - { - "name": "Item Wise Annual Expenditure", - "chart_name": "Item Wise Annual Expenditure", - "chart_type": "Report", - "custom_options": json.dumps({ - "axisOptions": {"shortenYAxisNumbers": 1}, - "tooltipOptions": {} - }), - "doctype": "Dashboard Chart", - "filters_json": json.dumps({ - "period": "Monthly", - "based_on": "Item", - "fiscal_year": fiscal_year_name, - "company": company.name, - "period_based_on": "posting_date" - }), - "is_custom": 1, - "is_public": 1, - "owner": "Administrator", - "report_name": "Purchase Receipt Trends", - "type": "Bar" - }, - { - "name": "Warehouse wise Stock Value", - "chart_name": "Warehouse wise Stock Value", - "chart_type": "Custom", - "doctype": "Dashboard Chart", - "filters_json": json.dumps({}), - "is_custom": 0, - "is_public": 1, - "owner": "Administrator", - "source": "Warehouse wise Stock Value", + "report_name": "Item Shortage Report", "type": "Bar" } - ] def get_number_cards(): return [ { - "name": "Amount Payable against Receipt", - "label": "Amount Payable against Receipt", - "function": "Sum", - "aggregate_function_based_on": "base_grand_total", - "doctype": "Number Card", - "document_type": "Purchase Receipt", - "filters_json": json.dumps( - [["Purchase Receipt","status","=","To Bill",False], - ["Purchase Receipt","company","=", company.name, False], - ["Purchase Receipt", "posting_date", "Between", [start_date,end_date], False]] - ), - "is_public": 1, - "owner": "Administrator", - "show_percentage_stats": 1, - "stats_time_interval": "Daily" - }, - { - "name": "Amount Receivable against Delivery", - "label": "Amount Receivable against Delivery", - "function": "Sum", - "aggregate_function_based_on": "base_grand_total", - "doctype": "Number Card", - "document_type": "Delivery Note", - "filters_json": json.dumps( - [["Delivery Note","company","=",company.name,False], - ["Delivery Note","status","=","To Bill",False], - ["Delivery Note", "posting_date", "Between", [start_date,end_date], False]] - ), - "is_public": 1, - "owner": "Administrator", - "show_percentage_stats": 1, - "stats_time_interval": "Daily" - }, - { - "name": "Purchase Receipts to Bill", - "label": "Purchase Receipts to Bill", + "name": "Total Active Items", + "label": _("Total Active Items"), "function": "Count", "doctype": "Number Card", - "document_type": "Purchase Receipt", - "filters_json": json.dumps( - [["Purchase Receipt","status","=","To Bill",False], - ["Purchase Receipt","company","=", company.name, False], - ["Purchase Receipt", "posting_date", "Between", [start_date,end_date], False]] - ), + "document_type": "Item", + "filters_json": json.dumps([["Item", "disabled", "=", 0]]), "is_public": 1, "owner": "Administrator", "show_percentage_stats": 1, - "stats_time_interval": "Daily" + "stats_time_interval": "Monthly" }, { - "name": "Delivery Notes to Bill", - "label": "Delivery Notes to Bill", + "name": "Total Warehouses", + "label": _("Total Warehouses"), "function": "Count", "doctype": "Number Card", - "document_type": "Delivery Note", - "filters_json": json.dumps( - [["Delivery Note","company","=",company.name,False], - ["Delivery Note","status","=","To Bill",False], - ["Delivery Note", "posting_date", "Between", [start_date,end_date], False]] - ), + "document_type": "Warehouse", + "filters_json": json.dumps([["Warehouse", "disabled", "=", 0]]), + "is_public": 1, + "owner": "Administrator", + "show_percentage_stats": 1, + "stats_time_interval": "Monthly" + }, + { + "name": "Total Stock Value", + "label": _("Total Stock Value"), + "function": "Sum", + "aggregate_function_based_on": "stock_value", + "doctype": "Number Card", + "document_type": "Bin", + "filters_json": json.dumps([]), "is_public": 1, "owner": "Administrator", "show_percentage_stats": 1, diff --git a/erpnext/stock/desk_page/stock/stock.json b/erpnext/stock/desk_page/stock/stock.json index 6b93449ecd4..4506664c1ed 100644 --- a/erpnext/stock/desk_page/stock/stock.json +++ b/erpnext/stock/desk_page/stock/stock.json @@ -54,10 +54,11 @@ "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, + "hide_custom": 0, "idx": 0, "is_standard": 1, "label": "Stock", - "modified": "2020-05-15 04:11:36.326013", + "modified": "2020-05-19 17:36:08.185652", "modified_by": "Administrator", "module": "Stock", "name": "Stock", @@ -112,6 +113,11 @@ "label": "Stock Balance", "link_to": "Stock Balance", "type": "Report" + }, + { + "label": "Stock Dashboard", + "link_to": "Stock", + "type": "Dashboard" } ], "shortcuts_label": "Quick Access" diff --git a/erpnext/stock/module_onboarding/stock/stock.json b/erpnext/stock/module_onboarding/stock/stock.json index 5208dcbbf8e..de24575a140 100644 --- a/erpnext/stock/module_onboarding/stock/stock.json +++ b/erpnext/stock/module_onboarding/stock/stock.json @@ -19,21 +19,18 @@ "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/stock", "idx": 0, "is_complete": 0, - "modified": "2020-05-19 13:13:19.157316", + "modified": "2020-05-19 19:03:23.602423", "modified_by": "Administrator", "module": "Stock", "name": "Stock", "owner": "Administrator", "steps": [ { - "step": "Create a Warehouse" + "step": "Setup your Warehouse" }, { "step": "Create a Product" }, - { - "step": "Stock Settings" - }, { "step": "Introduction to Stock Entry" }, @@ -41,10 +38,13 @@ "step": "Create a Stock Entry" }, { - "step": "Introduction to Price List and Item Price" + "step": "Create a Supplier" }, { - "step": "Create a Price List" + "step": "Create a Purchase Receipt" + }, + { + "step": "Stock Settings" } ], "subtitle": "Inventory, Warehouses, Analysis and more.", diff --git a/erpnext/stock/onboarding_step/create_a_price_list/create_a_price_list.json b/erpnext/stock/onboarding_step/create_a_purchase_receipt/create_a_purchase_receipt.json similarity index 56% rename from erpnext/stock/onboarding_step/create_a_price_list/create_a_price_list.json rename to erpnext/stock/onboarding_step/create_a_purchase_receipt/create_a_purchase_receipt.json index ce5b5ecf86e..b7811a46df4 100644 --- a/erpnext/stock/onboarding_step/create_a_price_list/create_a_price_list.json +++ b/erpnext/stock/onboarding_step/create_a_purchase_receipt/create_a_purchase_receipt.json @@ -1,6 +1,6 @@ { "action": "Create Entry", - "creation": "2020-05-15 03:26:41.917046", + "creation": "2020-05-19 18:59:13.266713", "docstatus": 0, "doctype": "Onboarding Step", "idx": 0, @@ -8,12 +8,12 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-15 03:33:09.305991", + "modified": "2020-05-19 18:59:13.266713", "modified_by": "Administrator", - "name": "Create a Price List", + "name": "Create a Purchase Receipt", "owner": "Administrator", - "reference_document": "Price List", + "reference_document": "Purchase Receipt", "show_full_form": 1, - "title": "Create a Price List", + "title": "Create a Purchase Receipt", "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/stock/onboarding_step/create_a_warehouse/create_a_warehouse.json b/erpnext/stock/onboarding_step/create_a_supplier/create_a_supplier.json similarity index 57% rename from erpnext/stock/onboarding_step/create_a_warehouse/create_a_warehouse.json rename to erpnext/stock/onboarding_step/create_a_supplier/create_a_supplier.json index 3269125efc2..7a64224bd43 100644 --- a/erpnext/stock/onboarding_step/create_a_warehouse/create_a_warehouse.json +++ b/erpnext/stock/onboarding_step/create_a_supplier/create_a_supplier.json @@ -1,6 +1,6 @@ { "action": "Create Entry", - "creation": "2020-05-12 18:00:03.027704", + "creation": "2020-05-14 22:09:10.043554", "docstatus": 0, "doctype": "Onboarding Step", "idx": 0, @@ -8,12 +8,12 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-12 18:27:44.128737", + "modified": "2020-05-14 22:09:10.043554", "modified_by": "Administrator", - "name": "Create a Warehouse", + "name": "Create a Supplier", "owner": "Administrator", - "reference_document": "Warehouse", + "reference_document": "Supplier", "show_full_form": 0, - "title": "Setup your Purchase Warehouse", + "title": "Create a Supplier", "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/stock/onboarding_step/introduction_to_price_list_and_item_price/introduction_to_price_list_and_item_price.json b/erpnext/stock/onboarding_step/introduction_to_price_list_and_item_price/introduction_to_price_list_and_item_price.json deleted file mode 100644 index f75523ab298..00000000000 --- a/erpnext/stock/onboarding_step/introduction_to_price_list_and_item_price/introduction_to_price_list_and_item_price.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "action": "Watch Video", - "creation": "2020-05-15 03:26:01.386069", - "docstatus": 0, - "doctype": "Onboarding Step", - "idx": 0, - "is_complete": 0, - "is_mandatory": 0, - "is_single": 0, - "is_skipped": 0, - "modified": "2020-05-15 03:31:01.267728", - "modified_by": "Administrator", - "name": "Introduction to Price List and Item Price", - "owner": "Administrator", - "show_full_form": 0, - "title": "Let's take a brief look at Price List and Item Price", - "validate_action": 1, - "video_url": "https://www.youtube.com/watch?v=lY6hAQM1I28" -} \ No newline at end of file diff --git a/erpnext/stock/onboarding_step/introduction_to_stock_entry/introduction_to_stock_entry.json b/erpnext/stock/onboarding_step/introduction_to_stock_entry/introduction_to_stock_entry.json index 229bcd49cf8..447611fe475 100644 --- a/erpnext/stock/onboarding_step/introduction_to_stock_entry/introduction_to_stock_entry.json +++ b/erpnext/stock/onboarding_step/introduction_to_stock_entry/introduction_to_stock_entry.json @@ -8,12 +8,12 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-15 03:30:39.606147", + "modified": "2020-05-19 18:55:41.457289", "modified_by": "Administrator", "name": "Introduction to Stock Entry", "owner": "Administrator", "show_full_form": 0, - "title": "Introduction to the backbone of Stock, Stock Entry.", + "title": "Introduction to the multi-purpose stock transaction", "validate_action": 1, "video_url": "https://www.youtube.com/watch?v=Njt107hlY3I" } \ No newline at end of file diff --git a/erpnext/stock/onboarding_step/setup_your_warehouse/setup_your_warehouse.json b/erpnext/stock/onboarding_step/setup_your_warehouse/setup_your_warehouse.json new file mode 100644 index 00000000000..557c905bd6c --- /dev/null +++ b/erpnext/stock/onboarding_step/setup_your_warehouse/setup_your_warehouse.json @@ -0,0 +1,20 @@ +{ + "action": "Go to Page", + "creation": "2020-05-19 18:54:19.383397", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-19 18:54:19.383397", + "modified_by": "Administrator", + "name": "Setup your Warehouse", + "owner": "Administrator", + "path": "Tree/Warehouse", + "reference_document": "Warehouse", + "show_full_form": 0, + "title": "Setup your Warehouse", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py b/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py index 40a639bc090..5a931e7efac 100644 --- a/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py +++ b/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py @@ -40,7 +40,7 @@ def get_chart_data(data, filters): "labels" : labels, "datasets" : [ { - "name": _("Total Revenue"), + "name": _("Total Delivered Amount"), "values": datapoints } ] diff --git a/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.py b/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.py index 3b8d8d2dcdb..43f1f373d35 100644 --- a/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.py +++ b/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.py @@ -40,7 +40,7 @@ def get_chart_data(data, filters): "labels" : labels, "datasets" : [ { - "name": _("Total Expenditure"), + "name": _("Total Received Amount"), "values": datapoints } ] diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.py b/erpnext/stock/report/stock_ageing/stock_ageing.py index c5b8f43f968..af997801551 100644 --- a/erpnext/stock/report/stock_ageing/stock_ageing.py +++ b/erpnext/stock/report/stock_ageing/stock_ageing.py @@ -53,7 +53,7 @@ def get_average_age(fifo_queue, to_date): age_qty += batch_age * 1 total_qty += 1 - return (age_qty / total_qty) if total_qty else 0.0 + return flt(age_qty / total_qty, 2) if total_qty else 0.0 def get_columns(filters): columns = [ @@ -242,6 +242,8 @@ def get_chart_data(data, filters): if filters.get("show_warehouse_wise_stock"): return {} + data.sort(key = lambda row: row[6], reverse=True) + if len(data) > 10: data = data[:10] From aa85e511da996626014a1bf5c93917d3b87e5a37 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Tue, 19 May 2020 19:51:45 +0530 Subject: [PATCH 111/608] feat: Production Planning Against Sales Order/Material Request/Work Order (#21763) Co-authored-by: Marica --- .../manufacturing/manufacturing.json | 4 +- .../report/bom_operations_time/__init__.py | 0 .../bom_operations_time.js | 9 + .../bom_operations_time.json | 28 ++ .../bom_operations_time.py | 112 ++++++ .../production_planning_report/__init__.py | 0 .../production_planning_report.js | 111 ++++++ .../production_planning_report.json | 31 ++ .../production_planning_report.py | 371 ++++++++++++++++++ erpnext/patches.txt | 1 + erpnext/patches/v12_0/update_bom_in_so_mr.py | 19 + .../doctype/sales_order/sales_order.js | 9 + .../sales_order_item/sales_order_item.json | 21 +- .../material_request/material_request.js | 11 +- .../material_request_item.json | 20 +- erpnext/stock/get_item_details.py | 3 +- 16 files changed, 741 insertions(+), 9 deletions(-) create mode 100644 erpnext/manufacturing/report/bom_operations_time/__init__.py create mode 100644 erpnext/manufacturing/report/bom_operations_time/bom_operations_time.js create mode 100644 erpnext/manufacturing/report/bom_operations_time/bom_operations_time.json create mode 100644 erpnext/manufacturing/report/bom_operations_time/bom_operations_time.py create mode 100644 erpnext/manufacturing/report/production_planning_report/__init__.py create mode 100644 erpnext/manufacturing/report/production_planning_report/production_planning_report.js create mode 100644 erpnext/manufacturing/report/production_planning_report/production_planning_report.json create mode 100644 erpnext/manufacturing/report/production_planning_report/production_planning_report.py create mode 100644 erpnext/patches/v12_0/update_bom_in_so_mr.py diff --git a/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json b/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json index bbc4c991df1..ecd2dc9b769 100644 --- a/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json +++ b/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json @@ -13,7 +13,7 @@ { "hidden": 0, "label": "Reports", - "links": "[{\n\t\"dependencies\": [\"Work Order\"],\n\t\"name\": \"Work Order Summary\",\n\t\"is_query_report\": true,\n\t\"type\": \"report\",\n\t\"doctype\": \"Work Order\",\n\t\"label\": \"Work Order Summary\"\n}, {\n\t\"dependencies\": [\"Work Order\"],\n\t\"name\": \"Production Analytics\",\n\t\"is_query_report\": true,\n\t\"type\": \"report\",\n\t\"doctype\": \"Work Order\",\n\t\"label\": \"Production Analytics\"\n}, {\n\t\"dependencies\": [\"Quality Inspection\"],\n\t\"name\": \"Quality Inspection Summary\",\n\t\"is_query_report\": true,\n\t\"type\": \"report\",\n\t\"doctype\": \"Quality Inspection\",\n\t\"label\": \"Quality Inspection Summary\"\n}, {\n\t\"dependencies\": [\"Downtime Entry\"],\n\t\"name\": \"Downtime Analysis\",\n\t\"is_query_report\": true,\n\t\"type\": \"report\",\n\t\"doctype\": \"Downtime Entry\",\n\t\"label\": \"Downtime Analysis\"\n}, {\n\t\"dependencies\": [\"Job Card\"],\n\t\"name\": \"Job Card Summary\",\n\t\"is_query_report\": true,\n\t\"type\": \"report\",\n\t\"doctype\": \"Job Card\",\n\t\"label\": \"Job Card Summary\"\n}, {\n\t\"dependencies\": [\"BOM\"],\n\t\"name\": \"BOM Search\",\n\t\"is_query_report\": true,\n\t\"type\": \"report\",\n\t\"doctype\": \"BOM\",\n\t\"label\": \"BOM Search\"\n}, {\n\t\"dependencies\": [\"BOM\"],\n\t\"name\": \"BOM Stock Report\",\n\t\"is_query_report\": true,\n\t\"type\": \"report\",\n\t\"doctype\": \"BOM\",\n\t\"label\": \"BOM Stock Report\"\n}]" + "links": "[{\n\t\"dependencies\": [\"Work Order\"],\n\t\"name\": \"Production Planning Report\",\n\t\"is_query_report\": true,\n\t\"type\": \"report\",\n\t\"doctype\": \"Work Order\",\n\t\"label\": \"Production Planning Report\"\n}, {\n\t\"dependencies\": [\"Work Order\"],\n\t\"name\": \"Work Order Summary\",\n\t\"is_query_report\": true,\n\t\"type\": \"report\",\n\t\"doctype\": \"Work Order\",\n\t\"label\": \"Work Order Summary\"\n}, {\n\t\"dependencies\": [\"Quality Inspection\"],\n\t\"name\": \"Quality Inspection Summary\",\n\t\"is_query_report\": true,\n\t\"type\": \"report\",\n\t\"doctype\": \"Quality Inspection\",\n\t\"label\": \"Quality Inspection Summary\"\n}, {\n\t\"dependencies\": [\"Downtime Entry\"],\n\t\"name\": \"Downtime Analysis\",\n\t\"is_query_report\": true,\n\t\"type\": \"report\",\n\t\"doctype\": \"Downtime Entry\",\n\t\"label\": \"Downtime Analysis\"\n}, {\n\t\"dependencies\": [\"Job Card\"],\n\t\"name\": \"Job Card Summary\",\n\t\"is_query_report\": true,\n\t\"type\": \"report\",\n\t\"doctype\": \"Job Card\",\n\t\"label\": \"Job Card Summary\"\n}, {\n\t\"dependencies\": [\"BOM\"],\n\t\"name\": \"BOM Search\",\n\t\"is_query_report\": true,\n\t\"type\": \"report\",\n\t\"doctype\": \"BOM\",\n\t\"label\": \"BOM Search\"\n}, {\n\t\"dependencies\": [\"BOM\"],\n\t\"name\": \"BOM Stock Report\",\n\t\"is_query_report\": true,\n\t\"type\": \"report\",\n\t\"doctype\": \"BOM\",\n\t\"label\": \"BOM Stock Report\"\n}, {\n\t\"dependencies\": [\"Work Order\"],\n\t\"name\": \"Production Analytics\",\n\t\"is_query_report\": true,\n\t\"type\": \"report\",\n\t\"doctype\": \"Work Order\",\n\t\"label\": \"Production Analytics\"\n}, {\n\t\"dependencies\": [\"BOM\"],\n\t\"name\": \"BOM Operations Time\",\n\t\"is_query_report\": true,\n\t\"type\": \"report\",\n\t\"doctype\": \"BOM\",\n\t\"label\": \"BOM Operations Time\"\n}]" }, { "hidden": 0, @@ -46,7 +46,7 @@ "idx": 0, "is_standard": 1, "label": "Manufacturing", - "modified": "2020-05-19 12:54:04.104444", + "modified": "2020-05-19 14:05:59.100891", "modified_by": "Administrator", "module": "Manufacturing", "name": "Manufacturing", diff --git a/erpnext/manufacturing/report/bom_operations_time/__init__.py b/erpnext/manufacturing/report/bom_operations_time/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.js b/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.js new file mode 100644 index 00000000000..7468e34020c --- /dev/null +++ b/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.js @@ -0,0 +1,9 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["BOM Operations Time"] = { + "filters": [ + + ] +}; diff --git a/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.json b/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.json new file mode 100644 index 00000000000..665c5b9f79e --- /dev/null +++ b/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.json @@ -0,0 +1,28 @@ +{ + "add_total_row": 0, + "creation": "2020-03-03 01:41:20.862521", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "letter_head": "", + "modified": "2020-03-03 01:41:20.862521", + "modified_by": "Administrator", + "module": "Manufacturing", + "name": "BOM Operations Time", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "BOM", + "report_name": "BOM Operations Time", + "report_type": "Script Report", + "roles": [ + { + "role": "Manufacturing Manager" + }, + { + "role": "Manufacturing User" + } + ] +} \ No newline at end of file diff --git a/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.py b/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.py new file mode 100644 index 00000000000..1279011b222 --- /dev/null +++ b/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.py @@ -0,0 +1,112 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe import _ + +def execute(filters=None): + data = get_data(filters) + columns = get_columns(filters) + return columns, data + +def get_data(filters): + data = [] + + bom_data = [] + for d in frappe.db.sql(""" + SELECT + bom.name, bom.item, bom.item_name, bom.uom, + bomps.operation, bomps.workstation, bomps.time_in_mins + FROM `tabBOM` bom, `tabBOM Operation` bomps + WHERE + bom.docstatus = 1 and bom.is_active = 1 and bom.name = bomps.parent + """, as_dict=1): + row = get_args() + if d.name not in bom_data: + bom_data.append(d.name) + row.update(d) + else: + row.update({ + "operation": d.operation, + "workstation": d.workstation, + "time_in_mins": d.time_in_mins + }) + + data.append(row) + + used_as_subassembly_items = get_bom_count(bom_data) + + for d in data: + d.used_as_subassembly_items = used_as_subassembly_items.get(d.name, 0) + + return data + +def get_bom_count(bom_data): + data = frappe.get_all("BOM Item", + fields=["count(name) as count", "bom_no"], + filters= {"bom_no": ("in", bom_data)}, group_by = "bom_no") + + bom_count = {} + for d in data: + bom_count.setdefault(d.bom_no, d.count) + + return bom_count + +def get_args(): + return frappe._dict({ + "name": "", + "item": "", + "item_name": "", + "uom": "" + }) + +def get_columns(filters): + return [{ + "label": _("BOM ID"), + "options": "BOM", + "fieldname": "name", + "fieldtype": "Link", + "width": 140 + }, { + "label": _("BOM Item Code"), + "options": "Item", + "fieldname": "item", + "fieldtype": "Link", + "width": 140 + }, { + "label": _("Item Name"), + "fieldname": "item_name", + "fieldtype": "Data", + "width": 110 + }, { + "label": _("UOM"), + "options": "UOM", + "fieldname": "uom", + "fieldtype": "Link", + "width": 140 + }, { + "label": _("Operation"), + "options": "Operation", + "fieldname": "operation", + "fieldtype": "Link", + "width": 120 + }, { + "label": _("Workstation"), + "options": "Workstation", + "fieldname": "workstation", + "fieldtype": "Link", + "width": 110 + }, { + "label": _("Time (In Mins)"), + "fieldname": "time_in_mins", + "fieldtype": "Int", + "width": 140 + }, { + "label": _("Subassembly BOM Count"), + "fieldname": "used_as_subassembly_items", + "fieldtype": "Int", + "width": 180 + }] + + diff --git a/erpnext/manufacturing/report/production_planning_report/__init__.py b/erpnext/manufacturing/report/production_planning_report/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/manufacturing/report/production_planning_report/production_planning_report.js b/erpnext/manufacturing/report/production_planning_report/production_planning_report.js new file mode 100644 index 00000000000..675b8a11008 --- /dev/null +++ b/erpnext/manufacturing/report/production_planning_report/production_planning_report.js @@ -0,0 +1,111 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Production Planning Report"] = { + "filters": [ + { + "fieldname":"company", + "label": __("Company"), + "fieldtype": "Link", + "options": "Company", + "reqd": 1, + "default": frappe.defaults.get_user_default("Company") + }, + { + "fieldname":"based_on", + "label": __("Based On"), + "fieldtype": "Select", + "options": ["Sales Order", "Material Request", "Work Order"], + "default": "Sales Order", + "reqd": 1, + on_change: function() { + let filters = frappe.query_report.filters; + let based_on = frappe.query_report.get_filter_value('based_on'); + let options = { + "Sales Order": ["Delivery Date", "Total Amount"], + "Material Request": ["Required Date"], + "Work Order": ["Planned Start Date"] + } + + filters.forEach(d => { + if (d.fieldname == "order_by") { + d.df.options = options[based_on]; + d.set_input(d.df.options) + } + }); + + frappe.query_report.refresh(); + } + }, + { + "fieldname":"docnames", + "label": __("Document Name"), + "fieldtype": "MultiSelectList", + "options": "Sales Order", + "get_data": function(txt) { + if (!frappe.query_report.filters) return; + + let based_on = frappe.query_report.get_filter_value('based_on'); + if (!based_on) return; + + return frappe.db.get_link_options(based_on, txt); + }, + "get_query": function() { + var company = frappe.query_report.get_filter_value('company'); + return { + filters: { + "docstatus": 1, + "company": company + } + }; + } + }, + { + "fieldname":"raw_material_warehouse", + "label": __("Raw Material Warehouse"), + "fieldtype": "Link", + "options": "Warehouse", + "depends_on": "eval: doc.based_on != 'Work Order'", + "get_query": function() { + var company = frappe.query_report.get_filter_value('company'); + return { + filters: { + "company": company + } + }; + } + }, + { + "fieldname":"order_by", + "label": __("Order By"), + "fieldtype": "Select", + "options": ["Delivery Date", "Total Amount"], + "default": "Delivery Date" + }, + { + "fieldname":"include_subassembly_raw_materials", + "label": __("Include Sub-assembly Raw Materials"), + "fieldtype": "Check", + "depends_on": "eval: doc.based_on != 'Work Order'", + "default": 0 + }, + ], + "formatter": function(value, row, column, data, default_formatter) { + value = default_formatter(value, row, column, data); + + if (column.fieldname == "production_item_name" && data && data.qty_to_manufacture > data.available_qty ) { + value = `
${value}
`; + } + + if (column.fieldname == "production_item" && !data.name ) { + value = ""; + } + + if (column.fieldname == "raw_material_name" && data && data.required_qty > data.allotted_qty ) { + value = `
${value}
`; + } + + return value; + }, +}; diff --git a/erpnext/manufacturing/report/production_planning_report/production_planning_report.json b/erpnext/manufacturing/report/production_planning_report/production_planning_report.json new file mode 100644 index 00000000000..f37dad39a43 --- /dev/null +++ b/erpnext/manufacturing/report/production_planning_report/production_planning_report.json @@ -0,0 +1,31 @@ +{ + "add_total_row": 0, + "creation": "2020-03-06 11:37:43.180095", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "letter_head": "", + "modified": "2020-03-06 11:38:05.789851", + "modified_by": "Administrator", + "module": "Manufacturing", + "name": "Production Planning Report", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Work Order", + "report_name": "Production Planning Report", + "report_type": "Script Report", + "roles": [ + { + "role": "Manufacturing User" + }, + { + "role": "Stock User" + }, + { + "role": "Manufacturing Manager" + } + ] +} \ No newline at end of file diff --git a/erpnext/manufacturing/report/production_planning_report/production_planning_report.py b/erpnext/manufacturing/report/production_planning_report/production_planning_report.py new file mode 100644 index 00000000000..b5e6c6fc853 --- /dev/null +++ b/erpnext/manufacturing/report/production_planning_report/production_planning_report.py @@ -0,0 +1,371 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe import _ +from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses + +# and bom_no is not null and bom_no !='' + +mapper = { + "Sales Order": { + "fields": """ item_code as production_item, item_name as production_item_name, stock_uom, + stock_qty as qty_to_manufacture, `tabSales Order Item`.parent as name, bom_no, warehouse, + `tabSales Order Item`.delivery_date, `tabSales Order`.base_grand_total """, + "filters": """`tabSales Order Item`.docstatus = 1 and stock_qty > produced_qty + and `tabSales Order`.per_delivered < 100.0""" + }, + "Material Request": { + "fields": """ item_code as production_item, item_name as production_item_name, stock_uom, + stock_qty as qty_to_manufacture, `tabMaterial Request Item`.parent as name, bom_no, warehouse, + `tabMaterial Request Item`.schedule_date """, + "filters": """`tabMaterial Request`.docstatus = 1 and `tabMaterial Request`.per_ordered < 100 + and `tabMaterial Request`.material_request_type = 'Manufacture' """ + }, + "Work Order": { + "fields": """ production_item, item_name as production_item_name, planned_start_date, + stock_uom, qty as qty_to_manufacture, name, bom_no, fg_warehouse as warehouse """, + "filters": "docstatus = 1 and status not in ('Completed', 'Stopped')" + }, +} + +order_mapper = { + "Sales Order": { + "Delivery Date": "`tabSales Order Item`.delivery_date asc", + "Total Amount": "`tabSales Order`.base_grand_total desc" + }, + "Material Request": { + "Required Date": "`tabMaterial Request Item`.schedule_date asc" + }, + "Work Order": { + "Planned Start Date": "planned_start_date asc" + } +} + +def execute(filters=None): + return ProductionPlanReport(filters).execute_report() + +class ProductionPlanReport(object): + def __init__(self, filters=None): + self.filters = frappe._dict(filters or {}) + self.raw_materials_dict = {} + self.data = [] + + def execute_report(self): + self.get_open_orders() + self.get_raw_materials() + self.get_item_details() + self.get_bin_details() + self.get_purchase_details() + self.prepare_data() + self.get_columns() + + return self.columns, self.data + + def get_open_orders(self): + doctype = ("`tabWork Order`" if self.filters.based_on == "Work Order" + else "`tab{doc}`, `tab{doc} Item`".format(doc=self.filters.based_on)) + + filters = mapper.get(self.filters.based_on)["filters"] + filters = self.prepare_other_conditions(filters, self.filters.based_on) + order_by = " ORDER BY %s" % (order_mapper[self.filters.based_on][self.filters.order_by]) + + self.orders = frappe.db.sql(""" SELECT {fields} from {doctype} + WHERE {filters} {order_by}""".format( + doctype = doctype, + filters = filters, + order_by = order_by, + fields = mapper.get(self.filters.based_on)["fields"] + ), tuple(self.filters.docnames), as_dict=1) + + def prepare_other_conditions(self, filters, doctype): + if self.filters.docnames: + field = "name" if doctype == "Work Order" else "`tab{} Item`.parent".format(doctype) + filters += " and %s in (%s)" % (field, ','.join(['%s'] * len(self.filters.docnames))) + + if doctype != "Work Order": + filters += " and `tab{doc}`.name = `tab{doc} Item`.parent".format(doc=doctype) + + if self.filters.company: + filters += " and `tab%s`.company = %s" %(doctype, frappe.db.escape(self.filters.company)) + + return filters + + def get_raw_materials(self): + if not self.orders: return + self.warehouses = [d.warehouse for d in self.orders] + self.item_codes = [d.production_item for d in self.orders] + + if self.filters.based_on == "Work Order": + work_orders = [d.name for d in self.orders] + + raw_materials = frappe.get_all("Work Order Item", + fields=["parent", "item_code", "item_name as raw_material_name", + "source_warehouse as warehouse", "required_qty"], + filters = {"docstatus": 1, "parent": ("in", work_orders), "source_warehouse": ("!=", "")}) or [] + self.warehouses.extend([d.source_warehouse for d in raw_materials]) + + else: + bom_nos = [] + + for d in self.orders: + bom_no = d.bom_no or frappe.get_cached_value("Item", d.production_item, "default_bom") + + if not d.bom_no: + d.bom_no = bom_no + + bom_nos.append(bom_no) + + bom_doctype = ("BOM Explosion Item" + if self.filters.include_subassembly_raw_materials else "BOM Item") + + qty_field = ("qty_consumed_per_unit" + if self.filters.include_subassembly_raw_materials else "(bom_item.qty / bom.quantity)") + + raw_materials = frappe.db.sql(""" SELECT bom_item.parent, bom_item.item_code, + bom_item.item_name as raw_material_name, {0} as required_qty + FROM + `tabBOM` as bom, `tab{1}` as bom_item + WHERE + bom_item.parent in ({2}) and bom_item.parent = bom.name and bom.docstatus = 1 + """.format(qty_field, bom_doctype, ','.join(["%s"] * len(bom_nos))), tuple(bom_nos), as_dict=1) + + if not raw_materials: return + + self.item_codes.extend([d.item_code for d in raw_materials]) + + for d in raw_materials: + if d.parent not in self.raw_materials_dict: + self.raw_materials_dict.setdefault(d.parent, []) + + rows = self.raw_materials_dict[d.parent] + rows.append(d) + + def get_item_details(self): + if not (self.orders and self.item_codes): return + + self.item_details = {} + for d in frappe.get_all("Item Default", fields = ["parent", "default_warehouse"], + filters = {"company": self.filters.company, "parent": ("in", self.item_codes)}): + self.item_details[d.parent] = d + + def get_bin_details(self): + if not (self.orders and self.raw_materials_dict): return + + self.bin_details = {} + self.mrp_warehouses = [] + if self.filters.raw_material_warehouse: + self.mrp_warehouses.extend(get_child_warehouses(self.filters.raw_material_warehouse)) + self.warehouses.extend(self.mrp_warehouses) + + for d in frappe.get_all("Bin", + fields=["warehouse", "item_code", "actual_qty", "ordered_qty", "projected_qty"], + filters = {"item_code": ("in", self.item_codes), "warehouse": ("in", self.warehouses)}): + key = (d.item_code, d.warehouse) + if key not in self.bin_details: + self.bin_details.setdefault(key, d) + + def get_purchase_details(self): + if not (self.orders and self.raw_materials_dict): return + + self.purchase_details = {} + + for d in frappe.get_all("Purchase Order Item", + fields=["item_code", "min(schedule_date) as arrival_date", "qty as arrival_qty", "warehouse"], + filters = {"item_code": ("in", self.item_codes), "warehouse": ("in", self.warehouses)}, + group_by = "item_code, warehouse"): + key = (d.item_code, d.warehouse) + if key not in self.purchase_details: + self.purchase_details.setdefault(key, d) + + def prepare_data(self): + if not self.orders: return + + for d in self.orders: + key = d.name if self.filters.based_on == "Work Order" else d.bom_no + + if not self.raw_materials_dict.get(key): continue + + bin_data = self.bin_details.get((d.production_item, d.warehouse)) or {} + d.update({ + "for_warehouse": d.warehouse, + "available_qty": 0 + }) + + if bin_data and bin_data.get("actual_qty") > 0 and d.qty_to_manufacture: + d.available_qty = (bin_data.get("actual_qty") + if (d.qty_to_manufacture > bin_data.get("actual_qty")) else d.qty_to_manufacture) + + bin_data["actual_qty"] -= d.available_qty + + self.update_raw_materials(d, key) + + def update_raw_materials(self, data, key): + self.index = 0 + self.raw_materials_dict.get(key) + + warehouses = self.mrp_warehouses or [] + for d in self.raw_materials_dict.get(key): + if self.filters.based_on != "Work Order": + d.required_qty = d.required_qty * data.qty_to_manufacture + + if not warehouses: + warehouses = [data.warehouse] + + if self.filters.based_on == "Work Order" and d.warehouse: + warehouses = [d.warehouse] + else: + item_details = self.item_details.get(d.item_code) + if item_details: + warehouses = [item_details["default_warehouse"]] + + d.remaining_qty = d.required_qty + self.pick_materials_from_warehouses(d, data, warehouses) + + if (d.remaining_qty and self.filters.raw_material_warehouse + and d.remaining_qty != d.required_qty): + row = self.get_args() + d.warehouse = self.filters.raw_material_warehouse + d.required_qty = d.remaining_qty + d.allotted_qty = 0 + row.update(d) + self.data.append(row) + + def pick_materials_from_warehouses(self, args, order_data, warehouses): + for index, warehouse in enumerate(warehouses): + if not args.remaining_qty: return + + row = self.get_args() + + key = (args.item_code, warehouse) + bin_data = self.bin_details.get(key) + + if bin_data: + row.update(bin_data) + + args.allotted_qty = 0 + if bin_data and bin_data.get("actual_qty") > 0: + args.allotted_qty = (bin_data.get("actual_qty") + if (args.required_qty > bin_data.get("actual_qty")) else args.required_qty) + + args.remaining_qty -= args.allotted_qty + bin_data["actual_qty"] -= args.allotted_qty + + if ((self.mrp_warehouses and (args.allotted_qty or index == len(warehouses) - 1)) + or not self.mrp_warehouses): + if not self.index: + row.update(order_data) + self.index += 1 + + args.warehouse = warehouse + row.update(args) + if self.purchase_details.get(key): + row.update(self.purchase_details.get(key)) + + self.data.append(row) + + def get_args(self): + return frappe._dict({ + "work_order": "", + "sales_order": "", + "production_item": "", + "production_item_name": "", + "qty_to_manufacture": "", + "produced_qty": "" + }) + + def get_columns(self): + based_on = self.filters.based_on + + self.columns = [{ + "label": _("ID"), + "options": based_on, + "fieldname": "name", + "fieldtype": "Link", + "width": 100 + }, { + "label": _("Item Code"), + "fieldname": "production_item", + "fieldtype": "Link", + "options": "Item", + "width": 120 + }, { + "label": _("Item Name"), + "fieldname": "production_item_name", + "fieldtype": "Data", + "width": 130 + }, { + "label": _("Warehouse"), + "options": "Warehouse", + "fieldname": "for_warehouse", + "fieldtype": "Link", + "width": 100 + }, { + "label": _("Order Qty"), + "fieldname": "qty_to_manufacture", + "fieldtype": "Float", + "width": 80 + }, { + "label": _("Available"), + "fieldname": "available_qty", + "fieldtype": "Float", + "width": 80 + }] + + fieldname, fieldtype = "delivery_date", "Date" + if self.filters.based_on == "Sales Order" and self.filters.order_by == "Total Amount": + fieldname, fieldtype = "base_grand_total", "Currency" + elif self.filters.based_on == "Material Request": + fieldname = "schedule_date" + elif self.filters.based_on == "Work Order": + fieldname = "planned_start_date" + + self.columns.append({ + "label": _(self.filters.order_by), + "fieldname": fieldname, + "fieldtype": fieldtype, + "width": 100 + }) + + self.columns.extend([{ + "label": _("Raw Material Code"), + "fieldname": "item_code", + "fieldtype": "Link", + "options": "Item", + "width": 120 + }, { + "label": _("Raw Material Name"), + "fieldname": "raw_material_name", + "fieldtype": "Data", + "width": 130 + }, { + "label": _("Warehouse"), + "options": "Warehouse", + "fieldname": "warehouse", + "fieldtype": "Link", + "width": 110 + }, { + "label": _("Required Qty"), + "fieldname": "required_qty", + "fieldtype": "Float", + "width": 100 + }, { + "label": _("Allotted Qty"), + "fieldname": "allotted_qty", + "fieldtype": "Float", + "width": 100 + }, { + "label": _("Expected Arrival Date"), + "fieldname": "arrival_date", + "fieldtype": "Date", + "width": 160 + }, { + "label": _("Arrival Quantity"), + "fieldname": "arrival_qty", + "fieldtype": "Float", + "width": 140 + }]) + +def document_query(doctype, txt, searchfield, start, page_len, filters): + pass \ No newline at end of file diff --git a/erpnext/patches.txt b/erpnext/patches.txt index dc88ffb6bea..56186c4e4ca 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -688,3 +688,4 @@ erpnext.patches.v12_0.set_serial_no_status erpnext.patches.v12_0.update_price_list_currency_in_bom execute:frappe.delete_doc_if_exists('Dashboard', 'Accounts') erpnext.patches.v13_0.update_actual_start_and_end_date_in_wo +erpnext.patches.v12_0.update_bom_in_so_mr diff --git a/erpnext/patches/v12_0/update_bom_in_so_mr.py b/erpnext/patches/v12_0/update_bom_in_so_mr.py new file mode 100644 index 00000000000..309ae4c2ab7 --- /dev/null +++ b/erpnext/patches/v12_0/update_bom_in_so_mr.py @@ -0,0 +1,19 @@ +from __future__ import unicode_literals +import frappe + +def execute(): + frappe.reload_doc("stock", "doctype", "material_request_item") + frappe.reload_doc("selling", "doctype", "sales_order_item") + + for doctype in ["Sales Order", "Material Request"]: + condition = " and child_doc.stock_qty > child_doc.produced_qty" + if doctype == "Material Request": + condition = " and doc.per_ordered < 100 and doc.material_request_type = 'Manufacture'" + + frappe.db.sql(""" UPDATE `tab{doc}` as doc, `tab{doc} Item` as child_doc, tabItem as item + SET + child_doc.bom_no = item.default_bom + WHERE + child_doc.item_code = item.name and child_doc.docstatus < 2 + and item.default_bom is not null and item.default_bom != '' {cond} + """.format(doc = doctype, cond = condition)) \ No newline at end of file diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index 45a43c5e7e9..705dcb8e03a 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -34,6 +34,15 @@ frappe.ui.form.on("Sales Order", { } }; }) + + frm.set_query("bom_no", "items", function(doc, cdt, cdn) { + var row = locals[cdt][cdn]; + return { + filters: { + "item": row.item_code + } + } + }); }, refresh: function(frm) { if(frm.doc.docstatus === 1 && frm.doc.status !== 'Closed' diff --git a/erpnext/selling/doctype/sales_order_item/sales_order_item.json b/erpnext/selling/doctype/sales_order_item/sales_order_item.json index 73f233c537c..e59349926e6 100644 --- a/erpnext/selling/doctype/sales_order_item/sales_order_item.json +++ b/erpnext/selling/doctype/sales_order_item/sales_order_item.json @@ -72,6 +72,8 @@ "against_blanket_order", "blanket_order", "blanket_order_rate", + "manufacturing_section_section", + "bom_no", "planning_section", "projected_qty", "actual_qty", @@ -212,6 +214,7 @@ "fieldtype": "Link", "label": "UOM", "options": "UOM", + "print_hide": 0, "reqd": 1 }, { @@ -764,12 +767,24 @@ "fieldname": "against_blanket_order", "fieldtype": "Check", "label": "Against Blanket Order" - } + }, + { + "fieldname": "bom_no", + "fieldtype": "Link", + "label": "BOM No", + "no_copy": 1, + "options": "BOM", + "print_hide": 1 + }, + { + "fieldname": "manufacturing_section_section", + "fieldtype": "Section Break", + "label": "Manufacturing Section" + } ], "idx": 1, "istable": 1, - "links": [], - "modified": "2020-03-05 14:20:28.085117", + "modified": "2020-05-15 18:13:43.006493", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order Item", diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js index db8bffda9dd..3562181e25f 100644 --- a/erpnext/stock/doctype/material_request/material_request.js +++ b/erpnext/stock/doctype/material_request/material_request.js @@ -30,7 +30,16 @@ frappe.ui.form.on('Material Request', { return { filters: {'company': doc.company} }; - }) + }); + + frm.set_query("bom_no", "items", function(doc, cdt, cdn) { + var row = locals[cdt][cdn]; + return { + filters: { + "item": row.item_code + } + } + }); }, onload: function(frm) { diff --git a/erpnext/stock/doctype/material_request_item/material_request_item.json b/erpnext/stock/doctype/material_request_item/material_request_item.json index df140ffd754..32bd4a0a57a 100644 --- a/erpnext/stock/doctype/material_request_item/material_request_item.json +++ b/erpnext/stock/doctype/material_request_item/material_request_item.json @@ -53,6 +53,8 @@ "dimension_col_break", "cost_center", "section_break_37", + "bom_no", + "section_break_46", "page_break" ], "fields": [ @@ -371,8 +373,10 @@ "label": "Image" }, { + "depends_on": "eval:parent.material_request_type == \"Manufacture\"", "fieldname": "section_break_37", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "label": "Manufacturing" }, { "fieldname": "received_qty", @@ -428,12 +432,24 @@ "fieldtype": "Link", "label": "Source Warehouse (Material Transfer)", "options": "Warehouse" + }, + { + "fieldname": "bom_no", + "fieldtype": "Link", + "label": "BOM No", + "no_copy": 1, + "options": "BOM", + "print_hide": 1 + }, + { + "fieldname": "section_break_46", + "fieldtype": "Section Break" } ], "idx": 1, "istable": 1, "links": [], - "modified": "2020-05-01 09:00:00.992835", + "modified": "2020-05-15 09:00:00.992835", "modified_by": "Administrator", "module": "Stock", "name": "Material Request Item", diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 11b64034198..0ed3b276e39 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -305,7 +305,8 @@ def get_basic_details(args, item, overwrite_warehouse=True): "weight_uom":item.weight_uom, "last_purchase_rate": item.last_purchase_rate if args.get("doctype") in ["Purchase Order"] else 0, "transaction_date": args.get("transaction_date"), - "against_blanket_order": args.get("against_blanket_order") + "against_blanket_order": args.get("against_blanket_order"), + "bom_no": item.get("default_bom") }) if item.get("enable_deferred_revenue") or item.get("enable_deferred_expense"): From 9a914dc5925819f7d2257cc3d64e576f41da2f33 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 19 May 2020 19:56:51 +0530 Subject: [PATCH 112/608] fix: Tax amount in GSTR-1 JSON --- erpnext/regional/report/gstr_1/gstr_1.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/erpnext/regional/report/gstr_1/gstr_1.py b/erpnext/regional/report/gstr_1/gstr_1.py index dd5bb4ae9b0..43b1ea83eb9 100644 --- a/erpnext/regional/report/gstr_1/gstr_1.py +++ b/erpnext/regional/report/gstr_1/gstr_1.py @@ -60,9 +60,6 @@ class Gstr1Report(object): for inv, items_based_on_rate in self.items_based_on_tax_rate.items(): invoice_details = self.invoices.get(inv) for rate, items in items_based_on_rate.items(): - if inv in self.cgst_igst_invoices: - rate = rate/2 - row, taxable_value = self.get_row_data_for_invoice(inv, invoice_details, rate, items) if self.filters.get("type_of_business") == "CDNR": @@ -120,9 +117,15 @@ class Gstr1Report(object): else: row.append(invoice_details.get(fieldname)) taxable_value = 0 + + if invoice in self.cgst_igst_invoices: + division_factor = 2 + else: + division_factor = 1 + for item_code, net_amount in self.invoice_items.get(invoice).items(): if item_code in items: - if self.item_tax_rate.get(invoice) and tax_rate in self.item_tax_rate.get(invoice, {}).get(item_code, []): + if self.item_tax_rate.get(invoice) and tax_rate/division_factor in self.item_tax_rate.get(invoice, {}).get(item_code, []): taxable_value += abs(net_amount) elif not self.item_tax_rate.get(invoice): taxable_value += abs(net_amount) From 99d348ffa3d88cd5e0c8a21fcb66f4b62ae73fb6 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 19 May 2020 20:28:19 +0530 Subject: [PATCH 113/608] fix: Patch for Setting Missing Company in Healthcare DocTypes (#21790) * fix: patch for setting missing company in Healthcare DocTypes * fix: check doctype exists Co-authored-by: Nabin Hait --- erpnext/patches.txt | 1 + .../v13_0/set_company_field_in_healthcare_doctypes.py | 9 +++++++++ 2 files changed, 10 insertions(+) create mode 100644 erpnext/patches/v13_0/set_company_field_in_healthcare_doctypes.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 56186c4e4ca..bb9bb97e64a 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -688,4 +688,5 @@ erpnext.patches.v12_0.set_serial_no_status erpnext.patches.v12_0.update_price_list_currency_in_bom execute:frappe.delete_doc_if_exists('Dashboard', 'Accounts') erpnext.patches.v13_0.update_actual_start_and_end_date_in_wo +erpnext.patches.v13_0.set_company_field_in_healthcare_doctypes erpnext.patches.v12_0.update_bom_in_so_mr diff --git a/erpnext/patches/v13_0/set_company_field_in_healthcare_doctypes.py b/erpnext/patches/v13_0/set_company_field_in_healthcare_doctypes.py new file mode 100644 index 00000000000..2c646b164fb --- /dev/null +++ b/erpnext/patches/v13_0/set_company_field_in_healthcare_doctypes.py @@ -0,0 +1,9 @@ +from __future__ import unicode_literals +import frappe + +def execute(): + company = frappe.db.get_single_value('Global Defaults', 'default_company') + doctypes = ['Clinical Procedure', 'Inpatient Record', 'Lab Test', 'Patient Appointment', 'Patient Encounter', 'Vital Signs'] + for entry in doctypes: + if frappe.db.exists('DocType', entry): + frappe.db.sql("update `tab{dt}` set company = '{company}' where ifnull(company, '') = ''".format(dt=entry, company=company)) From 0c03834190c046e699c161e88e3d9c54abb1da73 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 19 May 2020 20:30:20 +0530 Subject: [PATCH 114/608] fix(minor): pass ignore_mandatory flag for Lab Test Item Creation (#21784) --- .../healthcare/doctype/lab_test_template/lab_test_template.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py index e2b47b45593..3521561f34a 100644 --- a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py +++ b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py @@ -115,7 +115,7 @@ def make_item_price(item, price_list_name, item_price): "price_list": price_list_name, "item_code": item, "price_list_rate": item_price - }).insert(ignore_permissions=True) + }).insert(ignore_permissions=True, ignore_mandatory=True) @frappe.whitelist() def change_test_code_from_template(lab_test_code, doc): From 1b1787c61a47430fc113e691ae2c6dd87f0082db Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 19 May 2020 20:51:30 +0530 Subject: [PATCH 115/608] fix: Against voucher in General Ledger --- erpnext/accounts/report/general_ledger/general_ledger.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index 6afe208b5b1..f83a2595f6a 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -296,7 +296,7 @@ def get_accountwise_gle(filters, gl_entries, gle_map): data[key].debit_in_account_currency += flt(gle.debit_in_account_currency) data[key].credit_in_account_currency += flt(gle.credit_in_account_currency) - if data[key].against_voucher: + if data[key].against_voucher and gle.against_voucher: data[key].against_voucher += ', ' + gle.against_voucher from_date, to_date = getdate(filters.from_date), getdate(filters.to_date) From 041e31b8baa619c75e06226fed0a972ef773cdd5 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 19 May 2020 20:58:00 +0530 Subject: [PATCH 116/608] fix: added dashboard link in healthcare desk --- erpnext/healthcare/dashboard_fixtures.py | 26 +++++++++---------- .../desk_page/healthcare/healthcare.json | 8 +++++- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/erpnext/healthcare/dashboard_fixtures.py b/erpnext/healthcare/dashboard_fixtures.py index 4fb05a8f81f..59da71a0ec9 100644 --- a/erpnext/healthcare/dashboard_fixtures.py +++ b/erpnext/healthcare/dashboard_fixtures.py @@ -3,7 +3,7 @@ import frappe import json - +from frappe import _ def get_data(): return frappe._dict({ @@ -51,7 +51,7 @@ def get_charts(): "doctype": "Dashboard Chart", "time_interval": "Daily", "name": "Patient Appointments", - "chart_name": "Patient Appointments", + "chart_name": _("Patient Appointments"), "timespan": "Last Month", "filters_json": json.dumps([ ["Patient Appointment", "company", "=", company, False], @@ -68,7 +68,7 @@ def get_charts(): { "doctype": "Dashboard Chart", "name": "Department wise Patient Appointments", - "chart_name": "Department wise Patient Appointments", + "chart_name": _("Department wise Patient Appointments"), "chart_type": "Custom", "source": "Department wise Patient Appointments", "filters_json": json.dumps({}), @@ -87,7 +87,7 @@ def get_charts(): { "doctype": "Dashboard Chart", "name": "Lab Tests", - "chart_name": "Lab Tests", + "chart_name": _("Lab Tests"), "chart_type": "Group By", "document_type": "Lab Test", "group_by_type": "Count", @@ -104,7 +104,7 @@ def get_charts(): { "doctype": "Dashboard Chart", "name": "Clinical Procedures", - "chart_name": "Clinical Procedures", + "chart_name": _("Clinical Procedures"), "chart_type": "Group By", "document_type": "Clinical Procedure", "group_by_type": "Count", @@ -121,7 +121,7 @@ def get_charts(): { "doctype": "Dashboard Chart", "name": "In-Patient Status", - "chart_name": "In-Patient Status", + "chart_name": _("In-Patient Status"), "chart_type": "Group By", "document_type": "Inpatient Record", "group_by_type": "Count", @@ -137,7 +137,7 @@ def get_charts(): { "doctype": "Dashboard Chart", "name": "Clinical Procedures Status", - "chart_name": "Clinical Procedure Status", + "chart_name": _("Clinical Procedure Status"), "chart_type": "Group By", "document_type": "Clinical Procedure", "group_by_type": "Count", @@ -154,7 +154,7 @@ def get_charts(): { "doctype": "Dashboard Chart", "name": "Symptoms", - "chart_name": "Symptoms", + "chart_name": _("Symptoms"), "chart_type": "Group By", "document_type": "Patient Encounter Symptom", "group_by_type": "Count", @@ -168,7 +168,7 @@ def get_charts(): { "doctype": "Dashboard Chart", "name": "Diagnoses", - "chart_name": "Diagnoses", + "chart_name": _("Diagnoses"), "chart_type": "Group By", "document_type": "Patient Encounter Diagnosis", "group_by_type": "Count", @@ -186,7 +186,7 @@ def get_number_cards(): return [ { "name": "Total Patients", - "label": "Total Patients", + "label": _("Total Patients"), "function": "Count", "doctype": "Number Card", "document_type": "Patient", @@ -200,7 +200,7 @@ def get_number_cards(): }, { "name": "Total Patients Admitted", - "label": "Total Patients Admitted", + "label": _("Total Patients Admitted"), "function": "Count", "doctype": "Number Card", "document_type": "Patient", @@ -214,7 +214,7 @@ def get_number_cards(): }, { "name": "Open Appointments", - "label": "Open Appointments", + "label": _("Open Appointments"), "function": "Count", "doctype": "Number Card", "document_type": "Patient Appointment", @@ -229,7 +229,7 @@ def get_number_cards(): }, { "name": "Appointments to Bill", - "label": "Appointments to Bill", + "label": _("Appointments to Bill"), "function": "Count", "doctype": "Number Card", "document_type": "Patient Appointment", diff --git a/erpnext/healthcare/desk_page/healthcare/healthcare.json b/erpnext/healthcare/desk_page/healthcare/healthcare.json index 14ad5e450a9..60b53137cd7 100644 --- a/erpnext/healthcare/desk_page/healthcare/healthcare.json +++ b/erpnext/healthcare/desk_page/healthcare/healthcare.json @@ -60,10 +60,11 @@ "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, + "hide_custom": 0, "idx": 0, "is_standard": 1, "label": "Healthcare", - "modified": "2020-05-19 14:05:10.520457", + "modified": "2020-05-19 20:57:22.797267", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare", @@ -106,6 +107,11 @@ "label": "Patient History", "link_to": "patient_history", "type": "Page" + }, + { + "label": "Healthcare Dashboard", + "link_to": "Healthcare", + "type": "Dashboard" } ] } \ No newline at end of file From 6277367d21f65c7e5c544178d6dac04ae1265701 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 19 May 2020 21:18:26 +0530 Subject: [PATCH 117/608] fix: project desk --- erpnext/projects/dashboard_fixtures.py | 4 +++- erpnext/projects/desk_page/projects/projects.json | 8 +++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/erpnext/projects/dashboard_fixtures.py b/erpnext/projects/dashboard_fixtures.py index 63b3893b76a..d89ffe9d83b 100644 --- a/erpnext/projects/dashboard_fixtures.py +++ b/erpnext/projects/dashboard_fixtures.py @@ -3,6 +3,7 @@ import frappe import json +from frappe import _ def get_company_for_dashboards(): company = frappe.defaults.get_defaults().company @@ -37,10 +38,11 @@ def get_charts(): { 'doctype': 'Dashboard Chart', 'name': 'Project Summary', - 'chart_name': 'Project Summary', + 'chart_name': _('Project Summary'), 'chart_type': 'Report', 'report_name': 'Project Summary', 'is_public': 1, + 'is_custom': 1, 'filters_json': json.dumps({"company": company.name, "status": "Open"}), 'type': 'Bar', 'custom_options': '{"type": "bar", "colors": ["#fc4f51", "#78d6ff", "#7575ff"], "axisOptions": { "shortenYAxisNumbers": 1}, "barOptions": { "stacked": 1 }}', diff --git a/erpnext/projects/desk_page/projects/projects.json b/erpnext/projects/desk_page/projects/projects.json index 4d4450dbf53..fdbe13b2fb7 100644 --- a/erpnext/projects/desk_page/projects/projects.json +++ b/erpnext/projects/desk_page/projects/projects.json @@ -29,10 +29,11 @@ "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, + "hide_custom": 0, "idx": 0, "is_standard": 1, "label": "Projects", - "modified": "2020-05-04 20:27:51.591365", + "modified": "2020-05-19 21:09:52.031828", "modified_by": "Administrator", "module": "Projects", "name": "Projects", @@ -65,6 +66,11 @@ "label": "Project Billing Summary", "link_to": "Project Billing Summary", "type": "Report" + }, + { + "label": "Project Dashboard", + "link_to": "Project", + "type": "Dashboard" } ] } \ No newline at end of file From 75e8b1b519c17d6f2ad46191674536699a034e46 Mon Sep 17 00:00:00 2001 From: Anupam K Date: Tue, 19 May 2020 18:31:31 +0530 Subject: [PATCH 118/608] tax id is not fetched in when creating sales order from quoation --- erpnext/selling/doctype/sales_order/sales_order.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json index 6462d3bc8c3..b57c4f30981 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.json +++ b/erpnext/selling/doctype/sales_order/sales_order.json @@ -282,6 +282,7 @@ "width": "100px" }, { + "fetch_from": "customer.tax_id", "fieldname": "tax_id", "fieldtype": "Data", "label": "Tax Id", @@ -1196,7 +1197,7 @@ "idx": 105, "is_submittable": 1, "links": [], - "modified": "2020-04-17 12:50:39.640534", + "modified": "2020-05-19 21:39:19.486684", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order", From e6147ed9bc89639db843417704718ce98db5169c Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 12 May 2020 11:18:32 +0530 Subject: [PATCH 119/608] feat: production forecasting using exponential smoothing method --- .../accounts/report/financial_statements.py | 7 +- .../__init__.py | 0 .../exponential_smoothing_forecasting.js | 97 ++++++++ .../exponential_smoothing_forecasting.json | 40 ++++ .../exponential_smoothing_forecasting.py | 222 ++++++++++++++++++ 5 files changed, 363 insertions(+), 3 deletions(-) create mode 100644 erpnext/manufacturing/report/exponential_smoothing_forecasting/__init__.py create mode 100644 erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.js create mode 100644 erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.json create mode 100644 erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index 7fb598bd689..4a35a668658 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -19,7 +19,7 @@ from six import itervalues from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions, get_dimension_with_children def get_period_list(from_fiscal_year, to_fiscal_year, period_start_date, period_end_date, filter_based_on, periodicity, accumulated_values=False, - company=None, reset_period_on_fy_change=True): + company=None, reset_period_on_fy_change=True, ignore_fiscal_year=False): """Get a list of dict {"from_date": from_date, "to_date": to_date, "key": key, "label": label} Periodicity can be (Yearly, Quarterly, Monthly)""" @@ -67,8 +67,9 @@ def get_period_list(from_fiscal_year, to_fiscal_year, period_start_date, period_ # if a fiscal year ends before a 12 month period period.to_date = year_end_date - period.to_date_fiscal_year = get_fiscal_year(period.to_date, company=company)[0] - period.from_date_fiscal_year_start_date = get_fiscal_year(period.from_date, company=company)[1] + if not ignore_fiscal_year: + period.to_date_fiscal_year = get_fiscal_year(period.to_date, company=company)[0] + period.from_date_fiscal_year_start_date = get_fiscal_year(period.from_date, company=company)[1] period_list.append(period) diff --git a/erpnext/manufacturing/report/exponential_smoothing_forecasting/__init__.py b/erpnext/manufacturing/report/exponential_smoothing_forecasting/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.js b/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.js new file mode 100644 index 00000000000..123a82a3882 --- /dev/null +++ b/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.js @@ -0,0 +1,97 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Exponential Smoothing Forecasting"] = { + "filters": [ + { + "fieldname":"company", + "label": __("Company"), + "fieldtype": "Link", + "options": "Company", + "reqd": 1, + "default": frappe.defaults.get_user_default("Company") + }, + { + "fieldname":"from_date", + "label": __("From Date"), + "fieldtype": "Date", + "default": frappe.datetime.get_today(), + "reqd": 1 + }, + { + "fieldname":"to_date", + "label": __("To Date"), + "fieldtype": "Date", + "default": frappe.datetime.add_months(frappe.datetime.get_today(), 12), + "reqd": 1 + }, + { + "fieldname":"based_on_document", + "label": __("Based On Document"), + "fieldtype": "Select", + "options": ["Sales Order", "Delivery Note", "Quotation"], + "default": "Sales Order", + "reqd": 1 + }, + { + "fieldname":"based_on_field", + "label": __("Based On"), + "fieldtype": "Select", + "options": ["Qty", "Amount"], + "default": "Qty", + "reqd": 1 + }, + { + "fieldname":"no_of_years", + "label": __("Based On Data ( in years )"), + "fieldtype": "Select", + "options": [3, 6, 9], + "default": 3, + "reqd": 1 + }, + { + "fieldname": "periodicity", + "label": __("Periodicity"), + "fieldtype": "Select", + "options": [ + { "value": "Monthly", "label": __("Monthly") }, + { "value": "Quarterly", "label": __("Quarterly") }, + { "value": "Half-Yearly", "label": __("Half-Yearly") }, + { "value": "Yearly", "label": __("Yearly") } + ], + "default": "Yearly", + "reqd": 1 + }, + { + "fieldname":"smoothing_constant", + "label": __("Smoothing Constant"), + "fieldtype": "Select", + "options": [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0], + "reqd": 1, + "default": 0.3 + }, + { + "fieldname":"item_code", + "label": __("Item Code"), + "fieldtype": "Link", + "options": "Item" + }, + { + "fieldname":"warehouse", + "label": __("Warehouse"), + "fieldtype": "Link", + "options": "Warehouse", + get_query: () => { + var company = frappe.query_report.get_filter_value('company'); + if (company) { + return { + filters: { + 'company': company + } + }; + } + } + } + ] +}; diff --git a/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.json b/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.json new file mode 100644 index 00000000000..5092ef4e7a5 --- /dev/null +++ b/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.json @@ -0,0 +1,40 @@ +{ + "add_total_row": 0, + "creation": "2020-05-15 05:18:55.838030", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "letter_head": "", + "modified": "2020-05-15 05:18:55.838030", + "modified_by": "Administrator", + "module": "Manufacturing", + "name": "Exponential Smoothing Forecasting", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Sales Order", + "report_name": "Exponential Smoothing Forecasting", + "report_type": "Script Report", + "roles": [ + { + "role": "Manufacturing User" + }, + { + "role": "Stock User" + }, + { + "role": "Manufacturing Manager" + }, + { + "role": "Stock Manager" + }, + { + "role": "Sales Manager" + }, + { + "role": "Sales User" + } + ] +} \ No newline at end of file diff --git a/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py b/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py new file mode 100644 index 00000000000..b5127f133ca --- /dev/null +++ b/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py @@ -0,0 +1,222 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe, erpnext +from frappe import _ +from frappe.utils import flt, nowdate, add_years, cint, getdate +from erpnext.accounts.report.financial_statements import get_period_list +from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses + +def execute(filters=None): + return ForecastingReport(filters).execute_report() + +class ExponentialSmoothingForecast(object): + def forecast_future_data(self): + for key, value in self.period_wise_data.items(): + forecast_data = [] + for period in self.period_list: + forecast_key = "forecast_" + period.key + + if value.get(period.key) and not forecast_data: + value[forecast_key] = flt(value.get("avg", 0)) or flt(value.get(period.key)) + + # will be use to forecaset next period + forecast_data.append([value.get(period.key), value.get(forecast_key)]) + elif forecast_data: + previous_period_data = forecast_data[-1] + value[forecast_key] = (previous_period_data[1] + + flt(self.filters.smoothing_constant) * ( + flt(previous_period_data[0]) - flt(previous_period_data[1]) + ) + ) + +class ForecastingReport(ExponentialSmoothingForecast): + def __init__(self, filters=None): + self.filters = frappe._dict(filters or {}) + self.data = [] + self.doctype = self.filters.based_on_document + self.child_doctype = self.doctype + " Item" + self.based_on_field = ("qty" + if self.filters.based_on_field == "Qty" else "amount") + self.fieldtype = "Float" if self.based_on_field == "qty" else "Currency" + self.company_currency = erpnext.get_company_currency(self.filters.company) + + def execute_report(self): + self.prepare_periodical_data() + self.forecast_future_data() + self.data = self.period_wise_data.values() + self.add_total() + + columns = self.get_columns() + charts = self.get_chart_data() + summary_data = self.get_summary_data() + + return columns, self.data, None, charts, summary_data + + def prepare_periodical_data(self): + self.period_wise_data = {} + + from_date = add_years(self.filters.from_date, cint(self.filters.no_of_years) * -1) + self.period_list = get_period_list(from_date, self.filters.to_date, + from_date, self.filters.to_date, None, self.filters.periodicity, ignore_fiscal_year=True) + + order_data = self.get_data_for_forecast() or [] + + for entry in order_data: + key = (entry.item_code, entry.warehouse) + if key not in self.period_wise_data: + self.period_wise_data[key] = entry + + period_data = self.period_wise_data[key] + for period in self.period_list: + # check if posting date is within the period + if (entry.posting_date >= period.from_date and entry.posting_date <= period.to_date): + period_data[period.key] = period_data.get(period.key, 0.0) + flt(entry.get(self.based_on_field)) + + for key, value in self.period_wise_data.items(): + list_of_period_value = [value.get(p.key, 0) for p in self.period_list] + + if list_of_period_value: + value["avg"] = sum(list_of_period_value) / len(list_of_period_value) + + def get_data_for_forecast(self): + cond = "" + if self.filters.item_code: + cond = " AND soi.item_code = %s" %(frappe.db.escape(self.filters.item_code)) + + warehouses = [] + if self.filters.warehouse: + warehouses = get_child_warehouses(self.filters.warehouse) + cond += " AND soi.warehouse in ({})".format(','.join(['%s'] * len(warehouses))) + + input_data = [self.filters.from_date, self.filters.company] + if warehouses: + input_data.extend(warehouses) + + date_field = "posting_date" if self.doctype == "Delivery Note" else "transaction_date" + + return frappe.db.sql(""" + SELECT + so.{date_field} as posting_date, soi.item_code, soi.warehouse, + soi.item_name, soi.stock_qty as qty, soi.base_amount as amount + FROM + `tab{doc}` so, `tab{child_doc}` soi + WHERE + so.docstatus = 1 AND so.name = soi.parent AND + so.{date_field} < %s AND so.company = %s {cond} + """.format(doc=self.doctype, child_doc=self.child_doctype, date_field=date_field, cond=cond), + tuple(input_data), as_dict=1) + + def add_total(self): + total_row = { + "item_code": _(frappe.bold("Total Quantity")) + } + + for value in self.data: + for period in self.period_list: + forecast_key = "forecast_" + period.key + if forecast_key not in total_row: + total_row.setdefault(forecast_key, 0.0) + + if period.key not in total_row: + total_row.setdefault(period.key, 0.0) + + total_row[forecast_key] += value.get(forecast_key, 0.0) + total_row[period.key] += value.get(period.key, 0.0) + + self.data.append(total_row) + + def get_columns(self): + columns = [{ + "label": _("Item Code"), + "options": "Item", + "fieldname": "item_code", + "fieldtype": "Link", + "width": 130 + }, { + "label": _("Warehouse"), + "options": "Warehouse", + "fieldname": "warehouse", + "fieldtype": "Link", + "width": 130 + }] + + width = 150 if self.filters.periodicity in ['Yearly', "Half-Yearly", "Quarterly"] else 100 + for period in self.period_list: + if (self.filters.periodicity in ['Yearly', "Half-Yearly", "Quarterly"] + or period.from_date >= getdate(self.filters.from_date)): + + forecast_key = 'forecast_' + period.key + + columns.append({ + "label": _(period.label), + "fieldname": forecast_key, + "fieldtype": self.fieldtype, + "width": width, + "default": 0.0 + }) + + return columns + + def get_chart_data(self): + if not self.data: return + + labels = [] + self.total_demand = [] + self.total_forecast = [] + self.total_history_forecast = [] + self.total_future_forecast = [] + + for period in self.period_list: + forecast_key = "forecast_" + period.key + + labels.append(_(period.label)) + + if period.from_date < getdate(self.filters.from_date): + self.total_demand.append(self.data[-1].get(period.key, 0)) + self.total_history_forecast.append(self.data[-1].get(forecast_key, 0)) + else: + self.total_future_forecast.append(self.data[-1].get(forecast_key, 0)) + + self.total_forecast.append(self.data[-1].get(forecast_key, 0)) + + return { + "data": { + "labels": labels, + "datasets": [ + { + "name": "Demand", + "values": self.total_demand + }, + { + "name": "Forecast", + "values": self.total_forecast + } + ] + }, + "type": "line" + } + + def get_summary_data(self): + return [ + { + "value": sum(self.total_demand), + "label": _("Total Demand (Past Data)"), + "currency": self.company_currency, + "datatype": self.fieldtype + }, + { + "value": sum(self.total_history_forecast), + "label": _("Total Forecast (Past Data)"), + "currency": self.company_currency, + "datatype": self.fieldtype + }, + { + "value": sum(self.total_future_forecast), + "indicator": "Green", + "label": _("Total Forecast (Future Data)"), + "currency": self.company_currency, + "datatype": self.fieldtype + } + ] \ No newline at end of file From 841a8db5d1d28e3618924f8548ee7e100f485c60 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 20 May 2020 12:06:03 +0530 Subject: [PATCH 120/608] updated manufacturing desk --- .../manufacturing/manufacturing.json | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json b/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json index ecd2dc9b769..f2e07bfe204 100644 --- a/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json +++ b/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json @@ -43,10 +43,11 @@ "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, + "hide_custom": 0, "idx": 0, "is_standard": 1, "label": "Manufacturing", - "modified": "2020-05-19 14:05:59.100891", + "modified": "2020-05-20 11:50:20.029056", "modified_by": "Administrator", "module": "Manufacturing", "name": "Manufacturing", @@ -88,6 +89,17 @@ "stats_filter": "{ \n \"status\": [\"not in\", [\"Completed\"]]\n}", "type": "DocType" }, + { + "label": "Dashboard", + "link_to": "Manufacturing", + "restrict_to_domain": "Manufacturing", + "type": "Dashboard" + }, + { + "label": "Forecasting", + "link_to": "Exponential Smoothing Forecasting", + "type": "Report" + }, { "label": "Work Order Summary", "link_to": "Work Order Summary", @@ -95,10 +107,14 @@ "type": "Report" }, { - "label": "Dashboard", - "link_to": "Manufacturing", - "restrict_to_domain": "Manufacturing", - "type": "Dashboard" + "label": "BOM Stock Report", + "link_to": "BOM Stock Report", + "type": "Report" + }, + { + "label": "Production Planning Report", + "link_to": "Production Planning Report", + "type": "Report" } ] } \ No newline at end of file From 7cbbd68176680aa62ba1249fea799337d2ddbf81 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 20 May 2020 12:55:34 +0530 Subject: [PATCH 121/608] fix: CRM module dashboard and onboarding --- erpnext/crm/dashboard_fixtures.py | 135 +++++++++--------- erpnext/crm/desk_page/crm/crm.json | 8 +- erpnext/crm/module_onboarding/crm/crm.json | 11 +- .../create_and_send_quotation.json | 4 +- .../create_lead/create_lead.json | 4 +- .../create_opportunity.json | 4 +- .../introduction_to_crm.json | 2 + .../start_campaign/start_campaign.json | 17 --- 8 files changed, 89 insertions(+), 96 deletions(-) delete mode 100644 erpnext/crm/onboarding_step/start_campaign/start_campaign.json diff --git a/erpnext/crm/dashboard_fixtures.py b/erpnext/crm/dashboard_fixtures.py index 5b1ac9bf5e2..16904b34295 100644 --- a/erpnext/crm/dashboard_fixtures.py +++ b/erpnext/crm/dashboard_fixtures.py @@ -2,6 +2,7 @@ # License: GNU General Public License v3. See license.txt import frappe, erpnext, json +from frappe import _ def get_data(): return frappe._dict({ @@ -16,19 +17,19 @@ def get_dashboards(): "name": "CRM", "dashboard_name": "CRM", "charts": [ - { "chart": "Lead", "width": "Full" }, - { "chart": "Opportunity", "width": "Full"}, - { "chart": "Campaign", "width": "Half" }, - { "chart": "Opportunities Won", "width": "Half" }, - { "chart": "Territory Wise Opportunity", "width": "Half"}, + { "chart": "Incoming Leads", "width": "Full" }, + { "chart": "Opportunity Trends", "width": "Full"}, + { "chart": "Won Opportunities", "width": "Full" }, + { "chart": "Territory Wise Opportunity Count", "width": "Half"}, { "chart": "Territory Wise Sales", "width": "Half"}, - { "chart": "Qualified For Call", "width": "Full"}, + { "chart": "Opportunities via Campaigns", "width": "Half" }, { "chart": "Lead Source", "width": "Half"} ], "cards": [ - { "card": "New Lead" }, - { "card": "New Opportunity" }, - { "card": "Won Opportunity" }, + { "card": "New Lead (Last 1 Month)" }, + { "card": "New Opportunity (Last 1 Month)" }, + { "card": "Won Opportunity (Last 1 Month)" }, + { "card": "Open Opportunity"}, ] }] @@ -46,11 +47,11 @@ def get_charts(): company = get_company_for_dashboards() return [{ - "name": "Lead", + "name": "Incoming Leads", "doctype": "Dashboard Chart", "time_interval": "Yearly", "chart_type": "Count", - "chart_name": "Lead", + "chart_name": _("Incoming Leads"), "timespan": "Last Quarter", "time_interval": "Weekly", "document_type": "Lead", @@ -62,11 +63,11 @@ def get_charts(): "type": "Bar" }, { - "name": "Opportunity", + "name": "Opportunity Trends", "doctype": "Dashboard Chart", "time_interval": "Yearly", "chart_type": "Count", - "chart_name": "Opportunity", + "chart_name": _("Opportunity Trends"), "timespan": "Last Quarter", "time_interval": "Weekly", "document_type": "Opportunity", @@ -78,28 +79,25 @@ def get_charts(): "type": "Bar" }, { - "name": "Campaign", + "name": "Opportunities via Campaigns", + "chart_name": _("Opportunities via Campaigns"), "doctype": "Dashboard Chart", - "time_interval": "Yearly", - "chart_type": "Count", - "chart_name": "Campaign", - "timespan": "Last Year", - "time_interval": "Monthly", - "document_type": "Campaign", - "based_on": "creation", + "chart_type": "Group By", + "group_by_type": "Count", + "group_by_based_on": "campaign", + "document_type": "Opportunity", 'is_public': 1, 'timeseries': 1, "owner": "Administrator", "filters_json": json.dumps([["Opportunity", "company", "=", company, False]]), - "type": "Line", - "color": "#428b46" + "type": "Pie" }, { - "name": "Opportunities Won", + "name": "Won Opportunities", "doctype": "Dashboard Chart", "time_interval": "Yearly", "chart_type": "Count", - "chart_name": "Opportunities Won", + "chart_name": _("Won Opportunities"), "timespan": "Last Year", "time_interval": "Monthly", "document_type": "Opportunity", @@ -107,20 +105,25 @@ def get_charts(): 'is_public': 1, 'timeseries': 1, "owner": "Administrator", - "filters_json": json.dumps([["Opportunity", "company", "=", company, False],["Opportunity", "sales_stage", "=", "Converted", False]]), + "filters_json": json.dumps([ + ["Opportunity", "company", "=", company, False], + ["Opportunity", "status", "=", "Converted", False]]), "type": "Bar" }, { - "name": "Territory Wise Opportunity", + "name": "Territory Wise Opportunity Count", "doctype": "Dashboard Chart", "chart_type": "Group By", "group_by_type": "Count", "group_by_based_on": "territory", - "chart_name": "Territory Wise Opportunity", + "chart_name": _("Territory Wise Opportunity Count"), "document_type": "Opportunity", 'is_public': 1, + "filters_json": json.dumps([ + ["Opportunity", "company", "=", company, False] + ]), "owner": "Administrator", - "type": "Line" + "type": "Donut" }, { "name": "Territory Wise Sales", @@ -128,31 +131,16 @@ def get_charts(): "chart_type": "Group By", "group_by_type": "Sum", "group_by_based_on": "territory", - "chart_name": "Territory Wise Sales", + "chart_name": _("Territory Wise Sales"), "aggregate_function_based_on": "opportunity_amount", "document_type": "Opportunity", 'is_public': 1, "owner": "Administrator", - "filters_json": json.dumps([["Opportunity", "company", "=", company, False],["Opportunity", "sales_stage", "=", "Converted", False]]), - "type": "Bar", - "color": "#7575ff" - }, - { - "name": "Qualified For Call", - "doctype": "Dashboard Chart", - "time_interval": "Yearly", - "chart_type": "Count", - "chart_name": "Qualified For Call", - "timespan": "Last Quarter", - "time_interval": "Weekly", - "document_type": "Opportunity", - "based_on": "modified", - 'is_public': 1, - 'timeseries': 1, - "owner": "Administrator", - "filters_json": json.dumps([["Opportunity", "company", "=", company, False],["Opportunity", "sales_stage", "=", "Qualification", False]]), - "type": "Line", - "color": "#fff168" + "filters_json": json.dumps([ + ["Opportunity", "company", "=", company, False], + ["Opportunity", "status", "=", "Converted", False] + ]), + "type": "Donut" }, { "name": "Lead Source", @@ -160,44 +148,55 @@ def get_charts(): "chart_type": "Group By", "group_by_type": "Count", "group_by_based_on": "source", - "chart_name": "Lead Source", + "chart_name": _("Lead Source"), "document_type": "Lead", 'is_public': 1, "owner": "Administrator", - "type": "Donut" + "type": "Pie" }] def get_number_cards(): return [{ "doctype": "Number Card", "document_type": "Lead", - "name": "New Lead", - "filters_json": json.dumps([["Lead","status","=","Lead",False]]), + "name": "New Lead (Last 1 Month)", + "filters_json": json.dumps([["Lead","creation","Previous","1 month",False]]), "function": "Count", "is_public": 1, - "label": "New Lead", + "label": _("New Lead (Last 1 Month)"), "show_percentage_stats": 1, "stats_time_interval": "Daily" }, { "doctype": "Number Card", "document_type": "Opportunity", - "name": "New Opportunity", + "name": "New Opportunity (Last 1 Month)", + "filters_json": json.dumps([["Opportunity","creation","Previous","1 month",False]]), + "function": "Count", + "is_public": 1, + "label": _("New Opportunity (Last 1 Month)"), + "show_percentage_stats": 1, + "stats_time_interval": "Daily" + }, + { + "doctype": "Number Card", + "document_type": "Opportunity", + "name": "Won Opportunity (Last 1 Month)", + "filters_json": json.dumps([["Opportunity","creation","Previous","1 month",False]]), + "function": "Count", + "is_public": 1, + "label": _("Won Opportunity (Last 1 Month)"), + "show_percentage_stats": 1, + "stats_time_interval": "Daily" + }, + { + "doctype": "Number Card", + "document_type": "Opportunity", + "name": "Open Opportunity", "filters_json": json.dumps([["Opportunity","status","=","Open",False]]), "function": "Count", "is_public": 1, - "label": "New Opportunity", - "show_percentage_stats": 1, - "stats_time_interval": "Daily" - }, - { - "doctype": "Number Card", - "document_type": "Opportunity", - "name": "Won Opportunity", - "filters_json": json.dumps([["Opportunity","status","=","Converted",False]]), - "function": "Count", - "is_public": 1, - "label": "Won Opportunity", + "label": _("Open Opportunity"), "show_percentage_stats": 1, "stats_time_interval": "Daily" }] \ No newline at end of file diff --git a/erpnext/crm/desk_page/crm/crm.json b/erpnext/crm/desk_page/crm/crm.json index 80d3d18de97..2fc4582917a 100644 --- a/erpnext/crm/desk_page/crm/crm.json +++ b/erpnext/crm/desk_page/crm/crm.json @@ -38,10 +38,11 @@ "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, + "hide_custom": 0, "idx": 0, "is_standard": 1, "label": "CRM", - "modified": "2020-05-14 17:20:07.636751", + "modified": "2020-05-20 12:11:36.250491", "modified_by": "Administrator", "module": "CRM", "name": "CRM", @@ -73,6 +74,11 @@ "label": "Sales Analytics", "link_to": "Sales Analytics", "type": "Report" + }, + { + "label": "CRM Dashboard", + "link_to": "CRM", + "type": "Dashboard" } ] } \ No newline at end of file diff --git a/erpnext/crm/module_onboarding/crm/crm.json b/erpnext/crm/module_onboarding/crm/crm.json index 9129c2625a0..694763f7c7f 100644 --- a/erpnext/crm/module_onboarding/crm/crm.json +++ b/erpnext/crm/module_onboarding/crm/crm.json @@ -4,10 +4,10 @@ "role": "Sales Master Manager" }, { - "role": "Administrator" + "role": "Sales Manager" }, { - "role": "Sales Manager" + "role": "Sales User" } ], "creation": "2020-05-09 23:42:50.901548", @@ -16,7 +16,7 @@ "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/CRM", "idx": 0, "is_complete": 0, - "modified": "2020-05-14 17:41:11.083917", + "modified": "2020-05-20 12:53:47.029412", "modified_by": "Administrator", "module": "CRM", "name": "CRM", @@ -25,9 +25,6 @@ { "step": "Introduction to CRM" }, - { - "step": "Start Campaign" - }, { "step": "Create Lead" }, @@ -38,7 +35,7 @@ "step": "Create and Send Quotation" } ], - "subtitle": "Campaign, Lead, Opportunity, Customer and more", + "subtitle": "Lead, Opportunity, Customer and more", "success_message": "CRM Module is all setup!", "title": "Let's Setup Your CRM", "user_can_dismiss": 1 diff --git a/erpnext/crm/onboarding_step/create_and_send_quotation/create_and_send_quotation.json b/erpnext/crm/onboarding_step/create_and_send_quotation/create_and_send_quotation.json index 0997017933a..a6edfd7e53d 100644 --- a/erpnext/crm/onboarding_step/create_and_send_quotation/create_and_send_quotation.json +++ b/erpnext/crm/onboarding_step/create_and_send_quotation/create_and_send_quotation.json @@ -13,5 +13,7 @@ "name": "Create and Send Quotation", "owner": "Administrator", "reference_document": "Quotation", - "title": "Create and Send Quotation" + "show_full_form": 0, + "title": "Create and Send Quotation", + "validate_action": 0 } \ No newline at end of file diff --git a/erpnext/crm/onboarding_step/create_lead/create_lead.json b/erpnext/crm/onboarding_step/create_lead/create_lead.json index b1076e0bf95..47a45d70a8c 100644 --- a/erpnext/crm/onboarding_step/create_lead/create_lead.json +++ b/erpnext/crm/onboarding_step/create_lead/create_lead.json @@ -13,5 +13,7 @@ "name": "Create Lead", "owner": "Administrator", "reference_document": "Lead", - "title": "Create Lead" + "show_full_form": 0, + "title": "Create Lead", + "validate_action": 0 } \ No newline at end of file diff --git a/erpnext/crm/onboarding_step/create_opportunity/create_opportunity.json b/erpnext/crm/onboarding_step/create_opportunity/create_opportunity.json index 20949a435bc..231cf17bb57 100644 --- a/erpnext/crm/onboarding_step/create_opportunity/create_opportunity.json +++ b/erpnext/crm/onboarding_step/create_opportunity/create_opportunity.json @@ -13,5 +13,7 @@ "name": "Create Opportunity", "owner": "Administrator", "reference_document": "Opportunity", - "title": "Create Opportunity" + "show_full_form": 0, + "title": "Create Opportunity", + "validate_action": 0 } \ No newline at end of file diff --git a/erpnext/crm/onboarding_step/introduction_to_crm/introduction_to_crm.json b/erpnext/crm/onboarding_step/introduction_to_crm/introduction_to_crm.json index c923a77e1b9..552ade0fdda 100644 --- a/erpnext/crm/onboarding_step/introduction_to_crm/introduction_to_crm.json +++ b/erpnext/crm/onboarding_step/introduction_to_crm/introduction_to_crm.json @@ -12,6 +12,8 @@ "modified_by": "Administrator", "name": "Introduction to CRM", "owner": "Administrator", + "show_full_form": 0, "title": "Introduction to CRM", + "validate_action": 0, "video_url": "https://www.youtube.com/watch?v=o9XCSZHJfpA" } \ No newline at end of file diff --git a/erpnext/crm/onboarding_step/start_campaign/start_campaign.json b/erpnext/crm/onboarding_step/start_campaign/start_campaign.json deleted file mode 100644 index 53c3becea3b..00000000000 --- a/erpnext/crm/onboarding_step/start_campaign/start_campaign.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "action": "Create Entry", - "creation": "2020-05-09 23:38:10.284957", - "docstatus": 0, - "doctype": "Onboarding Step", - "idx": 0, - "is_complete": 0, - "is_mandatory": 0, - "is_single": 0, - "is_skipped": 0, - "modified": "2020-05-14 17:28:54.285958", - "modified_by": "Administrator", - "name": "Start Campaign", - "owner": "Administrator", - "reference_document": "Campaign", - "title": "Start Campaign" -} \ No newline at end of file From ca2b3231177bd10e9b41d8155239bee06e71ef62 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Wed, 20 May 2020 14:56:29 +0530 Subject: [PATCH 122/608] fix: Move address and contact templates to frappe --- erpnext/public/build.json | 2 - erpnext/public/js/templates/address_list.html | 22 -------- erpnext/public/js/templates/contact_list.html | 54 ------------------- 3 files changed, 78 deletions(-) delete mode 100644 erpnext/public/js/templates/address_list.html delete mode 100644 erpnext/public/js/templates/contact_list.html diff --git a/erpnext/public/build.json b/erpnext/public/build.json index e94d1ffe5c0..2695502269a 100644 --- a/erpnext/public/build.json +++ b/erpnext/public/build.json @@ -23,8 +23,6 @@ "public/js/queries.js", "public/js/sms_manager.js", "public/js/utils/party.js", - "public/js/templates/address_list.html", - "public/js/templates/contact_list.html", "public/js/controllers/stock_controller.js", "public/js/payment/payments.js", "public/js/controllers/taxes_and_totals.js", diff --git a/erpnext/public/js/templates/address_list.html b/erpnext/public/js/templates/address_list.html deleted file mode 100644 index 0f967b67a0f..00000000000 --- a/erpnext/public/js/templates/address_list.html +++ /dev/null @@ -1,22 +0,0 @@ -
-{% for(var i=0, l=addr_list.length; i -

- {%= i+1 %}. {%= addr_list[i].address_title %}{% if(addr_list[i].address_type!="Other") { %} - ({%= __(addr_list[i].address_type) %}){% } %} - {% if(addr_list[i].is_primary_address) { %} - ({%= __("Primary") %}){% } %} - {% if(addr_list[i].is_shipping_address) { %} - ({%= __("Shipping") %}){% } %} - - - {%= __("Edit") %} -

-

{%= addr_list[i].display %}

- -{% } %} -{% if(!addr_list.length) { %} -

{%= __("No address added yet.") %}

-{% } %} -

\ No newline at end of file diff --git a/erpnext/public/js/templates/contact_list.html b/erpnext/public/js/templates/contact_list.html deleted file mode 100644 index 7e6969163b7..00000000000 --- a/erpnext/public/js/templates/contact_list.html +++ /dev/null @@ -1,54 +0,0 @@ -
-{% for(var i=0, l=contact_list.length; i -

- {%= contact_list[i].first_name %} {%= contact_list[i].last_name %} - {% if(contact_list[i].is_primary_contact) { %} - ({%= __("Primary") %}) - {% } %} - {% if(contact_list[i].designation){ %} - – {%= contact_list[i].designation %} - {% } %} - - {%= __("Edit") %} -

- {% if (contact_list[i].phones || contact_list[i].email_ids) { %} -

- {% if(contact_list[i].phone) { %} - {%= __("Phone") %}: {%= contact_list[i].phone %} ({%= __("Primary") %})
- {% endif %} - {% if(contact_list[i].mobile_no) { %} - {%= __("Mobile No") %}: {%= contact_list[i].mobile_no %} ({%= __("Primary") %})
- {% endif %} - {% if(contact_list[i].phone_nos) { %} - {% for(var j=0, k=contact_list[i].phone_nos.length; j - {% } %} - {% endif %} -

-

- {% if(contact_list[i].email_id) { %} - {%= __("Email") %}: {%= contact_list[i].email_id %} ({%= __("Primary") %})
- {% endif %} - {% if(contact_list[i].email_ids) { %} - {% for(var j=0, k=contact_list[i].email_ids.length; j - {% } %} - {% endif %} -

- {% endif %} -

- {% if (contact_list[i].address) { %} - {%= __("Address") %}: {%= contact_list[i].address %}
- {% endif %} -

- -{% } %} -{% if(!contact_list.length) { %} -

{%= __("No contacts added yet.") %}

-{% } %} -

-

\ No newline at end of file From 91a9d3fbe56ddee81babd3c64c0760749804bfcb Mon Sep 17 00:00:00 2001 From: Marica Date: Wed, 20 May 2020 16:12:46 +0530 Subject: [PATCH 123/608] fix: Reload doc in Healthcare company patch (#21808) --- .../patches/v13_0/set_company_field_in_healthcare_doctypes.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/patches/v13_0/set_company_field_in_healthcare_doctypes.py b/erpnext/patches/v13_0/set_company_field_in_healthcare_doctypes.py index 2c646b164fb..9d0dae45535 100644 --- a/erpnext/patches/v13_0/set_company_field_in_healthcare_doctypes.py +++ b/erpnext/patches/v13_0/set_company_field_in_healthcare_doctypes.py @@ -6,4 +6,5 @@ def execute(): doctypes = ['Clinical Procedure', 'Inpatient Record', 'Lab Test', 'Patient Appointment', 'Patient Encounter', 'Vital Signs'] for entry in doctypes: if frappe.db.exists('DocType', entry): + frappe.reload_doc("Healthcare", "doctype", entry) frappe.db.sql("update `tab{dt}` set company = '{company}' where ifnull(company, '') = ''".format(dt=entry, company=company)) From 2b04e8eef096f52d93473f4537131c2233fb441e Mon Sep 17 00:00:00 2001 From: Marica Date: Wed, 20 May 2020 16:13:43 +0530 Subject: [PATCH 124/608] fix: Validate Payment Gateway only if it exists in Payment Request. (#21805) --- erpnext/accounts/doctype/payment_request/payment_request.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index 68aeb6d1d65..287e00f70fd 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -69,7 +69,7 @@ class PaymentRequest(Document): elif self.payment_request_type == 'Inward': self.db_set('status', 'Requested') - send_mail = self.payment_gateway_validation() + send_mail = self.payment_gateway_validation() if self.payment_gateway else None ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name) if (hasattr(ref_doc, "order_type") and getattr(ref_doc, "order_type") == "Shopping Cart") \ From 2346353a2231082868cd07de23a0ab12ea310c5e Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Wed, 20 May 2020 16:14:09 +0530 Subject: [PATCH 125/608] fix: missing parameter (#21802) --- .../monthly_attendance_sheet/monthly_attendance_sheet.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py b/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py index efc43bf1d1b..60767b5db08 100644 --- a/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py +++ b/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py @@ -66,11 +66,11 @@ def execute(filters=None): emp_att_map = {} for parameter in group_by_parameters: data.append([ ""+ parameter + ""]) - record, aaa = add_data(emp_map[parameter], att_map, filters, holiday_map, conditions, leave_list=leave_list) + record, aaa = add_data(emp_map[parameter], att_map, filters, holiday_map, conditions, default_holiday_list, leave_list=leave_list) emp_att_map.update(aaa) data += record else: - record, emp_att_map = add_data(emp_map, att_map, filters, holiday_map, conditions, leave_list=leave_list) + record, emp_att_map = add_data(emp_map, att_map, filters, holiday_map, conditions, default_holiday_list, leave_list=leave_list) data += record chart_data = get_chart_data(emp_att_map, days) @@ -120,7 +120,7 @@ def get_chart_data(emp_att_map, days): return chart -def add_data(employee_map, att_map, filters, holiday_map, conditions,leave_list=None): +def add_data(employee_map, att_map, filters, holiday_map, conditions, default_holiday_list, leave_list=None): record = [] emp_att_map = {} From ab250e4ed55aac4f3f451108b548f3fb71a31e09 Mon Sep 17 00:00:00 2001 From: Anupam Kumar Date: Wed, 20 May 2020 16:15:48 +0530 Subject: [PATCH 126/608] enable Allow Rename in sales stage (#21799) --- .../crm/doctype/sales_stage/sales_stage.json | 120 +++++------------- 1 file changed, 34 insertions(+), 86 deletions(-) diff --git a/erpnext/crm/doctype/sales_stage/sales_stage.json b/erpnext/crm/doctype/sales_stage/sales_stage.json index 4374bb58314..77aa559b771 100644 --- a/erpnext/crm/doctype/sales_stage/sales_stage.json +++ b/erpnext/crm/doctype/sales_stage/sales_stage.json @@ -1,96 +1,44 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "field:stage_name", - "beta": 0, - "creation": "2018-10-01 09:28:16.399518", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "allow_rename": 1, + "autoname": "field:stage_name", + "creation": "2018-10-01 09:28:16.399518", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "stage_name" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "stage_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Stage Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "fieldname": "stage_name", + "fieldtype": "Data", + "label": "Stage Name", "unique": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-10-01 09:29:43.230378", - "modified_by": "Administrator", - "module": "CRM", - "name": "Sales Stage", - "name_case": "", - "owner": "Administrator", + ], + "links": [], + "modified": "2020-05-20 12:22:01.866472", + "modified_by": "Administrator", + "module": "CRM", + "name": "Sales Stage", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Sales Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Sales Manager", + "share": 1, "write": 1 } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file From e091789332662a4e27d4c99cc788cb2cf321e7b4 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Wed, 20 May 2020 16:17:12 +0530 Subject: [PATCH 127/608] feat: remove website settings from boot (#21801) --- erpnext/startup/boot.py | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/startup/boot.py b/erpnext/startup/boot.py index 4ca43a89b8f..2b80fb8dfa1 100644 --- a/erpnext/startup/boot.py +++ b/erpnext/startup/boot.py @@ -10,7 +10,6 @@ def boot_session(bootinfo): """boot session - send website info if guest""" bootinfo.custom_css = frappe.db.get_value('Style Settings', None, 'custom_css') or '' - bootinfo.website_settings = frappe.get_doc('Website Settings') if frappe.session['user']!='Guest': update_page_info(bootinfo) From 22aec57bebb395b1f309ead0211d1819d197c622 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Fri, 8 May 2020 16:39:17 +0530 Subject: [PATCH 128/608] feat: asset onboarding, dashboards --- erpnext/assets/dashboard_fixtures.py | 96 ++++++++ erpnext/assets/desk_page/assets/assets.json | 4 +- erpnext/assets/doctype/asset/asset.js | 3 +- erpnext/assets/doctype/asset/asset.py | 2 + erpnext/assets/onboarding/assets/assets.json | 39 ++++ .../create_a_fixed_asset_item.json | 16 ++ .../create_the_asset/create_the_asset.json | 16 ++ .../introduction_to_assets.json | 16 ++ .../purchase_the_asset_item.json | 16 ++ .../category_wise_asset_value/__init__.py | 0 .../category_wise_asset_value.js | 43 ++++ .../category_wise_asset_value.json | 29 +++ .../category_wise_asset_value.py | 137 ++++++++++++ .../fixed_asset_register.js | 18 +- .../fixed_asset_register.py | 211 ++++++++++-------- .../location_wise_asset_value/__init__.py | 0 .../location_wise_asset_value.js | 43 ++++ .../location_wise_asset_value.json | 29 +++ .../location_wise_asset_value.py | 137 ++++++++++++ 19 files changed, 752 insertions(+), 103 deletions(-) create mode 100644 erpnext/assets/dashboard_fixtures.py create mode 100644 erpnext/assets/onboarding/assets/assets.json create mode 100644 erpnext/assets/onboarding_step/create_a_fixed_asset_item/create_a_fixed_asset_item.json create mode 100644 erpnext/assets/onboarding_step/create_the_asset/create_the_asset.json create mode 100644 erpnext/assets/onboarding_step/introduction_to_assets/introduction_to_assets.json create mode 100644 erpnext/assets/onboarding_step/purchase_the_asset_item/purchase_the_asset_item.json create mode 100644 erpnext/assets/report/category_wise_asset_value/__init__.py create mode 100644 erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.js create mode 100644 erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.json create mode 100644 erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.py create mode 100644 erpnext/assets/report/location_wise_asset_value/__init__.py create mode 100644 erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.js create mode 100644 erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.json create mode 100644 erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.py diff --git a/erpnext/assets/dashboard_fixtures.py b/erpnext/assets/dashboard_fixtures.py new file mode 100644 index 00000000000..aa4eed2f746 --- /dev/null +++ b/erpnext/assets/dashboard_fixtures.py @@ -0,0 +1,96 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +import frappe +import json + + +def get_data(): + return frappe._dict({ + "dashboards": get_dashboards(), + "charts": get_charts(), + "number_cards": get_number_cards(), + }) + +def get_dashboards(): + return [{ + "name": "Asset", + "dashboard_name": "Asset", + "charts": [ + { "chart": "Category-wise Asset Value" }, + { "chart": "Location-wise Asset Value" }, + ] + }] + +def get_charts(): + return [ + { + "name": "Category-wise Asset Value", + "chart_name": "Category-wise Asset Value", + "chart_type": "Report", + "report_name": "Category-wise Asset Value", + "is_custom": 1, + "x_field": "asset_category", + "timeseries": 0, + "filters_json": "{}", + "type": "Donut", + "doctype": "Dashboard Chart", + "y_axis": [ + { + "parent": "Category-wise Asset Value", + "parentfield": "y_axis", + "parenttype": "Dashboard Chart", + "y_field": "asset_value", + "doctype": "Dashboard Chart Field" + } + ], + "custom_options": json.dumps({ + "type": "donut", + "height": 300, + "axisOptions": {"shortenYAxisNumbers": 1} + }) + }, + { + "name": "Location-wise Asset Value", + "chart_name": "Location-wise Asset Value", + "chart_type": "Report", + "report_name": "Location-wise Asset Value", + "is_custom": 1, + "x_field": "location", + "timeseries": 0, + "filters_json": "{}", + "type": "Donut", + "doctype": "Dashboard Chart", + "y_axis": [ + { + "parent": "Location-wise Asset Value", + "parentfield": "y_axis", + "parenttype": "Dashboard Chart", + "y_field": "asset_value", + "doctype": "Dashboard Chart Field" + } + ], + "custom_options": json.dumps({ + "type": "donut", + "height": 300, + "axisOptions": {"shortenYAxisNumbers": 1} + }) + } + ] + + +def get_number_cards(): + return [ + { + "name": "Asset Value", + "label": "Asset Value ", + "function": "Sum", + "aggregate_function_based_on": "gross_purchase_amount", + "document_type": "Asset", + "is_public": 0, + "show_percentage_stats": 1, + "stats_time_interval": "Monthly", + "filters_json": "[]", + "doctype": "Number Card" + } + ] \ No newline at end of file diff --git a/erpnext/assets/desk_page/assets/assets.json b/erpnext/assets/desk_page/assets/assets.json index 03094160e5f..13709e370bb 100644 --- a/erpnext/assets/desk_page/assets/assets.json +++ b/erpnext/assets/desk_page/assets/assets.json @@ -24,14 +24,14 @@ "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, - "icon": "", "idx": 0, "is_standard": 1, "label": "Assets", - "modified": "2020-04-01 11:28:51.072198", + "modified": "2020-05-08 16:07:04.671296", "modified_by": "Administrator", "module": "Assets", "name": "Assets", + "onboarding": "Assets", "owner": "Administrator", "pin_to_bottom": 0, "pin_to_top": 0, diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index a53ff881777..fba20c0c879 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -387,7 +387,8 @@ frappe.ui.form.on('Asset', { } frm.set_value('gross_purchase_amount', item.base_net_rate + item.item_tax_amount); frm.set_value('purchase_receipt_amount', item.base_net_rate + item.item_tax_amount); - frm.set_value('location', item.asset_location); + item.asset_location && frm.set_value('location', item.asset_location); + frm.set_value('cost_center', item.cost_center || purchase_doc.cost_center); }, set_depreciation_rate: function(frm, row) { diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 505ba4c6b64..2ecabe60f03 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -127,6 +127,8 @@ class Asset(AccountsController): frappe.throw(_("Available-for-use Date should be after purchase date")) def validate_gross_and_purchase_amount(self): + if self.is_existing_asset: return + if self.gross_purchase_amount and self.gross_purchase_amount != self.purchase_receipt_amount: frappe.throw(_("Gross Purchase Amount should be {} to purchase amount of one single Asset. {}\ Please do not book expense of multiple assets against one single Asset.") diff --git a/erpnext/assets/onboarding/assets/assets.json b/erpnext/assets/onboarding/assets/assets.json new file mode 100644 index 00000000000..b32a7f2be5b --- /dev/null +++ b/erpnext/assets/onboarding/assets/assets.json @@ -0,0 +1,39 @@ +{ + "allow_roles": [ + { + "role": "Accounts User" + }, + { + "role": "Maintenance User" + } + ], + "creation": "2020-05-08 15:10:45.571457", + "docstatus": 0, + "doctype": "Onboarding", + "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/asset", + "idx": 0, + "is_complete": 0, + "modified": "2020-05-08 16:17:31.685943", + "modified_by": "Administrator", + "module": "Assets", + "name": "Assets", + "owner": "Administrator", + "steps": [ + { + "step": "Introduction to Assets" + }, + { + "step": "Create a Fixed Asset Item" + }, + { + "step": "Purchase the Asset Item" + }, + { + "step": "Create the Asset" + } + ], + "subtitle": "Assets, Depreciations, Repairs and more", + "success_message": "The Asset Module is all set up!", + "title": "Let's Setup Asset Management", + "user_can_dismiss": 1 +} \ No newline at end of file diff --git a/erpnext/assets/onboarding_step/create_a_fixed_asset_item/create_a_fixed_asset_item.json b/erpnext/assets/onboarding_step/create_a_fixed_asset_item/create_a_fixed_asset_item.json new file mode 100644 index 00000000000..f5818c091f1 --- /dev/null +++ b/erpnext/assets/onboarding_step/create_a_fixed_asset_item/create_a_fixed_asset_item.json @@ -0,0 +1,16 @@ +{ + "action": "Create Entry", + "creation": "2020-05-08 13:20:00.259985", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_skipped": 0, + "modified": "2020-05-08 13:20:00.259985", + "modified_by": "Administrator", + "name": "Create a Fixed Asset Item", + "owner": "Administrator", + "reference_document": "Item", + "title": "Create a Fixed Asset Item" +} \ No newline at end of file diff --git a/erpnext/assets/onboarding_step/create_the_asset/create_the_asset.json b/erpnext/assets/onboarding_step/create_the_asset/create_the_asset.json new file mode 100644 index 00000000000..28d8485f976 --- /dev/null +++ b/erpnext/assets/onboarding_step/create_the_asset/create_the_asset.json @@ -0,0 +1,16 @@ +{ + "action": "Create Entry", + "creation": "2020-05-08 13:21:53.332538", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_skipped": 0, + "modified": "2020-05-08 13:21:53.332538", + "modified_by": "Administrator", + "name": "Create the Asset", + "owner": "Administrator", + "reference_document": "Asset", + "title": "Create the Asset" +} \ No newline at end of file diff --git a/erpnext/assets/onboarding_step/introduction_to_assets/introduction_to_assets.json b/erpnext/assets/onboarding_step/introduction_to_assets/introduction_to_assets.json new file mode 100644 index 00000000000..8aceafd7e5f --- /dev/null +++ b/erpnext/assets/onboarding_step/introduction_to_assets/introduction_to_assets.json @@ -0,0 +1,16 @@ +{ + "action": "Watch Video", + "creation": "2020-05-08 13:18:25.424715", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_skipped": 0, + "modified": "2020-05-08 16:06:16.625646", + "modified_by": "Administrator", + "name": "Introduction to Assets", + "owner": "Administrator", + "title": "Introduction to Fixed Asset Management", + "video_url": "https://www.youtube.com/watch?v=I-K8pLRmvSo" +} \ No newline at end of file diff --git a/erpnext/assets/onboarding_step/purchase_the_asset_item/purchase_the_asset_item.json b/erpnext/assets/onboarding_step/purchase_the_asset_item/purchase_the_asset_item.json new file mode 100644 index 00000000000..b6f38decb9e --- /dev/null +++ b/erpnext/assets/onboarding_step/purchase_the_asset_item/purchase_the_asset_item.json @@ -0,0 +1,16 @@ +{ + "action": "Create Entry", + "creation": "2020-05-08 13:21:28.208059", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_skipped": 0, + "modified": "2020-05-08 13:21:28.208059", + "modified_by": "Administrator", + "name": "Purchase the Asset Item", + "owner": "Administrator", + "reference_document": "Purchase Receipt", + "title": "Purchase the Asset Item" +} \ No newline at end of file diff --git a/erpnext/assets/report/category_wise_asset_value/__init__.py b/erpnext/assets/report/category_wise_asset_value/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.js b/erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.js new file mode 100644 index 00000000000..aa643efb2df --- /dev/null +++ b/erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.js @@ -0,0 +1,43 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Category-wise Asset Value"] = { + "filters": [ + { + fieldname:"company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1 + }, + { + fieldname:"purchase_date", + label: __("Purchase Date"), + fieldtype: "Date" + }, + { + fieldname:"available_for_use_date", + label: __("Available For Use Date"), + fieldtype: "Date" + }, + { + fieldname:"cost_center", + label: __("Cost Center"), + fieldtype: "Link", + options: "Cost Center" + }, + { + fieldname:"finance_book", + label: __("Finance Book"), + fieldtype: "Link", + options: "Finance Book" + }, + { + fieldname:"is_existing_asset", + label: __("Is Existing Asset"), + fieldtype: "Check" + }, + ] +}; diff --git a/erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.json b/erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.json new file mode 100644 index 00000000000..a6dbce05163 --- /dev/null +++ b/erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.json @@ -0,0 +1,29 @@ +{ + "add_total_row": 0, + "creation": "2020-05-08 15:36:02.116096", + "disable_prepared_report": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "javascript": "", + "modified": "2020-05-08 15:36:02.116096", + "modified_by": "Administrator", + "module": "Assets", + "name": "Category-wise Asset Value", + "owner": "Administrator", + "prepared_report": 0, + "query": "", + "ref_doctype": "Asset", + "report_name": "Category-wise Asset Value", + "report_type": "Script Report", + "roles": [ + { + "role": "Accounts User" + }, + { + "role": "Quality Manager" + } + ] +} \ No newline at end of file diff --git a/erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.py b/erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.py new file mode 100644 index 00000000000..96131e92605 --- /dev/null +++ b/erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.py @@ -0,0 +1,137 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe import _ +from frappe.utils import cstr, today, flt + +def execute(filters=None): + filters = frappe._dict(filters or {}) + columns = get_columns(filters) + data = get_data(filters) + + return columns, data + +def get_conditions(filters): + conditions = { 'docstatus': 1 } + + if filters.get('company'): + conditions["company"] = filters.company + if filters.get('purchase_date'): + conditions["purchase_date"] = ('<=', filters.get('purchase_date')) + if filters.get('available_for_use_date'): + conditions["available_for_use_date"] = ('<=', filters.get('available_for_use_date')) + if filters.get('is_existing_asset'): + conditions["is_existing_asset"] = filters.get('is_existing_asset') + if filters.get('cost_center'): + conditions["cost_center"] = filters.get('cost_center') + + return conditions + +def get_data(filters): + + data = [] + depreciation_amount_map = get_finance_book_value_map(filters) + + assets_record = frappe.db.get_all("Asset", + filters=get_conditions(filters), + fields=["name", "asset_name", "asset_category", "gross_purchase_amount", + "opening_accumulated_depreciation", "available_for_use_date", "purchase_date"], + group_by="asset_category") + + for asset in assets_record: + asset_value = asset.gross_purchase_amount - flt(asset.opening_accumulated_depreciation) \ + - flt(depreciation_amount_map.get(asset.name)) + if asset_value: + row = { + "asset_category": asset.asset_category, + "asset_id": asset.name, + "asset_name": asset.asset_name, + "purchase_date": asset.purchase_date, + "available_for_use_date": asset.available_for_use_date, + "gross_purchase_amount": asset.gross_purchase_amount, + "opening_accumulated_depreciation": asset.opening_accumulated_depreciation, + "depreciated_amount": depreciation_amount_map.get(asset.name) or 0.0, + "asset_value": asset_value + } + data.append(row) + + return data + +def get_finance_book_value_map(filters): + date = filters.get('purchase_date') or filters.get('available_for_use_date') or today() + + return frappe._dict(frappe.db.sql(''' Select + parent, SUM(depreciation_amount) + FROM `tabDepreciation Schedule` + WHERE + parentfield='schedules' + AND schedule_date<=%s + AND journal_entry IS NOT NULL + AND ifnull(finance_book, '')=%s + GROUP BY parent''', (date, cstr(filters.finance_book or '')))) + +def get_columns(filters): + return [ + { + "label": _("Asset Category"), + "fieldtype": "Link", + "fieldname": "asset_category", + "options": "Asset Category", + "width": 120 + }, + { + "label": _("Asset Id"), + "fieldtype": "Link", + "fieldname": "asset_id", + "options": "Asset", + "width": 100 + }, + { + "label": _("Asset Name"), + "fieldtype": "Data", + "fieldname": "asset_name", + "width": 140 + }, + { + "label": _("Purchase Date"), + "fieldtype": "Date", + "fieldname": "purchase_date", + "width": 90 + }, + { + "label": _("Available For Use Date"), + "fieldtype": "Date", + "fieldname": "available_for_use_date", + "width": 90 + }, + { + "label": _("Gross Purchase Amount"), + "fieldname": "gross_purchase_amount", + "fieldtype": "Currency", + "options": "company:currency", + "width": 100 + }, + { + "label": _("Opening Accumulated Depreciation"), + "fieldname": "opening_accumulated_depreciation", + "fieldtype": "Currency", + "options": "company:currency", + "width": 90 + }, + { + "label": _("Depreciated Amount"), + "fieldname": "depreciated_amount", + "fieldtype": "Currency", + "options": "company:currency", + "width": 100 + }, + { + "label": _("Asset Value"), + "fieldname": "asset_value", + "fieldtype": "Currency", + "options": "company:currency", + "width": 100 + } + ] \ No newline at end of file diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js index 91ce9ce7fe9..d68993c2976 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js @@ -30,18 +30,24 @@ frappe.query_reports["Fixed Asset Register"] = { label: __("Available For Use Date"), fieldtype: "Date" }, - { - fieldname:"finance_book", - label: __("Finance Book"), - fieldtype: "Link", - options: "Finance Book" - }, { fieldname:"asset_category", label: __("Asset Category"), fieldtype: "Link", options: "Asset Category" }, + { + fieldname:"cost_center", + label: __("Cost Center"), + fieldtype: "Link", + options: "Cost Center" + }, + { + fieldname:"finance_book", + label: __("Finance Book"), + fieldtype: "Link", + options: "Finance Book" + }, { fieldname:"is_existing_asset", label: __("Is Existing Asset"), diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py index fa2fe7b4a3c..64ee6a35037 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py @@ -10,105 +10,13 @@ def execute(filters=None): filters = frappe._dict(filters or {}) columns = get_columns(filters) data = get_data(filters) - return columns, data + chart = prepare_chart_data(data, columns) -def get_columns(filters): - return [ - { - "label": _("Asset Id"), - "fieldtype": "Link", - "fieldname": "asset_id", - "options": "Asset", - "width": 100 - }, - { - "label": _("Asset Name"), - "fieldtype": "Data", - "fieldname": "asset_name", - "width": 140 - }, - { - "label": _("Asset Category"), - "fieldtype": "Link", - "fieldname": "asset_category", - "options": "Asset Category", - "width": 100 - }, - { - "label": _("Status"), - "fieldtype": "Data", - "fieldname": "status", - "width": 90 - }, - { - "label": _("Purchase Date"), - "fieldtype": "Date", - "fieldname": "purchase_date", - "width": 90 - }, - { - "label": _("Available For Use Date"), - "fieldtype": "Date", - "fieldname": "available_for_use_date", - "width": 90 - }, - { - "label": _("Gross Purchase Amount"), - "fieldname": "gross_purchase_amount", - "options": "Currency", - "width": 90 - }, - { - "label": _("Asset Value"), - "fieldname": "asset_value", - "options": "Currency", - "width": 90 - }, - { - "label": _("Opening Accumulated Depreciation"), - "fieldname": "opening_accumulated_depreciation", - "options": "Currency", - "width": 90 - }, - { - "label": _("Depreciated Amount"), - "fieldname": "depreciated_amount", - "options": "Currency", - "width": 90 - }, - { - "label": _("Cost Center"), - "fieldtype": "Link", - "fieldname": "cost_center", - "options": "Cost Center", - "width": 100 - }, - { - "label": _("Department"), - "fieldtype": "Link", - "fieldname": "department", - "options": "Department", - "width": 100 - }, - { - "label": _("Vendor Name"), - "fieldtype": "Data", - "fieldname": "vendor_name", - "width": 100 - }, - { - "label": _("Location"), - "fieldtype": "Link", - "fieldname": "location", - "options": "Location", - "width": 100 - }, - ] + return columns, data, None, chart def get_conditions(filters): conditions = { 'docstatus': 1 } status = filters.status - date = filters.date if filters.get('company'): conditions["company"] = filters.company @@ -120,6 +28,8 @@ def get_conditions(filters): conditions["is_existing_asset"] = filters.get('is_existing_asset') if filters.get('asset_category'): conditions["asset_category"] = filters.get('asset_category') + if filters.get('cost_center'): + conditions["cost_center"] = filters.get('cost_center') # In Store assets are those that are not sold or scrapped operand = 'not in' @@ -169,6 +79,22 @@ def get_data(filters): return data +def prepare_chart_data(data, columns): + label_values_map = {} + for d in data: + if not label_values_map.get(d.get('asset_category')): + label_values_map[d.get('asset_category')] = 0 + label_values_map[d.get('asset_category')] += d.get('asset_value') + + return { + "data" : { + "labels": label_values_map.keys(), + "datasets": [{ "values": label_values_map.values() }] + }, + "type": 'donut', + "height": 250 + } + def get_finance_book_value_map(filters): date = filters.get('purchase_date') or filters.get('available_for_use_date') or today() @@ -201,3 +127,100 @@ def get_purchase_invoice_supplier_map(): AND pii.is_fixed_asset=1 AND pi.docstatus=1 AND pi.is_return=0''')) + +def get_columns(filters): + return [ + { + "label": _("Asset Id"), + "fieldtype": "Link", + "fieldname": "asset_id", + "options": "Asset", + "width": 60 + }, + { + "label": _("Asset Name"), + "fieldtype": "Data", + "fieldname": "asset_name", + "width": 140 + }, + { + "label": _("Asset Category"), + "fieldtype": "Link", + "fieldname": "asset_category", + "options": "Asset Category", + "width": 100 + }, + { + "label": _("Status"), + "fieldtype": "Data", + "fieldname": "status", + "width": 80 + }, + { + "label": _("Purchase Date"), + "fieldtype": "Date", + "fieldname": "purchase_date", + "width": 90 + }, + { + "label": _("Available For Use Date"), + "fieldtype": "Date", + "fieldname": "available_for_use_date", + "width": 90 + }, + { + "label": _("Gross Purchase Amount"), + "fieldname": "gross_purchase_amount", + "fieldtype": "Currency", + "options": "company:currency", + "width": 100 + }, + { + "label": _("Asset Value"), + "fieldname": "asset_value", + "fieldtype": "Currency", + "options": "company:currency", + "width": 100 + }, + { + "label": _("Opening Accumulated Depreciation"), + "fieldname": "opening_accumulated_depreciation", + "fieldtype": "Currency", + "options": "company:currency", + "width": 90 + }, + { + "label": _("Depreciated Amount"), + "fieldname": "depreciated_amount", + "fieldtype": "Currency", + "options": "company:currency", + "width": 100 + }, + { + "label": _("Cost Center"), + "fieldtype": "Link", + "fieldname": "cost_center", + "options": "Cost Center", + "width": 100 + }, + { + "label": _("Department"), + "fieldtype": "Link", + "fieldname": "department", + "options": "Department", + "width": 100 + }, + { + "label": _("Vendor Name"), + "fieldtype": "Data", + "fieldname": "vendor_name", + "width": 100 + }, + { + "label": _("Location"), + "fieldtype": "Link", + "fieldname": "location", + "options": "Location", + "width": 100 + }, + ] \ No newline at end of file diff --git a/erpnext/assets/report/location_wise_asset_value/__init__.py b/erpnext/assets/report/location_wise_asset_value/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.js b/erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.js new file mode 100644 index 00000000000..efdafa18c5c --- /dev/null +++ b/erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.js @@ -0,0 +1,43 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Location-wise Asset Value"] = { + "filters": [ + { + fieldname:"company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1 + }, + { + fieldname:"purchase_date", + label: __("Purchase Date"), + fieldtype: "Date" + }, + { + fieldname:"available_for_use_date", + label: __("Available For Use Date"), + fieldtype: "Date" + }, + { + fieldname:"cost_center", + label: __("Cost Center"), + fieldtype: "Link", + options: "Cost Center" + }, + { + fieldname:"finance_book", + label: __("Finance Book"), + fieldtype: "Link", + options: "Finance Book" + }, + { + fieldname:"is_existing_asset", + label: __("Is Existing Asset"), + fieldtype: "Check" + }, + ] +}; diff --git a/erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.json b/erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.json new file mode 100644 index 00000000000..2a554d83d0e --- /dev/null +++ b/erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.json @@ -0,0 +1,29 @@ +{ + "add_total_row": 0, + "creation": "2020-05-08 15:47:55.036143", + "disable_prepared_report": 1, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "javascript": "", + "modified": "2020-05-08 15:47:55.036143", + "modified_by": "Administrator", + "module": "Assets", + "name": "Location-wise Asset Value", + "owner": "Administrator", + "prepared_report": 0, + "query": "", + "ref_doctype": "Asset", + "report_name": "Location-wise Asset Value", + "report_type": "Script Report", + "roles": [ + { + "role": "Accounts User" + }, + { + "role": "Quality Manager" + } + ] +} \ No newline at end of file diff --git a/erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.py b/erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.py new file mode 100644 index 00000000000..6aade0e27aa --- /dev/null +++ b/erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.py @@ -0,0 +1,137 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe import _ +from frappe.utils import cstr, today, flt + +def execute(filters=None): + filters = frappe._dict(filters or {}) + columns = get_columns(filters) + data = get_data(filters) + + return columns, data + +def get_conditions(filters): + conditions = { 'docstatus': 1 } + + if filters.get('company'): + conditions["company"] = filters.company + if filters.get('purchase_date'): + conditions["purchase_date"] = ('<=', filters.get('purchase_date')) + if filters.get('available_for_use_date'): + conditions["available_for_use_date"] = ('<=', filters.get('available_for_use_date')) + if filters.get('is_existing_asset'): + conditions["is_existing_asset"] = filters.get('is_existing_asset') + if filters.get('cost_center'): + conditions["cost_center"] = filters.get('cost_center') + + return conditions + +def get_data(filters): + + data = [] + depreciation_amount_map = get_finance_book_value_map(filters) + + assets_record = frappe.db.get_all("Asset", + filters=get_conditions(filters), + fields=["name", "asset_name", "location", "gross_purchase_amount", + "opening_accumulated_depreciation", "available_for_use_date", "purchase_date"], + group_by="location") + + for asset in assets_record: + asset_value = asset.gross_purchase_amount - flt(asset.opening_accumulated_depreciation) \ + - flt(depreciation_amount_map.get(asset.name)) + if asset_value: + row = { + "location": asset.location, + "asset_id": asset.name, + "asset_name": asset.asset_name, + "purchase_date": asset.purchase_date, + "available_for_use_date": asset.available_for_use_date, + "gross_purchase_amount": asset.gross_purchase_amount, + "opening_accumulated_depreciation": asset.opening_accumulated_depreciation, + "depreciated_amount": depreciation_amount_map.get(asset.name) or 0.0, + "asset_value": asset_value + } + data.append(row) + + return data + +def get_finance_book_value_map(filters): + date = filters.get('purchase_date') or filters.get('available_for_use_date') or today() + + return frappe._dict(frappe.db.sql(''' Select + parent, SUM(depreciation_amount) + FROM `tabDepreciation Schedule` + WHERE + parentfield='schedules' + AND schedule_date<=%s + AND journal_entry IS NOT NULL + AND ifnull(finance_book, '')=%s + GROUP BY parent''', (date, cstr(filters.finance_book or '')))) + +def get_columns(filters): + return [ + { + "label": _("Location"), + "fieldtype": "Link", + "fieldname": "location", + "options": "Location", + "width": 120 + }, + { + "label": _("Asset Id"), + "fieldtype": "Link", + "fieldname": "asset_id", + "options": "Asset", + "width": 100 + }, + { + "label": _("Asset Name"), + "fieldtype": "Data", + "fieldname": "asset_name", + "width": 140 + }, + { + "label": _("Purchase Date"), + "fieldtype": "Date", + "fieldname": "purchase_date", + "width": 90 + }, + { + "label": _("Available For Use Date"), + "fieldtype": "Date", + "fieldname": "available_for_use_date", + "width": 90 + }, + { + "label": _("Gross Purchase Amount"), + "fieldname": "gross_purchase_amount", + "fieldtype": "Currency", + "options": "company:currency", + "width": 100 + }, + { + "label": _("Opening Accumulated Depreciation"), + "fieldname": "opening_accumulated_depreciation", + "fieldtype": "Currency", + "options": "company:currency", + "width": 90 + }, + { + "label": _("Depreciated Amount"), + "fieldname": "depreciated_amount", + "fieldtype": "Currency", + "options": "company:currency", + "width": 100 + }, + { + "label": _("Asset Value"), + "fieldname": "asset_value", + "fieldtype": "Currency", + "options": "company:currency", + "width": 100 + } + ] \ No newline at end of file From 03007de4d79273df5072731eb5ee4139d2d25b88 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Fri, 8 May 2020 16:55:19 +0530 Subject: [PATCH 129/608] fix: unexpected removal of finance book filter --- .../report/fixed_asset_register/fixed_asset_register.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js index d68993c2976..a88f6accb89 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js @@ -36,6 +36,12 @@ frappe.query_reports["Fixed Asset Register"] = { fieldtype: "Link", options: "Asset Category" }, + { + fieldname:"finance_book", + label: __("Finance Book"), + fieldtype: "Link", + options: "Finance Book" + }, { fieldname:"cost_center", label: __("Cost Center"), From 1dcf10339879e58dbbc4354eaa7ab8582e2c48c3 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 11 May 2020 14:55:38 +0530 Subject: [PATCH 130/608] chore: enhance fixed asset report and remove redundant reports --- .../category_wise_asset_value/__init__.py | 0 .../category_wise_asset_value.js | 43 ------ .../category_wise_asset_value.json | 29 ---- .../category_wise_asset_value.py | 137 ------------------ .../fixed_asset_register.js | 7 + .../fixed_asset_register.py | 61 +++++++- .../location_wise_asset_value/__init__.py | 0 .../location_wise_asset_value.js | 43 ------ .../location_wise_asset_value.json | 29 ---- .../location_wise_asset_value.py | 137 ------------------ 10 files changed, 62 insertions(+), 424 deletions(-) delete mode 100644 erpnext/assets/report/category_wise_asset_value/__init__.py delete mode 100644 erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.js delete mode 100644 erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.json delete mode 100644 erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.py delete mode 100644 erpnext/assets/report/location_wise_asset_value/__init__.py delete mode 100644 erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.js delete mode 100644 erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.json delete mode 100644 erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.py diff --git a/erpnext/assets/report/category_wise_asset_value/__init__.py b/erpnext/assets/report/category_wise_asset_value/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.js b/erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.js deleted file mode 100644 index aa643efb2df..00000000000 --- a/erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.js +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt -/* eslint-disable */ - -frappe.query_reports["Category-wise Asset Value"] = { - "filters": [ - { - fieldname:"company", - label: __("Company"), - fieldtype: "Link", - options: "Company", - default: frappe.defaults.get_user_default("Company"), - reqd: 1 - }, - { - fieldname:"purchase_date", - label: __("Purchase Date"), - fieldtype: "Date" - }, - { - fieldname:"available_for_use_date", - label: __("Available For Use Date"), - fieldtype: "Date" - }, - { - fieldname:"cost_center", - label: __("Cost Center"), - fieldtype: "Link", - options: "Cost Center" - }, - { - fieldname:"finance_book", - label: __("Finance Book"), - fieldtype: "Link", - options: "Finance Book" - }, - { - fieldname:"is_existing_asset", - label: __("Is Existing Asset"), - fieldtype: "Check" - }, - ] -}; diff --git a/erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.json b/erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.json deleted file mode 100644 index a6dbce05163..00000000000 --- a/erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "add_total_row": 0, - "creation": "2020-05-08 15:36:02.116096", - "disable_prepared_report": 1, - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 0, - "is_standard": "Yes", - "javascript": "", - "modified": "2020-05-08 15:36:02.116096", - "modified_by": "Administrator", - "module": "Assets", - "name": "Category-wise Asset Value", - "owner": "Administrator", - "prepared_report": 0, - "query": "", - "ref_doctype": "Asset", - "report_name": "Category-wise Asset Value", - "report_type": "Script Report", - "roles": [ - { - "role": "Accounts User" - }, - { - "role": "Quality Manager" - } - ] -} \ No newline at end of file diff --git a/erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.py b/erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.py deleted file mode 100644 index 96131e92605..00000000000 --- a/erpnext/assets/report/category_wise_asset_value/category_wise_asset_value.py +++ /dev/null @@ -1,137 +0,0 @@ -# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - -from __future__ import unicode_literals -import frappe -from frappe import _ -from frappe.utils import cstr, today, flt - -def execute(filters=None): - filters = frappe._dict(filters or {}) - columns = get_columns(filters) - data = get_data(filters) - - return columns, data - -def get_conditions(filters): - conditions = { 'docstatus': 1 } - - if filters.get('company'): - conditions["company"] = filters.company - if filters.get('purchase_date'): - conditions["purchase_date"] = ('<=', filters.get('purchase_date')) - if filters.get('available_for_use_date'): - conditions["available_for_use_date"] = ('<=', filters.get('available_for_use_date')) - if filters.get('is_existing_asset'): - conditions["is_existing_asset"] = filters.get('is_existing_asset') - if filters.get('cost_center'): - conditions["cost_center"] = filters.get('cost_center') - - return conditions - -def get_data(filters): - - data = [] - depreciation_amount_map = get_finance_book_value_map(filters) - - assets_record = frappe.db.get_all("Asset", - filters=get_conditions(filters), - fields=["name", "asset_name", "asset_category", "gross_purchase_amount", - "opening_accumulated_depreciation", "available_for_use_date", "purchase_date"], - group_by="asset_category") - - for asset in assets_record: - asset_value = asset.gross_purchase_amount - flt(asset.opening_accumulated_depreciation) \ - - flt(depreciation_amount_map.get(asset.name)) - if asset_value: - row = { - "asset_category": asset.asset_category, - "asset_id": asset.name, - "asset_name": asset.asset_name, - "purchase_date": asset.purchase_date, - "available_for_use_date": asset.available_for_use_date, - "gross_purchase_amount": asset.gross_purchase_amount, - "opening_accumulated_depreciation": asset.opening_accumulated_depreciation, - "depreciated_amount": depreciation_amount_map.get(asset.name) or 0.0, - "asset_value": asset_value - } - data.append(row) - - return data - -def get_finance_book_value_map(filters): - date = filters.get('purchase_date') or filters.get('available_for_use_date') or today() - - return frappe._dict(frappe.db.sql(''' Select - parent, SUM(depreciation_amount) - FROM `tabDepreciation Schedule` - WHERE - parentfield='schedules' - AND schedule_date<=%s - AND journal_entry IS NOT NULL - AND ifnull(finance_book, '')=%s - GROUP BY parent''', (date, cstr(filters.finance_book or '')))) - -def get_columns(filters): - return [ - { - "label": _("Asset Category"), - "fieldtype": "Link", - "fieldname": "asset_category", - "options": "Asset Category", - "width": 120 - }, - { - "label": _("Asset Id"), - "fieldtype": "Link", - "fieldname": "asset_id", - "options": "Asset", - "width": 100 - }, - { - "label": _("Asset Name"), - "fieldtype": "Data", - "fieldname": "asset_name", - "width": 140 - }, - { - "label": _("Purchase Date"), - "fieldtype": "Date", - "fieldname": "purchase_date", - "width": 90 - }, - { - "label": _("Available For Use Date"), - "fieldtype": "Date", - "fieldname": "available_for_use_date", - "width": 90 - }, - { - "label": _("Gross Purchase Amount"), - "fieldname": "gross_purchase_amount", - "fieldtype": "Currency", - "options": "company:currency", - "width": 100 - }, - { - "label": _("Opening Accumulated Depreciation"), - "fieldname": "opening_accumulated_depreciation", - "fieldtype": "Currency", - "options": "company:currency", - "width": 90 - }, - { - "label": _("Depreciated Amount"), - "fieldname": "depreciated_amount", - "fieldtype": "Currency", - "options": "company:currency", - "width": 100 - }, - { - "label": _("Asset Value"), - "fieldname": "asset_value", - "fieldtype": "Currency", - "options": "company:currency", - "width": 100 - } - ] \ No newline at end of file diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js index a88f6accb89..e886a35e5d3 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js @@ -54,6 +54,13 @@ frappe.query_reports["Fixed Asset Register"] = { fieldtype: "Link", options: "Finance Book" }, + { + fieldname:"group_by", + label: __("Group By"), + fieldtype: "Select", + options: " \nAsset Category\nLocation", + default: '', + }, { fieldname:"is_existing_asset", label: __("Is Existing Asset"), diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py index 64ee6a35037..c6b0c4ecf53 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py @@ -44,23 +44,33 @@ def get_data(filters): data = [] - conditions = get_conditions(filters) depreciation_amount_map = get_finance_book_value_map(filters) pr_supplier_map = get_purchase_receipt_supplier_map() pi_supplier_map = get_purchase_invoice_supplier_map() - assets_record = frappe.db.get_all("Asset", - filters=conditions, - fields=["name", "asset_name", "department", "cost_center", "purchase_receipt", + conditions = get_conditions(filters) + group_by = frappe.scrub(filters.get("group_by") or "") + + if group_by: + if group_by == "asset_category": + fields = ["asset_category", "gross_purchase_amount", "opening_accumulated_depreciation"] + else: + fields = ["location", "gross_purchase_amount", "opening_accumulated_depreciation"] + + assets_record = frappe.db.get_all("Asset", filters=conditions, fields=fields, group_by=group_by) + print(assets_record) + else: + fields = ["name as asset_id", "asset_name", "status", "department", "cost_center", "purchase_receipt", "asset_category", "purchase_date", "gross_purchase_amount", "location", - "available_for_use_date", "status", "purchase_invoice", "opening_accumulated_depreciation"]) + "available_for_use_date", "purchase_invoice", "opening_accumulated_depreciation"] + assets_record = frappe.db.get_all("Asset", filters=conditions, fields=fields) for asset in assets_record: asset_value = asset.gross_purchase_amount - flt(asset.opening_accumulated_depreciation) \ - flt(depreciation_amount_map.get(asset.name)) if asset_value: row = { - "asset_id": asset.name, + "asset_id": asset.asset_id, "asset_name": asset.asset_name, "status": asset.status, "department": asset.department, @@ -129,6 +139,45 @@ def get_purchase_invoice_supplier_map(): AND pi.is_return=0''')) def get_columns(filters): + if filters.get("group_by"): + return [ + { + "label": _("{}").format(filters.get("group_by")), + "fieldtype": "Link", + "fieldname": frappe.scrub(filters.get("group_by")), + "options": filters.get("group_by"), + "width": 120 + }, + { + "label": _("Gross Purchase Amount"), + "fieldname": "gross_purchase_amount", + "fieldtype": "Currency", + "options": "company:currency", + "width": 100 + }, + { + "label": _("Opening Accumulated Depreciation"), + "fieldname": "opening_accumulated_depreciation", + "fieldtype": "Currency", + "options": "company:currency", + "width": 90 + }, + { + "label": _("Depreciated Amount"), + "fieldname": "depreciated_amount", + "fieldtype": "Currency", + "options": "company:currency", + "width": 100 + }, + { + "label": _("Asset Value"), + "fieldname": "asset_value", + "fieldtype": "Currency", + "options": "company:currency", + "width": 100 + } + ] + return [ { "label": _("Asset Id"), diff --git a/erpnext/assets/report/location_wise_asset_value/__init__.py b/erpnext/assets/report/location_wise_asset_value/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.js b/erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.js deleted file mode 100644 index efdafa18c5c..00000000000 --- a/erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.js +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt -/* eslint-disable */ - -frappe.query_reports["Location-wise Asset Value"] = { - "filters": [ - { - fieldname:"company", - label: __("Company"), - fieldtype: "Link", - options: "Company", - default: frappe.defaults.get_user_default("Company"), - reqd: 1 - }, - { - fieldname:"purchase_date", - label: __("Purchase Date"), - fieldtype: "Date" - }, - { - fieldname:"available_for_use_date", - label: __("Available For Use Date"), - fieldtype: "Date" - }, - { - fieldname:"cost_center", - label: __("Cost Center"), - fieldtype: "Link", - options: "Cost Center" - }, - { - fieldname:"finance_book", - label: __("Finance Book"), - fieldtype: "Link", - options: "Finance Book" - }, - { - fieldname:"is_existing_asset", - label: __("Is Existing Asset"), - fieldtype: "Check" - }, - ] -}; diff --git a/erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.json b/erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.json deleted file mode 100644 index 2a554d83d0e..00000000000 --- a/erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "add_total_row": 0, - "creation": "2020-05-08 15:47:55.036143", - "disable_prepared_report": 1, - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 0, - "is_standard": "Yes", - "javascript": "", - "modified": "2020-05-08 15:47:55.036143", - "modified_by": "Administrator", - "module": "Assets", - "name": "Location-wise Asset Value", - "owner": "Administrator", - "prepared_report": 0, - "query": "", - "ref_doctype": "Asset", - "report_name": "Location-wise Asset Value", - "report_type": "Script Report", - "roles": [ - { - "role": "Accounts User" - }, - { - "role": "Quality Manager" - } - ] -} \ No newline at end of file diff --git a/erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.py b/erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.py deleted file mode 100644 index 6aade0e27aa..00000000000 --- a/erpnext/assets/report/location_wise_asset_value/location_wise_asset_value.py +++ /dev/null @@ -1,137 +0,0 @@ -# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - -from __future__ import unicode_literals -import frappe -from frappe import _ -from frappe.utils import cstr, today, flt - -def execute(filters=None): - filters = frappe._dict(filters or {}) - columns = get_columns(filters) - data = get_data(filters) - - return columns, data - -def get_conditions(filters): - conditions = { 'docstatus': 1 } - - if filters.get('company'): - conditions["company"] = filters.company - if filters.get('purchase_date'): - conditions["purchase_date"] = ('<=', filters.get('purchase_date')) - if filters.get('available_for_use_date'): - conditions["available_for_use_date"] = ('<=', filters.get('available_for_use_date')) - if filters.get('is_existing_asset'): - conditions["is_existing_asset"] = filters.get('is_existing_asset') - if filters.get('cost_center'): - conditions["cost_center"] = filters.get('cost_center') - - return conditions - -def get_data(filters): - - data = [] - depreciation_amount_map = get_finance_book_value_map(filters) - - assets_record = frappe.db.get_all("Asset", - filters=get_conditions(filters), - fields=["name", "asset_name", "location", "gross_purchase_amount", - "opening_accumulated_depreciation", "available_for_use_date", "purchase_date"], - group_by="location") - - for asset in assets_record: - asset_value = asset.gross_purchase_amount - flt(asset.opening_accumulated_depreciation) \ - - flt(depreciation_amount_map.get(asset.name)) - if asset_value: - row = { - "location": asset.location, - "asset_id": asset.name, - "asset_name": asset.asset_name, - "purchase_date": asset.purchase_date, - "available_for_use_date": asset.available_for_use_date, - "gross_purchase_amount": asset.gross_purchase_amount, - "opening_accumulated_depreciation": asset.opening_accumulated_depreciation, - "depreciated_amount": depreciation_amount_map.get(asset.name) or 0.0, - "asset_value": asset_value - } - data.append(row) - - return data - -def get_finance_book_value_map(filters): - date = filters.get('purchase_date') or filters.get('available_for_use_date') or today() - - return frappe._dict(frappe.db.sql(''' Select - parent, SUM(depreciation_amount) - FROM `tabDepreciation Schedule` - WHERE - parentfield='schedules' - AND schedule_date<=%s - AND journal_entry IS NOT NULL - AND ifnull(finance_book, '')=%s - GROUP BY parent''', (date, cstr(filters.finance_book or '')))) - -def get_columns(filters): - return [ - { - "label": _("Location"), - "fieldtype": "Link", - "fieldname": "location", - "options": "Location", - "width": 120 - }, - { - "label": _("Asset Id"), - "fieldtype": "Link", - "fieldname": "asset_id", - "options": "Asset", - "width": 100 - }, - { - "label": _("Asset Name"), - "fieldtype": "Data", - "fieldname": "asset_name", - "width": 140 - }, - { - "label": _("Purchase Date"), - "fieldtype": "Date", - "fieldname": "purchase_date", - "width": 90 - }, - { - "label": _("Available For Use Date"), - "fieldtype": "Date", - "fieldname": "available_for_use_date", - "width": 90 - }, - { - "label": _("Gross Purchase Amount"), - "fieldname": "gross_purchase_amount", - "fieldtype": "Currency", - "options": "company:currency", - "width": 100 - }, - { - "label": _("Opening Accumulated Depreciation"), - "fieldname": "opening_accumulated_depreciation", - "fieldtype": "Currency", - "options": "company:currency", - "width": 90 - }, - { - "label": _("Depreciated Amount"), - "fieldname": "depreciated_amount", - "fieldtype": "Currency", - "options": "company:currency", - "width": 100 - }, - { - "label": _("Asset Value"), - "fieldname": "asset_value", - "fieldtype": "Currency", - "options": "company:currency", - "width": 100 - } - ] \ No newline at end of file From ecddf3358d69c56a438d404886d9458e86a1f51f Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 11 May 2020 15:12:11 +0530 Subject: [PATCH 131/608] fix: dashboard fixtures --- erpnext/assets/dashboard_fixtures.py | 125 ++++++++---------- .../fixed_asset_register.py | 25 ++-- 2 files changed, 68 insertions(+), 82 deletions(-) diff --git a/erpnext/assets/dashboard_fixtures.py b/erpnext/assets/dashboard_fixtures.py index aa4eed2f746..22c4a3e4f9e 100644 --- a/erpnext/assets/dashboard_fixtures.py +++ b/erpnext/assets/dashboard_fixtures.py @@ -8,8 +8,7 @@ import json def get_data(): return frappe._dict({ "dashboards": get_dashboards(), - "charts": get_charts(), - "number_cards": get_number_cards(), + "charts": get_charts() }) def get_dashboards(): @@ -24,73 +23,55 @@ def get_dashboards(): def get_charts(): return [ - { - "name": "Category-wise Asset Value", - "chart_name": "Category-wise Asset Value", - "chart_type": "Report", - "report_name": "Category-wise Asset Value", - "is_custom": 1, - "x_field": "asset_category", - "timeseries": 0, - "filters_json": "{}", - "type": "Donut", - "doctype": "Dashboard Chart", - "y_axis": [ - { - "parent": "Category-wise Asset Value", - "parentfield": "y_axis", - "parenttype": "Dashboard Chart", - "y_field": "asset_value", - "doctype": "Dashboard Chart Field" - } - ], - "custom_options": json.dumps({ - "type": "donut", - "height": 300, - "axisOptions": {"shortenYAxisNumbers": 1} - }) - }, - { - "name": "Location-wise Asset Value", - "chart_name": "Location-wise Asset Value", - "chart_type": "Report", - "report_name": "Location-wise Asset Value", - "is_custom": 1, - "x_field": "location", - "timeseries": 0, - "filters_json": "{}", - "type": "Donut", - "doctype": "Dashboard Chart", - "y_axis": [ - { - "parent": "Location-wise Asset Value", - "parentfield": "y_axis", - "parenttype": "Dashboard Chart", - "y_field": "asset_value", - "doctype": "Dashboard Chart Field" - } - ], - "custom_options": json.dumps({ - "type": "donut", - "height": 300, - "axisOptions": {"shortenYAxisNumbers": 1} - }) - } - ] - - -def get_number_cards(): - return [ - { - "name": "Asset Value", - "label": "Asset Value ", - "function": "Sum", - "aggregate_function_based_on": "gross_purchase_amount", - "document_type": "Asset", - "is_public": 0, - "show_percentage_stats": 1, - "stats_time_interval": "Monthly", - "filters_json": "[]", - "doctype": "Number Card" - } - ] \ No newline at end of file + { + "name": "Category-wise Asset Value", + "chart_name": "Category-wise Asset Value", + "chart_type": "Report", + "report_name": "Fixed Asset Report", + "is_custom": 1, + "x_field": "asset_category", + "timeseries": 0, + "filters_json": json.dumps("""{"status":"In Location","group_by":"Asset Category","is_existing_asset":0}"""), + "type": "Donut", + "doctype": "Dashboard Chart", + "y_axis": [ + { + "parent": "Category-wise Asset Value", + "parentfield": "y_axis", + "parenttype": "Dashboard Chart", + "y_field": "asset_value", + "doctype": "Dashboard Chart Field" + } + ], + "custom_options": json.dumps({ + "type": "donut", + "height": 300, + "axisOptions": {"shortenYAxisNumbers": 1} + }) + }, + { + "name": "Location-wise Asset Value", + "chart_name": "Location-wise Asset Value", + "chart_type": "Report", + "report_name": "Location-wise Asset Value", + "is_custom": 1, + "x_field": "location", + "timeseries": 0, + "filters_json": json.dumps("""{"status":"In Location","group_by":"Location","is_existing_asset":0}"""), + "type": "Donut", + "doctype": "Dashboard Chart", + "y_axis": [ + { + "parent": "Location-wise Asset Value", + "parentfield": "y_axis", + "parenttype": "Dashboard Chart", + "y_field": "asset_value", + "doctype": "Dashboard Chart Field" + } + ], + "custom_options": json.dumps({ + "type": "donut", + "height": 300, + "axisOptions": {"shortenYAxisNumbers": 1} + }) + }] diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py index c6b0c4ecf53..81db57636b2 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py @@ -10,7 +10,7 @@ def execute(filters=None): filters = frappe._dict(filters or {}) columns = get_columns(filters) data = get_data(filters) - chart = prepare_chart_data(data, columns) + chart = prepare_chart_data(data) if not filters.get("group_by") else {} return columns, data, None, chart @@ -89,20 +89,25 @@ def get_data(filters): return data -def prepare_chart_data(data, columns): - label_values_map = {} +def prepare_chart_data(data): + labels, asset_values, depreciated_amounts = [], [], [] for d in data: - if not label_values_map.get(d.get('asset_category')): - label_values_map[d.get('asset_category')] = 0 - label_values_map[d.get('asset_category')] += d.get('asset_value') + labels.append(d.asset_id) + asset_values.append(d.asset_value) + depreciated_amounts.append(d.depreciated_amount) return { "data" : { - "labels": label_values_map.keys(), - "datasets": [{ "values": label_values_map.values() }] + "labels": labels, + "datasets": [ + { 'name': _('Asset Value'), 'values': asset_values }, + { 'name': _('Depreciatied Amount'), 'values': depreciated_amounts} + ] + }, + "type": "bar", + "barOptions": { + "stacked": 1 }, - "type": 'donut', - "height": 250 } def get_finance_book_value_map(filters): From 3882939fd9daf057f93871c148cd89e340503aa2 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 11 May 2020 15:49:18 +0530 Subject: [PATCH 132/608] fix: dashboard fixtures and FAR chart --- erpnext/assets/dashboard_fixtures.py | 2 +- .../introduction_to_assets/introduction_to_assets.json | 2 +- .../report/fixed_asset_register/fixed_asset_register.py | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/assets/dashboard_fixtures.py b/erpnext/assets/dashboard_fixtures.py index 22c4a3e4f9e..93a06544f0b 100644 --- a/erpnext/assets/dashboard_fixtures.py +++ b/erpnext/assets/dashboard_fixtures.py @@ -53,7 +53,7 @@ def get_charts(): "name": "Location-wise Asset Value", "chart_name": "Location-wise Asset Value", "chart_type": "Report", - "report_name": "Location-wise Asset Value", + "report_name": "Fixed Asset Report", "is_custom": 1, "x_field": "location", "timeseries": 0, diff --git a/erpnext/assets/onboarding_step/introduction_to_assets/introduction_to_assets.json b/erpnext/assets/onboarding_step/introduction_to_assets/introduction_to_assets.json index 8aceafd7e5f..d48dd1cd3d2 100644 --- a/erpnext/assets/onboarding_step/introduction_to_assets/introduction_to_assets.json +++ b/erpnext/assets/onboarding_step/introduction_to_assets/introduction_to_assets.json @@ -11,6 +11,6 @@ "modified_by": "Administrator", "name": "Introduction to Assets", "owner": "Administrator", - "title": "Introduction to Fixed Asset Management", + "title": "Introduction to Assets", "video_url": "https://www.youtube.com/watch?v=I-K8pLRmvSo" } \ No newline at end of file diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py index 81db57636b2..0b4b3343264 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py @@ -92,9 +92,9 @@ def get_data(filters): def prepare_chart_data(data): labels, asset_values, depreciated_amounts = [], [], [] for d in data: - labels.append(d.asset_id) - asset_values.append(d.asset_value) - depreciated_amounts.append(d.depreciated_amount) + labels.append(d.get("asset_id")) + asset_values.append(d.get("asset_value")) + depreciated_amounts.append(d.get("depreciated_amount")) return { "data" : { From 8990697fee02a5680ecfe2de4ffe5dd6f6904e36 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Tue, 12 May 2020 17:38:34 +0530 Subject: [PATCH 133/608] feat: fixed asset register based on fiscal year *asset value chart --- erpnext/assets/dashboard_fixtures.py | 105 ++++++++++++++++-- erpnext/assets/desk_page/assets/assets.json | 13 ++- .../fixed_asset_register.js | 70 +++++++++--- .../fixed_asset_register.py | 62 +++++++---- 4 files changed, 199 insertions(+), 51 deletions(-) diff --git a/erpnext/assets/dashboard_fixtures.py b/erpnext/assets/dashboard_fixtures.py index 93a06544f0b..84e67cf648e 100644 --- a/erpnext/assets/dashboard_fixtures.py +++ b/erpnext/assets/dashboard_fixtures.py @@ -3,12 +3,13 @@ import frappe import json - +from frappe.utils import nowdate, add_months def get_data(): return frappe._dict({ "dashboards": get_dashboards(), - "charts": get_charts() + "charts": get_charts(), + "number_cards": get_number_cards(), }) def get_dashboards(): @@ -16,22 +17,63 @@ def get_dashboards(): "name": "Asset", "dashboard_name": "Asset", "charts": [ - { "chart": "Category-wise Asset Value" }, - { "chart": "Location-wise Asset Value" }, + { "chart": "Asset Value Analytics", "width": "Full" }, + { "chart": "Category-wise Asset Value", "width": "Half" }, + { "chart": "Location-wise Asset Value", "width": "Half" }, ] }] def get_charts(): + company = get_company_for_dashboards() + fiscal_year = get_fiscal_year() + return [ + { + "name": "Asset Value Analytics", + "chart_name": "Asset Value Analytics", + "chart_type": "Report", + "report_name": "Fixed Asset Register", + "is_custom": 1, + "group_by_type": "Count", + "number_of_groups": 0, + "is_public": 0, + "timespan": "Last Year", + "time_interval": "Yearly", + "timeseries": 0, + "filters_json": json.dumps({ + "company": company, + "status": "In Location", + "filter_based_on": "Fiscal Year", + "from_fiscal_year": fiscal_year, + "to_fiscal_year": fiscal_year, + "period_start_date": add_months(nowdate(), -12), + "period_end_date": nowdate(), + "date_based_on": "Purchase Date", + "group_by": "--Select a group--" + }), + "type": "Bar", + "custom_options": json.dumps({ + "type": "bar", + "barOptions": { "stacked": 1 }, + "axisOptions": { "shortenYAxisNumbers": 1 }, + "tooltipOptions": {} + }), + "doctype": "Dashboard Chart", + "y_axis": [] + }, { "name": "Category-wise Asset Value", "chart_name": "Category-wise Asset Value", "chart_type": "Report", - "report_name": "Fixed Asset Report", - "is_custom": 1, + "report_name": "Fixed Asset Register", "x_field": "asset_category", "timeseries": 0, - "filters_json": json.dumps("""{"status":"In Location","group_by":"Asset Category","is_existing_asset":0}"""), + "filters_json": json.dumps({ + "company": company, + "status":"In Location", + "group_by":"Asset Category", + "is_existing_asset":0 + }), "type": "Donut", "doctype": "Dashboard Chart", "y_axis": [ @@ -53,11 +95,15 @@ def get_charts(): "name": "Location-wise Asset Value", "chart_name": "Location-wise Asset Value", "chart_type": "Report", - "report_name": "Fixed Asset Report", - "is_custom": 1, + "report_name": "Fixed Asset Register", "x_field": "location", "timeseries": 0, - "filters_json": json.dumps("""{"status":"In Location","group_by":"Location","is_existing_asset":0}"""), + "filters_json": json.dumps({ + "company": company, + "status":"In Location", + "group_by":"Location", + "is_existing_asset":0 + }), "type": "Donut", "doctype": "Dashboard Chart", "y_axis": [ @@ -74,4 +120,41 @@ def get_charts(): "height": 300, "axisOptions": {"shortenYAxisNumbers": 1} }) - }] + } + ] + +def get_number_cards(): + return [ + { + "name": "Asset Value", + "label": "Asset Value", + "function": "Sum", + "aggregate_function_based_on": "value_after_depreciation", + "document_type": "Asset", + "is_public": 1, + "show_percentage_stats": 1, + "stats_time_interval": "Monthly", + "filters_json": "[]", + "doctype": "Number Card", + } + ] + +def get_company_for_dashboards(): + company = frappe.defaults.get_defaults().company + if company: + return company + else: + company_list = frappe.get_list("Company") + if company_list: + return company_list[0].name + return None + +def get_fiscal_year(): + fiscal_year = frappe.defaults.get_defaults().fiscal_year + if fiscal_year: + return fiscal_year + else: + fiscal_year_list = frappe.get_list("Fiscal Year") + if fiscal_year_list: + return fiscal_year_list[0].name + return None \ No newline at end of file diff --git a/erpnext/assets/desk_page/assets/assets.json b/erpnext/assets/desk_page/assets/assets.json index 13709e370bb..429a8a897c4 100644 --- a/erpnext/assets/desk_page/assets/assets.json +++ b/erpnext/assets/desk_page/assets/assets.json @@ -17,7 +17,12 @@ } ], "category": "Modules", - "charts": [], + "charts": [ + { + "chart_name": "Asset Value Analytics", + "label": "Asset Value Analytics" + } + ], "creation": "2020-03-02 15:43:27.634865", "developer_mode_only": 0, "disable_user_customization": 0, @@ -27,7 +32,7 @@ "idx": 0, "is_standard": 1, "label": "Assets", - "modified": "2020-05-08 16:07:04.671296", + "modified": "2020-05-12 17:35:14.770662", "modified_by": "Administrator", "module": "Assets", "name": "Assets", @@ -42,8 +47,8 @@ "type": "DocType" }, { - "label": "Asset Movement", - "link_to": "Asset Movement", + "label": "Asset Category", + "link_to": "Asset Category", "type": "DocType" }, { diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js index e886a35e5d3..a08851743cf 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js @@ -21,14 +21,61 @@ frappe.query_reports["Fixed Asset Register"] = { reqd: 1 }, { - fieldname:"purchase_date", - label: __("Purchase Date"), - fieldtype: "Date" + "fieldname":"filter_based_on", + "label": __("Period Based On"), + "fieldtype": "Select", + "options": ["Fiscal Year", "Date Range"], + "default": ["Fiscal Year"], + "reqd": 1, + on_change: function() { + let filter_based_on = frappe.query_report.get_filter_value('filter_based_on'); + frappe.query_report.toggle_filter_display('from_fiscal_year', filter_based_on === 'Date Range'); + frappe.query_report.toggle_filter_display('to_fiscal_year', filter_based_on === 'Date Range'); + frappe.query_report.toggle_filter_display('from_date', filter_based_on === 'Fiscal Year'); + frappe.query_report.toggle_filter_display('to_date', filter_based_on === 'Fiscal Year'); + + frappe.query_report.refresh(); + } }, { - fieldname:"available_for_use_date", - label: __("Available For Use Date"), - fieldtype: "Date" + "fieldname":"from_date", + "label": __("Start Date"), + "fieldtype": "Date", + "default": frappe.datetime.nowdate(), + "hidden": 1, + "reqd": 1 + }, + { + "fieldname":"to_date", + "label": __("End Date"), + "fieldtype": "Date", + "default": frappe.datetime.add_months(frappe.datetime.nowdate(), 12), + "hidden": 1, + "reqd": 1 + }, + { + "fieldname":"from_fiscal_year", + "label": __("Start Year"), + "fieldtype": "Link", + "options": "Fiscal Year", + "default": frappe.defaults.get_user_default("fiscal_year"), + "reqd": 1 + }, + { + "fieldname":"to_fiscal_year", + "label": __("End Year"), + "fieldtype": "Link", + "options": "Fiscal Year", + "default": frappe.defaults.get_user_default("fiscal_year"), + "reqd": 1 + }, + { + "fieldname":"date_based_on", + "label": __("Date Based On"), + "fieldtype": "Select", + "options": ["Purchase Date", "Available For Use Date"], + "default": "Purchase Date", + "reqd": 1 }, { fieldname:"asset_category", @@ -48,18 +95,13 @@ frappe.query_reports["Fixed Asset Register"] = { fieldtype: "Link", options: "Cost Center" }, - { - fieldname:"finance_book", - label: __("Finance Book"), - fieldtype: "Link", - options: "Finance Book" - }, { fieldname:"group_by", label: __("Group By"), fieldtype: "Select", - options: " \nAsset Category\nLocation", - default: '', + options: ["--Select a group--", "Asset Category", "Location"], + default: "--Select a group--", + reqd: 1 }, { fieldname:"is_existing_asset", diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py index 0b4b3343264..23714e6f66f 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py @@ -4,26 +4,33 @@ from __future__ import unicode_literals import frappe from frappe import _ -from frappe.utils import cstr, today, flt +from frappe.utils import cstr, today, flt, add_years, formatdate, getdate +from erpnext.accounts.report.financial_statements import get_period_list, get_fiscal_year_data, validate_fiscal_year def execute(filters=None): filters = frappe._dict(filters or {}) columns = get_columns(filters) data = get_data(filters) - chart = prepare_chart_data(data) if not filters.get("group_by") else {} + chart = prepare_chart_data(data, filters) if filters.get("group_by") not in ("Asset Category", "Location") else {} return columns, data, None, chart def get_conditions(filters): conditions = { 'docstatus': 1 } status = filters.status + date_field = frappe.scrub(filters.date_based_on or "Purchase Date") if filters.get('company'): conditions["company"] = filters.company - if filters.get('purchase_date'): - conditions["purchase_date"] = ('<=', filters.get('purchase_date')) - if filters.get('available_for_use_date'): - conditions["available_for_use_date"] = ('<=', filters.get('available_for_use_date')) + if filters.filter_based_on == "Date Range": + conditions[date_field] = ["between", [filters.from_date, filters.to_date]] + if filters.filter_based_on == "Fiscal Year": + fiscal_year = get_fiscal_year_data(filters.from_fiscal_year, filters.to_fiscal_year) + validate_fiscal_year(fiscal_year, filters.from_fiscal_year, filters.to_fiscal_year) + year_start_date = getdate(fiscal_year.year_start_date) + year_end_date = getdate(fiscal_year.year_end_date) + + conditions[date_field] = ["between", [year_start_date, year_end_date]] if filters.get('is_existing_asset'): conditions["is_existing_asset"] = filters.get('is_existing_asset') if filters.get('asset_category'): @@ -49,16 +56,17 @@ def get_data(filters): pi_supplier_map = get_purchase_invoice_supplier_map() conditions = get_conditions(filters) - group_by = frappe.scrub(filters.get("group_by") or "") - if group_by: - if group_by == "asset_category": - fields = ["asset_category", "gross_purchase_amount", "opening_accumulated_depreciation"] - else: - fields = ["location", "gross_purchase_amount", "opening_accumulated_depreciation"] + group_by = frappe.scrub(filters.get("group_by")) + if group_by == "asset_category": + fields = ["asset_category", "gross_purchase_amount", "opening_accumulated_depreciation"] assets_record = frappe.db.get_all("Asset", filters=conditions, fields=fields, group_by=group_by) - print(assets_record) + + elif group_by == "location": + fields = ["location", "gross_purchase_amount", "opening_accumulated_depreciation"] + assets_record = frappe.db.get_all("Asset", filters=conditions, fields=fields, group_by=group_by) + else: fields = ["name as asset_id", "asset_name", "status", "department", "cost_center", "purchase_receipt", "asset_category", "purchase_date", "gross_purchase_amount", "location", @@ -89,19 +97,29 @@ def get_data(filters): return data -def prepare_chart_data(data): - labels, asset_values, depreciated_amounts = [], [], [] +def prepare_chart_data(data, filters): + labels_values_map = {} + date_field = frappe.scrub(filters.date_based_on) + + period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year, + filters.from_date, filters.to_date, filters.filter_based_on, "Monthly", company=filters.company) + + for d in period_list: + labels_values_map.setdefault(d.get('label'), frappe._dict({'asset_value': 0, 'depreciated_amount': 0})) + for d in data: - labels.append(d.get("asset_id")) - asset_values.append(d.get("asset_value")) - depreciated_amounts.append(d.get("depreciated_amount")) + date = d.get(date_field) + belongs_to_month = formatdate(date, "MMM YYYY") + + labels_values_map[belongs_to_month].asset_value += d.get("asset_value") + labels_values_map[belongs_to_month].depreciated_amount += d.get("depreciated_amount") return { "data" : { - "labels": labels, + "labels": labels_values_map.keys(), "datasets": [ - { 'name': _('Asset Value'), 'values': asset_values }, - { 'name': _('Depreciatied Amount'), 'values': depreciated_amounts} + { 'name': _('Asset Value'), 'values': [d.get("asset_value") for d in labels_values_map.values()] }, + { 'name': _('Depreciatied Amount'), 'values': [d.get("depreciated_amount") for d in labels_values_map.values()] } ] }, "type": "bar", @@ -144,7 +162,7 @@ def get_purchase_invoice_supplier_map(): AND pi.is_return=0''')) def get_columns(filters): - if filters.get("group_by"): + if filters.get("group_by") in ["Asset Category", "Location"]: return [ { "label": _("{}").format(filters.get("group_by")), From 37c3bd28928ba0510429f8b015ad0fe8e2affba9 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Wed, 13 May 2020 14:10:05 +0530 Subject: [PATCH 134/608] chore: rename onboarding to module onboarding --- .../assets/{onboarding => module_onboarding}/assets/assets.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename erpnext/assets/{onboarding => module_onboarding}/assets/assets.json (95%) diff --git a/erpnext/assets/onboarding/assets/assets.json b/erpnext/assets/module_onboarding/assets/assets.json similarity index 95% rename from erpnext/assets/onboarding/assets/assets.json rename to erpnext/assets/module_onboarding/assets/assets.json index b32a7f2be5b..dc77726d28a 100644 --- a/erpnext/assets/onboarding/assets/assets.json +++ b/erpnext/assets/module_onboarding/assets/assets.json @@ -9,7 +9,7 @@ ], "creation": "2020-05-08 15:10:45.571457", "docstatus": 0, - "doctype": "Onboarding", + "doctype": "Module Onboarding", "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/asset", "idx": 0, "is_complete": 0, From 7c5e22e9d89b947061d3f94565621e4f8d3237fa Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Wed, 13 May 2020 15:24:29 +0530 Subject: [PATCH 135/608] fix: depreciation amount not shown in asset register --- .../fixed_asset_register/fixed_asset_register.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py index 23714e6f66f..af08a2a601e 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py @@ -27,10 +27,10 @@ def get_conditions(filters): if filters.filter_based_on == "Fiscal Year": fiscal_year = get_fiscal_year_data(filters.from_fiscal_year, filters.to_fiscal_year) validate_fiscal_year(fiscal_year, filters.from_fiscal_year, filters.to_fiscal_year) - year_start_date = getdate(fiscal_year.year_start_date) - year_end_date = getdate(fiscal_year.year_end_date) + filters.year_start_date = getdate(fiscal_year.year_start_date) + filters.year_end_date = getdate(fiscal_year.year_end_date) - conditions[date_field] = ["between", [year_start_date, year_end_date]] + conditions[date_field] = ["between", [filters.year_start_date, filters.year_end_date]] if filters.get('is_existing_asset'): conditions["is_existing_asset"] = filters.get('is_existing_asset') if filters.get('asset_category'): @@ -51,12 +51,11 @@ def get_data(filters): data = [] + conditions = get_conditions(filters) depreciation_amount_map = get_finance_book_value_map(filters) pr_supplier_map = get_purchase_receipt_supplier_map() pi_supplier_map = get_purchase_invoice_supplier_map() - conditions = get_conditions(filters) - group_by = frappe.scrub(filters.get("group_by")) if group_by == "asset_category": @@ -86,7 +85,7 @@ def get_data(filters): "vendor_name": pr_supplier_map.get(asset.purchase_receipt) or pi_supplier_map.get(asset.purchase_invoice), "gross_purchase_amount": asset.gross_purchase_amount, "opening_accumulated_depreciation": asset.opening_accumulated_depreciation, - "depreciated_amount": depreciation_amount_map.get(asset.name) or 0.0, + "depreciated_amount": depreciation_amount_map.get(asset.asset_id) or 0.0, "available_for_use_date": asset.available_for_use_date, "location": asset.location, "asset_category": asset.asset_category, @@ -129,7 +128,7 @@ def prepare_chart_data(data, filters): } def get_finance_book_value_map(filters): - date = filters.get('purchase_date') or filters.get('available_for_use_date') or today() + date = filters.to_date if filters.filter_based_on == "Date Range" else filters.year_end_date return frappe._dict(frappe.db.sql(''' Select parent, SUM(depreciation_amount) From 4f024128af7b796c6c5a863404a82332d7605f7d Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Thu, 14 May 2020 15:33:37 +0530 Subject: [PATCH 136/608] fix: dashboard chart dialog filters --- .../fixed_asset_register.js | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js index a08851743cf..1a6ef54a830 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js @@ -26,31 +26,22 @@ frappe.query_reports["Fixed Asset Register"] = { "fieldtype": "Select", "options": ["Fiscal Year", "Date Range"], "default": ["Fiscal Year"], - "reqd": 1, - on_change: function() { - let filter_based_on = frappe.query_report.get_filter_value('filter_based_on'); - frappe.query_report.toggle_filter_display('from_fiscal_year', filter_based_on === 'Date Range'); - frappe.query_report.toggle_filter_display('to_fiscal_year', filter_based_on === 'Date Range'); - frappe.query_report.toggle_filter_display('from_date', filter_based_on === 'Fiscal Year'); - frappe.query_report.toggle_filter_display('to_date', filter_based_on === 'Fiscal Year'); - - frappe.query_report.refresh(); - } + "reqd": 1 }, { "fieldname":"from_date", "label": __("Start Date"), "fieldtype": "Date", - "default": frappe.datetime.nowdate(), - "hidden": 1, + "default": frappe.datetime.add_months(frappe.datetime.nowdate(), -12), + "depends_on": "eval: doc.filter_based_on == 'Date Range'", "reqd": 1 }, { "fieldname":"to_date", "label": __("End Date"), "fieldtype": "Date", - "default": frappe.datetime.add_months(frappe.datetime.nowdate(), 12), - "hidden": 1, + "default": frappe.datetime.nowdate(), + "depends_on": "eval: doc.filter_based_on == 'Date Range'", "reqd": 1 }, { @@ -59,6 +50,7 @@ frappe.query_reports["Fixed Asset Register"] = { "fieldtype": "Link", "options": "Fiscal Year", "default": frappe.defaults.get_user_default("fiscal_year"), + "depends_on": "eval: doc.filter_based_on == 'Fiscal Year'", "reqd": 1 }, { @@ -67,6 +59,7 @@ frappe.query_reports["Fixed Asset Register"] = { "fieldtype": "Link", "options": "Fiscal Year", "default": frappe.defaults.get_user_default("fiscal_year"), + "depends_on": "eval: doc.filter_based_on == 'Fiscal Year'", "reqd": 1 }, { From 054aafa1d7c10ed91798e66de34fbe866943f942 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 20 May 2020 18:21:51 +0530 Subject: [PATCH 137/608] fix: Assets module onboarding and dashboard --- erpnext/assets/dashboard_fixtures.py | 69 +++++++++++++------ erpnext/assets/desk_page/assets/assets.json | 8 ++- .../module_onboarding/assets/assets.json | 7 +- .../create_an_asset.json} | 4 +- .../create_an_asset_category.json | 16 +++++ .../purchase_an_asset_item.json} | 4 +- 6 files changed, 79 insertions(+), 29 deletions(-) rename erpnext/assets/onboarding_step/{create_the_asset/create_the_asset.json => create_an_asset/create_an_asset.json} (84%) create mode 100644 erpnext/assets/onboarding_step/create_an_asset_category/create_an_asset_category.json rename erpnext/assets/onboarding_step/{purchase_the_asset_item/purchase_the_asset_item.json => purchase_an_asset_item/purchase_an_asset_item.json} (82%) diff --git a/erpnext/assets/dashboard_fixtures.py b/erpnext/assets/dashboard_fixtures.py index 84e67cf648e..9af45d16b62 100644 --- a/erpnext/assets/dashboard_fixtures.py +++ b/erpnext/assets/dashboard_fixtures.py @@ -3,7 +3,10 @@ import frappe import json -from frappe.utils import nowdate, add_months +from frappe.utils import nowdate, add_months, get_date_str +from frappe import _ +from erpnext.accounts.utils import get_fiscal_year + def get_data(): return frappe._dict({ @@ -20,17 +23,25 @@ def get_dashboards(): { "chart": "Asset Value Analytics", "width": "Full" }, { "chart": "Category-wise Asset Value", "width": "Half" }, { "chart": "Location-wise Asset Value", "width": "Half" }, - ] + ], + "cards": [ + {"card": "Total Assets"}, + {"card": "New Assets (This Year)"}, + {"card": "Asset Value"} + ] }] +fiscal_year = get_fiscal_year(date=nowdate()) +year_start_date = get_date_str(fiscal_year[1]) +year_end_date = get_date_str(fiscal_year[2]) + + def get_charts(): company = get_company_for_dashboards() - fiscal_year = get_fiscal_year() - return [ { "name": "Asset Value Analytics", - "chart_name": "Asset Value Analytics", + "chart_name": _("Asset Value Analytics"), "chart_type": "Report", "report_name": "Fixed Asset Register", "is_custom": 1, @@ -44,10 +55,10 @@ def get_charts(): "company": company, "status": "In Location", "filter_based_on": "Fiscal Year", - "from_fiscal_year": fiscal_year, - "to_fiscal_year": fiscal_year, - "period_start_date": add_months(nowdate(), -12), - "period_end_date": nowdate(), + "from_fiscal_year": fiscal_year[0], + "to_fiscal_year": fiscal_year[0], + "period_start_date": year_start_date, + "period_end_date": year_end_date, "date_based_on": "Purchase Date", "group_by": "--Select a group--" }), @@ -63,7 +74,7 @@ def get_charts(): }, { "name": "Category-wise Asset Value", - "chart_name": "Category-wise Asset Value", + "chart_name": _("Category-wise Asset Value"), "chart_type": "Report", "report_name": "Fixed Asset Register", "x_field": "asset_category", @@ -125,9 +136,33 @@ def get_charts(): def get_number_cards(): return [ + { + "name": "Total Assets", + "label": _("Total Assets"), + "function": "Count", + "document_type": "Asset", + "is_public": 1, + "show_percentage_stats": 1, + "stats_time_interval": "Monthly", + "filters_json": "[]", + "doctype": "Number Card", + }, + { + "name": "New Assets (This Year)", + "label": _("New Assets (This Year)"), + "function": "Count", + "document_type": "Asset", + "is_public": 1, + "show_percentage_stats": 1, + "stats_time_interval": "Monthly", + "filters_json": json.dumps([ + ['Asset', 'creation', 'between', [year_start_date, year_end_date]] + ]), + "doctype": "Number Card", + }, { "name": "Asset Value", - "label": "Asset Value", + "label": _("Asset Value"), "function": "Sum", "aggregate_function_based_on": "value_after_depreciation", "document_type": "Asset", @@ -135,7 +170,7 @@ def get_number_cards(): "show_percentage_stats": 1, "stats_time_interval": "Monthly", "filters_json": "[]", - "doctype": "Number Card", + "doctype": "Number Card" } ] @@ -147,14 +182,4 @@ def get_company_for_dashboards(): company_list = frappe.get_list("Company") if company_list: return company_list[0].name - return None - -def get_fiscal_year(): - fiscal_year = frappe.defaults.get_defaults().fiscal_year - if fiscal_year: - return fiscal_year - else: - fiscal_year_list = frappe.get_list("Fiscal Year") - if fiscal_year_list: - return fiscal_year_list[0].name return None \ No newline at end of file diff --git a/erpnext/assets/desk_page/assets/assets.json b/erpnext/assets/desk_page/assets/assets.json index 429a8a897c4..94939fdd2a2 100644 --- a/erpnext/assets/desk_page/assets/assets.json +++ b/erpnext/assets/desk_page/assets/assets.json @@ -29,10 +29,11 @@ "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, + "hide_custom": 0, "idx": 0, "is_standard": 1, "label": "Assets", - "modified": "2020-05-12 17:35:14.770662", + "modified": "2020-05-20 18:05:23.994795", "modified_by": "Administrator", "module": "Assets", "name": "Assets", @@ -55,6 +56,11 @@ "label": "Fixed Asset Register", "link_to": "Fixed Asset Register", "type": "Report" + }, + { + "label": "Assets Dashboard", + "link_to": "Asset", + "type": "Dashboard" } ] } \ No newline at end of file diff --git a/erpnext/assets/module_onboarding/assets/assets.json b/erpnext/assets/module_onboarding/assets/assets.json index dc77726d28a..66dd60ae81f 100644 --- a/erpnext/assets/module_onboarding/assets/assets.json +++ b/erpnext/assets/module_onboarding/assets/assets.json @@ -26,10 +26,13 @@ "step": "Create a Fixed Asset Item" }, { - "step": "Purchase the Asset Item" + "step": "Create an Asset Category" + }, + { + "step": "Purchase an Asset Item" }, { - "step": "Create the Asset" + "step": "Create an Asset" } ], "subtitle": "Assets, Depreciations, Repairs and more", diff --git a/erpnext/assets/onboarding_step/create_the_asset/create_the_asset.json b/erpnext/assets/onboarding_step/create_an_asset/create_an_asset.json similarity index 84% rename from erpnext/assets/onboarding_step/create_the_asset/create_the_asset.json rename to erpnext/assets/onboarding_step/create_an_asset/create_an_asset.json index 28d8485f976..5488b1d7b4e 100644 --- a/erpnext/assets/onboarding_step/create_the_asset/create_the_asset.json +++ b/erpnext/assets/onboarding_step/create_an_asset/create_an_asset.json @@ -9,8 +9,8 @@ "is_skipped": 0, "modified": "2020-05-08 13:21:53.332538", "modified_by": "Administrator", - "name": "Create the Asset", + "name": "Create an Asset", "owner": "Administrator", "reference_document": "Asset", - "title": "Create the Asset" + "title": "Create an Asset" } \ No newline at end of file diff --git a/erpnext/assets/onboarding_step/create_an_asset_category/create_an_asset_category.json b/erpnext/assets/onboarding_step/create_an_asset_category/create_an_asset_category.json new file mode 100644 index 00000000000..3bf54af348a --- /dev/null +++ b/erpnext/assets/onboarding_step/create_an_asset_category/create_an_asset_category.json @@ -0,0 +1,16 @@ +{ + "action": "Create Entry", + "creation": "2020-05-08 13:21:53.332538", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_skipped": 0, + "modified": "2020-05-08 13:21:53.332538", + "modified_by": "Administrator", + "name": "Create an Asset Category", + "owner": "Administrator", + "reference_document": "Asset Category", + "title": "Create an Asset Category" + } \ No newline at end of file diff --git a/erpnext/assets/onboarding_step/purchase_the_asset_item/purchase_the_asset_item.json b/erpnext/assets/onboarding_step/purchase_an_asset_item/purchase_an_asset_item.json similarity index 82% rename from erpnext/assets/onboarding_step/purchase_the_asset_item/purchase_the_asset_item.json rename to erpnext/assets/onboarding_step/purchase_an_asset_item/purchase_an_asset_item.json index b6f38decb9e..732ff7f733b 100644 --- a/erpnext/assets/onboarding_step/purchase_the_asset_item/purchase_the_asset_item.json +++ b/erpnext/assets/onboarding_step/purchase_an_asset_item/purchase_an_asset_item.json @@ -9,8 +9,8 @@ "is_skipped": 0, "modified": "2020-05-08 13:21:28.208059", "modified_by": "Administrator", - "name": "Purchase the Asset Item", + "name": "Purchase an Asset Item", "owner": "Administrator", "reference_document": "Purchase Receipt", - "title": "Purchase the Asset Item" + "title": "Purchase an Asset Item" } \ No newline at end of file From 858593b96e9e76dfacb23c0742e29ed3de1dac67 Mon Sep 17 00:00:00 2001 From: anoop Date: Wed, 20 May 2020 15:41:37 +0530 Subject: [PATCH 138/608] fix: service unit create - set fields based on service unit type, added validations --- .../healthcare_service_unit.json | 19 +++++++++++++------ .../healthcare_service_unit.py | 10 ++++++++-- .../healthcare_service_unit_type.json | 16 ++++++++-------- .../healthcare_service_unit_type.py | 14 ++++++++++++++ 4 files changed, 43 insertions(+), 16 deletions(-) diff --git a/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.json b/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.json index ea4ae846f7d..9ee865a62a4 100644 --- a/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.json +++ b/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.json @@ -12,7 +12,6 @@ "engine": "InnoDB", "field_order": [ "healthcare_service_unit_name", - "parent_healthcare_service_unit", "is_group", "service_unit_type", "allow_appointments", @@ -20,8 +19,10 @@ "inpatient_occupancy", "occupancy_status", "column_break_9", - "warehouse", "company", + "warehouse", + "tree_details_section", + "parent_healthcare_service_unit", "lft", "rgt", "old_parent" @@ -51,7 +52,6 @@ "depends_on": "eval:doc.inpatient_occupancy != 1 && doc.allow_appointments != 1", "fieldname": "is_group", "fieldtype": "Check", - "in_list_view": 1, "label": "Is Group" }, { @@ -63,12 +63,12 @@ "options": "Healthcare Service Unit Type" }, { - "bold": 1, "default": "0", "depends_on": "eval:doc.is_group != 1 && doc.inpatient_occupancy != 1", "fetch_from": "service_unit_type.allow_appointments", "fieldname": "allow_appointments", "fieldtype": "Check", + "in_list_view": 1, "label": "Allow Appointments", "no_copy": 1, "read_only": 1 @@ -90,6 +90,7 @@ "fetch_from": "service_unit_type.inpatient_occupancy", "fieldname": "inpatient_occupancy", "fieldtype": "Check", + "in_list_view": 1, "label": "Inpatient Occupancy", "no_copy": 1, "read_only": 1, @@ -101,7 +102,7 @@ "fieldtype": "Select", "label": "Occupancy Status", "no_copy": 1, - "options": "\nVacant\nOccupied", + "options": "Vacant\nOccupied", "read_only": 1 }, { @@ -157,10 +158,16 @@ "options": "Healthcare Service Unit", "print_hide": 1, "report_hide": 1 + }, + { + "collapsible": 1, + "fieldname": "tree_details_section", + "fieldtype": "Section Break", + "label": "Tree Details" } ], "links": [], - "modified": "2020-03-26 16:13:08.675952", + "modified": "2020-05-20 18:26:56.065543", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare Service Unit", diff --git a/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.py b/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.py index 13cc43d2be4..9720078e32e 100644 --- a/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.py +++ b/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.py @@ -27,5 +27,11 @@ class HealthcareServiceUnit(NestedSet): self.allow_appointments = 0 self.overlap_appointments = 0 self.inpatient_occupancy = 0 - elif not self.allow_appointments: - self.overlap_appointments = 0 + elif self.service_unit_type: + service_unit_type = frappe.get_doc('Healthcare Service Unit Type', self.service_unit_type) + self.allow_appointments = service_unit_type.allow_appointments + self.overlap_appointments = service_unit_type.overlap_appointments + self.inpatient_occupancy = service_unit_type.inpatient_occupancy + if self.inpatient_occupancy: + self.occupancy_status = 'Vacant' + self.overlap_appointments = 0 diff --git a/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.json b/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.json index 5fa47d91bc2..4b8503d0286 100644 --- a/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.json +++ b/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.json @@ -31,6 +31,7 @@ "fieldtype": "Data", "in_list_view": 1, "label": "Service Unit Type", + "no_copy": 1, "reqd": 1, "unique": 1 }, @@ -40,8 +41,7 @@ "depends_on": "eval:doc.inpatient_occupancy != 1", "fieldname": "allow_appointments", "fieldtype": "Check", - "label": "Allow Appointments", - "no_copy": 1 + "label": "Allow Appointments" }, { "bold": 1, @@ -49,8 +49,7 @@ "depends_on": "eval:doc.allow_appointments == 1 && doc.inpatient_occupany != 1", "fieldname": "overlap_appointments", "fieldtype": "Check", - "label": "Allow Overlap", - "no_copy": 1 + "label": "Allow Overlap" }, { "bold": 1, @@ -58,8 +57,7 @@ "depends_on": "eval:doc.allow_appointments != 1", "fieldname": "inpatient_occupancy", "fieldtype": "Check", - "label": "Inpatient Occupancy", - "no_copy": 1 + "label": "Inpatient Occupancy" }, { "bold": 1, @@ -79,6 +77,7 @@ "fieldname": "item", "fieldtype": "Link", "label": "Item", + "no_copy": 1, "options": "Item", "read_only": 1 }, @@ -86,7 +85,8 @@ "fieldname": "item_code", "fieldtype": "Data", "label": "Item Code", - "mandatory_depends_on": "eval: doc.is_billable == 1" + "mandatory_depends_on": "eval: doc.is_billable == 1", + "no_copy": 1 }, { "fieldname": "item_group", @@ -138,7 +138,7 @@ } ], "links": [], - "modified": "2020-01-30 16:06:00.624496", + "modified": "2020-05-20 15:31:09.627516", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare Service Unit Type", diff --git a/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.py b/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.py index 286ecc0ff8e..a99358cdc60 100644 --- a/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.py +++ b/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.py @@ -10,6 +10,20 @@ from frappe.model.rename_doc import rename_doc class HealthcareServiceUnitType(Document): def validate(self): + if self.allow_appointments and self.inpatient_occupancy: + frappe.msgprint( + _('Healthcare Service Unit Type cannot be both Allow Appointments and Inpatient Occupancy'), + raise_exception=1, title=_('Validation Error'), indicator='red' + ) + elif not self.allow_appointments and not self.inpatient_occupancy: + frappe.msgprint( + _('Healthcare Service Unit Type cannot be both Allow Appointments and Inpatient Occupancy'), + raise_exception=1, title=_('Validation Error'), indicator='red' + ) + + if not self.allow_appointments: + self.overlap_appointments = 0 + if self.is_billable: if self.disabled: frappe.db.set_value('Item', self.item, 'disabled', 1) From 29c748184c7e5fc2df43176ae398ab7f7f83cebe Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 20 May 2020 22:15:12 +0530 Subject: [PATCH 139/608] fix: Project filter in Trial Baalance Report --- erpnext/accounts/report/trial_balance/trial_balance.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/report/trial_balance/trial_balance.py b/erpnext/accounts/report/trial_balance/trial_balance.py index 8bd4399e608..5a699b6580a 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.py +++ b/erpnext/accounts/report/trial_balance/trial_balance.py @@ -71,7 +71,8 @@ def get_data(filters): opening_balances = get_opening_balances(filters) #add filter inside list so that the query in financial_statements.py doesn't break - filters.project = [filters.project] + if filters.project: + filters.project = [filters.project] set_gl_entries_by_account(filters.company, filters.from_date, filters.to_date, min_lft, max_rgt, filters, gl_entries_by_account, ignore_closing_entries=not flt(filters.with_period_closing_entry)) From 53257833f8e2f672c572e98bf1bb7af48e903985 Mon Sep 17 00:00:00 2001 From: anoop Date: Thu, 21 May 2020 00:46:40 +0530 Subject: [PATCH 140/608] feat: admission and discharge schedule detials via dialog --- .../inpatient_record/inpatient_record.js | 66 ++++-- .../inpatient_record/inpatient_record.json | 219 ++++++++++++++++-- .../inpatient_record/inpatient_record.py | 101 +++++--- .../patient_encounter/patient_encounter.js | 125 ++++++++-- .../patient_encounter/patient_encounter.json | 4 +- 5 files changed, 417 insertions(+), 98 deletions(-) diff --git a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.js b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.js index 67c12f6c14d..b640239b701 100644 --- a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.js +++ b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.js @@ -2,22 +2,37 @@ // For license information, please see license.txt frappe.ui.form.on('Inpatient Record', { + setup: function(frm) { + frm.get_field('drug_prescription').grid.editable_fields = [ + {fieldname: 'drug_code', columns: 2}, + {fieldname: 'drug_name', columns: 2}, + {fieldname: 'dosage', columns: 2}, + {fieldname: 'period', columns: 2} + ]; + }, refresh: function(frm) { - if(!frm.doc.__islocal && frm.doc.status == "Admission Scheduled"){ + if(!frm.doc.__islocal && (frm.doc.status == 'Admission Scheduled' || frm.doc.status == 'Admitted')) { + frm.enable_save(); + } else { + frm.disable_save(); + } + + if(!frm.doc.__islocal && frm.doc.status == 'Admission Scheduled') { frm.add_custom_button(__('Admit'), function() { admit_patient_dialog(frm); } ); - frm.set_df_property("btn_transfer", "hidden", 1); } - if(!frm.doc.__islocal && frm.doc.status == "Discharge Scheduled"){ + + if(!frm.doc.__islocal && frm.doc.status == 'Discharge Scheduled') { frm.add_custom_button(__('Discharge'), function() { discharge_patient(frm); } ); - frm.set_df_property("btn_transfer", "hidden", 0); } - if(!frm.doc.__islocal && (frm.doc.status == "Discharged" || frm.doc.status == "Discharge Scheduled")){ + if(!frm.doc.__islocal && frm.doc.status != 'Admitted') { frm.disable_save(); - frm.set_df_property("btn_transfer", "hidden", 1); + frm.set_df_property('btn_transfer', 'hidden', 1); + } else { + frm.set_df_property('btn_transfer', 'hidden', 0); } }, btn_transfer: function(frm) { @@ -28,14 +43,14 @@ frappe.ui.form.on('Inpatient Record', { var discharge_patient = function(frm) { frappe.call({ doc: frm.doc, - method: "discharge", + method: 'discharge', callback: function(data) { if(!data.exc){ frm.reload_doc(); } }, freeze: true, - freeze_message: "Process Discharge" + freeze_message: 'Processing Inpatient Discharge' }); }; @@ -44,12 +59,20 @@ var admit_patient_dialog = function(frm){ title: 'Admit Patient', width: 100, fields: [ - {fieldtype: "Link", label: "Service Unit Type", fieldname: "service_unit_type", options: "Healthcare Service Unit Type"}, - {fieldtype: "Link", label: "Service Unit", fieldname: "service_unit", options: "Healthcare Service Unit", reqd: 1}, - {fieldtype: "Datetime", label: "Admission Datetime", fieldname: "check_in", reqd: 1}, - {fieldtype: "Date", label: "Expected Discharge", fieldname: "expected_discharge"} + {fieldtype: 'Link', label: 'Service Unit Type', fieldname: 'service_unit_type', + options: 'Healthcare Service Unit Type', default: frm.doc.admission_service_unit_type + }, + {fieldtype: 'Link', label: 'Service Unit', fieldname: 'service_unit', + options: 'Healthcare Service Unit', reqd: 1 + }, + {fieldtype: 'Datetime', label: 'Admission Datetime', fieldname: 'check_in', + reqd: 1, default: frappe.datetime.now_datetime() + }, + {fieldtype: 'Date', label: 'Expected Discharge', fieldname: 'expected_discharge', + default: frm.doc.expected_length_of_stay ? frappe.datetime.add_days(frappe.datetime.now_datetime(), frm.doc.expected_length_of_stay) : '' + } ], - primary_action_label: __("Admit"), + primary_action_label: __('Admit'), primary_action : function(){ var service_unit = dialog.get_value('service_unit'); var check_in = dialog.get_value('check_in'); @@ -74,27 +97,28 @@ var admit_patient_dialog = function(frm){ } }, freeze: true, - freeze_message: "Process Admission" + freeze_message: 'Processing Patient Admission' }); frm.refresh_fields(); dialog.hide(); } }); - dialog.fields_dict["service_unit_type"].get_query = function(){ + dialog.fields_dict['service_unit_type'].get_query = function() { return { filters: { - "inpatient_occupancy": 1, - "allow_appointments": 0 + 'inpatient_occupancy': 1, + 'allow_appointments': 0 } }; }; - dialog.fields_dict["service_unit"].get_query = function(){ + dialog.fields_dict['service_unit'].get_query = function() { return { filters: { - "is_group": 0, - "service_unit_type": dialog.get_value("service_unit_type"), - "occupancy_status" : "Vacant" + 'is_group': 0, + 'company': frm.doc.company, + 'service_unit_type': dialog.get_value('service_unit_type'), + 'occupancy_status' : 'Vacant' } }; }; diff --git a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.json b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.json index c1b516d5363..d3835409d9a 100644 --- a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.json +++ b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.json @@ -22,17 +22,41 @@ "scheduled_date", "admitted_datetime", "expected_discharge", - "discharge_date", "references", - "cb_admission", - "admission_practitioner", "admission_encounter", - "cb_discharge", - "discharge_practitioner", - "discharge_encounter", + "admission_practitioner", + "medical_department", + "admission_ordered_for", + "expected_length_of_stay", + "admission_service_unit_type", + "cb_admission", + "primary_practitioner", + "secondary_practitioner", + "admission_instruction", + "encounter_details_section", + "chief_complaint", + "column_break_29", + "diagnosis", + "medication_section", + "drug_prescription", + "investigations_section", + "lab_test_prescription", + "procedures_section", + "procedure_prescription", + "rehabilitation_section", + "therapy_plan", + "therapies", "sb_inpatient_occupancy", "inpatient_occupancies", "btn_transfer", + "sb_discharge_details", + "discharge_ordered_date", + "discharge_practitioner", + "discharge_encounter", + "discharge_date", + "cb_discharge", + "discharge_instructions", + "followup_date", "sb_discharge_note", "discharge_note" ], @@ -54,7 +78,8 @@ "in_list_view": 1, "label": "Patient", "options": "Patient", - "reqd": 1 + "reqd": 1, + "set_only_once": 1 }, { "fetch_from": "patient.patient_name", @@ -108,11 +133,31 @@ "label": "Phone", "read_only": 1 }, + { + "fieldname": "medical_department", + "fieldtype": "Link", + "label": "Medical Department", + "options": "Medical Department", + "set_only_once": 1 + }, + { + "fieldname": "primary_practitioner", + "fieldtype": "Link", + "label": "Healthcare Practitioner (Primary)", + "options": "Healthcare Practitioner" + }, + { + "fieldname": "secondary_practitioner", + "fieldtype": "Link", + "label": "Healthcare Practitioner (Secondary)", + "options": "Healthcare Practitioner" + }, { "fieldname": "column_break_8", "fieldtype": "Column Break" }, { + "default": "Admission Scheduled", "fieldname": "status", "fieldtype": "Select", "in_list_view": 1, @@ -126,37 +171,45 @@ "fieldtype": "Date", "in_list_view": 1, "label": "Admission Schedule Date", + "read_only": 1, "reqd": 1 }, { - "default": "Today", + "fieldname": "admission_ordered_for", + "fieldtype": "Date", + "label": "Admission Ordered For", + "read_only": 1 + }, + { "fieldname": "admitted_datetime", "fieldtype": "Datetime", "in_list_view": 1, - "label": "Admitted Datetime" + "label": "Admitted Datetime", + "read_only": 1 + }, + { + "depends_on": "eval:(doc.expected_length_of_stay > 0)", + "fieldname": "expected_length_of_stay", + "fieldtype": "Int", + "label": "Expected Length of Stay", + "set_only_once": 1 }, { "fieldname": "expected_discharge", "fieldtype": "Date", "in_list_view": 1, - "label": "Expected Discharge" - }, - { - "fieldname": "discharge_date", - "fieldtype": "Date", - "in_list_view": 1, - "label": "Discharge Date" + "label": "Expected Discharge", + "read_only": 1 }, { "collapsible": 1, "fieldname": "references", "fieldtype": "Section Break", - "label": "References" + "label": "Admission Order Details" }, { "fieldname": "cb_admission", - "fieldtype": "Column Break", - "label": "Admission" + "fieldtype": "Column Break" }, { "fieldname": "admission_practitioner", @@ -172,10 +225,21 @@ "options": "Patient Encounter", "read_only": 1 }, + { + "fieldname": "chief_complaint", + "fieldtype": "Table MultiSelect", + "label": "Chief Complaint", + "options": "Patient Encounter Symptom" + }, + { + "fieldname": "admission_instruction", + "fieldtype": "Small Text", + "label": "Admission Instruction", + "set_only_once": 1 + }, { "fieldname": "cb_discharge", - "fieldtype": "Column Break", - "label": "Discharge" + "fieldtype": "Column Break" }, { "fieldname": "discharge_practitioner", @@ -192,10 +256,51 @@ "read_only": 1 }, { + "collapsible": 1, + "fieldname": "medication_section", + "fieldtype": "Section Break", + "label": "Medications" + }, + { + "fieldname": "drug_prescription", + "fieldtype": "Table", + "options": "Drug Prescription" + }, + { + "collapsible": 1, + "fieldname": "investigations_section", + "fieldtype": "Section Break", + "label": "Investigations" + }, + { + "fieldname": "lab_test_prescription", + "fieldtype": "Table", + "options": "Lab Prescription" + }, + { + "collapsible": 1, + "fieldname": "procedures_section", + "fieldtype": "Section Break", + "label": "Procedures" + }, + { + "fieldname": "procedure_prescription", + "fieldtype": "Table", + "options": "Procedure Prescription" + }, + { + "depends_on": "eval:(doc.status != \"Admission Scheduled\")", "fieldname": "sb_inpatient_occupancy", "fieldtype": "Section Break", "label": "Inpatient Occupancy" }, + { + "fieldname": "admission_service_unit_type", + "fieldtype": "Link", + "label": "Admission Service Unit Type", + "options": "Healthcare Service Unit Type", + "read_only": 1 + }, { "fieldname": "inpatient_occupancies", "fieldtype": "Table", @@ -208,10 +313,10 @@ "label": "Transfer" }, { - "depends_on": "eval:doc.status != \"Admission Scheduled\"", + "depends_on": "eval:(doc.status == \"Discharge Scheduled\" || doc.status == \"Discharged\")", "fieldname": "sb_discharge_note", "fieldtype": "Section Break", - "label": "Discharge Note" + "label": "Discharge Notes" }, { "fieldname": "discharge_note", @@ -224,10 +329,76 @@ "in_standard_filter": 1, "label": "Company", "options": "Company" + }, + { + "collapsible": 1, + "collapsible_depends_on": "eval:(doc.status == \"Admitted\")", + "fieldname": "encounter_details_section", + "fieldtype": "Section Break", + "label": "Encounter Impression" + }, + { + "fieldname": "column_break_29", + "fieldtype": "Column Break" + }, + { + "fieldname": "diagnosis", + "fieldtype": "Table MultiSelect", + "label": "Diagnosis", + "options": "Patient Encounter Diagnosis" + }, + { + "fieldname": "followup_date", + "fieldtype": "Date", + "label": "Follow Up Date" + }, + { + "collapsible": 1, + "depends_on": "eval:(doc.status == \"Discharge Scheduled\" || doc.status == \"Discharged\")", + "fieldname": "sb_discharge_details", + "fieldtype": "Section Break", + "label": "Discharge Detials" + }, + { + "fieldname": "discharge_instructions", + "fieldtype": "Small Text", + "label": "Discharge Instructions" + }, + { + "fieldname": "discharge_ordered_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Discharge Ordered Date", + "read_only": 1 + }, + { + "collapsible": 1, + "fieldname": "rehabilitation_section", + "fieldtype": "Section Break", + "label": "Rehabilitation" + }, + { + "fieldname": "therapy_plan", + "fieldtype": "Link", + "hidden": 1, + "label": "Therapy Plan", + "options": "Therapy Plan", + "read_only": 1 + }, + { + "fieldname": "therapies", + "fieldtype": "Table", + "options": "Therapy Plan Detail" + }, + { + "fieldname": "discharge_date", + "fieldtype": "Date", + "label": "Discharge Date", + "read_only": 1 } ], "links": [], - "modified": "2020-04-07 13:13:39.351977", + "modified": "2020-05-21 00:37:12.939925", "modified_by": "Administrator", "module": "Healthcare", "name": "Inpatient Record", diff --git a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py index 835b38bedf8..e668204dcf4 100644 --- a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py +++ b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py @@ -3,7 +3,7 @@ # For license information, please see license.txt from __future__ import unicode_literals -import frappe +import frappe, json from frappe import _ from frappe.utils import today, now_datetime, getdate from frappe.model.document import Document @@ -11,8 +11,12 @@ from frappe.desk.reportview import get_match_cond class InpatientRecord(Document): def after_insert(self): - frappe.db.set_value("Patient", self.patient, "inpatient_status", "Admission Scheduled") - frappe.db.set_value("Patient", self.patient, "inpatient_record", self.name) + frappe.db.set_value('Patient', self.patient, 'inpatient_record', self.name) + frappe.db.set_value('Patient', self.patient, 'inpatient_status', self.status) + + if self.admission_encounter: # Update encounter + frappe.db.set_value('Patient Encounter', self.admission_encounter, 'inpatient_record', self.name) + frappe.db.set_value('Patient Encounter', self.admission_encounter, 'inpatient_status', self.status) def validate(self): self.validate_dates() @@ -26,7 +30,7 @@ class InpatientRecord(Document): (getdate(self.admitted_datetime) < getdate(today())): frappe.throw(_("Scheduled and Admitted dates can not be less than today")) if (getdate(self.expected_discharge) < getdate(self.scheduled_date)) or \ - (getdate(self.discharge_date) < getdate(self.scheduled_date)): + (getdate(self.discharge_ordered_date) < getdate(self.scheduled_date)): frappe.throw(_("Expected and Discharge dates cannot be less than Admission Schedule date")) def validate_already_scheduled_or_admitted(self): @@ -59,37 +63,76 @@ class InpatientRecord(Document): if service_unit: transfer_patient(self, service_unit, check_in) + @frappe.whitelist() -def schedule_inpatient(patient, encounter_id, practitioner): - patient_obj = frappe.get_doc('Patient', patient) +def schedule_inpatient(args): + admission_order = json.loads(args) # admission order via Encounter + if not admission_order or not admission_order['patient'] or not admission_order['admission_encounter']: + frappe.throw(_('Missing required details, did not create Inpatient Record')) + inpatient_record = frappe.new_doc('Inpatient Record') - inpatient_record.patient = patient - inpatient_record.patient_name = patient_obj.patient_name - inpatient_record.gender = patient_obj.sex - inpatient_record.blood_group = patient_obj.blood_group - inpatient_record.dob = patient_obj.dob - inpatient_record.mobile = patient_obj.mobile - inpatient_record.email = patient_obj.email - inpatient_record.phone = patient_obj.phone - inpatient_record.status = "Admission Scheduled" + + # Admission order details + set_details_from_ip_order(inpatient_record, admission_order) + + # Patient details + patient = frappe.get_doc('Patient', admission_order['patient']) + inpatient_record.patient = patient.name + inpatient_record.patient_name = patient.patient_name + inpatient_record.gender = patient.sex + inpatient_record.blood_group = patient.blood_group + inpatient_record.dob = patient.dob + inpatient_record.mobile = patient.mobile + inpatient_record.email = patient.email + inpatient_record.phone = patient.phone inpatient_record.scheduled_date = today() - inpatient_record.admission_practitioner = practitioner - inpatient_record.admission_encounter = encounter_id + + # Set encounter detials + encounter = frappe.get_doc('Patient Encounter', admission_order['admission_encounter']) + if encounter and encounter.symptoms: # Symptoms + set_ip_child_records(inpatient_record, 'chief_complaint', encounter.symptoms) + + if encounter and encounter.diagnosis: # Diagnosis + set_ip_child_records(inpatient_record, 'diagnosis', encounter.diagnosis) + + if encounter and encounter.drug_prescription: # Medication + set_ip_child_records(inpatient_record, 'drug_prescription', encounter.drug_prescription) + + if encounter and encounter.lab_test_prescription: # Lab Tests + set_ip_child_records(inpatient_record, 'lab_test_prescription', encounter.lab_test_prescription) + + if encounter and encounter.procedure_prescription: # Procedure Prescription + set_ip_child_records(inpatient_record, 'procedure_prescription', encounter.procedure_prescription) + + if encounter and encounter.therapies: # Therapies + inpatient_record.therapy_plan = encounter.therapy_plan + set_ip_child_records(inpatient_record, 'therapies', encounter.therapies) + + inpatient_record.status = 'Admission Scheduled' inpatient_record.save(ignore_permissions = True) @frappe.whitelist() -def schedule_discharge(patient, encounter_id=None, practitioner=None): - inpatient_record_id = frappe.db.get_value('Patient', patient, 'inpatient_record') +def schedule_discharge(args): + discharge_order = json.loads(args) + inpatient_record_id = frappe.db.get_value('Patient', discharge_order['patient'], 'inpatient_record') if inpatient_record_id: - inpatient_record = frappe.get_doc("Inpatient Record", inpatient_record_id) - inpatient_record.discharge_practitioner = practitioner - inpatient_record.discharge_encounter = encounter_id - inpatient_record.status = "Discharge Scheduled" - + inpatient_record = frappe.get_doc('Inpatient Record', inpatient_record_id) check_out_inpatient(inpatient_record) - + set_details_from_ip_order(inpatient_record, discharge_order) + inpatient_record.status = 'Discharge Scheduled' inpatient_record.save(ignore_permissions = True) - frappe.db.set_value("Patient", patient, "inpatient_status", "Discharge Scheduled") + frappe.db.set_value('Patient', discharge_order['patient'], 'inpatient_status', inpatient_record.status) + frappe.db.set_value('Patient Encounter', inpatient_record.discharge_encounter, 'inpatient_status', inpatient_record.status) + +def set_details_from_ip_order(inpatient_record, ip_order): + for key in ip_order: + inpatient_record.set(key, ip_order[key]) + +def set_ip_child_records(inpatient_record, inpatient_record_child, encounter_child): + for item in encounter_child: + table = inpatient_record.append(inpatient_record_child) + for df in table.meta.get('fields'): + table.set(df.fieldname, item.get(df.fieldname)) def check_out_inpatient(inpatient_record): if inpatient_record.inpatient_occupancies: @@ -149,14 +192,14 @@ def get_inpatient_docs_not_invoiced(doc, inpatient_record): def admit_patient(inpatient_record, service_unit, check_in, expected_discharge=None): inpatient_record.admitted_datetime = check_in - inpatient_record.status = "Admitted" + inpatient_record.status = 'Admitted' inpatient_record.expected_discharge = expected_discharge inpatient_record.set('inpatient_occupancies', []) transfer_patient(inpatient_record, service_unit, check_in) - frappe.db.set_value("Patient", inpatient_record.patient, "inpatient_status", "Admitted") - frappe.db.set_value("Patient", inpatient_record.patient, "inpatient_record", inpatient_record.name) + frappe.db.set_value('Patient', inpatient_record.patient, 'inpatient_status', 'Admitted') + frappe.db.set_value('Patient', inpatient_record.patient, 'inpatient_record', inpatient_record.name) def transfer_patient(inpatient_record, service_unit, check_in): item_line = inpatient_record.append('inpatient_occupancies', {}) diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js index 2410f8e10dd..43e43acda48 100644 --- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js +++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js @@ -180,35 +180,114 @@ frappe.ui.form.on('Patient Encounter', { } }); -let schedule_inpatient = function(frm) { - frappe.call({ - method: 'erpnext.healthcare.doctype.inpatient_record.inpatient_record.schedule_inpatient', - args: {patient: frm.doc.patient, encounter_id: frm.doc.name, practitioner: frm.doc.practitioner}, - callback: function(data) { - if (!data.exc) { - frm.reload_doc(); +var schedule_inpatient = function(frm) { + var dialog = new frappe.ui.Dialog({ + title: 'Patient Admission', + fields: [ + {fieldtype: 'Link', label: 'Medical Department', fieldname: 'medical_department', options: 'Medical Department', reqd: 1}, + {fieldtype: 'Link', label: 'Healthcare Practitioner (Primary)', fieldname: 'primary_practitioner', options: 'Healthcare Practitioner', reqd: 1}, + {fieldtype: 'Link', label: 'Healthcare Practitioner (Secondary)', fieldname: 'secondary_practitioner', options: 'Healthcare Practitioner'}, + {fieldtype: 'Column Break'}, + {fieldtype: 'Date', label: 'Admission Ordered For', fieldname: 'admission_ordered_for', default: 'Today'}, + {fieldtype: 'Link', label: 'Service Unit Type', fieldname: 'service_unit_type', options: 'Healthcare Service Unit Type'}, + {fieldtype: 'Int', label: 'Expected Length of Stay', fieldname: 'expected_length_of_stay'}, + {fieldtype: 'Section Break', label: 'Admission Instructions'}, + {fieldtype: 'Small Text', fieldname: 'admission_instruction'} + ], + primary_action_label: __('Order Admission'), + primary_action : function() { + var args = { + patient: frm.doc.patient, + admission_encounter: frm.doc.name, + referring_practitioner: frm.doc.practitioner, + company: frm.doc.company, + medical_department: dialog.get_value('medical_department'), + primary_practitioner: dialog.get_value('primary_practitioner'), + secondary_practitioner: dialog.get_value('secondary_practitioner'), + admission_ordered_for: dialog.get_value('admission_ordered_for'), + admission_service_unit_type: dialog.get_value('service_unit_type'), + expected_length_of_stay: dialog.get_value('expected_length_of_stay'), + admission_instruction: dialog.get_value('admission_instruction') } - }, - freeze: true, - freeze_message: __('Process Inpatient Scheduling') + frappe.call({ + method: 'erpnext.healthcare.doctype.inpatient_record.inpatient_record.schedule_inpatient', + args: { + args: args + }, + callback: function(data) { + if(!data.exc){ + frm.reload_doc(); + } + }, + freeze: true, + freeze_message: 'Scheduling Patient Admission' + }); + frm.refresh_fields(); + dialog.hide(); + } }); + + dialog.set_values({ + 'medical_department': frm.doc.medical_department, + 'primary_practitioner': frm.doc.practitioner, + }); + + dialog.fields_dict['service_unit_type'].get_query = function() { + return { + filters: { + 'inpatient_occupancy': 1, + 'allow_appointments': 0 + } + }; + }; + + dialog.show(); + dialog.$wrapper.find('.modal-dialog').css('width', '800px'); }; -let schedule_discharge = function(frm) { - frappe.call({ - method: 'erpnext.healthcare.doctype.inpatient_record.inpatient_record.schedule_discharge', - args: {patient: frm.doc.patient, encounter_id: frm.doc.name, practitioner: frm.doc.practitioner}, - callback: function(data) { - if (!data.exc) { - frm.reload_doc(); +var schedule_discharge = function(frm) { + var dialog = new frappe.ui.Dialog ({ + title: 'Inpatient Discharge', + fields: [ + {fieldtype: 'Date', label: 'Discharge Ordered Date', fieldname: 'discharge_ordered_date', default: 'Today', read_only: 1}, + {fieldtype: 'Date', label: 'Followup Date', fieldname: 'followup_date'}, + {fieldtype: 'Column Break'}, + {fieldtype: 'Small Text', label: 'Discharge Instructions', fieldname: 'discharge_instructions'}, + {fieldtype: 'Section Break', label:'Discharge Summary'}, + {fieldtype: 'Text Editor', label: 'Discharge Note', fieldname: 'discharge_note'} + ], + primary_action_label: __('Order Discharge'), + primary_action : function() { + var args = { + patient: frm.doc.patient, + discharge_encounter: frm.doc.name, + discharge_practitioner: frm.doc.practitioner, + discharge_ordered_date: dialog.get_value('discharge_ordered_date'), + followup_date: dialog.get_value('followup_date'), + discharge_instructions: dialog.get_value('discharge_instructions'), + discharge_note: dialog.get_value('discharge_note') } - }, - freeze: true, - freeze_message: 'Process Discharge' + frappe.call ({ + method: 'erpnext.healthcare.doctype.inpatient_record.inpatient_record.schedule_discharge', + args: {args}, + callback: function(data) { + if(!data.exc){ + frm.reload_doc(); + } + }, + freeze: true, + freeze_message: 'Scheduling Inpatient Discharge' + }); + frm.refresh_fields(); + dialog.hide(); + } }); + + dialog.show(); + dialog.$wrapper.find('.modal-dialog').css('width', '800px'); }; -let create_medical_record = function (frm) { +let create_medical_record = function(frm) { if (!frm.doc.patient) { frappe.throw(__('Please select patient')); } @@ -221,7 +300,7 @@ let create_medical_record = function (frm) { frappe.new_doc('Patient Medical Record'); }; -let create_vital_signs = function (frm) { +let create_vital_signs = function(frm) { if (!frm.doc.patient) { frappe.throw(__('Please select patient')); } @@ -233,7 +312,7 @@ let create_vital_signs = function (frm) { frappe.new_doc('Vital Signs'); }; -let create_procedure = function (frm) { +let create_procedure = function(frm) { if (!frm.doc.patient) { frappe.throw(__('Please select patient')); } diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json index 05eec87398b..15675f4673f 100644 --- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json +++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json @@ -52,6 +52,7 @@ ], "fields": [ { + "allow_on_submit": 1, "fieldname": "inpatient_record", "fieldtype": "Link", "label": "Inpatient Record", @@ -296,6 +297,7 @@ "read_only": 1 }, { + "allow_on_submit": 1, "fieldname": "inpatient_status", "fieldtype": "Data", "label": "Inpatient Status", @@ -326,7 +328,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-04-27 21:58:29.789797", + "modified": "2020-05-16 21:00:08.644531", "modified_by": "Administrator", "module": "Healthcare", "name": "Patient Encounter", From 4053c46609c7f1c3a47ed5c1420a621ef5c09a5f Mon Sep 17 00:00:00 2001 From: anoop Date: Thu, 21 May 2020 01:31:48 +0530 Subject: [PATCH 141/608] fix: consider only submitted docs for invoicing --- .../healthcare/doctype/inpatient_record/inpatient_record.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py index e668204dcf4..8056074668f 100644 --- a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py +++ b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py @@ -187,8 +187,8 @@ def get_pending_doc(doc, doc_name_list, pending_invoices): return pending_invoices def get_inpatient_docs_not_invoiced(doc, inpatient_record): - return frappe.db.get_list(doc, filters = {"patient": inpatient_record.patient, - "inpatient_record": inpatient_record.name, "invoiced": 0}) + return frappe.db.get_list(doc, filters = {'patient': inpatient_record.patient, + 'inpatient_record': inpatient_record.name, 'docstatus': 1, 'invoiced': 0}) def admit_patient(inpatient_record, service_unit, check_in, expected_discharge=None): inpatient_record.admitted_datetime = check_in From b53638c0a3216dfc7c27659d7a7ee5bf0a505443 Mon Sep 17 00:00:00 2001 From: anoop Date: Thu, 21 May 2020 01:34:18 +0530 Subject: [PATCH 142/608] fix: ip-order dialogs use long text field --- .../doctype/patient_encounter/patient_encounter.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js index 43e43acda48..ef1068e6cb5 100644 --- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js +++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js @@ -191,8 +191,8 @@ var schedule_inpatient = function(frm) { {fieldtype: 'Date', label: 'Admission Ordered For', fieldname: 'admission_ordered_for', default: 'Today'}, {fieldtype: 'Link', label: 'Service Unit Type', fieldname: 'service_unit_type', options: 'Healthcare Service Unit Type'}, {fieldtype: 'Int', label: 'Expected Length of Stay', fieldname: 'expected_length_of_stay'}, - {fieldtype: 'Section Break', label: 'Admission Instructions'}, - {fieldtype: 'Small Text', fieldname: 'admission_instruction'} + {fieldtype: 'Section Break'}, + {fieldtype: 'Long Text', label: 'Admission Instructions', fieldname: 'admission_instruction'} ], primary_action_label: __('Order Admission'), primary_action : function() { @@ -254,7 +254,7 @@ var schedule_discharge = function(frm) { {fieldtype: 'Column Break'}, {fieldtype: 'Small Text', label: 'Discharge Instructions', fieldname: 'discharge_instructions'}, {fieldtype: 'Section Break', label:'Discharge Summary'}, - {fieldtype: 'Text Editor', label: 'Discharge Note', fieldname: 'discharge_note'} + {fieldtype: 'Long Text', label: 'Discharge Note', fieldname: 'discharge_note'} ], primary_action_label: __('Order Discharge'), primary_action : function() { From c6ee83bf123f6ca15635c130c44a07910a3c2be9 Mon Sep 17 00:00:00 2001 From: anoop Date: Thu, 21 May 2020 02:26:55 +0530 Subject: [PATCH 143/608] fix: inpatient date validation removed, added role perms service unit defaults not set when created from tree, added validations on after_insert --- .../healthcare_service_unit.py | 2 +- .../inpatient_record/inpatient_record.json | 75 +++++++++++++++---- .../inpatient_record/inpatient_record.py | 5 +- 3 files changed, 64 insertions(+), 18 deletions(-) diff --git a/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.py b/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.py index 9720078e32e..9e0417a2bef 100644 --- a/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.py +++ b/erpnext/healthcare/doctype/healthcare_service_unit/healthcare_service_unit.py @@ -22,7 +22,7 @@ class HealthcareServiceUnit(NestedSet): super(HealthcareServiceUnit, self).on_update() self.validate_one_root() - def validate(self): + def after_insert(self): if self.is_group: self.allow_appointments = 0 self.overlap_appointments = 0 diff --git a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.json b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.json index d3835409d9a..5ced845c1b0 100644 --- a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.json +++ b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.json @@ -229,7 +229,8 @@ "fieldname": "chief_complaint", "fieldtype": "Table MultiSelect", "label": "Chief Complaint", - "options": "Patient Encounter Symptom" + "options": "Patient Encounter Symptom", + "permlevel": 1 }, { "fieldname": "admission_instruction", @@ -259,34 +260,40 @@ "collapsible": 1, "fieldname": "medication_section", "fieldtype": "Section Break", - "label": "Medications" + "label": "Medications", + "permlevel": 1 }, { "fieldname": "drug_prescription", "fieldtype": "Table", - "options": "Drug Prescription" + "options": "Drug Prescription", + "permlevel": 1 }, { "collapsible": 1, "fieldname": "investigations_section", "fieldtype": "Section Break", - "label": "Investigations" + "label": "Investigations", + "permlevel": 1 }, { "fieldname": "lab_test_prescription", "fieldtype": "Table", - "options": "Lab Prescription" + "options": "Lab Prescription", + "permlevel": 1 }, { "collapsible": 1, "fieldname": "procedures_section", "fieldtype": "Section Break", - "label": "Procedures" + "label": "Procedures", + "permlevel": 1 }, { "fieldname": "procedure_prescription", "fieldtype": "Table", - "options": "Procedure Prescription" + "options": "Procedure Prescription", + "permlevel": 1 }, { "depends_on": "eval:(doc.status != \"Admission Scheduled\")", @@ -320,7 +327,8 @@ }, { "fieldname": "discharge_note", - "fieldtype": "Text Editor" + "fieldtype": "Text Editor", + "permlevel": 1 }, { "fetch_from": "admission_encounter.company", @@ -335,7 +343,8 @@ "collapsible_depends_on": "eval:(doc.status == \"Admitted\")", "fieldname": "encounter_details_section", "fieldtype": "Section Break", - "label": "Encounter Impression" + "label": "Encounter Impression", + "permlevel": 1 }, { "fieldname": "column_break_29", @@ -345,7 +354,8 @@ "fieldname": "diagnosis", "fieldtype": "Table MultiSelect", "label": "Diagnosis", - "options": "Patient Encounter Diagnosis" + "options": "Patient Encounter Diagnosis", + "permlevel": 1 }, { "fieldname": "followup_date", @@ -375,7 +385,8 @@ "collapsible": 1, "fieldname": "rehabilitation_section", "fieldtype": "Section Break", - "label": "Rehabilitation" + "label": "Rehabilitation", + "permlevel": 1 }, { "fieldname": "therapy_plan", @@ -383,12 +394,14 @@ "hidden": 1, "label": "Therapy Plan", "options": "Therapy Plan", + "permlevel": 1, "read_only": 1 }, { "fieldname": "therapies", "fieldtype": "Table", - "options": "Therapy Plan Detail" + "options": "Therapy Plan Detail", + "permlevel": 1 }, { "fieldname": "discharge_date", @@ -398,7 +411,7 @@ } ], "links": [], - "modified": "2020-05-21 00:37:12.939925", + "modified": "2020-05-21 02:26:22.144575", "modified_by": "Administrator", "module": "Healthcare", "name": "Inpatient Record", @@ -415,6 +428,42 @@ "role": "Healthcare Administrator", "share": 1, "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Physician", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Nursing User", + "share": 1, + "write": 1 + }, + { + "permlevel": 1, + "read": 1, + "role": "Physician", + "write": 1 + }, + { + "permlevel": 1, + "read": 1, + "report": 1, + "role": "Nursing User" } ], "restrict_to_domain": "Healthcare", diff --git a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py index 8056074668f..802ab414c0c 100644 --- a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py +++ b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py @@ -26,12 +26,9 @@ class InpatientRecord(Document): frappe.db.set_value("Patient", self.patient, "inpatient_record", None) def validate_dates(self): - if (getdate(self.scheduled_date) < getdate(today())) or \ - (getdate(self.admitted_datetime) < getdate(today())): - frappe.throw(_("Scheduled and Admitted dates can not be less than today")) if (getdate(self.expected_discharge) < getdate(self.scheduled_date)) or \ (getdate(self.discharge_ordered_date) < getdate(self.scheduled_date)): - frappe.throw(_("Expected and Discharge dates cannot be less than Admission Schedule date")) + frappe.throw(_('Expected and Discharge dates cannot be less than Admission Schedule date')) def validate_already_scheduled_or_admitted(self): query = """ From 94762ff4ffaa8b91e08213850ec80a8ef2415a5b Mon Sep 17 00:00:00 2001 From: anoop Date: Thu, 21 May 2020 03:08:47 +0530 Subject: [PATCH 144/608] fix: invoiced field position, medical department field corrected in query --- .../doctype/patient_appointment/patient_appointment.json | 4 ++-- .../doctype/patient_appointment/patient_appointment.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json index b8a400c6b7a..ac35acc21ac 100644 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json @@ -39,9 +39,9 @@ "section_break_16", "mode_of_payment", "billing_item", + "invoiced", "column_break_2", "paid_amount", - "invoiced", "ref_sales_invoice", "section_break_3", "referring_practitioner", @@ -348,7 +348,7 @@ } ], "links": [], - "modified": "2020-04-27 21:36:06.404062", + "modified": "2020-05-21 03:04:21.400893", "modified_by": "Administrator", "module": "Healthcare", "name": "Patient Appointment", diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py index 9eb6e77c855..512fb48360f 100755 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py @@ -447,7 +447,7 @@ def get_prescribed_therapies(patient): """ SELECT t.therapy_type, t.name, t.parent, e.practitioner, - e.encounter_date, e.therapy_plan, e.visit_department + e.encounter_date, e.therapy_plan, e.medical_department FROM `tabPatient Encounter` e, `tabTherapy Plan Detail` t WHERE From d9d1f442df3414796ffeb7b6e1d86acb66fdd079 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 21 May 2020 09:02:46 +0530 Subject: [PATCH 145/608] fix: service unit validation and translation --- .../healthcare_service_unit_type.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.py b/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.py index a99358cdc60..bb86eaacc40 100644 --- a/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.py +++ b/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.py @@ -12,12 +12,14 @@ class HealthcareServiceUnitType(Document): def validate(self): if self.allow_appointments and self.inpatient_occupancy: frappe.msgprint( - _('Healthcare Service Unit Type cannot be both Allow Appointments and Inpatient Occupancy'), + _('Healthcare Service Unit Type cannot have both {0} and {1}').format( + frappe.bold('Allow Appointments'), frappe.bold('Inpatient Occupancy')), raise_exception=1, title=_('Validation Error'), indicator='red' ) elif not self.allow_appointments and not self.inpatient_occupancy: frappe.msgprint( - _('Healthcare Service Unit Type cannot be both Allow Appointments and Inpatient Occupancy'), + _('Healthcare Service Unit Type must allow atleast one among {0} and {1}').format( + frappe.bold('Allow Appointments'), frappe.bold('Inpatient Occupancy')), raise_exception=1, title=_('Validation Error'), indicator='red' ) From f3a2f1fe207fbc7f637bc26ef461f946eaa07a8d Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 21 May 2020 09:27:42 +0530 Subject: [PATCH 146/608] fix(ip): code cleanup and translations --- .../inpatient_record/inpatient_record.js | 82 +++++++++---------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.js b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.js index b640239b701..971e166067e 100644 --- a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.js +++ b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.js @@ -11,24 +11,24 @@ frappe.ui.form.on('Inpatient Record', { ]; }, refresh: function(frm) { - if(!frm.doc.__islocal && (frm.doc.status == 'Admission Scheduled' || frm.doc.status == 'Admitted')) { + if (!frm.doc.__islocal && (frm.doc.status == 'Admission Scheduled' || frm.doc.status == 'Admitted')) { frm.enable_save(); } else { frm.disable_save(); } - if(!frm.doc.__islocal && frm.doc.status == 'Admission Scheduled') { + if (!frm.doc.__islocal && frm.doc.status == 'Admission Scheduled') { frm.add_custom_button(__('Admit'), function() { admit_patient_dialog(frm); } ); } - if(!frm.doc.__islocal && frm.doc.status == 'Discharge Scheduled') { + if (!frm.doc.__islocal && frm.doc.status == 'Discharge Scheduled') { frm.add_custom_button(__('Discharge'), function() { discharge_patient(frm); } ); } - if(!frm.doc.__islocal && frm.doc.status != 'Admitted') { + if (!frm.doc.__islocal && frm.doc.status != 'Admitted') { frm.disable_save(); frm.set_df_property('btn_transfer', 'hidden', 1); } else { @@ -40,22 +40,22 @@ frappe.ui.form.on('Inpatient Record', { } }); -var discharge_patient = function(frm) { +let discharge_patient = function(frm) { frappe.call({ doc: frm.doc, method: 'discharge', callback: function(data) { - if(!data.exc){ + if (!data.exc) { frm.reload_doc(); } }, freeze: true, - freeze_message: 'Processing Inpatient Discharge' + freeze_message: __('Processing Inpatient Discharge') }); }; -var admit_patient_dialog = function(frm){ - var dialog = new frappe.ui.Dialog({ +let admit_patient_dialog = function(frm) { + let dialog = new frappe.ui.Dialog({ title: 'Admit Patient', width: 100, fields: [ @@ -74,13 +74,13 @@ var admit_patient_dialog = function(frm){ ], primary_action_label: __('Admit'), primary_action : function(){ - var service_unit = dialog.get_value('service_unit'); - var check_in = dialog.get_value('check_in'); - var expected_discharge = null; - if(dialog.get_value('expected_discharge')){ + let service_unit = dialog.get_value('service_unit'); + let check_in = dialog.get_value('check_in'); + let expected_discharge = null; + if (dialog.get_value('expected_discharge')) { expected_discharge = dialog.get_value('expected_discharge'); } - if(!service_unit && !check_in){ + if (!service_unit && !check_in) { return; } frappe.call({ @@ -92,12 +92,12 @@ var admit_patient_dialog = function(frm){ 'expected_discharge': expected_discharge }, callback: function(data) { - if(!data.exc){ + if (!data.exc) { frm.reload_doc(); } }, freeze: true, - freeze_message: 'Processing Patient Admission' + freeze_message: __('Processing Patient Admission') }); frm.refresh_fields(); dialog.hide(); @@ -126,21 +126,21 @@ var admit_patient_dialog = function(frm){ dialog.show(); }; -var transfer_patient_dialog = function(frm){ - var dialog = new frappe.ui.Dialog({ +let transfer_patient_dialog = function(frm) { + let dialog = new frappe.ui.Dialog({ title: 'Transfer Patient', width: 100, fields: [ - {fieldtype: "Link", label: "Leave From", fieldname: "leave_from", options: "Healthcare Service Unit", reqd: 1, read_only:1}, - {fieldtype: "Link", label: "Service Unit Type", fieldname: "service_unit_type", options: "Healthcare Service Unit Type"}, - {fieldtype: "Link", label: "Transfer To", fieldname: "service_unit", options: "Healthcare Service Unit", reqd: 1}, - {fieldtype: "Datetime", label: "Check In", fieldname: "check_in", reqd: 1} + {fieldtype: 'Link', label: 'Leave From', fieldname: 'leave_from', options: 'Healthcare Service Unit', reqd: 1, read_only:1}, + {fieldtype: 'Link', label: 'Service Unit Type', fieldname: 'service_unit_type', options: 'Healthcare Service Unit Type'}, + {fieldtype: 'Link', label: 'Transfer To', fieldname: 'service_unit', options: 'Healthcare Service Unit', reqd: 1}, + {fieldtype: 'Datetime', label: 'Check In', fieldname: 'check_in', reqd: 1} ], - primary_action_label: __("Transfer"), - primary_action : function(){ - var service_unit = null; - var check_in = dialog.get_value('check_in'); - var leave_from = null; + primary_action_label: __('Transfer'), + primary_action : function() { + let service_unit = null; + let check_in = dialog.get_value('check_in'); + let leave_from = null; if(dialog.get_value('leave_from')){ leave_from = dialog.get_value('leave_from'); } @@ -159,47 +159,47 @@ var transfer_patient_dialog = function(frm){ 'leave_from': leave_from }, callback: function(data) { - if(!data.exc){ + if (!data.exc) { frm.reload_doc(); } }, freeze: true, - freeze_message: "Process Transfer" + freeze_message: __('Process Transfer') }); frm.refresh_fields(); dialog.hide(); } }); - dialog.fields_dict["leave_from"].get_query = function(){ + dialog.fields_dict['leave_from'].get_query = function(){ return { - query : "erpnext.healthcare.doctype.inpatient_record.inpatient_record.get_leave_from", + query : 'erpnext.healthcare.doctype.inpatient_record.inpatient_record.get_leave_from', filters: {docname:frm.doc.name} }; }; - dialog.fields_dict["service_unit_type"].get_query = function(){ + dialog.fields_dict['service_unit_type'].get_query = function(){ return { filters: { - "inpatient_occupancy": 1, - "allow_appointments": 0 + 'inpatient_occupancy': 1, + 'allow_appointments': 0 } }; }; - dialog.fields_dict["service_unit"].get_query = function(){ + dialog.fields_dict['service_unit'].get_query = function(){ return { filters: { - "is_group": 0, - "service_unit_type": dialog.get_value("service_unit_type"), - "occupancy_status" : "Vacant" + 'is_group': 0, + 'service_unit_type': dialog.get_value('service_unit_type'), + 'occupancy_status' : 'Vacant' } }; }; dialog.show(); - var not_left_service_unit = null; - for(let inpatient_occupancy in frm.doc.inpatient_occupancies){ - if(frm.doc.inpatient_occupancies[inpatient_occupancy].left != 1){ + let not_left_service_unit = null; + for (let inpatient_occupancy in frm.doc.inpatient_occupancies) { + if (frm.doc.inpatient_occupancies[inpatient_occupancy].left != 1) { not_left_service_unit = frm.doc.inpatient_occupancies[inpatient_occupancy].service_unit; } } From 3a6e6af84f0d48b67fcca92f4ae91c03d6fb4b6a Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 21 May 2020 10:02:31 +0530 Subject: [PATCH 147/608] fix: add title to validation dialog --- .../healthcare/doctype/inpatient_record/inpatient_record.py | 4 ++-- .../healthcare/doctype/patient_encounter/patient_encounter.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py index 802ab414c0c..cf63b65f4d6 100644 --- a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py +++ b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py @@ -29,7 +29,7 @@ class InpatientRecord(Document): if (getdate(self.expected_discharge) < getdate(self.scheduled_date)) or \ (getdate(self.discharge_ordered_date) < getdate(self.scheduled_date)): frappe.throw(_('Expected and Discharge dates cannot be less than Admission Schedule date')) - + def validate_already_scheduled_or_admitted(self): query = """ select name, status @@ -168,7 +168,7 @@ def validate_invoiced_inpatient(inpatient_record): if pending_invoices: frappe.throw(_("Can not mark Inpatient Record Discharged, there are Unbilled Invoices {0}").format(", " - .join(pending_invoices))) + .join(pending_invoices)), title=_('Unbilled Invoices')) def get_pending_doc(doc, doc_name_list, pending_invoices): if doc_name_list: diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js index ef1068e6cb5..edcee99d4bc 100644 --- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js +++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js @@ -215,7 +215,7 @@ var schedule_inpatient = function(frm) { args: args }, callback: function(data) { - if(!data.exc){ + if (!data.exc) { frm.reload_doc(); } }, From 82f639da5980c0aa6337e545b6be5d3899641843 Mon Sep 17 00:00:00 2001 From: Marica Date: Thu, 21 May 2020 11:35:00 +0530 Subject: [PATCH 148/608] fix: Employee Advance Return not working (#21812) --- erpnext/hr/doctype/employee_advance/employee_advance.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.py b/erpnext/hr/doctype/employee_advance/employee_advance.py index 23e4992066b..db39eff0e4d 100644 --- a/erpnext/hr/doctype/employee_advance/employee_advance.py +++ b/erpnext/hr/doctype/employee_advance/employee_advance.py @@ -146,7 +146,7 @@ def create_return_through_additional_salary(doc): return additional_salary @frappe.whitelist() -def make_return_entry(employee_name, company, employee_advance_name, return_amount, mode_of_payment, advance_account): +def make_return_entry(employee, company, employee_advance_name, return_amount, advance_account, mode_of_payment=None): return_account = get_default_bank_cash_account(company, account_type='Cash', mode_of_payment = mode_of_payment) mode_of_payment_type = '' From 1a6dcd7f45a27925d82ac3681443ebdab8b2f023 Mon Sep 17 00:00:00 2001 From: Marica Date: Thu, 21 May 2020 12:07:43 +0530 Subject: [PATCH 149/608] fix: plc conversion rate set infinitely (#21820) --- erpnext/manufacturing/doctype/bom/bom.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js index 44da9cae355..898955e51b2 100644 --- a/erpnext/manufacturing/doctype/bom/bom.js +++ b/erpnext/manufacturing/doctype/bom/bom.js @@ -265,7 +265,7 @@ erpnext.bom.BomController = erpnext.TransactionController.extend({ plc_conversion_rate: function(doc) { if (!this.in_apply_price_list) { - this.apply_price_list(); + this.apply_price_list(null, true); } }, From 7afee887a841ed3a1a9b733d7363e5e0f88ad3ae Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 21 May 2020 13:11:48 +0530 Subject: [PATCH 150/608] fix: Fetch customer into Delivery Note from Pick List --- erpnext/stock/doctype/pick_list/pick_list.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index 231af1a0227..1f8d009f925 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -300,6 +300,7 @@ def create_delivery_note(source_name, target_doc=None): set_delivery_note_missing_values(delivery_note) delivery_note.pick_list = pick_list.name + delivery_note.customer = pick_list.customer if pick_list.customer else None return delivery_note From 90b4452a254627ca82573dafab5342d1c73e2aae Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 21 May 2020 14:03:29 +0530 Subject: [PATCH 151/608] fix: Supplier Invoice No not fetched in Import Supplier Invoice --- .../import_supplier_invoice/import_supplier_invoice.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py index 6784ea8a5bc..6b9567c0e57 100644 --- a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py +++ b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py @@ -58,7 +58,7 @@ class ImportSupplierInvoice(Document): "naming_series": self.invoice_series, "document_type": line.TipoDocumento.text, "bill_date": get_datetime_str(line.Data.text), - "invoice_no": line.Numero.text, + "bill_no": line.Numero.text, "total_discount": 0, "items": [], "buying_price_list": self.default_buying_price_list @@ -249,7 +249,7 @@ def create_supplier(supplier_group, args): return existing_supplier_name else: - + new_supplier = frappe.new_doc("Supplier") new_supplier.supplier_name = re.sub('&', '&', args.supplier) new_supplier.supplier_group = supplier_group From feaf5c7edb0b712574b48d3e314058668562642c Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 21 May 2020 14:08:25 +0530 Subject: [PATCH 152/608] fix: dict object has no attribute append --- .../exponential_smoothing_forecasting.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py b/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py index b5127f133ca..1b49df6af87 100644 --- a/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py +++ b/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py @@ -45,7 +45,7 @@ class ForecastingReport(ExponentialSmoothingForecast): def execute_report(self): self.prepare_periodical_data() self.forecast_future_data() - self.data = self.period_wise_data.values() + self.prepare_final_data() self.add_total() columns = self.get_columns() @@ -108,7 +108,17 @@ class ForecastingReport(ExponentialSmoothingForecast): """.format(doc=self.doctype, child_doc=self.child_doctype, date_field=date_field, cond=cond), tuple(input_data), as_dict=1) + def prepare_final_data(self): + self.data = [] + + if not self.period_wise_data: return + + for key in self.period_wise_data: + self.data.append(self.period_wise_data.get(key)) + def add_total(self): + if not self.data: return + total_row = { "item_code": _(frappe.bold("Total Quantity")) } From 5cef8db4db7b7ddf4d2f789cd10dcab13ce18987 Mon Sep 17 00:00:00 2001 From: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Date: Thu, 21 May 2020 14:32:24 +0530 Subject: [PATCH 153/608] fix(patch): Handle single value in patch (#21823) --- .../patches/v12_0/remove_duplicate_leave_ledger_entries.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py b/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py index 98a2fcf27ea..6353304d7aa 100644 --- a/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py +++ b/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py @@ -40,5 +40,5 @@ def get_duplicate_records(): def delete_duplicate_ledger_entries(duplicate_records_list): """Delete duplicate leave ledger entries.""" - if duplicate_records_list: - frappe.db.sql(''' DELETE FROM `tabLeave Ledger Entry` WHERE name in {0}'''.format(tuple(duplicate_records_list))) #nosec \ No newline at end of file + if not duplicate_records_list: return + frappe.db.sql('''DELETE FROM `tabLeave Ledger Entry` WHERE name in %s''', ((tuple(duplicate_records_list)), )) \ No newline at end of file From 7978907cc80ee106d0fbf95126f7b93ebdf85d4b Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Thu, 21 May 2020 18:10:13 +0530 Subject: [PATCH 154/608] feat: BOM template (#21262) Co-authored-by: Marica --- erpnext/controllers/queries.py | 9 +- erpnext/manufacturing/doctype/bom/bom.js | 189 +++++- erpnext/manufacturing/doctype/bom/bom.json | 14 +- erpnext/manufacturing/doctype/bom/bom.py | 215 ++++-- erpnext/manufacturing/doctype/bom/bom_list.js | 6 +- .../doctype/bom_item/bom_item.json | 17 +- .../doctype/work_order/work_order.js | 26 + .../doctype/work_order/work_order.py | 50 +- .../work_order_item/work_order_item.json | 632 ++++-------------- erpnext/stock/doctype/item/item.py | 11 + 10 files changed, 546 insertions(+), 623 deletions(-) diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index 5fbc460a973..f6a8d27d440 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -188,12 +188,6 @@ def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=Fals # scan description only if items are less than 50000 description_cond = 'or tabItem.description LIKE %(txt)s' - extra_cond = " and tabItem.has_variants=0" - if (filters and isinstance(filters, dict) - and filters.get("doctype") == "BOM"): - extra_cond = "" - del filters["doctype"] - return frappe.db.sql("""select tabItem.name, if(length(tabItem.item_name) > 40, concat(substr(tabItem.item_name, 1, 40), "..."), item_name) as item_name, @@ -204,10 +198,10 @@ def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=Fals from tabItem where tabItem.docstatus < 2 and tabItem.disabled=0 + and tabItem.has_variants=0 and (tabItem.end_of_life > %(today)s or ifnull(tabItem.end_of_life, '0000-00-00')='0000-00-00') and ({scond} or tabItem.item_code IN (select parent from `tabItem Barcode` where barcode LIKE %(txt)s) {description_cond}) - {extra_cond} {fcond} {mcond} order by if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999), @@ -218,7 +212,6 @@ def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=Fals key=searchfield, columns=columns, scond=searchfields, - extra_cond=extra_cond, fcond=get_filters_cond(doctype, filters, conditions).replace('%', '%%'), mcond=get_match_cond(doctype).replace('%', '%%'), description_cond = description_cond), diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js index 898955e51b2..47b42072412 100644 --- a/erpnext/manufacturing/doctype/bom/bom.js +++ b/erpnext/manufacturing/doctype/bom/bom.js @@ -29,10 +29,7 @@ frappe.ui.form.on("BOM", { frm.set_query("item", function() { return { - query: "erpnext.controllers.queries.item_query", - filters: { - "doctype": "BOM" - } + query: "erpnext.manufacturing.doctype.bom.bom.item_query" }; }); @@ -44,9 +41,12 @@ frappe.ui.form.on("BOM", { }; }); - frm.set_query("item_code", "items", function() { + frm.set_query("item_code", "items", function(doc) { return { - query: "erpnext.controllers.queries.item_query" + query: "erpnext.manufacturing.doctype.bom.bom.item_query", + filters: { + "item_code": doc.item + } }; }); @@ -96,6 +96,12 @@ frappe.ui.form.on("BOM", { frm.trigger("make_work_order"); }, __("Create")); + if (frm.doc.has_variants) { + frm.add_custom_button(__("Variant BOM"), function() { + frm.trigger("make_variant_bom"); + }, __("Create")); + } + if (frm.doc.inspection_required) { frm.add_custom_button(__("Quality Inspection"), function() { frm.trigger("make_quality_inspection"); @@ -124,7 +130,7 @@ frappe.ui.form.on("BOM", { } - if (frm.doc.__onload && frm.doc.__onload["has_variants"]) { + if (frm.doc.has_variants) { frm.set_intro(__('This is a Template BOM and will be used to make the work order for {0} of the item {1}', [ `variants`, @@ -138,9 +144,52 @@ frappe.ui.form.on("BOM", { }, make_work_order: function(frm) { + frm.events.setup_variant_prompt(frm, "Work Order", (frm, item, data, variant_items) => { + frappe.call({ + method: "erpnext.manufacturing.doctype.work_order.work_order.make_work_order", + args: { + bom_no: frm.doc.name, + item: item, + qty: data.qty || 0.0, + project: frm.doc.project, + variant_items: variant_items + }, + freeze: true, + callback: function(r) { + if(r.message) { + let doc = frappe.model.sync(r.message)[0]; + frappe.set_route("Form", doc.doctype, doc.name); + } + } + }); + }); + }, + + make_variant_bom: function(frm) { + frm.events.setup_variant_prompt(frm, "Variant BOM", (frm, item, data, variant_items) => { + frappe.call({ + method: "erpnext.manufacturing.doctype.bom.bom.make_variant_bom", + args: { + source_name: frm.doc.name, + bom_no: frm.doc.name, + item: item, + variant_items: variant_items + }, + freeze: true, + callback: function(r) { + if(r.message) { + let doc = frappe.model.sync(r.message)[0]; + frappe.set_route("Form", doc.doctype, doc.name); + } + } + }); + }, true); + }, + + setup_variant_prompt: function(frm, title, callback, skip_qty_field) { const fields = []; - if (frm.doc.__onload && frm.doc.__onload["has_variants"]) { + if (frm.doc.has_variants) { fields.push({ fieldtype: 'Link', label: __('Variant Item'), @@ -158,34 +207,106 @@ frappe.ui.form.on("BOM", { }); } - fields.push({ - fieldtype: 'Float', - label: __('Qty To Manufacture'), - fieldname: 'qty', - reqd: 1, - default: 1 + if (!skip_qty_field) { + fields.push({ + fieldtype: 'Float', + label: __('Qty To Manufacture'), + fieldname: 'qty', + reqd: 1, + default: 1 + }); + } + + var has_template_rm = frm.doc.items.filter(d => d.has_variants === 1) || []; + if (has_template_rm && has_template_rm.length > 0) { + fields.push({ + fieldname: "items", + fieldtype: "Table", + label: __("Raw Materials"), + fields: [ + { + fieldname: "item_code", + options: "Item", + label: __("Template Item"), + fieldtype: "Link", + in_list_view: 1, + reqd: 1, + }, + { + fieldname: "varint_item_code", + options: "Item", + label: __("Variant Item"), + fieldtype: "Link", + in_list_view: 1, + reqd: 1, + get_query: function(data) { + if (!data.item_code) { + frappe.throw(__("Select template item")); + } + + return { + query: "erpnext.controllers.queries.item_query", + filters: { + "variant_of": data.item_code + } + }; + } + }, + { + fieldname: "qty", + label: __("Quantity"), + fieldtype: "Float", + in_list_view: 1, + reqd: 1, + }, + { + fieldname: "source_warehouse", + label: __("Source Warehouse"), + fieldtype: "Link", + options: "Warehouse" + }, + { + fieldname: "operation", + label: __("Operation"), + fieldtype: "Data", + hidden: 1, + } + ], + in_place_edit: true, + data: [], + get_data: function () { + return []; + }, + }); + } + + let dialog = frappe.prompt(fields, data => { + let item = data.item || frm.doc.item; + let variant_items = data.items || []; + + variant_items.forEach(d => { + if (!d.varint_item_code) { + frappe.throw(__("Select variant item code for the template item {0}", [d.item_code])); + } + }) + + callback(frm, item, data, variant_items); + + }, __(title), __("Create")); + + has_template_rm.forEach(d => { + dialog.fields_dict.items.df.data.push({ + "item_code": d.item_code, + "varint_item_code": "", + "qty": d.qty, + "source_warehouse": d.source_warehouse, + "operation": d.operation + }); }); - frappe.prompt(fields, data => { - let item = data.item || frm.doc.item; - - frappe.call({ - method: "erpnext.manufacturing.doctype.work_order.work_order.make_work_order", - args: { - bom_no: frm.doc.name, - item: item, - qty: data.qty || 0.0, - project: frm.doc.project - }, - freeze: true, - callback: function(r) { - if(r.message) { - var doc = frappe.model.sync(r.message)[0]; - frappe.set_route("Form", doc.doctype, doc.name); - } - } - }); - }, __("Enter Value"), __("Create")); + if (has_template_rm) { + dialog.fields_dict.items.grid.refresh(); + } }, make_quality_inspection: function(frm) { diff --git a/erpnext/manufacturing/doctype/bom/bom.json b/erpnext/manufacturing/doctype/bom/bom.json index 4ce0ecf3f27..f551b91597f 100644 --- a/erpnext/manufacturing/doctype/bom/bom.json +++ b/erpnext/manufacturing/doctype/bom/bom.json @@ -53,6 +53,7 @@ "section_break_25", "description", "column_break_27", + "has_variants", "section_break0", "exploded_items", "website_section", @@ -498,6 +499,17 @@ "options": "Currency", "print_hide": 1, "read_only": 1 + }, + { + "default": "0", + "fetch_from": "item.has_variants", + "fieldname": "has_variants", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Has Variants", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 } ], "icon": "fa fa-sitemap", @@ -505,7 +517,7 @@ "image_field": "image", "is_submittable": 1, "links": [], - "modified": "2020-05-05 14:29:32.634952", + "modified": "2020-05-21 12:29:32.634952", "modified_by": "Administrator", "module": "Manufacturing", "name": "BOM", diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 6ac653e37a4..1bdac5731ef 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -3,13 +3,16 @@ from __future__ import unicode_literals import frappe, erpnext -from frappe.utils import cint, cstr, flt +from frappe.utils import cint, cstr, flt, today from frappe import _ from erpnext.setup.utils import get_exchange_rate from frappe.website.website_generator import WebsiteGenerator from erpnext.stock.get_item_details import get_conversion_factor from erpnext.stock.get_item_details import get_price_list_rate from frappe.core.doctype.version.version import get_diff +from erpnext.controllers.queries import get_match_cond +from erpnext.stock.doctype.item.item import get_item_details +from frappe.model.mapper import get_mapped_doc import functools @@ -59,11 +62,6 @@ class BOM(WebsiteGenerator): self.name = name - def onload(self): - super(BOM, self).onload() - if self.get("item") and cint(frappe.db.get_value("Item", self.item, "has_variants")): - self.set_onload("has_variants", True) - def validate(self): self.route = frappe.scrub(self.name).replace('_', '-') self.clear_operations() @@ -103,9 +101,7 @@ class BOM(WebsiteGenerator): self.manage_default_bom() def get_item_det(self, item_code): - item = frappe.db.sql("""select name, item_name, docstatus, description, image, - is_sub_contracted_item, stock_uom, default_bom, last_purchase_rate, include_item_in_manufacturing - from `tabItem` where name=%s""", item_code, as_dict = 1) + item = get_item_details(item_code) if not item: frappe.throw(_("Item: {0} does not exist in the system").format(item_code)) @@ -150,10 +146,10 @@ class BOM(WebsiteGenerator): item = self.get_item_det(args['item_code']) - args['bom_no'] = args['bom_no'] or item and cstr(item[0]['default_bom']) or '' + args['bom_no'] = args['bom_no'] or item and cstr(item['default_bom']) or '' args['transfer_for_manufacture'] = (cstr(args.get('include_item_in_manufacturing', '')) or - item and item[0].include_item_in_manufacturing or 0) - args.update(item[0]) + item and item.include_item_in_manufacturing or 0) + args.update(item) rate = self.get_rm_rate(args) ret_item = { @@ -185,40 +181,14 @@ class BOM(WebsiteGenerator): self.rm_cost_as_per = "Valuation Rate" if arg.get('scrap_items'): - rate = self.get_valuation_rate(arg) + rate = get_valuation_rate(arg) elif arg: #Customer Provided parts will have zero rate if not frappe.db.get_value('Item', arg["item_code"], 'is_customer_provided_item'): if arg.get('bom_no') and self.set_rate_of_sub_assembly_item_based_on_bom: rate = flt(self.get_bom_unitcost(arg['bom_no'])) * (arg.get("conversion_factor") or 1) else: - if self.rm_cost_as_per == 'Valuation Rate': - rate = self.get_valuation_rate(arg) * (arg.get("conversion_factor") or 1) - elif self.rm_cost_as_per == 'Last Purchase Rate': - rate = flt(arg.get('last_purchase_rate') \ - or frappe.db.get_value("Item", arg['item_code'], "last_purchase_rate")) \ - * (arg.get("conversion_factor") or 1) - elif self.rm_cost_as_per == "Price List": - if not self.buying_price_list: - frappe.throw(_("Please select Price List")) - args = frappe._dict({ - "doctype": "BOM", - "price_list": self.buying_price_list, - "qty": arg.get("qty") or 1, - "uom": arg.get("uom") or arg.get("stock_uom"), - "stock_uom": arg.get("stock_uom"), - "transaction_type": "buying", - "company": self.company, - "currency": self.currency, - "conversion_rate": 1, # Passed conversion rate as 1 purposefully, as conversion rate is applied at the end of the function - "conversion_factor": arg.get("conversion_factor") or 1, - "plc_conversion_rate": 1, - "ignore_party": True - }) - item_doc = frappe.get_doc("Item", arg.get("item_code")) - out = frappe._dict() - get_price_list_rate(args, item_doc, out) - rate = out.price_list_rate + rate = get_bom_item_rate(arg, self) if not rate: if self.rm_cost_as_per == "Price List": @@ -286,31 +256,6 @@ class BOM(WebsiteGenerator): where is_active = 1 and name = %s""", bom_no, as_dict=1) return bom and bom[0]['unit_cost'] or 0 - def get_valuation_rate(self, args): - """ Get weighted average of valuation rate from all warehouses """ - - total_qty, total_value, valuation_rate = 0.0, 0.0, 0.0 - for d in frappe.db.sql("""select actual_qty, stock_value from `tabBin` - where item_code=%s""", args['item_code'], as_dict=1): - total_qty += flt(d.actual_qty) - total_value += flt(d.stock_value) - - if total_qty: - valuation_rate = total_value / total_qty - - if valuation_rate <= 0: - last_valuation_rate = frappe.db.sql("""select valuation_rate - from `tabStock Ledger Entry` - where item_code = %s and valuation_rate > 0 - order by posting_date desc, posting_time desc, creation desc limit 1""", args['item_code']) - - valuation_rate = flt(last_valuation_rate[0][0]) if last_valuation_rate else 0 - - if not valuation_rate: - valuation_rate = frappe.db.get_value("Item", args['item_code'], "valuation_rate") - - return flt(valuation_rate) - def manage_default_bom(self): """ Uncheck others if current one is selected as default or check the current one as default if it the only bom for the selected item, @@ -624,6 +569,62 @@ class BOM(WebsiteGenerator): if not d.batch_size or d.batch_size <= 0: d.batch_size = 1 +def get_bom_item_rate(args, bom_doc): + if bom_doc.rm_cost_as_per == 'Valuation Rate': + rate = get_valuation_rate(args) * (args.get("conversion_factor") or 1) + elif bom_doc.rm_cost_as_per == 'Last Purchase Rate': + rate = ( flt(args.get('last_purchase_rate')) \ + or frappe.db.get_value("Item", args['item_code'], "last_purchase_rate")) \ + * (args.get("conversion_factor") or 1) + elif bom_doc.rm_cost_as_per == "Price List": + if not bom_doc.buying_price_list: + frappe.throw(_("Please select Price List")) + bom_args = frappe._dict({ + "doctype": "BOM", + "price_list": bom_doc.buying_price_list, + "qty": args.get("qty") or 1, + "uom": args.get("uom") or args.get("stock_uom"), + "stock_uom": args.get("stock_uom"), + "transaction_type": "buying", + "company": bom_doc.company, + "currency": bom_doc.currency, + "conversion_rate": 1, # Passed conversion rate as 1 purposefully, as conversion rate is applied at the end of the function + "conversion_factor": args.get("conversion_factor") or 1, + "plc_conversion_rate": 1, + "ignore_party": True + }) + item_doc = frappe.get_cached_doc("Item", args.get("item_code")) + out = frappe._dict() + get_price_list_rate(bom_args, item_doc, out) + rate = out.price_list_rate + + return rate + +def get_valuation_rate(args): + """ Get weighted average of valuation rate from all warehouses """ + + total_qty, total_value, valuation_rate = 0.0, 0.0, 0.0 + for d in frappe.db.sql("""select actual_qty, stock_value from `tabBin` + where item_code=%s""", args['item_code'], as_dict=1): + total_qty += flt(d.actual_qty) + total_value += flt(d.stock_value) + + if total_qty: + valuation_rate = total_value / total_qty + + if valuation_rate <= 0: + last_valuation_rate = frappe.db.sql("""select valuation_rate + from `tabStock Ledger Entry` + where item_code = %s and valuation_rate > 0 + order by posting_date desc, posting_time desc, creation desc limit 1""", args['item_code']) + + valuation_rate = flt(last_valuation_rate[0][0]) if last_valuation_rate else 0 + + if not valuation_rate: + valuation_rate = frappe.db.get_value("Item", args['item_code'], "valuation_rate") + + return flt(valuation_rate) + def get_list_context(context): context.title = _("Bill of Materials") # context.introduction = _('Boms') @@ -639,6 +640,8 @@ def get_bom_items_as_dict(bom, company, qty=1, fetch_exploded=1, fetch_scrap_ite sum(bom_item.{qty_field}/ifnull(bom.quantity, 1)) * %(qty)s as qty, item.image, bom.project, + bom_item.rate, + bom_item.amount, item.stock_uom, item.item_group, item.allow_alternative_item, @@ -655,6 +658,7 @@ def get_bom_items_as_dict(bom, company, qty=1, fetch_exploded=1, fetch_scrap_ite where bom_item.docstatus < 2 and bom.name = %(bom)s + and ifnull(item.has_variants, 0) = 0 and item.is_stock_item in (1, {is_stock_item}) {where_conditions} group by item_code, stock_uom @@ -897,3 +901,84 @@ def get_bom_diff(bom1, bom2): out.removed.append([df.fieldname, d.as_dict()]) return out + +def item_query(doctype, txt, searchfield, start, page_len, filters): + meta = frappe.get_meta("Item", cached=True) + searchfields = meta.get_search_fields() + + order_by = "idx desc, name, item_name" + + fields = ["name", "item_group", "item_name", "description"] + fields.extend([field for field in searchfields + if not field in ["name", "item_group", "description"]]) + + searchfields = searchfields + [field for field in [searchfield or "name", "item_code", "item_group", "item_name"] + if not field in searchfields] + + query_filters = { + "disabled": 0, + "ifnull(end_of_life, '5050-50-50')": (">", today()) + } + + or_cond_filters = {} + if txt: + for s_field in searchfields: + or_cond_filters[s_field] = ("like", "%{0}%".format(txt)) + + barcodes = frappe.get_all("Item Barcode", + fields=["distinct parent as item_code"], + filters = {"barcode": ("like", "%{0}%".format(txt))}) + + barcodes = [d.item_code for d in barcodes] + if barcodes: + or_cond_filters["name"] = ("in", barcodes) + + for cond in get_match_cond(doctype, as_condition=False): + for key, value in cond.items(): + if key == doctype: + key = "name" + + query_filters[key] = ("in", value) + + if filters and filters.get("item_code"): + has_variants = frappe.get_cached_value("Item", filters.get("item_code"), "has_variants") + if not has_variants: + query_filters["has_variants"] = 0 + + return frappe.get_all("Item", + fields = fields, filters=query_filters, + or_filters = or_cond_filters, order_by=order_by, + limit_start=start, limit_page_length=page_len, as_list=1) + +@frappe.whitelist() +def make_variant_bom(source_name, bom_no, item, variant_items, target_doc=None): + from erpnext.manufacturing.doctype.work_order.work_order import add_variant_item + + def postprocess(source, doc): + doc.item = item + doc.quantity = 1 + + item_data = get_item_details(item) + doc.update({ + "item_name": item_data.item_name, + "description": item_data.description, + "uom": item_data.stock_uom, + "allow_alternative_item": item_data.allow_alternative_item + }) + + add_variant_item(variant_items, doc, source_name) + + doc = get_mapped_doc('BOM', source_name, { + 'BOM': { + 'doctype': 'BOM', + 'validation': { + 'docstatus': ['=', 1] + } + }, + 'BOM Item': { + 'doctype': 'BOM Item', + 'condition': lambda doc: doc.has_variants == 0 + }, + }, target_doc, postprocess) + + return doc \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/bom/bom_list.js b/erpnext/manufacturing/doctype/bom/bom_list.js index 2b06ed72eda..94cb466bd8a 100644 --- a/erpnext/manufacturing/doctype/bom/bom_list.js +++ b/erpnext/manufacturing/doctype/bom/bom_list.js @@ -1,7 +1,9 @@ frappe.listview_settings['BOM'] = { - add_fields: ["is_active", "is_default", "total_cost"], + add_fields: ["is_active", "is_default", "total_cost", "has_variants"], get_indicator: function(doc) { - if(doc.is_default) { + if(doc.is_active && doc.has_variants) { + return [__("Template"), "orange", "has_variants,=,Yes"]; + } else if(doc.is_default) { return [__("Default"), "green", "is_default,=,Yes"]; } else if(doc.is_active) { return [__("Active"), "blue", "is_active,=,Yes"]; diff --git a/erpnext/manufacturing/doctype/bom_item/bom_item.json b/erpnext/manufacturing/doctype/bom_item/bom_item.json index f094be4c647..e34be61bc75 100644 --- a/erpnext/manufacturing/doctype/bom_item/bom_item.json +++ b/erpnext/manufacturing/doctype/bom_item/bom_item.json @@ -1,8 +1,10 @@ { + "actions": [], "creation": "2013-02-22 01:27:49", "doctype": "DocType", "document_type": "Setup", "editable_grid": 1, + "engine": "InnoDB", "field_order": [ "item_code", "item_name", @@ -33,6 +35,7 @@ "scrap", "qty_consumed_per_unit", "section_break_27", + "has_variants", "include_item_in_manufacturing", "original_item" ], @@ -57,6 +60,7 @@ "label": "Item Name" }, { + "depends_on": "eval:parent.with_operations == 1", "fieldname": "operation", "fieldtype": "Link", "label": "Item operation", @@ -258,11 +262,22 @@ "label": "Original Item", "options": "Item", "read_only": 1 + }, + { + "default": "0", + "fetch_from": "item_code.has_variants", + "fieldname": "has_variants", + "fieldtype": "Check", + "label": "Has Variants", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 } ], "idx": 1, "istable": 1, - "modified": "2019-11-22 11:38:52.087303", + "links": [], + "modified": "2020-04-09 14:30:26.535546", "modified_by": "Administrator", "module": "Manufacturing", "name": "BOM Item", diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js index c1255719606..a244f582c42 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.js +++ b/erpnext/manufacturing/doctype/work_order/work_order.js @@ -449,6 +449,32 @@ frappe.ui.form.on("Work Order Item", { } }); } + }, + + item_code: function(frm, cdt, cdn) { + let row = locals[cdt][cdn]; + + if (row.item_code) { + frappe.call({ + method: "erpnext.stock.doctype.item.item.get_item_details", + args: { + item_code: row.item_code, + company: frm.doc.company + }, + callback: function(r) { + if (r.message) { + frappe.model.set_value(cdt, cdn, { + "required_qty": 1, + "item_name": r.message.item_name, + "description": r.message.description, + "source_warehouse": r.message.default_warehouse, + "allow_alternative_item": r.message.allow_alternative_item, + "include_item_in_manufacturing": r.message.include_item_in_manufacturing + }); + } + } + }); + } } }); diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index c2789559b07..e2233a3e2f6 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -8,9 +8,9 @@ import math from frappe import _ from frappe.utils import flt, get_datetime, getdate, date_diff, cint, nowdate, get_link_to_form, time_diff_in_hours from frappe.model.document import Document -from erpnext.manufacturing.doctype.bom.bom import validate_bom_no, get_bom_items_as_dict +from erpnext.manufacturing.doctype.bom.bom import validate_bom_no, get_bom_items_as_dict, get_bom_item_rate from dateutil.relativedelta import relativedelta -from erpnext.stock.doctype.item.item import validate_end_of_life +from erpnext.stock.doctype.item.item import validate_end_of_life, get_item_defaults from erpnext.manufacturing.doctype.workstation.workstation import WorkstationHolidayError from erpnext.projects.doctype.timesheet.timesheet import OverlapError from erpnext.manufacturing.doctype.manufacturing_settings.manufacturing_settings import get_mins_between_operations @@ -541,6 +541,8 @@ class WorkOrder(Document): # For instance in BOM Explosion Item child table, the items coming from sub assembly items for item in sorted(item_dict.values(), key=lambda d: d['idx'] or 9999): self.append('required_items', { + 'rate': item.rate, + 'amount': item.amount, 'operation': item.operation, 'item_code': item.item_code, 'item_name': item.item_name, @@ -637,9 +639,10 @@ def get_bom_operations(doctype, txt, searchfield, start, page_len, filters): filters = filters, fields = ['operation'], as_list=1) @frappe.whitelist() -def get_item_details(item, project = None): +def get_item_details(item, project = None, skip_bom_info=False): res = frappe.db.sql(""" - select stock_uom, description + select stock_uom, description, item_name, allow_alternative_item, + include_item_in_manufacturing from `tabItem` where disabled=0 and (end_of_life is null or end_of_life='0000-00-00' or end_of_life > %s) @@ -650,6 +653,7 @@ def get_item_details(item, project = None): return {} res = res[0] + if skip_bom_info: return res filters = {"item": item, "is_default": 1} @@ -681,7 +685,7 @@ def get_item_details(item, project = None): return res @frappe.whitelist() -def make_work_order(bom_no, item, qty=0, project=None): +def make_work_order(bom_no, item, qty=0, project=None, variant_items=None): if not frappe.has_permission("Work Order", "write"): frappe.throw(_("Not permitted"), frappe.PermissionError) @@ -696,8 +700,44 @@ def make_work_order(bom_no, item, qty=0, project=None): wo_doc.qty = flt(qty) wo_doc.get_items_and_operations_from_bom() + if variant_items: + add_variant_item(variant_items, wo_doc, bom_no, "required_items") + return wo_doc +def add_variant_item(variant_items, wo_doc, bom_no, table_name="items"): + if isinstance(variant_items, string_types): + variant_items = json.loads(variant_items) + + for item in variant_items: + args = frappe._dict({ + "item_code": item.get("varint_item_code"), + "required_qty": item.get("qty"), + "qty": item.get("qty"), # for bom + "source_warehouse": item.get("source_warehouse"), + "operation": item.get("operation") + }) + + bom_doc = frappe.get_cached_doc("BOM", bom_no) + item_data = get_item_details(args.item_code, skip_bom_info=True) + args.update(item_data) + + args["rate"] = get_bom_item_rate({ + "item_code": args.get("item_code"), + "qty": args.get("required_qty"), + "uom": args.get("stock_uom"), + "stock_uom": args.get("stock_uom"), + "conversion_factor": 1 + }, bom_doc) + + if not args.source_warehouse: + args["source_warehouse"] = get_item_defaults(item.get("varint_item_code"), + wo_doc.company).default_warehouse + + args["amount"] = flt(args.get("required_qty")) * flt(args.get("rate")) + args["uom"] = item_data.stock_uom + wo_doc.append(table_name, args) + @frappe.whitelist() def check_if_scrap_warehouse_mandatory(bom_no): res = {"set_scrap_wh_mandatory": False } diff --git a/erpnext/manufacturing/doctype/work_order_item/work_order_item.json b/erpnext/manufacturing/doctype/work_order_item/work_order_item.json index 44421626361..3acf5727d19 100644 --- a/erpnext/manufacturing/doctype/work_order_item/work_order_item.json +++ b/erpnext/manufacturing/doctype/work_order_item/work_order_item.json @@ -1,526 +1,144 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2016-04-18 07:38:26.314642", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "creation": "2016-04-18 07:38:26.314642", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "operation", + "item_code", + "source_warehouse", + "column_break_3", + "item_name", + "description", + "allow_alternative_item", + "include_item_in_manufacturing", + "qty_section", + "required_qty", + "rate", + "amount", + "column_break_11", + "transferred_qty", + "consumed_qty", + "available_qty_at_source_warehouse", + "available_qty_at_wip_warehouse" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "operation", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Operation", - "length": 0, - "no_copy": 0, - "options": "Operation", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "operation", + "fieldtype": "Link", + "label": "Operation", + "options": "Operation" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "item_code", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Item Code", - "length": 0, - "no_copy": 0, - "options": "Item", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "item_code", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Item Code", + "options": "Item" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "source_warehouse", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Source Warehouse", - "length": 0, - "no_copy": 0, - "options": "Warehouse", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "source_warehouse", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "in_list_view": 1, + "label": "Source Warehouse", + "options": "Warehouse" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "item_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Item Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "item_name", + "fieldtype": "Data", + "label": "Item Name", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "description", - "fieldtype": "Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Description", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "description", + "fieldtype": "Text", + "label": "Description", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "qty_section", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Qty", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "qty_section", + "fieldtype": "Section Break", + "label": "Qty" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "required_qty", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Required Qty", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "required_qty", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Required Qty" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:!parent.skip_transfer", - "fieldname": "transferred_qty", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Transferred Qty", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:!parent.skip_transfer", + "fieldname": "transferred_qty", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Transferred Qty", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "allow_alternative_item", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Allow Alternative Item", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "allow_alternative_item", + "fieldtype": "Check", + "label": "Allow Alternative Item" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "include_item_in_manufacturing", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Include Item In Manufacturing", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "include_item_in_manufacturing", + "fieldtype": "Check", + "label": "Include Item In Manufacturing" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_11", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_11", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:!parent.skip_transfer", - "fieldname": "consumed_qty", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Consumed Qty", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:!parent.skip_transfer", + "fieldname": "consumed_qty", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Consumed Qty", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "available_qty_at_source_warehouse", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Available Qty at Source Warehouse", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "available_qty_at_source_warehouse", + "fieldtype": "Float", + "label": "Available Qty at Source Warehouse", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "available_qty_at_wip_warehouse", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Available Qty at WIP Warehouse", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "available_qty_at_wip_warehouse", + "fieldtype": "Float", + "label": "Available Qty at WIP Warehouse", + "read_only": 1 + }, + { + "fieldname": "rate", + "fieldtype": "Currency", + "label": "Rate", + "read_only": 1 + }, + { + "fieldname": "amount", + "fieldtype": "Currency", + "label": "Amount", + "read_only": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-11-20 19:04:38.508839", - "modified_by": "Administrator", - "module": "Manufacturing", - "name": "Work Order Item", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + ], + "istable": 1, + "links": [], + "modified": "2020-04-13 18:46:32.966416", + "modified_by": "Administrator", + "module": "Manufacturing", + "name": "Work Order Item", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index 7a1c1279eae..3436a5d0136 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -1143,6 +1143,17 @@ def set_item_default(item_code, company, fieldname, value): d.db_insert() item.clear_cache() +@frappe.whitelist() +def get_item_details(item_code, company=None): + out = frappe._dict() + if company: + out = get_item_defaults(item_code, company) or frappe._dict() + + doc = frappe.get_cached_doc("Item", item_code) + out.update(doc.as_dict()) + + return out + @frappe.whitelist() def get_uom_conv_factor(uom, stock_uom): uoms = [uom, stock_uom] From ce8f8f4ed7909decaa01fa61833f69d260925576 Mon Sep 17 00:00:00 2001 From: Prssanna Desai Date: Thu, 21 May 2020 18:35:03 +0530 Subject: [PATCH 155/608] fix: hide delete company transacations button if not system manager (#21839) --- erpnext/setup/doctype/company/company.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js index 0fbe49eab76..875904fe6fe 100644 --- a/erpnext/setup/doctype/company/company.js +++ b/erpnext/setup/doctype/company/company.js @@ -107,6 +107,9 @@ frappe.ui.form.on("Company", { erpnext.company.set_chart_of_accounts_options(frm.doc); + if (!frappe.user.has_role('System Manager')) { + frm.get_field("delete_company_transactions").hide(); + } }, make_default_tax_template: function(frm) { @@ -134,7 +137,7 @@ frappe.ui.form.on("Company", { var d = frappe.prompt({ fieldtype:"Data", fieldname: "company_name", - label: __("Please re-type company name to confirm"), + label: __("Please enter the company name to confirm"), reqd: 1, description: __("Please make sure you really want to delete all the transactions for this company. Your master data will remain as it is. This action cannot be undone.") }, From 82523a5487c17382c1fe854ea041e9ca21aacdc6 Mon Sep 17 00:00:00 2001 From: sahil28297 <37302950+sahil28297@users.noreply.github.com> Date: Thu, 21 May 2020 18:42:10 +0530 Subject: [PATCH 156/608] fix(set_serial_no_status): auto commit on many writes (#21845) --- erpnext/patches/v12_0/set_serial_no_status.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/erpnext/patches/v12_0/set_serial_no_status.py b/erpnext/patches/v12_0/set_serial_no_status.py index 4ec84ef0f9e..abba37d48ef 100644 --- a/erpnext/patches/v12_0/set_serial_no_status.py +++ b/erpnext/patches/v12_0/set_serial_no_status.py @@ -5,8 +5,12 @@ from frappe.utils import getdate, nowdate def execute(): frappe.reload_doc('stock', 'doctype', 'serial_no') - for serial_no in frappe.db.sql("""select name, delivery_document_type, warranty_expiry_date from `tabSerial No` - where (status is NULL OR status='')""", as_dict = 1): + serial_no_list = frappe.db.sql("""select name, delivery_document_type, warranty_expiry_date from `tabSerial No` + where (status is NULL OR status='')""", as_dict = 1) + if len(serial_no_list) > 20000: + frappe.db.auto_commit_on_many_writes = True + + for serial_no in serial_no_list: if serial_no.get("delivery_document_type"): status = "Delivered" elif serial_no.get("warranty_expiry_date") and getdate(serial_no.get("warranty_expiry_date")) <= getdate(nowdate()): @@ -14,4 +18,7 @@ def execute(): else: status = "Active" - frappe.db.set_value("Serial No", serial_no.get("name"), "status", status) \ No newline at end of file + frappe.db.set_value("Serial No", serial_no.get("name"), "status", status) + + if frappe.db.auto_commit_on_many_writes: + frappe.db.auto_commit_on_many_writes = False From c07265ea3fcecb6ef04368fe2ece92c5163e47e4 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Thu, 21 May 2020 18:47:39 +0530 Subject: [PATCH 157/608] fix: convert goals point to flt (#21844) --- .../hr/doctype/appraisal_template/appraisal_template.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/hr/doctype/appraisal_template/appraisal_template.py b/erpnext/hr/doctype/appraisal_template/appraisal_template.py index e5d3c42e1bd..d0dfad4be31 100644 --- a/erpnext/hr/doctype/appraisal_template/appraisal_template.py +++ b/erpnext/hr/doctype/appraisal_template/appraisal_template.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals import frappe -from frappe.utils import cint +from frappe.utils import cint, flt from frappe import _ from frappe.model.document import Document @@ -11,11 +11,11 @@ from frappe.model.document import Document class AppraisalTemplate(Document): def validate(self): self.check_total_points() - - def check_total_points(self): + + def check_total_points(self): total_points = 0 for d in self.get("goals"): - total_points += int(d.per_weightage or 0) + total_points += flt(d.per_weightage) if cint(total_points) != 100: frappe.throw(_("Sum of points for all goals should be 100. It is {0}").format(total_points)) From d7d4bed0b13f8a84ccf2d34eab1fa4b6351b1ae6 Mon Sep 17 00:00:00 2001 From: Anupam K Date: Fri, 22 May 2020 01:10:55 +0530 Subject: [PATCH 158/608] adding report card in education desk --- erpnext/education/desk_page/education/education.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/erpnext/education/desk_page/education/education.json b/erpnext/education/desk_page/education/education.json index fc2697f0d75..b341ec4b99b 100644 --- a/erpnext/education/desk_page/education/education.json +++ b/erpnext/education/desk_page/education/education.json @@ -64,6 +64,11 @@ "hidden": 0, "label": "Assessment Reports", "links": "[\n {\n \"dependencies\": [\n \"Assessment Result\"\n ],\n \"doctype\": \"Assessment Result\",\n \"is_query_report\": true,\n \"label\": \"Course wise Assessment Report\",\n \"name\": \"Course wise Assessment Report\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Assessment Result\"\n ],\n \"doctype\": \"Assessment Result\",\n \"is_query_report\": true,\n \"label\": \"Final Assessment Grades\",\n \"name\": \"Final Assessment Grades\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Assessment Plan\"\n ],\n \"doctype\": \"Assessment Plan\",\n \"is_query_report\": true,\n \"label\": \"Assessment Plan Status\",\n \"name\": \"Assessment Plan Status\",\n \"type\": \"report\"\n },\n {\n \"label\": \"Student Report Generation Tool\",\n \"name\": \"Student Report Generation Tool\",\n \"type\": \"doctype\"\n }\n]" + }, + { + "hidden": 0, + "label": "Reports", + "links": "[\n {\n \"dependencies\": [\n \"Fees\"\n ],\n \"doctype\": \"Fees\",\n \"is_query_report\": true,\n \"label\": \"Student Fee Collection\",\n \"name\": \"Student Fee Collection\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Student Attendance\"\n ],\n \"doctype\": \"Student Attendance\",\n \"is_query_report\": true,\n \"label\": \"Student Monthly Attendance Sheet\",\n \"name\": \"Student Monthly Attendance Sheet\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Student Attendance\"\n ],\n \"doctype\": \"Student Attendance\",\n \"is_query_report\": true,\n \"label\": \"Absent Student Report\",\n \"name\": \"Absent Student Report\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Program Enrollment\"\n ],\n \"doctype\": \"Program Enrollment\",\n \"is_query_report\": true,\n \"label\": \"Student and Guardian Contact Details\",\n \"name\": \"Student and Guardian Contact Details\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Student Attendance\"\n ],\n \"doctype\": \"Student Attendance\",\n \"is_query_report\": true,\n \"label\": \"Student Batch-Wise Attendance\",\n \"name\": \"Student Batch-Wise Attendance\",\n \"type\": \"report\"\n }\n]" } ], "category": "Domains", @@ -77,7 +82,7 @@ "idx": 0, "is_standard": 1, "label": "Education", - "modified": "2020-04-01 11:28:51.011309", + "modified": "2020-05-22 01:09:13.058482", "modified_by": "Administrator", "module": "Education", "name": "Education", From dd3e52184c5e41fba7e0e8337e580118d46de31f Mon Sep 17 00:00:00 2001 From: Marica Date: Fri, 22 May 2020 10:48:35 +0530 Subject: [PATCH 159/608] fix: Added Inactive serial no status (#21848) --- erpnext/patches.txt | 2 +- erpnext/patches/v12_0/set_serial_no_status.py | 4 +++- erpnext/stock/doctype/serial_no/serial_no.json | 4 ++-- erpnext/stock/doctype/serial_no/serial_no.py | 2 ++ erpnext/stock/doctype/serial_no/serial_no_list.js | 2 ++ 5 files changed, 10 insertions(+), 4 deletions(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index af7cb8eeda9..2435b1c5174 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -684,7 +684,7 @@ execute:frappe.delete_doc_if_exists("Page", "appointment-analytic") execute:frappe.rename_doc("Desk Page", "Getting Started", "Home", force=True) erpnext.patches.v12_0.unset_customer_supplier_based_on_type_of_item_price erpnext.patches.v12_0.set_valid_till_date_in_supplier_quotation -erpnext.patches.v12_0.set_serial_no_status +erpnext.patches.v12_0.set_serial_no_status #2020-05-21 erpnext.patches.v12_0.update_price_list_currency_in_bom execute:frappe.delete_doc_if_exists('Dashboard', 'Accounts') erpnext.patches.v13_0.update_actual_start_and_end_date_in_wo diff --git a/erpnext/patches/v12_0/set_serial_no_status.py b/erpnext/patches/v12_0/set_serial_no_status.py index abba37d48ef..3b5f5ef3407 100644 --- a/erpnext/patches/v12_0/set_serial_no_status.py +++ b/erpnext/patches/v12_0/set_serial_no_status.py @@ -5,7 +5,7 @@ from frappe.utils import getdate, nowdate def execute(): frappe.reload_doc('stock', 'doctype', 'serial_no') - serial_no_list = frappe.db.sql("""select name, delivery_document_type, warranty_expiry_date from `tabSerial No` + serial_no_list = frappe.db.sql("""select name, delivery_document_type, warranty_expiry_date, warehouse from `tabSerial No` where (status is NULL OR status='')""", as_dict = 1) if len(serial_no_list) > 20000: frappe.db.auto_commit_on_many_writes = True @@ -15,6 +15,8 @@ def execute(): status = "Delivered" elif serial_no.get("warranty_expiry_date") and getdate(serial_no.get("warranty_expiry_date")) <= getdate(nowdate()): status = "Expired" + elif not serial_no.get("warehouse"): + status = "Inactive" else: status = "Active" diff --git a/erpnext/stock/doctype/serial_no/serial_no.json b/erpnext/stock/doctype/serial_no/serial_no.json index 731a7302797..d9f8b627545 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.json +++ b/erpnext/stock/doctype/serial_no/serial_no.json @@ -420,14 +420,14 @@ "fieldtype": "Select", "in_standard_filter": 1, "label": "Status", - "options": "\nActive\nDelivered\nExpired", + "options": "\nActive\nInactive\nDelivered\nExpired", "read_only": 1 } ], "icon": "fa fa-barcode", "idx": 1, "links": [], - "modified": "2020-04-08 13:29:58.517772", + "modified": "2020-05-21 19:29:58.517772", "modified_by": "Administrator", "module": "Stock", "name": "Serial No", diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py index 914eea379a8..f3514c7385d 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.py +++ b/erpnext/stock/doctype/serial_no/serial_no.py @@ -42,6 +42,8 @@ class SerialNo(StockController): self.status = "Delivered" elif self.warranty_expiry_date and getdate(self.warranty_expiry_date) <= getdate(nowdate()): self.status = "Expired" + elif not self.warehouse: + self.status = "Inactive" else: self.status = "Active" diff --git a/erpnext/stock/doctype/serial_no/serial_no_list.js b/erpnext/stock/doctype/serial_no/serial_no_list.js index 651f790583d..7526d1d8a5c 100644 --- a/erpnext/stock/doctype/serial_no/serial_no_list.js +++ b/erpnext/stock/doctype/serial_no/serial_no_list.js @@ -5,6 +5,8 @@ frappe.listview_settings['Serial No'] = { return [__("Delivered"), "green", "delivery_document_type,is,set"]; } else if (doc.warranty_expiry_date && frappe.datetime.get_diff(doc.warranty_expiry_date, frappe.datetime.nowdate()) <= 0) { return [__("Expired"), "red", "warranty_expiry_date,not in,|warranty_expiry_date,<=,Today|delivery_document_type,is,not set"]; + } else if (!doc.warehouse) { + return [__("Inactive"), "grey", "warehouse,is,not set"]; } else { return [__("Active"), "green", "delivery_document_type,is,not set"]; } From baef43977be81a9db11480cdbce7a3514093e108 Mon Sep 17 00:00:00 2001 From: Chinmay Pai Date: Fri, 22 May 2020 10:50:13 +0530 Subject: [PATCH 160/608] fix: set customer and supplier details using sql (#21846) * fix: set customer and supplier details using sql instead of slowing down the query with get_doc and save() we can just use sql to update the required values for customer and supplier Signed-off-by: Chinmay D. Pai * chore: remove extra quote Co-authored-by: Himanshu * fix: update sql query to include tabPrice List Signed-off-by: Chinmay D. Pai Co-authored-by: Himanshu --- ...er_supplier_based_on_type_of_item_price.py | 36 +++++++++++++------ 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/erpnext/patches/v12_0/unset_customer_supplier_based_on_type_of_item_price.py b/erpnext/patches/v12_0/unset_customer_supplier_based_on_type_of_item_price.py index 60aec05466a..b8efb210a03 100644 --- a/erpnext/patches/v12_0/unset_customer_supplier_based_on_type_of_item_price.py +++ b/erpnext/patches/v12_0/unset_customer_supplier_based_on_type_of_item_price.py @@ -1,15 +1,29 @@ from __future__ import unicode_literals import frappe + def execute(): - invalid_selling_item_price = frappe.db.sql( - """SELECT name FROM `tabItem Price` WHERE selling = 1 and buying = 0 and (supplier IS NOT NULL or supplier = '')""" - ) - invalid_buying_item_price = frappe.db.sql( - """SELECT name FROM `tabItem Price` WHERE selling = 0 and buying = 1 and (customer IS NOT NULL or customer = '')""" - ) - docs_to_modify = invalid_buying_item_price + invalid_selling_item_price - for d in docs_to_modify: - # saving the doc will auto reset invalid customer/supplier field - doc = frappe.get_doc("Item Price", d[0]) - doc.save() \ No newline at end of file + """ + set proper customer and supplier details for item price + based on selling and buying values + """ + + # update for selling + frappe.db.sql( + """UPDATE `tabItem Price` ip, `tabPrice List` pl + SET ip.`reference` = ip.`customer`, ip.`supplier` = NULL + WHERE ip.`selling` = 1 + AND ip.`buying` = 0 + AND (ip.`supplier` IS NOT NULL OR ip.`supplier` = '') + AND ip.`price_list` = pl.`name` + AND pl.`enabled` = 1""") + + # update for buying + frappe.db.sql( + """UPDATE `tabItem Price` ip, `tabPrice List` pl + SET ip.`reference` = ip.`supplier`, ip.`customer` = NULL + WHERE ip.`selling` = 0 + AND ip.`buying` = 1 + AND (ip.`customer` IS NOT NULL OR ip.`customer` = '') + AND ip.`price_list` = pl.`name` + AND pl.`enabled` = 1""") From 1763d3e7067c95678089afe17dbb268f4ec9c4df Mon Sep 17 00:00:00 2001 From: Michelle Alva <50285544+michellealva@users.noreply.github.com> Date: Fri, 22 May 2020 10:51:19 +0530 Subject: [PATCH 161/608] fix: expense account error message in DN (#21851) Changed error message if expense account not set for item in Delivery Note. Earlier: Expense or Difference account is mandatory for Item IT - 6 as it impacts overall stock value After fix: Expense Account not set for Item IT - 6. Please set an Expense Account for the item in the Items table --- erpnext/controllers/stock_controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 86de80815db..90d293088b0 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -226,7 +226,7 @@ class StockController(AccountsController): def check_expense_account(self, item): if not item.get("expense_account"): - frappe.throw(_("Expense or Difference account is mandatory for Item {0} as it impacts overall stock value").format(item.item_code)) + frappe.throw(_("Expense Account not set for Item {0}. Please set an Expense Account for the item in the Items table").format(item.item_code)) else: is_expense_account = frappe.db.get_value("Account", From 8d7f5d342df4bb0536490d115b33bbc18457a40d Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Fri, 22 May 2020 12:28:45 +0530 Subject: [PATCH 162/608] fix: Item tax template not getting mapped from source to traget doc (#21862) --- erpnext/public/js/controllers/transaction.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 28c2102aef5..637d3b3267d 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1652,8 +1652,10 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ if(!r.exc) { $.each(me.frm.doc.items || [], function(i, item) { if(item.item_code && r.message.hasOwnProperty(item.item_code)) { - item.item_tax_template = r.message[item.item_code].item_tax_template; - item.item_tax_rate = r.message[item.item_code].item_tax_rate; + if (!item.item_tax_template) { + item.item_tax_template = r.message[item.item_code].item_tax_template; + item.item_tax_rate = r.message[item.item_code].item_tax_rate; + } me.add_taxes_from_item_tax_template(item.item_tax_rate); } else { item.item_tax_template = ""; From 78a6b6b45dceee27b4fab3d1e822299d98463956 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 22 May 2020 13:13:17 +0530 Subject: [PATCH 163/608] fix: Remove duplicate leave ledger entry (#21871) * fix: Remove duplicate leave ledger entry * fix: Remove duplicate leave ledger entry --- .../remove_duplicate_leave_ledger_entries.py | 48 ++++++++++--------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py b/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py index 6353304d7aa..24286dcebf9 100644 --- a/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py +++ b/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py @@ -6,6 +6,7 @@ import frappe def execute(): """Delete duplicate leave ledger entries of type allocation created.""" + frappe.reload_doc('hr', 'doctype', 'leave_ledger_entry') if not frappe.db.a_row_exists("Leave Ledger Entry"): return @@ -14,31 +15,32 @@ def execute(): def get_duplicate_records(): """Fetch all but one duplicate records from the list of expired leave allocation.""" - return frappe.db.sql_list(""" - WITH duplicate_records AS - (SELECT - name, transaction_name, is_carry_forward, - ROW_NUMBER() over(partition by transaction_name order by creation)as row - FROM `tabLeave Ledger Entry` l - WHERE (EXISTS - (SELECT name - FROM `tabLeave Ledger Entry` - WHERE - transaction_name = l.transaction_name - AND transaction_type = 'Leave Allocation' - AND name <> l.name - AND employee = l.employee - AND docstatus = 1 - AND leave_type = l.leave_type - AND is_carry_forward=l.is_carry_forward - AND to_date = l.to_date - AND from_date = l.from_date - AND is_expired = 1 - ))) - SELECT name FROM duplicate_records WHERE row > 1 + return frappe.db.sql(""" + SELECT name, employee, transaction_name, leave_type, is_carry_forward, from_date, to_date + FROM `tabLeave Ledger Entry` + WHERE + transaction_type = 'Leave Allocation' + AND docstatus = 1 + AND is_expired = 1 + GROUP BY + employee, transaction_name, leave_type, is_carry_forward, from_date, to_date + HAVING + count(name) > 1 + ORDER BY + creation """) def delete_duplicate_ledger_entries(duplicate_records_list): """Delete duplicate leave ledger entries.""" if not duplicate_records_list: return - frappe.db.sql('''DELETE FROM `tabLeave Ledger Entry` WHERE name in %s''', ((tuple(duplicate_records_list)), )) \ No newline at end of file + for d in duplicate_records_list: + frappe.db.sql(''' + DELETE FROM `tabLeave Ledger Entry` + WHERE name != %s + AND employee = %s + AND transaction_name = %s + AND leave_type = %s + AND is_carry_forward = %s + AND from_date = %s + AND to_date = %s + ''', tuple(d)) \ No newline at end of file From 5cf90baf165913e99de6a17ac5d7ab65066d8e96 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Fri, 22 May 2020 15:09:57 +0530 Subject: [PATCH 164/608] feat: rename loan management to loan on Desk Page --- .../desk_page/loan_management/loan_management.json | 6 +++--- erpnext/patches.txt | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/erpnext/loan_management/desk_page/loan_management/loan_management.json b/erpnext/loan_management/desk_page/loan_management/loan_management.json index f9ea978ed6e..d2a17630c9e 100644 --- a/erpnext/loan_management/desk_page/loan_management/loan_management.json +++ b/erpnext/loan_management/desk_page/loan_management/loan_management.json @@ -36,11 +36,11 @@ "extends_another_page": 0, "idx": 0, "is_standard": 1, - "label": "Loan Management", - "modified": "2020-04-02 11:28:51.380509", + "label": "Loan", + "modified": "2020-05-22 11:28:51.380509", "modified_by": "Administrator", "module": "Loan Management", - "name": "Loan Management", + "name": "Loan", "owner": "Administrator", "pin_to_bottom": 0, "pin_to_top": 0, diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 2435b1c5174..eacede6a92f 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -691,3 +691,4 @@ erpnext.patches.v13_0.update_actual_start_and_end_date_in_wo erpnext.patches.v13_0.set_company_field_in_healthcare_doctypes erpnext.patches.v12_0.update_bom_in_so_mr execute:frappe.delete_doc("Report", "Department Analytics") +execute:frappe.rename_doc("Desk Page", "Loan Management", "Loan", force=True) From 2b277a7eec48549157275ae3495413358f038377 Mon Sep 17 00:00:00 2001 From: sahil28297 <37302950+sahil28297@users.noreply.github.com> Date: Fri, 22 May 2020 15:52:15 +0530 Subject: [PATCH 165/608] fix(patch): rerun remove_duplicate_leave_ledger_entries (#21878) --- erpnext/patches.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 2435b1c5174..c23357466c8 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -678,7 +678,7 @@ erpnext.patches.v13_0.move_tax_slabs_from_payroll_period_to_income_tax_slab #123 erpnext.patches.v12_0.fix_quotation_expired_status erpnext.patches.v12_0.update_appointment_reminder_scheduler_entry erpnext.patches.v12_0.retain_permission_rules_for_video_doctype -erpnext.patches.v12_0.remove_duplicate_leave_ledger_entries +erpnext.patches.v12_0.remove_duplicate_leave_ledger_entries #2020-05-22 erpnext.patches.v13_0.patch_to_fix_reverse_linking_in_additional_salary_encashment_and_incentive execute:frappe.delete_doc_if_exists("Page", "appointment-analytic") execute:frappe.rename_doc("Desk Page", "Getting Started", "Home", force=True) From 0d147b011e5a9685de81564170f0668070d75af4 Mon Sep 17 00:00:00 2001 From: "Chinmay D. Pai" Date: Sun, 24 May 2020 00:54:04 +0530 Subject: [PATCH 166/608] fix: set default posting_date value to None using mutable python defaults, and especially function calls, inside function definitions causes bugs that can be really hard to debug sometimes. please refrain from using such defaults. instead, using None is almost always a sane default. the values can then be manipulated inside the function instead. Signed-off-by: Chinmay D. Pai --- erpnext/accounts/deferred_revenue.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/deferred_revenue.py b/erpnext/accounts/deferred_revenue.py index b57e6783ceb..448011016e7 100644 --- a/erpnext/accounts/deferred_revenue.py +++ b/erpnext/accounts/deferred_revenue.py @@ -199,10 +199,13 @@ def book_deferred_income_or_expense(doc, deferred_process, posting_date=None): if item.get(enable_check): _book_deferred_revenue_or_expense(item) -def process_deferred_accounting(posting_date=today()): +def process_deferred_accounting(posting_date=None): ''' Converts deferred income/expense into income/expense Executed via background jobs on every month end ''' + if not posting_date: + posting_date = today() + if not cint(frappe.db.get_singles_value('Accounts Settings', 'automatically_process_deferred_accounting_entry')): return From 9f963a2ac730e38dc480a8ecdc523f1af168d956 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 24 May 2020 21:55:47 +0530 Subject: [PATCH 167/608] fix: Company query for number cards --- erpnext/healthcare/dashboard_fixtures.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/healthcare/dashboard_fixtures.py b/erpnext/healthcare/dashboard_fixtures.py index 59da71a0ec9..967117d22c2 100644 --- a/erpnext/healthcare/dashboard_fixtures.py +++ b/erpnext/healthcare/dashboard_fixtures.py @@ -19,7 +19,7 @@ def get_company(): else: company = frappe.get_list("Company", limit=1) if company: - return company.name + return company[0].name return None def get_dashboards(): From 6ff11de2f0bb76f296d51bf8c76255743136a687 Mon Sep 17 00:00:00 2001 From: Vishal Dhayagude Date: Mon, 25 May 2020 14:54:19 +0530 Subject: [PATCH 168/608] fix: Added gram, pound to ounce conversion (#21709) * fix: pound, grams to Ounce converion factor added * fix: patch date updated for UOM Conversion Factor * fix: grams move to gram * fix: duplicate entry of uom converion factor and patch added * fix: typo fix * fix: minor changes * Update patches.txt Co-authored-by: Marica --- erpnext/patches.txt | 1 + .../patches/v12_0/update_uom_conversion_factor.py | 11 +++++++++++ .../setup_wizard/data/uom_conversion_data.json | 14 ++++++++++++++ .../setup_wizard/operations/install_fixtures.py | 15 ++++++++------- 4 files changed, 34 insertions(+), 7 deletions(-) create mode 100644 erpnext/patches/v12_0/update_uom_conversion_factor.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index cb82901697d..05f42bdda21 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -692,3 +692,4 @@ erpnext.patches.v13_0.set_company_field_in_healthcare_doctypes erpnext.patches.v12_0.update_bom_in_so_mr execute:frappe.delete_doc("Report", "Department Analytics") execute:frappe.rename_doc("Desk Page", "Loan Management", "Loan", force=True) +erpnext.patches.v12_0.update_uom_conversion_factor diff --git a/erpnext/patches/v12_0/update_uom_conversion_factor.py b/erpnext/patches/v12_0/update_uom_conversion_factor.py new file mode 100644 index 00000000000..b5a20aa6fd9 --- /dev/null +++ b/erpnext/patches/v12_0/update_uom_conversion_factor.py @@ -0,0 +1,11 @@ +from __future__ import unicode_literals +import frappe, json + +def execute(): + from erpnext.setup.setup_wizard.operations.install_fixtures import add_uom_data + + frappe.reload_doc("setup", "doctype", "UOM Conversion Factor") + frappe.reload_doc("setup", "doctype", "UOM") + frappe.reload_doc("stock", "doctype", "UOM Category") + + add_uom_data() \ No newline at end of file diff --git a/erpnext/setup/setup_wizard/data/uom_conversion_data.json b/erpnext/setup/setup_wizard/data/uom_conversion_data.json index 174ecd5903e..27a917d9db8 100644 --- a/erpnext/setup/setup_wizard/data/uom_conversion_data.json +++ b/erpnext/setup/setup_wizard/data/uom_conversion_data.json @@ -1571,5 +1571,19 @@ "to_uom": "Parts Per Million", "abbr": "ppm", "value": "10000" + }, + { + "category": "Mass", + "from_uom": "Pound", + "to_uom": "Ounce", + "abbr": "oz", + "value": "16" + }, + { + "category": "Mass", + "from_uom": "Gram", + "to_uom": "Ounce", + "abbr": "oz", + "value": "0.035274" } ] \ No newline at end of file diff --git a/erpnext/setup/setup_wizard/operations/install_fixtures.py b/erpnext/setup/setup_wizard/operations/install_fixtures.py index 8bb0a0529d7..0d70d91f739 100644 --- a/erpnext/setup/setup_wizard/operations/install_fixtures.py +++ b/erpnext/setup/setup_wizard/operations/install_fixtures.py @@ -336,13 +336,14 @@ def add_uom_data(): "category_name": _(d.get("category")) }).insert(ignore_permissions=True) - uom_conversion = frappe.get_doc({ - "doctype": "UOM Conversion Factor", - "category": _(d.get("category")), - "from_uom": _(d.get("from_uom")), - "to_uom": _(d.get("to_uom")), - "value": d.get("value") - }).insert(ignore_permissions=True) + if not frappe.db.exists("UOM Conversion Factor", {"from_uom": _(d.get("from_uom")), "to_uom": _(d.get("to_uom"))}): + uom_conversion = frappe.get_doc({ + "doctype": "UOM Conversion Factor", + "category": _(d.get("category")), + "from_uom": _(d.get("from_uom")), + "to_uom": _(d.get("to_uom")), + "value": d.get("value") + }).insert(ignore_permissions=True) def add_market_segments(): records = [ From e5395c3ae8bf33f35139b321aedf57e7e1bbff20 Mon Sep 17 00:00:00 2001 From: Rohan Date: Mon, 25 May 2020 15:25:21 +0530 Subject: [PATCH 169/608] fix: filter batches based on item and warehouse in Pick List (#21780) Co-authored-by: Marica --- erpnext/stock/doctype/pick_list/pick_list.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/pick_list/pick_list.js b/erpnext/stock/doctype/pick_list/pick_list.js index d46b98b461b..3a5ef769805 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.js +++ b/erpnext/stock/doctype/pick_list/pick_list.js @@ -31,10 +31,16 @@ frappe.ui.form.on('Pick List', { }; }); frm.set_query('item_code', 'locations', () => { + return erpnext.queries.item({ "is_stock_item": 1 }); + }); + frm.set_query('batch_no', 'locations', (frm, cdt, cdn) => { + const row = locals[cdt][cdn]; return { + query: 'erpnext.controllers.queries.get_batch_no', filters: { - is_stock_item: 1 - } + item_code: row.item_code, + warehouse: row.warehouse + }, }; }); }, From 07f305a1d69df116efcb610405a28723639db2eb Mon Sep 17 00:00:00 2001 From: Marica Date: Mon, 25 May 2020 16:30:59 +0530 Subject: [PATCH 170/608] refactor: open link in new tab (#21910) --- erpnext/buying/doctype/buying_settings/buying_settings.js | 2 +- erpnext/stock/doctype/stock_settings/stock_settings.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/buying/doctype/buying_settings/buying_settings.js b/erpnext/buying/doctype/buying_settings/buying_settings.js index a27950a9414..01b40cd26fe 100644 --- a/erpnext/buying/doctype/buying_settings/buying_settings.js +++ b/erpnext/buying/doctype/buying_settings/buying_settings.js @@ -11,7 +11,7 @@ frappe.tour['Buying Settings'] = [ { fieldname: "supp_master_name", title: "Supplier Naming By", - description: __("By default, the Item Name is set as per the Item Code entered. If you want Items to be named by a set ") + "Naming Series" + __(" choose the 'Naming Series' option."), + description: __("By default, the Item Name is set as per the Item Code entered. If you want Items to be named by a set ") + "Naming Series" + __(" choose the 'Naming Series' option."), }, { fieldname: "buying_price_list", diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.js b/erpnext/stock/doctype/stock_settings/stock_settings.js index 6f9757274d1..877d0c3bbf4 100644 --- a/erpnext/stock/doctype/stock_settings/stock_settings.js +++ b/erpnext/stock/doctype/stock_settings/stock_settings.js @@ -36,7 +36,7 @@ frappe.tour['Stock Settings'] = [ { fieldname: "valuation_method", title: __("Valuation Method"), - description: __("Choose between FIFO and Moving Average Valuation Methods. Click ") + "here" + __(" to know more about them.") + description: __("Choose between FIFO and Moving Average Valuation Methods. Click ") + "here" + __(" to know more about them.") }, { fieldname: "show_barcode_field", From 1392489b609f2843115d8a6be3fcabaef6845f52 Mon Sep 17 00:00:00 2001 From: Marica Date: Mon, 25 May 2020 18:26:01 +0530 Subject: [PATCH 171/608] fix: Item Price and Add to Cart not showing on Website (#21903) * fix: Item Price and Add to Cart not showing on Website * fix: Use None as default argument --- erpnext/shopping_cart/cart.py | 19 ++++++++++++------- erpnext/shopping_cart/product_info.py | 8 +++++--- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/erpnext/shopping_cart/cart.py b/erpnext/shopping_cart/cart.py index 4ac546e82c6..d04c8c25a34 100644 --- a/erpnext/shopping_cart/cart.py +++ b/erpnext/shopping_cart/cart.py @@ -319,7 +319,7 @@ def apply_cart_settings(party=None, quotation=None): def set_price_list_and_rate(quotation, cart_settings): """set price list based on billing territory""" - _set_price_list(quotation, cart_settings) + _set_price_list(cart_settings, quotation) # reset values quotation.price_list_currency = quotation.currency = \ @@ -334,23 +334,28 @@ def set_price_list_and_rate(quotation, cart_settings): # set it in cookies for using in product page frappe.local.cookie_manager.set_cookie("selling_price_list", quotation.selling_price_list) -def _set_price_list(quotation, cart_settings): +def _set_price_list(cart_settings, quotation=None): """Set price list based on customer or shopping cart default""" from erpnext.accounts.party import get_default_price_list # check if customer price list exists selling_price_list = None - if quotation.party_name: - selling_price_list = frappe.db.get_value('Customer', quotation.party_name, 'default_price_list') + if quotation and quotation.get("party_name"): + selling_price_list = frappe.db.get_value('Customer', quotation.get("party_name"), 'default_price_list') # else check for territory based price list if not selling_price_list: selling_price_list = cart_settings.price_list - if not selling_price_list and quotation.party_name: - selling_price_list = get_default_price_list(frappe.get_doc("Customer", quotation.party_name)) + party_name = quotation.get("party_name") if quotation else get_party().get("name") - quotation.selling_price_list = selling_price_list + if not selling_price_list and party_name: + selling_price_list = get_default_price_list(frappe.get_doc("Customer", party_name)) + + if quotation: + quotation.selling_price_list = selling_price_list + + return selling_price_list def set_taxes(quotation, cart_settings): """set taxes based on billing territory""" diff --git a/erpnext/shopping_cart/product_info.py b/erpnext/shopping_cart/product_info.py index 21ee335125b..7c08f5b5b24 100644 --- a/erpnext/shopping_cart/product_info.py +++ b/erpnext/shopping_cart/product_info.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import frappe -from erpnext.shopping_cart.cart import _get_cart_quotation +from erpnext.shopping_cart.cart import _get_cart_quotation, _set_price_list from erpnext.shopping_cart.doctype.shopping_cart_settings.shopping_cart_settings \ import get_shopping_cart_settings, show_quantity_in_website from erpnext.utilities.product import get_price, get_qty_in_stock, get_non_stock_item_status @@ -21,9 +21,11 @@ def get_product_info_for_website(item_code, skip_quotation_creation=False): if not skip_quotation_creation: cart_quotation = _get_cart_quotation() + selling_price_list = cart_quotation.get("selling_price_list") if cart_quotation else _set_price_list(cart_settings, None) + price = get_price( item_code, - cart_quotation.selling_price_list, + selling_price_list, cart_settings.default_customer_group, cart_settings.company ) @@ -42,7 +44,7 @@ def get_product_info_for_website(item_code, skip_quotation_creation=False): if product_info["price"]: if frappe.session.user != "Guest": - item = cart_quotation.get({"item_code": item_code}) + item = cart_quotation.get({"item_code": item_code}) if cart_quotation else None if item: product_info["qty"] = item[0].qty From 8a1cf454739b31c94bb8d4102237c3b0abf052a2 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Mon, 25 May 2020 18:39:19 +0530 Subject: [PATCH 172/608] fix: Validation for dates (#21886) --- erpnext/hr/doctype/upload_attendance/upload_attendance.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/hr/doctype/upload_attendance/upload_attendance.py b/erpnext/hr/doctype/upload_attendance/upload_attendance.py index 61faea18713..edf05e827b9 100644 --- a/erpnext/hr/doctype/upload_attendance/upload_attendance.py +++ b/erpnext/hr/doctype/upload_attendance/upload_attendance.py @@ -22,6 +22,9 @@ def get_template(): args = frappe.local.form_dict + if getdate(args.from_date) > getdate(args.to_date): + frappe.throw(_("To Date should be greater than From Date")) + w = UnicodeWriter() w = add_header(w) From 316d136acabba37e58c5987f22570bc1ebf9b64b Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 25 May 2020 18:41:47 +0530 Subject: [PATCH 173/608] fix(Healthcare): unhide company field in Sample Collection, add field in Rehab DocTypes (#21907) * fix: unhide company field in Sample Collection * fix: add and set company field in rehab doctypes --- .../doctype/patient_assessment/patient_assessment.json | 10 +++++++++- .../doctype/sample_collection/sample_collection.json | 8 +++----- .../healthcare/doctype/therapy_plan/therapy_plan.json | 10 +++++++++- erpnext/patches.txt | 2 +- .../v13_0/set_company_field_in_healthcare_doctypes.py | 4 ++-- 5 files changed, 24 insertions(+), 10 deletions(-) diff --git a/erpnext/healthcare/doctype/patient_assessment/patient_assessment.json b/erpnext/healthcare/doctype/patient_assessment/patient_assessment.json index 3952a8153f7..15c94344e9b 100644 --- a/erpnext/healthcare/doctype/patient_assessment/patient_assessment.json +++ b/erpnext/healthcare/doctype/patient_assessment/patient_assessment.json @@ -11,6 +11,7 @@ "patient", "assessment_template", "column_break_4", + "company", "healthcare_practitioner", "assessment_datetime", "assessment_description", @@ -127,11 +128,18 @@ "fieldname": "assessment_description", "fieldtype": "Small Text", "label": "Assessment Description" + }, + { + "fieldname": "company", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Company", + "options": "Company" } ], "is_submittable": 1, "links": [], - "modified": "2020-04-21 13:23:09.815007", + "modified": "2020-05-25 14:38:38.302399", "modified_by": "Administrator", "module": "Healthcare", "name": "Patient Assessment", diff --git a/erpnext/healthcare/doctype/sample_collection/sample_collection.json b/erpnext/healthcare/doctype/sample_collection/sample_collection.json index c352287faf4..016cfbc3ae2 100644 --- a/erpnext/healthcare/doctype/sample_collection/sample_collection.json +++ b/erpnext/healthcare/doctype/sample_collection/sample_collection.json @@ -85,11 +85,9 @@ { "fieldname": "company", "fieldtype": "Link", - "hidden": 1, + "in_standard_filter": 1, "label": "Company", - "options": "Company", - "print_hide": 1, - "report_hide": 1 + "options": "Company" }, { "fieldname": "section_break_6", @@ -167,7 +165,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-04-04 19:17:02.707203", + "modified": "2020-05-25 14:36:46.990469", "modified_by": "Administrator", "module": "Healthcare", "name": "Sample Collection", diff --git a/erpnext/healthcare/doctype/therapy_plan/therapy_plan.json b/erpnext/healthcare/doctype/therapy_plan/therapy_plan.json index ca78b6618ec..9edfeb2faa1 100644 --- a/erpnext/healthcare/doctype/therapy_plan/therapy_plan.json +++ b/erpnext/healthcare/doctype/therapy_plan/therapy_plan.json @@ -10,6 +10,7 @@ "patient", "patient_name", "column_break_4", + "company", "status", "start_date", "section_break_3", @@ -98,10 +99,17 @@ "label": "Status", "options": "Not Started\nIn Progress\nCompleted\nCancelled", "read_only": 1 + }, + { + "fieldname": "company", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Company", + "options": "Company" } ], "links": [], - "modified": "2020-04-21 13:13:43.956014", + "modified": "2020-05-25 14:38:53.649315", "modified_by": "Administrator", "module": "Healthcare", "name": "Therapy Plan", diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 05f42bdda21..929f8d6b98b 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -688,7 +688,7 @@ erpnext.patches.v12_0.set_serial_no_status #2020-05-21 erpnext.patches.v12_0.update_price_list_currency_in_bom execute:frappe.delete_doc_if_exists('Dashboard', 'Accounts') erpnext.patches.v13_0.update_actual_start_and_end_date_in_wo -erpnext.patches.v13_0.set_company_field_in_healthcare_doctypes +erpnext.patches.v13_0.set_company_field_in_healthcare_doctypes #2020-05-25 erpnext.patches.v12_0.update_bom_in_so_mr execute:frappe.delete_doc("Report", "Department Analytics") execute:frappe.rename_doc("Desk Page", "Loan Management", "Loan", force=True) diff --git a/erpnext/patches/v13_0/set_company_field_in_healthcare_doctypes.py b/erpnext/patches/v13_0/set_company_field_in_healthcare_doctypes.py index 9d0dae45535..a7d4c665a1e 100644 --- a/erpnext/patches/v13_0/set_company_field_in_healthcare_doctypes.py +++ b/erpnext/patches/v13_0/set_company_field_in_healthcare_doctypes.py @@ -3,8 +3,8 @@ import frappe def execute(): company = frappe.db.get_single_value('Global Defaults', 'default_company') - doctypes = ['Clinical Procedure', 'Inpatient Record', 'Lab Test', 'Patient Appointment', 'Patient Encounter', 'Vital Signs'] + doctypes = ['Clinical Procedure', 'Inpatient Record', 'Lab Test', 'Sample Collection' 'Patient Appointment', 'Patient Encounter', 'Vital Signs', 'Therapy Session', 'Therapy Plan', 'Patient Assessment'] for entry in doctypes: if frappe.db.exists('DocType', entry): - frappe.reload_doc("Healthcare", "doctype", entry) + frappe.reload_doc('Healthcare', 'doctype', entry) frappe.db.sql("update `tab{dt}` set company = '{company}' where ifnull(company, '') = ''".format(dt=entry, company=company)) From 4dd6b9986fbf5320a80288b871d32ded4c4e39f1 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 25 May 2020 18:42:01 +0530 Subject: [PATCH 174/608] fix(healthcare): patient vitals undefined (#21906) --- erpnext/healthcare/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/healthcare/utils.py b/erpnext/healthcare/utils.py index f092578003e..9abaa0784a7 100644 --- a/erpnext/healthcare/utils.py +++ b/erpnext/healthcare/utils.py @@ -512,10 +512,10 @@ def get_children(doctype, parent, company, is_root=False): def get_patient_vitals(patient, from_date=None, to_date=None): if not patient: return - vitals = frappe.db.get_all('Vital Signs', { + vitals = frappe.db.get_all('Vital Signs', filters={ 'docstatus': 1, 'patient': patient - }, order_by='signs_date, signs_time') + }, order_by='signs_date, signs_time', fields=['*']) if len(vitals): return vitals From 48e4abf43f34ef04ce5f06a0a423c6c640d66865 Mon Sep 17 00:00:00 2001 From: Saqib Date: Mon, 25 May 2020 19:21:08 +0530 Subject: [PATCH 175/608] fix: fetch depreciation amount only if depr entry is made (#21893) --- .../asset_depreciations_and_balances.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py index d7efbad240d..80bccafd28e 100644 --- a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py +++ b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py @@ -111,7 +111,7 @@ def get_assets(filters): 0 end), 0) as depreciation_amount_during_the_period from `tabAsset` a, `tabDepreciation Schedule` ds - where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s and a.name = ds.parent + where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s and a.name = ds.parent and ifnull(ds.journal_entry, '') != '' group by a.asset_category union SELECT a.asset_category, From 0a147d5c766a69ee98543fd12f1add57c8792e44 Mon Sep 17 00:00:00 2001 From: Anupam Kumar Date: Mon, 25 May 2020 19:21:48 +0530 Subject: [PATCH 176/608] fix: adding dashboard in course and assessment plan (#21889) * Adding dashboards * adding dashboard in course and assessment plan --- .../assessment_plan_dashboard.py | 17 +++++++++++++ .../doctype/course/course_dashboard.py | 25 +++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 erpnext/education/doctype/assessment_plan/assessment_plan_dashboard.py create mode 100644 erpnext/education/doctype/course/course_dashboard.py diff --git a/erpnext/education/doctype/assessment_plan/assessment_plan_dashboard.py b/erpnext/education/doctype/assessment_plan/assessment_plan_dashboard.py new file mode 100644 index 00000000000..c36dfb11b54 --- /dev/null +++ b/erpnext/education/doctype/assessment_plan/assessment_plan_dashboard.py @@ -0,0 +1,17 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt +from __future__ import unicode_literals +from frappe import _ + +def get_data(): + return { + 'fieldname': 'assessment_plan', + 'non_standard_fieldnames': { + }, + 'transactions': [ + { + 'label': _('Assessment'), + 'items': ['Assessment Result'] + } + ] + } \ No newline at end of file diff --git a/erpnext/education/doctype/course/course_dashboard.py b/erpnext/education/doctype/course/course_dashboard.py new file mode 100644 index 00000000000..752af29a9db --- /dev/null +++ b/erpnext/education/doctype/course/course_dashboard.py @@ -0,0 +1,25 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt +from __future__ import unicode_literals +from frappe import _ + +def get_data(): + return { + 'fieldname': 'course', + 'non_standard_fieldnames': { + }, + 'transactions': [ + { + 'label': _('Course'), + 'items': ['Course Enrollment', 'Course Schedule'] + }, + { + 'label': _('Student'), + 'items': ['Student Group'] + }, + { + 'label': _('Assessment'), + 'items': ['Assessment Plan'] + }, + ] + } \ No newline at end of file From a7f42f72565ef1e47629101cabb0ad5e57c62d71 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Mon, 25 May 2020 19:23:17 +0530 Subject: [PATCH 177/608] fix: showing wrong record (#21884) --- erpnext/hr/desk_page/hr/hr.json | 5 ++--- .../monthly_attendance_sheet.py | 16 +++++++++------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/erpnext/hr/desk_page/hr/hr.json b/erpnext/hr/desk_page/hr/hr.json index 2d0e8857cab..33132a6898a 100644 --- a/erpnext/hr/desk_page/hr/hr.json +++ b/erpnext/hr/desk_page/hr/hr.json @@ -89,11 +89,10 @@ "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, - "hide_custom": 0, "idx": 0, "is_standard": 1, "label": "HR", - "modified": "2020-05-20 11:20:54.255557", + "modified": "2020-05-23 12:41:52.543438", "modified_by": "Administrator", "module": "HR", "name": "HR", @@ -127,7 +126,7 @@ "type": "DocType" }, { - "label": "Salary Register", + "label": "Monthly Attendance Sheet", "link_to": "Monthly Attendance Sheet", "type": "Report" }, diff --git a/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py b/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py index 60767b5db08..47daab19017 100644 --- a/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py +++ b/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py @@ -135,7 +135,7 @@ def add_data(employee_map, att_map, filters, holiday_map, conditions, default_ho row += [emp, emp_det.employee_name] total_p = total_a = total_l = total_h = total_um= 0.0 - ggg = [] + emp_status_map = [] for day in range(filters["total_days_in_month"]): status = None status = att_map.get(emp).get(day + 1) @@ -152,11 +152,10 @@ def add_data(employee_map, att_map, filters, holiday_map, conditions, default_ho status = "Holiday" total_h += 1 - ggg.append(status_map.get(status, "")) + abbr = status_map.get(status, "") + emp_status_map.append(abbr) - if not filters.summarized_view: - row += ggg - else: + if filters.summarized_view: if status == "Present" or status == "Work From Home": total_p += 1 elif status == "Absent": @@ -170,6 +169,9 @@ def add_data(employee_map, att_map, filters, holiday_map, conditions, default_ho elif not status: total_um += 1 + if not filters.summarized_view: + row += emp_status_map + if filters.summarized_view: row += [total_p, total_l, total_a, total_h, total_um] @@ -203,7 +205,7 @@ def add_data(employee_map, att_map, filters, holiday_map, conditions, default_ho row.append("0.0") row.extend([time_default_counts[0][0],time_default_counts[0][1]]) - emp_att_map[emp] = ggg + emp_att_map[emp] = emp_status_map record.append(row) return record, emp_att_map @@ -216,7 +218,7 @@ def get_columns(filters): columns = [_(filters.group_by)+ ":Link/Branch:120"] columns += [ - _("Employee") + ":Link/Employee:120", _("Employee Name") + ":Link/Employee:120" + _("Employee") + ":Link/Employee:120", _("Employee Name") + ":Data/:120" ] days = [] for day in range(filters["total_days_in_month"]): From 372aa44b1953850fb493c2941fa45ba20ec85cf5 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 25 May 2020 21:37:58 +0530 Subject: [PATCH 178/608] feat: implement hold time on Replied status for SLA --- erpnext/support/doctype/issue/issue.js | 14 +++++++- erpnext/support/doctype/issue/issue.json | 20 +++++++++++- erpnext/support/doctype/issue/issue.py | 41 +++++++++++++++++++++++- 3 files changed, 72 insertions(+), 3 deletions(-) diff --git a/erpnext/support/doctype/issue/issue.js b/erpnext/support/doctype/issue/issue.js index bad40cc37fc..66c62d1ff2b 100644 --- a/erpnext/support/doctype/issue/issue.js +++ b/erpnext/support/doctype/issue/issue.js @@ -41,7 +41,19 @@ frappe.ui.form.on("Issue", { if (frm.doc.status !== "Closed" && frm.doc.agreement_fulfilled === "Ongoing") { if (frm.doc.service_level_agreement) { - set_time_to_resolve_and_response(frm); + if (frm.doc.status == "Replied") { + frm.dashboard.clear_headline(); + let message = {"indicator": "orange", "msg": __("Replied {0}", [moment(frm.doc.on_hold_since).fromNow()])}; + frm.dashboard.set_headline_alert( + '
' + + '
' + + ' ' + + '
' + + '
' + ); + } else { + set_time_to_resolve_and_response(frm); + } } frm.add_custom_button(__("Close"), function () { diff --git a/erpnext/support/doctype/issue/issue.json b/erpnext/support/doctype/issue/issue.json index 131e1cb9bfb..82443a51cb2 100644 --- a/erpnext/support/doctype/issue/issue.json +++ b/erpnext/support/doctype/issue/issue.json @@ -31,6 +31,8 @@ "resolution_by", "resolution_by_variance", "service_level_agreement_creation", + "on_hold_since", + "total_hold_time", "response", "mins_to_first_response", "first_responded_on", @@ -395,12 +397,28 @@ "read_only": 1, "show_days": 1, "show_seconds": 1 + }, + { + "fieldname": "on_hold_since", + "fieldtype": "Datetime", + "label": "On Hold Since", + "read_only": 1, + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "total_hold_time", + "fieldtype": "Duration", + "label": "Total Hold Time", + "read_only": 1, + "show_days": 1, + "show_seconds": 1 } ], "icon": "fa fa-ticket", "idx": 7, "links": [], - "modified": "2020-05-06 12:28:58.093654", + "modified": "2020-05-14 23:59:01.172007", "modified_by": "Administrator", "module": "Support", "name": "Issue", diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index a9c4897017f..47b9c05161f 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -72,7 +72,36 @@ class Issue(Document): # if no date, it should be set as None and not a blank string "", as per mysql strict config self.resolution_date = None self.reset_issue_metrics() - self.agreement_fulfilled = "Ongoing" + + if self.status == "Replied" and status != "Replied": + self.on_hold_since = frappe.flags.current_time or now_datetime() + if not self.first_responded_on: + self.response_by = None + self.response_by_variance = None + self.resolution_by = None + self.resolution_by_variance = None + + if self.status != "Replied" and status == "Replied": + hold_time = self.total_hold_time if self.total_hold_time else 0 + self.total_hold_time = hold_time + time_diff_in_seconds(now_datetime(), self.on_hold_since) + + if self.status == "Open" and status == "Replied": + start_date_time = get_datetime(self.service_level_agreement_creation) + priority = get_priority(self) + hold_time = time_diff_in_seconds(now_datetime(), self.on_hold_since) + + if not self.first_responded_on: + response_by = get_expected_time_for(parameter='response', service_level=priority, start_date_time=start_date_time) + self.response_by = add_to_date(response_by, seconds=round(hold_time)) + response_by_variance = round(time_diff_in_hours(self.response_by, now_datetime())) + self.response_by_variance = response_by_variance + (hold_time // 3600) + + resolution_by = get_expected_time_for(parameter='resolution', service_level=priority, start_date_time=start_date_time) + self.resolution_by = add_to_date(resolution_by, seconds=round(hold_time)) + resolution_by_variance = round(time_diff_in_hours(self.resolution_by, now_datetime())) + self.resolution_by_variance = resolution_by_variance + (hold_time // 3600) + self.on_hold_since = None + def update_agreement_status(self): if self.service_level_agreement and self.agreement_fulfilled == "Ongoing": @@ -233,6 +262,16 @@ class Issue(Document): self.db_set('avg_response_time', None) +def get_priority(issue): + service_level_agreement = frappe.get_doc("Service Level Agreement", issue.service_level_agreement) + priority = service_level_agreement.get_service_level_agreement_priority(issue.priority) + priority.update({ + "support_and_resolution": service_level_agreement.support_and_resolution, + "holiday_list": service_level_agreement.holiday_list + }) + return priority + + def get_expected_time_for(parameter, service_level, start_date_time): current_date_time = start_date_time expected_time = current_date_time From 79ed5755be8f4d393a2d4045165d42858bede911 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 25 May 2020 22:04:04 +0530 Subject: [PATCH 179/608] refactor: remove Hold status --- erpnext/support/doctype/issue/issue.json | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/erpnext/support/doctype/issue/issue.json b/erpnext/support/doctype/issue/issue.json index 82443a51cb2..2b79e03f456 100644 --- a/erpnext/support/doctype/issue/issue.json +++ b/erpnext/support/doctype/issue/issue.json @@ -120,7 +120,7 @@ "no_copy": 1, "oldfieldname": "status", "oldfieldtype": "Select", - "options": "Open\nReplied\nHold\nClosed", + "options": "Open\nReplied\nClosed", "search_index": 1 }, { @@ -167,6 +167,7 @@ "options": "Service Level Agreement" }, { + "depends_on": "eval: doc.status != 'Replied';", "fieldname": "response_by", "fieldtype": "Datetime", "label": "Response By", @@ -180,6 +181,7 @@ "read_only": 1 }, { + "depends_on": "eval: doc.status != 'Replied';", "fieldname": "resolution_by", "fieldtype": "Datetime", "label": "Resolution By", @@ -334,7 +336,7 @@ "read_only": 1 }, { - "depends_on": "eval: doc.service_level_agreement", + "depends_on": "eval: doc.service_level_agreement && doc.status != 'Replied';", "description": "in hours", "fieldname": "response_by_variance", "fieldtype": "Float", @@ -342,7 +344,7 @@ "read_only": 1 }, { - "depends_on": "eval: doc.service_level_agreement", + "depends_on": "eval: doc.service_level_agreement && doc.status != 'Replied';", "description": "in hours", "fieldname": "resolution_by_variance", "fieldtype": "Float", @@ -418,7 +420,7 @@ "icon": "fa fa-ticket", "idx": 7, "links": [], - "modified": "2020-05-14 23:59:01.172007", + "modified": "2020-05-25 22:02:32.974165", "modified_by": "Administrator", "module": "Support", "name": "Issue", From 899ec3653259b4973987e7a5f7d324c944bb3ec8 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 26 May 2020 12:23:28 +0530 Subject: [PATCH 180/608] feat: added Resolved status with same functionality as Closed --- erpnext/support/doctype/issue/issue.json | 4 ++-- erpnext/support/doctype/issue/issue.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/support/doctype/issue/issue.json b/erpnext/support/doctype/issue/issue.json index 2b79e03f456..8fb94013af1 100644 --- a/erpnext/support/doctype/issue/issue.json +++ b/erpnext/support/doctype/issue/issue.json @@ -120,7 +120,7 @@ "no_copy": 1, "oldfieldname": "status", "oldfieldtype": "Select", - "options": "Open\nReplied\nClosed", + "options": "Open\nReplied\nResolved\nClosed", "search_index": 1 }, { @@ -420,7 +420,7 @@ "icon": "fa fa-ticket", "idx": 7, "links": [], - "modified": "2020-05-25 22:02:32.974165", + "modified": "2020-05-26 12:12:59.343559", "modified_by": "Administrator", "module": "Support", "name": "Issue", diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index 47b9c05161f..8bc00fd2f01 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -59,7 +59,7 @@ class Issue(Document): if self.status!="Open" and status =="Open" and not self.first_responded_on: self.first_responded_on = frappe.flags.current_time or now_datetime() - if self.status=="Closed" and status !="Closed": + if self.status in ["Closed", "Resolved"] and status not in ["Resolved", "Closed"]: self.resolution_date = frappe.flags.current_time or now_datetime() if frappe.db.get_value("Issue", self.name, "agreement_fulfilled") == "Ongoing": set_service_level_agreement_variance(issue=self.name) From ec6246306cfc6fa43cca182e6d67e4ea6ba77136 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 26 May 2020 12:32:12 +0530 Subject: [PATCH 181/608] refactor: set variance and SLA as Ongoing on Issue reopen --- erpnext/support/doctype/issue/issue.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index 8bc00fd2f01..02a2df1c19c 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -72,6 +72,8 @@ class Issue(Document): # if no date, it should be set as None and not a blank string "", as per mysql strict config self.resolution_date = None self.reset_issue_metrics() + self.agreement_fulfilled = "Ongoing" + set_service_level_agreement_variance(issue=self.name) if self.status == "Replied" and status != "Replied": self.on_hold_since = frappe.flags.current_time or now_datetime() From 5379e87880409c300a2d22a219b810e32ed4a2e9 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 26 May 2020 12:48:03 +0530 Subject: [PATCH 182/608] refactor: code cleanup, break functions --- erpnext/support/doctype/issue/issue.py | 62 +++++++++++++------------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index 02a2df1c19c..b7da29649d0 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -47,8 +47,8 @@ class Issue(Document): self.contact = frappe.db.get_value("Contact", {"email_id": email_id}) if self.contact: - contact = frappe.get_doc('Contact', self.contact) - self.customer = contact.get_link_for('Customer') + contact = frappe.get_doc("Contact", self.contact) + self.customer = contact.get_link_for("Customer") if not self.company: self.company = frappe.db.get_value("Lead", self.lead, "company") or \ @@ -56,7 +56,7 @@ class Issue(Document): def update_status(self): status = frappe.db.get_value("Issue", self.name, "status") - if self.status!="Open" and status =="Open" and not self.first_responded_on: + if self.status != "Open" and status == "Open" and not self.first_responded_on: self.first_responded_on = frappe.flags.current_time or now_datetime() if self.status in ["Closed", "Resolved"] and status not in ["Resolved", "Closed"]: @@ -68,13 +68,18 @@ class Issue(Document): set_resolution_time(issue=self) set_user_resolution_time(issue=self) - if self.status=="Open" and status !="Open": + if self.status == "Open" and status != "Open": # if no date, it should be set as None and not a blank string "", as per mysql strict config self.resolution_date = None self.reset_issue_metrics() + # enable SLA and variance on Reopen self.agreement_fulfilled = "Ongoing" set_service_level_agreement_variance(issue=self.name) + self.handle_hold_time() + + def handle_hold_time(self): + # set response and resolution variance as None as the issue is on Hold for status as Replied if self.status == "Replied" and status != "Replied": self.on_hold_since = frappe.flags.current_time or now_datetime() if not self.first_responded_on: @@ -83,28 +88,30 @@ class Issue(Document): self.resolution_by = None self.resolution_by_variance = None + # calculate hold time when status is changed from Replied to any other status if self.status != "Replied" and status == "Replied": hold_time = self.total_hold_time if self.total_hold_time else 0 self.total_hold_time = hold_time + time_diff_in_seconds(now_datetime(), self.on_hold_since) + # re-calculate SLA variables after issue changes from Replied to Open + # add hold time to SLA variables if self.status == "Open" and status == "Replied": start_date_time = get_datetime(self.service_level_agreement_creation) priority = get_priority(self) hold_time = time_diff_in_seconds(now_datetime(), self.on_hold_since) if not self.first_responded_on: - response_by = get_expected_time_for(parameter='response', service_level=priority, start_date_time=start_date_time) + response_by = get_expected_time_for(parameter="response", service_level=priority, start_date_time=start_date_time) self.response_by = add_to_date(response_by, seconds=round(hold_time)) response_by_variance = round(time_diff_in_hours(self.response_by, now_datetime())) self.response_by_variance = response_by_variance + (hold_time // 3600) - resolution_by = get_expected_time_for(parameter='resolution', service_level=priority, start_date_time=start_date_time) + resolution_by = get_expected_time_for(parameter="resolution", service_level=priority, start_date_time=start_date_time) self.resolution_by = add_to_date(resolution_by, seconds=round(hold_time)) resolution_by_variance = round(time_diff_in_hours(self.resolution_by, now_datetime())) self.resolution_by_variance = resolution_by_variance + (hold_time // 3600) self.on_hold_since = None - def update_agreement_status(self): if self.service_level_agreement and self.agreement_fulfilled == "Ongoing": if frappe.db.get_value("Issue", self.name, "response_by_variance") < 0 or \ @@ -174,7 +181,7 @@ class Issue(Document): communications = frappe.get_all("Communication", filters={"reference_doctype": "Issue", "reference_name": comm_to_split_from.reference_name, - "creation": ('>=', comm_to_split_from.creation)}) + "creation": (">=", comm_to_split_from.creation)}) for communication in communications: doc = frappe.get_doc("Communication", communication.name) @@ -210,20 +217,15 @@ class Issue(Document): self.service_level_agreement = service_level_agreement.name self.priority = service_level_agreement.default_priority if not priority else priority - service_level_agreement = frappe.get_doc("Service Level Agreement", service_level_agreement.name) - priority = service_level_agreement.get_service_level_agreement_priority(self.priority) - priority.update({ - "support_and_resolution": service_level_agreement.support_and_resolution, - "holiday_list": service_level_agreement.holiday_list - }) + priority = get_priority(self) if not self.creation: self.creation = now_datetime() self.service_level_agreement_creation = now_datetime() start_date_time = get_datetime(self.service_level_agreement_creation) - self.response_by = get_expected_time_for(parameter='response', service_level=priority, start_date_time=start_date_time) - self.resolution_by = get_expected_time_for(parameter='resolution', service_level=priority, start_date_time=start_date_time) + self.response_by = get_expected_time_for(parameter="response", service_level=priority, start_date_time=start_date_time) + self.resolution_by = get_expected_time_for(parameter="resolution", service_level=priority, start_date_time=start_date_time) self.response_by_variance = round(time_diff_in_hours(self.response_by, now_datetime())) self.resolution_by_variance = round(time_diff_in_hours(self.resolution_by, now_datetime())) @@ -259,9 +261,9 @@ class Issue(Document): self.save() def reset_issue_metrics(self): - self.db_set('resolution_time', None) - self.db_set('user_resolution_time', None) - self.db_set('avg_response_time', None) + self.db_set("resolution_time", None) + self.db_set("user_resolution_time", None) + self.db_set("avg_response_time", None) def get_priority(issue): @@ -280,9 +282,9 @@ def get_expected_time_for(parameter, service_level, start_date_time): start_time = None end_time = None - if parameter == 'response': + if parameter == "response": allotted_seconds = service_level.get("response_time") - elif parameter == 'resolution': + elif parameter == "resolution": allotted_seconds = service_level.get("resolution_time") else: frappe.throw(_("{0} parameter is invalid").format(parameter)) @@ -292,8 +294,8 @@ def get_expected_time_for(parameter, service_level, start_date_time): support_days = {} for service in service_level.get("support_and_resolution"): support_days[service.workday] = frappe._dict({ - 'start_time': service.start_time, - 'end_time': service.end_time, + "start_time": service.start_time, + "end_time": service.end_time, }) holidays = get_holidays(service_level.get("holiday_list")) @@ -370,13 +372,13 @@ def set_average_response_time(issue): response_times.append(response_time) if response_times: avg_response_time = sum(response_times) / len(response_times) - issue.db_set('avg_response_time', avg_response_time) + issue.db_set("avg_response_time", avg_response_time) def set_resolution_time(issue): # total time taken from issue creation to closing resolution_time = time_diff_in_seconds(now_datetime(), issue.creation) - issue.db_set('resolution_time', resolution_time) + issue.db_set("resolution_time", resolution_time) def set_user_resolution_time(issue): @@ -399,7 +401,7 @@ def set_user_resolution_time(issue): total_pending_time = sum(pending_time) resolution_time_in_secs = time_diff_in_seconds(now_datetime(), issue.creation) user_resolution_time = resolution_time_in_secs - total_pending_time - issue.db_set('user_resolution_time', user_resolution_time) + issue.db_set("user_resolution_time", user_resolution_time) def get_list_context(context=None): @@ -409,7 +411,7 @@ def get_list_context(context=None): "row_template": "templates/includes/issue_row.html", "show_sidebar": True, "show_search": True, - 'no_breadcrumbs': True + "no_breadcrumbs": True } @@ -417,12 +419,12 @@ def get_issue_list(doctype, txt, filters, limit_start, limit_page_length=20, ord from frappe.www.list import get_list user = frappe.session.user - contact = frappe.db.get_value('Contact', {'user': user}, 'name') + contact = frappe.db.get_value("Contact", {"user": user}, "name") customer = None if contact: - contact_doc = frappe.get_doc('Contact', contact) - customer = contact_doc.get_link_for('Customer') + contact_doc = frappe.get_doc("Contact", contact) + customer = contact_doc.get_link_for("Customer") ignore_permissions = False if is_website_user(): From f3fc544918643502e0ec12f19fa18901c62795de Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 26 May 2020 15:03:15 +0530 Subject: [PATCH 183/608] fix: SLA tests --- .../test_service_level_agreement.py | 70 +++++++++++++------ 1 file changed, 47 insertions(+), 23 deletions(-) diff --git a/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py index 4a741ea4e18..94e0b582f97 100644 --- a/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py +++ b/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py @@ -5,19 +5,19 @@ from __future__ import unicode_literals import frappe import unittest -from erpnext.support.doctype.service_level.test_service_level import create_service_level_for_sla +from erpnext.hr.doctype.employee_group.test_employee_group import make_employee_group +from erpnext.support.doctype.issue_priority.test_issue_priority import make_priorities class TestServiceLevelAgreement(unittest.TestCase): def test_service_level_agreement(self): frappe.db.set_value("Support Settings", None, "track_service_level_agreement", 1) - create_service_level_for_sla() - # Default Service Level Agreement create_default_service_level_agreement = create_service_level_agreement(default_service_level_agreement=1, - service_level="__Test Service Level", holiday_list="__Test Holiday List", employee_group="_Test Employee Group", + holiday_list="__Test Holiday List", employee_group="_Test Employee Group", entity_type=None, entity=None, response_time=4, resolution_time=6) + get_default_service_level_agreement = get_service_level_agreement(default_service_level_agreement=1) self.assertEqual(create_default_service_level_agreement.name, get_default_service_level_agreement.name) @@ -28,7 +28,7 @@ class TestServiceLevelAgreement(unittest.TestCase): # Service Level Agreement for Customer customer = create_customer() create_customer_service_level_agreement = create_service_level_agreement(default_service_level_agreement=0, - service_level="_Test Service Level", holiday_list="__Test Holiday List", employee_group="_Test Employee Group", + holiday_list="__Test Holiday List", employee_group="_Test Employee Group", entity_type="Customer", entity=customer, response_time=2, resolution_time=3) get_customer_service_level_agreement = get_service_level_agreement(entity_type="Customer", entity=customer) @@ -40,7 +40,7 @@ class TestServiceLevelAgreement(unittest.TestCase): # Service Level Agreement for Customer Group customer_group = create_customer_group() create_customer_group_service_level_agreement = create_service_level_agreement(default_service_level_agreement=0, - service_level="_Test Service Level", holiday_list="__Test Holiday List", employee_group="_Test Employee Group", + holiday_list="__Test Holiday List", employee_group="_Test Employee Group", entity_type="Customer Group", entity=customer_group, response_time=2, resolution_time=3) get_customer_group_service_level_agreement = get_service_level_agreement(entity_type="Customer Group", entity=customer_group) @@ -52,7 +52,7 @@ class TestServiceLevelAgreement(unittest.TestCase): # Service Level Agreement for Territory territory = create_territory() create_territory_service_level_agreement = create_service_level_agreement(default_service_level_agreement=0, - service_level="_Test Service Level", holiday_list="__Test Holiday List", employee_group="_Test Employee Group", + holiday_list="__Test Holiday List", employee_group="_Test Employee Group", entity_type="Territory", entity=territory, response_time=2, resolution_time=3) get_territory_service_level_agreement = get_service_level_agreement(entity_type="Territory", entity=territory) @@ -71,14 +71,18 @@ def get_service_level_agreement(default_service_level_agreement=None, entity_typ service_level_agreement = frappe.get_doc("Service Level Agreement", filters) return service_level_agreement -def create_service_level_agreement(default_service_level_agreement, service_level, holiday_list, employee_group, +def create_service_level_agreement(default_service_level_agreement, holiday_list, employee_group, response_time, entity_type, entity, resolution_time): + employee_group = make_employee_group() + make_holiday_list() + make_priorities() + service_level_agreement = frappe.get_doc({ "doctype": "Service Level Agreement", "enable": 1, + "service_level": "__Test Service Level", "default_service_level_agreement": default_service_level_agreement, - "service_level": service_level, "holiday_list": holiday_list, "employee_group": employee_group, "entity_type": entity_type, @@ -167,6 +171,7 @@ def create_service_level_agreement(default_service_level_agreement, service_leve else: return frappe.get_doc("Service Level Agreement", service_level_agreement_exists) + def create_customer(): customer = frappe.get_doc({ "doctype": "Customer", @@ -206,23 +211,42 @@ def create_territory(): return frappe.db.exists("Territory", {"territory_name": "_Test SLA Territory"}) def create_service_level_agreements_for_issues(): - create_service_level_for_sla() - - create_service_level_agreement(default_service_level_agreement=1, - service_level="__Test Service Level", holiday_list="__Test Holiday List", employee_group="_Test Employee Group", - entity_type=None, entity=None, response_time=4, resolution_time=6) + create_service_level_agreement(default_service_level_agreement=1, holiday_list="__Test Holiday List", + employee_group="_Test Employee Group", entity_type=None, entity=None, response_time=4, resolution_time=6) create_customer() - create_service_level_agreement(default_service_level_agreement=0, - service_level="_Test Service Level", holiday_list="__Test Holiday List", employee_group="_Test Employee Group", - entity_type="Customer", entity="_Test Customer", response_time=2, resolution_time=3) + create_service_level_agreement(default_service_level_agreement=0, holiday_list="__Test Holiday List", + employee_group="_Test Employee Group", entity_type="Customer", entity="_Test Customer", response_time=2, resolution_time=3) create_customer_group() - create_service_level_agreement(default_service_level_agreement=0, - service_level="_Test Service Level", holiday_list="__Test Holiday List", employee_group="_Test Employee Group", - entity_type="Customer Group", entity="_Test SLA Customer Group", response_time=2, resolution_time=3) + create_service_level_agreement(default_service_level_agreement=0, holiday_list="__Test Holiday List", + employee_group="_Test Employee Group", entity_type="Customer Group", entity="_Test SLA Customer Group", response_time=2, resolution_time=3) create_territory() - create_service_level_agreement(default_service_level_agreement=0, - service_level="_Test Service Level", holiday_list="__Test Holiday List", employee_group="_Test Employee Group", - entity_type="Territory", entity="_Test SLA Territory", response_time=2, resolution_time=3) + create_service_level_agreement(default_service_level_agreement=0, holiday_list="__Test Holiday List", + employee_group="_Test Employee Group", entity_type="Territory", entity="_Test SLA Territory", response_time=2, resolution_time=3) + +def make_holiday_list(): + holiday_list = frappe.db.exists("Holiday List", "__Test Holiday List") + if not holiday_list: + now = frappe.utils.now_datetime() + holiday_list = frappe.get_doc({ + "doctype": "Holiday List", + "holiday_list_name": "__Test Holiday List", + "from_date": "2019-01-01", + "to_date": "2019-12-31", + "holidays": [ + { + "description": "Test Holiday 1", + "holiday_date": "2019-03-05" + }, + { + "description": "Test Holiday 2", + "holiday_date": "2019-03-07" + }, + { + "description": "Test Holiday 3", + "holiday_date": "2019-02-11" + }, + ] + }).insert() From f91ed4ce248490d7fe93856ab4159d4a11e1f78b Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 26 May 2020 16:03:47 +0530 Subject: [PATCH 184/608] fix: add default priority field in SLA --- .../service_level_agreement.json | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.json b/erpnext/support/doctype/service_level_agreement/service_level_agreement.json index 2d33c3e033f..ede5f98ebae 100644 --- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.json +++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.json @@ -9,6 +9,7 @@ "enable", "section_break_2", "service_level", + "default_priority", "default_service_level_agreement", "column_break_2", "employee_group", @@ -150,10 +151,19 @@ "fieldname": "default_service_level_agreement", "fieldtype": "Check", "label": "Default Service Level Agreement" + }, + { + "fieldname": "default_priority", + "fieldtype": "Link", + "label": "Default Priority", + "options": "Issue Priority", + "read_only": 1, + "show_days": 1, + "show_seconds": 1 } ], "links": [], - "modified": "2020-05-06 11:46:38.834810", + "modified": "2020-05-26 16:02:59.859980", "modified_by": "Administrator", "module": "Support", "name": "Service Level Agreement", From ee70d7a7ebff2ec22b24bf9f3e3264e4aa01fcd6 Mon Sep 17 00:00:00 2001 From: Prssanna Desai Date: Tue, 26 May 2020 17:02:51 +0530 Subject: [PATCH 185/608] fix: fix function import (#21927) --- erpnext/patches/v7_0/update_mins_to_first_response.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/patches/v7_0/update_mins_to_first_response.py b/erpnext/patches/v7_0/update_mins_to_first_response.py index 1df4b42cedb..16681357e68 100644 --- a/erpnext/patches/v7_0/update_mins_to_first_response.py +++ b/erpnext/patches/v7_0/update_mins_to_first_response.py @@ -1,7 +1,7 @@ from __future__ import unicode_literals import frappe -from frappe.core.doctype.communication.email import update_mins_to_first_communication +from frappe.core.doctype.communication.communication import update_mins_to_first_communication def execute(): frappe.reload_doctype('Issue') From 74a910ae92b52b4ca515d738a489c41a3e3f5bc2 Mon Sep 17 00:00:00 2001 From: Marica Date: Tue, 26 May 2020 17:49:34 +0530 Subject: [PATCH 186/608] fix: title for onboarding step (#21933) Co-authored-by: Shivam Mishra Co-authored-by: Shivam Mishra --- .../introduction_to_stock_entry.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/onboarding_step/introduction_to_stock_entry/introduction_to_stock_entry.json b/erpnext/stock/onboarding_step/introduction_to_stock_entry/introduction_to_stock_entry.json index 447611fe475..009a44f6e4d 100644 --- a/erpnext/stock/onboarding_step/introduction_to_stock_entry/introduction_to_stock_entry.json +++ b/erpnext/stock/onboarding_step/introduction_to_stock_entry/introduction_to_stock_entry.json @@ -8,12 +8,12 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-19 18:55:41.457289", + "modified": "2020-05-26 15:55:41.457289", "modified_by": "Administrator", "name": "Introduction to Stock Entry", "owner": "Administrator", "show_full_form": 0, - "title": "Introduction to the multi-purpose stock transaction", + "title": "Introduction to Stock Entry", "validate_action": 1, "video_url": "https://www.youtube.com/watch?v=Njt107hlY3I" } \ No newline at end of file From c52bbd79bf02be46248c5daa100d80168c59f151 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 26 May 2020 17:54:46 +0530 Subject: [PATCH 187/608] fix(Healthcare): set company in healthcare service unit setup (#21929) --- erpnext/healthcare/setup.py | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/erpnext/healthcare/setup.py b/erpnext/healthcare/setup.py index 2087f49f32f..06840801d37 100644 --- a/erpnext/healthcare/setup.py +++ b/erpnext/healthcare/setup.py @@ -195,10 +195,21 @@ def create_sensitivity(): def add_healthcare_service_unit_tree_root(): record = [ - { - "doctype": "Healthcare Service Unit", - "healthcare_service_unit_name": "All Healthcare Service Units", - "is_group": 1 - } + { + "doctype": "Healthcare Service Unit", + "healthcare_service_unit_name": "All Healthcare Service Units", + "is_group": 1, + "company": get_company() + } ] insert_record(record) + +def get_company(): + company = frappe.defaults.get_defaults().company + if company: + return company + else: + company = frappe.get_list("Company", limit=1) + if company: + return company[0].name + return None From ddc170521f896567a1e2d875628432a6ab0c0abe Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Tue, 26 May 2020 17:55:56 +0530 Subject: [PATCH 188/608] fix: check for warehouse in the woocommerce settings (#21925) --- .../erpnext_integrations/connectors/woocommerce_connection.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py index 44f87e0462e..1b0c9f60b6e 100644 --- a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py +++ b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py @@ -182,7 +182,8 @@ def set_items_in_sales_order(new_sales_order, woocommerce_settings, order, sys_l company_abbr = frappe.db.get_value('Company', woocommerce_settings.company, 'abbr') default_warehouse = _("Stores - {0}", sys_lang).format(company_abbr) - if not frappe.db.exists("Warehouse", default_warehouse): + if not frappe.db.exists("Warehouse", default_warehouse) \ + and not woocommerce_settings.warehouse: frappe.throw(_("Please set Warehouse in Woocommerce Settings")) for item in order.get("line_items"): From 7402451b9695f97937d534ae649c4029491cd36a Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Tue, 26 May 2020 17:56:40 +0530 Subject: [PATCH 189/608] fix: shopify error message on failure of sales order creation (#21924) --- erpnext/erpnext_integrations/connectors/shopify_connection.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/erpnext_integrations/connectors/shopify_connection.py b/erpnext/erpnext_integrations/connectors/shopify_connection.py index ca0e1609cb0..7046038fb24 100644 --- a/erpnext/erpnext_integrations/connectors/shopify_connection.py +++ b/erpnext/erpnext_integrations/connectors/shopify_connection.py @@ -95,10 +95,10 @@ def create_sales_order(shopify_order, shopify_settings, company=None): items = get_order_items(shopify_order.get("line_items"), shopify_settings) if not items: - message = 'Following items are exists in order but relevant record not found in Product master' + message = 'Following items exists in the shopify order but relevant records were not found in the shopify Product master' message += "\n" + ", ".join(product_not_exists) - make_shopify_log(status="Error", exception=e, rollback=True) + make_shopify_log(status="Error", exception=message, rollback=True) return '' From 93e15c5f5c7581047929407fa599cce5abaaea43 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Tue, 26 May 2020 18:00:37 +0530 Subject: [PATCH 190/608] fix: showing Wrong balance on allocation boundary dates (#21904) --- .../employee_leave_balance.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py index 97be5cd813c..db1d191758f 100644 --- a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py +++ b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py @@ -3,13 +3,13 @@ from __future__ import unicode_literals import frappe -from frappe.utils import flt +from frappe.utils import flt, add_days from frappe import _ -from erpnext.hr.doctype.leave_application.leave_application import get_leaves_for_period, get_leave_balance_on, get_leave_allocation_records +from erpnext.hr.doctype.leave_application.leave_application import get_leaves_for_period, get_leave_balance_on def execute(filters=None): if filters.to_date <= filters.from_date: - frappe.throw(_('From date can not be greater than than To date')) + frappe.throw(_('"From date" can not be greater than or equal to "To date"')) columns = get_columns() data = get_data(filters) @@ -104,14 +104,17 @@ def get_data(filters): new_allocation, expired_leaves = get_allocated_and_expired_leaves(filters.from_date, filters.to_date, employee.name, leave_type) - opening = get_leave_balance_on(employee.name, leave_type, filters.from_date) - closing = get_leave_balance_on(employee.name, leave_type, filters.to_date) + opening = get_leave_balance_on(employee.name, leave_type, add_days(filters.from_date, -1)) #allocation boundary condition row.leaves_allocated = new_allocation row.leaves_expired = expired_leaves - leaves_taken if expired_leaves - leaves_taken > 0 else 0 row.opening_balance = opening row.leaves_taken = leaves_taken - row.closing_balance = closing + + # not be shown on the basis of days left it create in user mind for carry_forward leave + row.closing_balance = (new_allocation + opening - (row.leaves_expired + leaves_taken)) + + row.indent = 1 data.append(row) new_leaves_allocated = 0 @@ -177,7 +180,7 @@ def get_allocated_and_expired_leaves(from_date, to_date, employee, leave_type): }, as_dict=1) for record in records: - if record.to_date <= getdate(to_date): + if record.to_date < getdate(to_date): expired_leaves += record.leaves if record.from_date >= getdate(from_date): From 752ca63fea5f722e0bc119a4f303e42b056b5b70 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Tue, 26 May 2020 18:01:03 +0530 Subject: [PATCH 191/608] fix: Was showing opening on date not remaimning leave (#21899) --- .../employee_leave_balance_summary.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py b/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py index a5cdecf36a7..92715d34453 100644 --- a/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py +++ b/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py @@ -6,7 +6,7 @@ import frappe from frappe import _ from frappe.utils import flt from erpnext.hr.doctype.leave_application.leave_application \ - import get_leave_balance_on, get_leaves_for_period + import get_leave_details from erpnext.hr.report.employee_leave_balance.employee_leave_balance \ import get_department_leave_approver_map @@ -61,14 +61,14 @@ def get_data(filters, leave_types): if (len(leave_approvers) and user in leave_approvers) or (user in ["Administrator", employee.user_id]) or ("HR Manager" in frappe.get_roles(user)): row = [employee.name, employee.employee_name, employee.department] - + available_leave = get_leave_details(employee.name, filters.date) for leave_type in leave_types: - + remaining = 0 + if leave_type in available_leave["leave_allocation"]: # opening balance - opening = get_leave_balance_on(employee.name, leave_type, filters.date) + remaining = available_leave["leave_allocation"][leave_type]['remaining_leaves'] - - row += [opening] + row += [remaining] data.append(row) From 5fa207f0200048bd521443d32f157b29969fdf03 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Tue, 26 May 2020 18:02:21 +0530 Subject: [PATCH 192/608] fix(UAE): Incorrect VAT rate display in Sales Invoice (#21882) --- erpnext/regional/united_arab_emirates/utils.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/erpnext/regional/united_arab_emirates/utils.py b/erpnext/regional/united_arab_emirates/utils.py index a01c6ceec36..772bbf5914b 100644 --- a/erpnext/regional/united_arab_emirates/utils.py +++ b/erpnext/regional/united_arab_emirates/utils.py @@ -1,6 +1,8 @@ from __future__ import unicode_literals +import frappe from frappe.utils import flt from erpnext.controllers.taxes_and_totals import get_itemised_tax +from six import iteritems def update_itemised_tax_data(doc): if not doc.taxes: return @@ -9,7 +11,14 @@ def update_itemised_tax_data(doc): for row in doc.items: tax_rate = 0.0 - if itemised_tax.get(row.item_code): + item_tax_rate = frappe.parse_json(row.item_tax_rate) + + # First check if tax rate is present + # If not then look up in item_wise_tax_detail + if item_tax_rate: + for account, rate in iteritems(item_tax_rate): + tax_rate += rate + elif itemised_tax.get(row.item_code): tax_rate = sum([tax.get('tax_rate', 0) for d, tax in itemised_tax.get(row.item_code).items()]) row.tax_rate = flt(tax_rate, row.precision("tax_rate")) From 56357fafeb9bad805e466a5dc9c1ca29295ed192 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Tue, 26 May 2020 18:08:22 +0530 Subject: [PATCH 193/608] fix: Added column Expired Leave (#21857) --- .../doctype/leave_application/leave_application.py | 11 ++++++++++- .../leave_application_dashboard.html | 12 +++++++----- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index 84f2c836cc6..f2968bcd889 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -438,14 +438,23 @@ def get_leave_details(employee, date): leave_allocation = {} for d in allocation_records: allocation = allocation_records.get(d, frappe._dict()) + + total_allocated_leaves = frappe.db.get_value('Leave Allocation', { + 'from_date': ('<=', date), + 'to_date': ('>=', date), + 'leave_type': allocation.leave_type, + }, 'SUM(total_leaves_allocated)') or 0 + remaining_leaves = get_leave_balance_on(employee, d, date, to_date = allocation.to_date, consider_all_leaves_in_the_allocation_period=True) + end_date = allocation.to_date leaves_taken = get_leaves_for_period(employee, d, allocation.from_date, end_date) * -1 leaves_pending = get_pending_leaves_for_period(employee, d, allocation.from_date, end_date) leave_allocation[d] = { - "total_leaves": allocation.total_leaves_allocated, + "total_leaves": total_allocated_leaves, + "expired_leaves": total_allocated_leaves - (remaining_leaves + leaves_taken), "leaves_taken": leaves_taken, "pending_leaves": leaves_pending, "remaining_leaves": remaining_leaves} diff --git a/erpnext/hr/doctype/leave_application/leave_application_dashboard.html b/erpnext/hr/doctype/leave_application/leave_application_dashboard.html index 2385b6ac1c4..295f3b43419 100644 --- a/erpnext/hr/doctype/leave_application/leave_application_dashboard.html +++ b/erpnext/hr/doctype/leave_application/leave_application_dashboard.html @@ -4,11 +4,12 @@ - - - - - + + + + + + @@ -17,6 +18,7 @@ + From d28854b3c23d83c95c3f461a8a2aa35f5082c490 Mon Sep 17 00:00:00 2001 From: Rohan Date: Tue, 26 May 2020 18:13:25 +0530 Subject: [PATCH 194/608] fix: throw error if no serial numbers are found in Pick List (#21915) Co-authored-by: Marica --- erpnext/stock/doctype/pick_list/pick_list.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index 1f8d009f925..93b29c8daff 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -24,6 +24,9 @@ class PickList(Document): for item in self.locations: if not frappe.get_cached_value('Item', item.item_code, 'has_serial_no'): continue + if not item.serial_no: + frappe.throw(_("Row #{0}: {1} does not have any available serial numbers in {2}".format( + frappe.bold(item.idx), frappe.bold(item.item_code), frappe.bold(item.warehouse)))) if len(item.serial_no.split('\n')) == item.picked_qty: continue frappe.throw(_('For item {0} at row {1}, count of serial numbers does not match with the picked quantity') From c0b4ec52de7e2c03fc0e478825750d2c060b618b Mon Sep 17 00:00:00 2001 From: Afshan <33727827+AfshanKhan@users.noreply.github.com> Date: Tue, 26 May 2020 18:20:47 +0530 Subject: [PATCH 195/608] fix: UX date range field separated in from and to date fields. (#21765) * fix for issue #21419 * changing group by filter to default Group by Voucher (Consolidated) * divided date range to seperate from and to date * divided date range to seperate from and to date for purcase and sales register --- .../item_wise_purchase_register.js | 17 ++++++++++++----- .../item_wise_purchase_register.py | 1 - .../item_wise_sales_register.js | 17 ++++++++++++----- .../item_wise_sales_register.py | 1 - 4 files changed, 24 insertions(+), 12 deletions(-) diff --git a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.js b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.js index f88906a0f1f..b709ab9b57d 100644 --- a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.js +++ b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.js @@ -4,11 +4,18 @@ frappe.query_reports["Item-wise Purchase Register"] = { "filters": [ { - "fieldname":"date_range", - "label": __("Date Range"), - "fieldtype": "DateRange", - "default": [frappe.datetime.add_months(frappe.datetime.get_today(),-1), frappe.datetime.get_today()], - "reqd": 1 + "fieldname":"from_date", + "label": __("From Date"), + "fieldtype": "Date", + "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1), + "reqd": 1, + }, + { + "fieldname":"to_date", + "label": __("To Date"), + "fieldtype": "Date", + "default": frappe.datetime.get_today(), + "reqd": 1, }, { "fieldname": "item_code", diff --git a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py index 1f78c7a006f..9777ed1dfde 100644 --- a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py +++ b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py @@ -14,7 +14,6 @@ def execute(filters=None): def _execute(filters=None, additional_table_columns=None, additional_query_columns=None): if not filters: filters = {} - filters.update({"from_date": filters.get("date_range")[0], "to_date": filters.get("date_range")[1]}) columns = get_columns(additional_table_columns, filters) company_currency = erpnext.get_company_currency(filters.company) diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.js b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.js index 8a9c76f26f8..39fb3ca5ee3 100644 --- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.js +++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.js @@ -4,11 +4,18 @@ frappe.query_reports["Item-wise Sales Register"] = { "filters": [ { - "fieldname": "date_range", - "label": __("Date Range"), - "fieldtype": "DateRange", - "default": [frappe.datetime.add_months(frappe.datetime.get_today(),-1), frappe.datetime.get_today()], - "reqd": 1 + "fieldname":"from_date", + "label": __("From Date"), + "fieldtype": "Date", + "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1), + "reqd": 1, + }, + { + "fieldname":"to_date", + "label": __("To Date"), + "fieldtype": "Date", + "default": frappe.datetime.get_today(), + "reqd": 1, }, { "fieldname": "customer", diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py index 92a22e62f14..bb78ee2d675 100644 --- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py +++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py @@ -14,7 +14,6 @@ def execute(filters=None): def _execute(filters=None, additional_table_columns=None, additional_query_columns=None): if not filters: filters = {} - filters.update({"from_date": filters.get("date_range") and filters.get("date_range")[0], "to_date": filters.get("date_range") and filters.get("date_range")[1]}) columns = get_columns(additional_table_columns, filters) company_currency = frappe.get_cached_value('Company', filters.get("company"), "default_currency") From 49b2b155b6122ade55bb723c2d979a96e568446d Mon Sep 17 00:00:00 2001 From: Chinmay Pai Date: Tue, 26 May 2020 18:21:11 +0530 Subject: [PATCH 196/608] fix: sanitize all-products search before displaying results (#21764) Signed-off-by: Chinmay D. Pai --- erpnext/www/all-products/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/www/all-products/index.html b/erpnext/www/all-products/index.html index f09021412b7..0126b59c64a 100644 --- a/erpnext/www/all-products/index.html +++ b/erpnext/www/all-products/index.html @@ -11,7 +11,7 @@
From 18e7b60acc103ef505afd2c0880576bf9d143288 Mon Sep 17 00:00:00 2001 From: Abhishek Balam Date: Tue, 26 May 2020 18:37:02 +0530 Subject: [PATCH 197/608] feat(Selling): Gross Profit In Quotation (#21795) * added valuation rate and gross_profit in quotation item table * modified code to not fetch valuation_rate manually, used set_gross_profit from selling controller --- erpnext/controllers/selling_controller.py | 2 +- erpnext/public/js/controllers/transaction.js | 2 +- .../selling/doctype/quotation/quotation.py | 4 +-- .../quotation_item/quotation_item.json | 34 ++++++++++++++++++- 4 files changed, 37 insertions(+), 5 deletions(-) diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index 1e0a48c134b..b696ac39f69 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -361,7 +361,7 @@ class SellingController(StockController): self.po_no = ', '.join(list(set([d.po_no for d in po_nos if d.po_no]))) def set_gross_profit(self): - if self.doctype == "Sales Order": + if self.doctype in ["Sales Order", "Quotation"]: for item in self.items: item.gross_profit = flt(((item.base_rate - item.valuation_rate) * item.stock_qty), self.precision("amount", item)) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 637d3b3267d..524a95804fd 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1711,7 +1711,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ }, set_gross_profit: function(item) { - if (this.frm.doc.doctype == "Sales Order" && item.valuation_rate) { + if (["Sales Order", "Quotation"].includes(this.frm.doc.doctype) && item.valuation_rate) { var rate = flt(item.rate) * flt(this.frm.doc.conversion_rate || 1); item.gross_profit = flt(((rate - item.valuation_rate) * item.stock_qty), precision("amount", item)); } diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index 7cfec5a046a..0e771c3025a 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -197,9 +197,9 @@ def set_expired_status(): cond = "qo.docstatus = 1 and qo.status != 'Expired' and qo.valid_till < %s" # check if those QUO have SO against it so_against_quo = """ - SELECT + SELECT so.name FROM `tabSales Order` so, `tabSales Order Item` so_item - WHERE + WHERE so_item.docstatus = 1 and so.docstatus = 1 and so_item.parent = so.name and so_item.prevdoc_docname = qo.name""" diff --git a/erpnext/selling/doctype/quotation_item/quotation_item.json b/erpnext/selling/doctype/quotation_item/quotation_item.json index d50397cfad4..59ae7b23239 100644 --- a/erpnext/selling/doctype/quotation_item/quotation_item.json +++ b/erpnext/selling/doctype/quotation_item/quotation_item.json @@ -48,6 +48,10 @@ "base_net_amount", "pricing_rules", "is_free_item", + "section_break_43", + "valuation_rate", + "column_break_45", + "gross_profit", "item_weight_details", "weight_per_unit", "total_weight", @@ -602,12 +606,40 @@ "label": "Against Blanket Order", "no_copy": 1, "print_hide": 1 + }, + { + "fieldname": "section_break_43", + "fieldtype": "Section Break" + }, + { + "fieldname": "valuation_rate", + "fieldtype": "Currency", + "label": "Valuation Rate", + "no_copy": 1, + "options": "Company:company:default_currency", + "print_hide": 1, + "read_only": 1, + "report_hide": 1 + }, + { + "fieldname": "column_break_45", + "fieldtype": "Column Break" + }, + { + "fieldname": "gross_profit", + "fieldtype": "Currency", + "label": "Gross Profit", + "no_copy": 1, + "options": "Company:company:default_currency", + "print_hide": 1, + "read_only": 1, + "report_hide": 1 } ], "idx": 1, "istable": 1, "links": [], - "modified": "2020-03-30 18:40:28.782720", + "modified": "2020-05-19 20:48:43.222229", "modified_by": "Administrator", "module": "Selling", "name": "Quotation Item", From 11ea0b1289288f61c1a4744b1d1983527c9239ab Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 26 May 2020 19:23:45 +0530 Subject: [PATCH 198/608] fix: Do not add filters in report on accounting dimension creation if it already exists --- .../accounts_payable/accounts_payable.js | 9 +-------- .../accounts_payable_summary.js | 9 +-------- .../accounts_receivable/accounts_receivable.js | 9 +-------- .../accounts_receivable_summary.js | 9 +-------- .../report/balance_sheet/balance_sheet.js | 2 ++ erpnext/accounts/report/cash_flow/cash_flow.js | 2 ++ .../report/general_ledger/general_ledger.js | 9 +-------- .../profit_and_loss_statement.js | 2 ++ .../purchase_register/purchase_register.js | 9 +-------- .../report/sales_register/sales_register.js | 9 +-------- .../report/trial_balance/trial_balance.js | 9 +-------- erpnext/public/js/financial_statements.js | 11 +---------- erpnext/public/js/utils.js | 18 ++++++++++++++++++ 13 files changed, 33 insertions(+), 74 deletions(-) diff --git a/erpnext/accounts/report/accounts_payable/accounts_payable.js b/erpnext/accounts/report/accounts_payable/accounts_payable.js index 4e09f99ae3e..2aa9618e559 100644 --- a/erpnext/accounts/report/accounts_payable/accounts_payable.js +++ b/erpnext/accounts/report/accounts_payable/accounts_payable.js @@ -135,12 +135,5 @@ frappe.query_reports["Accounts Payable"] = { } } -erpnext.dimension_filters.forEach((dimension) => { - frappe.query_reports["Accounts Payable"].filters.splice(9, 0 ,{ - "fieldname": dimension["fieldname"], - "label": __(dimension["label"]), - "fieldtype": "Link", - "options": dimension["document_type"] - }); -}); +erpnext.utils.add_dimensions('Accounts Payable', 9); diff --git a/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js b/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js index d5f18b09827..9c6b0639c0a 100644 --- a/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js +++ b/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js @@ -104,12 +104,5 @@ frappe.query_reports["Accounts Payable Summary"] = { } } -erpnext.dimension_filters.forEach((dimension) => { - frappe.query_reports["Accounts Payable Summary"].filters.splice(9, 0 ,{ - "fieldname": dimension["fieldname"], - "label": __(dimension["label"]), - "fieldtype": "Link", - "options": dimension["document_type"] - }); -}); +erpnext.utils.add_dimensions('Accounts Payable Summary', 9); diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js index 6208eb6946b..8dc558a611d 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js @@ -199,12 +199,5 @@ frappe.query_reports["Accounts Receivable"] = { } } -erpnext.dimension_filters.forEach((dimension) => { - frappe.query_reports["Accounts Receivable"].filters.splice(9, 0 ,{ - "fieldname": dimension["fieldname"], - "label": __(dimension["label"]), - "fieldtype": "Link", - "options": dimension["document_type"] - }); -}); +erpnext.utils.add_dimensions('Accounts Receivable', 9); diff --git a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js index b316f108d04..32ecc63ab33 100644 --- a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js +++ b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js @@ -122,11 +122,4 @@ frappe.query_reports["Accounts Receivable Summary"] = { } } -erpnext.dimension_filters.forEach((dimension) => { - frappe.query_reports["Accounts Receivable Summary"].filters.splice(9, 0 ,{ - "fieldname": dimension["fieldname"], - "label": __(dimension["label"]), - "fieldtype": "Link", - "options": dimension["document_type"] - }); -}); +erpnext.utils.add_dimensions('Accounts Receivable Summary', 9); diff --git a/erpnext/accounts/report/balance_sheet/balance_sheet.js b/erpnext/accounts/report/balance_sheet/balance_sheet.js index c4c24c0bab3..4a4ad4d71cb 100644 --- a/erpnext/accounts/report/balance_sheet/balance_sheet.js +++ b/erpnext/accounts/report/balance_sheet/balance_sheet.js @@ -4,6 +4,8 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { frappe.query_reports["Balance Sheet"] = $.extend({}, erpnext.financial_statements); + erpnext.utils.add_dimensions('Balance Sheet', 10); + frappe.query_reports["Balance Sheet"]["filters"].push({ "fieldname": "accumulated_values", "label": __("Accumulated Values"), diff --git a/erpnext/accounts/report/cash_flow/cash_flow.js b/erpnext/accounts/report/cash_flow/cash_flow.js index e5d0c899181..a984bf46b50 100644 --- a/erpnext/accounts/report/cash_flow/cash_flow.js +++ b/erpnext/accounts/report/cash_flow/cash_flow.js @@ -5,6 +5,8 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { frappe.query_reports["Cash Flow"] = $.extend({}, erpnext.financial_statements); + erpnext.utils.add_dimensions('Cash Flow', 10); + // The last item in the array is the definition for Presentation Currency // filter. It won't be used in cash flow for now so we pop it. Please take // of this if you are working here. diff --git a/erpnext/accounts/report/general_ledger/general_ledger.js b/erpnext/accounts/report/general_ledger/general_ledger.js index 2aecd6b717d..1fc0f794788 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.js +++ b/erpnext/accounts/report/general_ledger/general_ledger.js @@ -164,12 +164,5 @@ frappe.query_reports["General Ledger"] = { ] } -erpnext.dimension_filters.forEach((dimension) => { - frappe.query_reports["General Ledger"].filters.splice(15, 0 ,{ - "fieldname": dimension["fieldname"], - "label": __(dimension["label"]), - "fieldtype": "Link", - "options": dimension["document_type"] - }); -}); +erpnext.utils.add_dimensions('General Ledger', 15) diff --git a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js index 2b946c05401..1c461efbcd3 100644 --- a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js +++ b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js @@ -6,6 +6,8 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { frappe.query_reports["Profit and Loss Statement"] = $.extend({}, erpnext.financial_statements); + erpnext.utils.add_dimensions('Profit and Loss Statement', 10); + frappe.query_reports["Profit and Loss Statement"]["filters"].push( { "fieldname": "project", diff --git a/erpnext/accounts/report/purchase_register/purchase_register.js b/erpnext/accounts/report/purchase_register/purchase_register.js index b2b95b2b81b..f34ea571639 100644 --- a/erpnext/accounts/report/purchase_register/purchase_register.js +++ b/erpnext/accounts/report/purchase_register/purchase_register.js @@ -56,11 +56,4 @@ frappe.query_reports["Purchase Register"] = { ] } -erpnext.dimension_filters.forEach((dimension) => { - frappe.query_reports["Purchase Register"].filters.splice(7, 0 ,{ - "fieldname": dimension["fieldname"], - "label": __(dimension["label"]), - "fieldtype": "Link", - "options": dimension["document_type"] - }); -}); \ No newline at end of file +erpnext.utils.add_dimensions('Purchase Register', 7); \ No newline at end of file diff --git a/erpnext/accounts/report/sales_register/sales_register.js b/erpnext/accounts/report/sales_register/sales_register.js index 9dee656d4a9..85bbceab827 100644 --- a/erpnext/accounts/report/sales_register/sales_register.js +++ b/erpnext/accounts/report/sales_register/sales_register.js @@ -68,12 +68,5 @@ frappe.query_reports["Sales Register"] = { ] } -erpnext.dimension_filters.forEach((dimension) => { - frappe.query_reports["Sales Register"].filters.splice(7, 0 ,{ - "fieldname": dimension["fieldname"], - "label": __(dimension["label"]), - "fieldtype": "Link", - "options": dimension["document_type"] - }); -}); +erpnext.utils.add_dimensions('Sales Register', 7); diff --git a/erpnext/accounts/report/trial_balance/trial_balance.js b/erpnext/accounts/report/trial_balance/trial_balance.js index 07752e1e626..9c0854c5d3a 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.js +++ b/erpnext/accounts/report/trial_balance/trial_balance.js @@ -102,14 +102,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { "initial_depth": 3 } - erpnext.dimension_filters.forEach((dimension) => { - frappe.query_reports["Trial Balance"].filters.splice(6, 0 ,{ - "fieldname": dimension["fieldname"], - "label": __(dimension["label"]), - "fieldtype": "Link", - "options": dimension["document_type"] - }); - }); + erpnext.utils.add_dimensions('Trial Balance', 6); }); diff --git a/erpnext/public/js/financial_statements.js b/erpnext/public/js/financial_statements.js index 296c6280d84..cf98b7534e9 100644 --- a/erpnext/public/js/financial_statements.js +++ b/erpnext/public/js/financial_statements.js @@ -62,7 +62,7 @@ erpnext.financial_statements = { } }; -function get_filters(){ +function get_filters() { let filters = [ { "fieldname":"company", @@ -162,15 +162,6 @@ function get_filters(){ } ] - erpnext.dimension_filters.forEach((dimension) => { - filters.push({ - "fieldname": dimension["fieldname"], - "label": __(dimension["label"]), - "fieldtype": "Link", - "options": dimension["document_type"] - }); - }); - return filters; } diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index 58969f2a9fd..3eeafe124cc 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -145,6 +145,7 @@ $.extend(erpnext.utils, { }, get_party_name: function(party_type) { + console.log("********"); var dict = {'Customer': 'customer_name', 'Supplier': 'supplier_name', 'Employee': 'employee_name', 'Member': 'member_name'}; return dict[party_type]; @@ -191,6 +192,23 @@ $.extend(erpnext.utils, { }) }, + add_dimensions: function(report_name, index) { + let filters = frappe.query_reports[report_name].filters; + + erpnext.dimension_filters.forEach((dimension) => { + let found = filters.some(el => el.fieldname === dimension['fieldname']); + + if (!found) { + filters.splice(index, 0 ,{ + "fieldname": dimension["fieldname"], + "label": __(dimension["label"]), + "fieldtype": "Link", + "options": dimension["document_type"] + }); + } + }); + }, + make_subscription: function(doctype, docname) { frappe.call({ method: "frappe.automation.doctype.auto_repeat.auto_repeat.make_auto_repeat", From bcee78d054ddda75421c7c0ea98e4427683cd087 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 26 May 2020 19:26:47 +0530 Subject: [PATCH 199/608] fix: Remove console statement --- erpnext/public/js/utils.js | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index 3eeafe124cc..2cd79b5d5e7 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -145,7 +145,6 @@ $.extend(erpnext.utils, { }, get_party_name: function(party_type) { - console.log("********"); var dict = {'Customer': 'customer_name', 'Supplier': 'supplier_name', 'Employee': 'employee_name', 'Member': 'member_name'}; return dict[party_type]; From 1e04b455f07173f8f5522e47d6ef7d9035e39990 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 26 May 2020 23:07:11 +0530 Subject: [PATCH 200/608] fix: manufacturing dashboard and work order summary chart --- erpnext/manufacturing/dashboard_fixtures.py | 42 ++++++++++--------- .../downtime_entry/downtime_entry.json | 11 ++++- .../downtime_analysis/downtime_analysis.py | 11 +++-- .../work_order_summary/work_order_summary.py | 11 ++--- 4 files changed, 44 insertions(+), 31 deletions(-) diff --git a/erpnext/manufacturing/dashboard_fixtures.py b/erpnext/manufacturing/dashboard_fixtures.py index ef61f230acd..4a17fd07fbb 100644 --- a/erpnext/manufacturing/dashboard_fixtures.py +++ b/erpnext/manufacturing/dashboard_fixtures.py @@ -3,7 +3,7 @@ import frappe, erpnext, json from frappe import _ -from frappe.utils import nowdate, get_date_str +from frappe.utils import nowdate, get_first_day, get_last_day, add_months from erpnext.accounts.utils import get_fiscal_year def get_data(): @@ -28,10 +28,10 @@ def get_dashboards(): { "chart": "Job Card Analysis", "width": "Full" } ], "cards": [ - { "card": "Total Work Order" }, - { "card": "Completed Work Order" }, + { "card": "Monthly Total Work Order" }, + { "card": "Monthly Completed Work Order" }, { "card": "Ongoing Job Card" }, - { "card": "Total Quality Inspection"} + { "card": "Monthly Quality Inspection"} ] }] @@ -180,38 +180,37 @@ def get_charts(): }] def get_number_cards(): - fiscal_year = get_fiscal_year(date=nowdate()) - year_start_date = get_date_str(fiscal_year[1]) - year_end_date = get_date_str(fiscal_year[2]) + start_date = add_months(nowdate(), -1) + end_date = nowdate() return [{ "doctype": "Number Card", "document_type": "Work Order", - "name": "Total Work Order", + "name": "Monthly Total Work Order", "filters_json": json.dumps([ ['Work Order', 'docstatus', '=', 1], - ['Work Order', 'creation', 'between', [year_start_date, year_end_date]] + ['Work Order', 'creation', 'between', [start_date, end_date]] ]), "function": "Count", "is_public": 1, - "label": _("Total Work Order"), + "label": _("Monthly Total Work Order"), "show_percentage_stats": 1, - "stats_time_interval": "Monthly" + "stats_time_interval": "Weekly" }, { "doctype": "Number Card", "document_type": "Work Order", - "name": "Completed Work Order", + "name": "Monthly Completed Work Order", "filters_json": json.dumps([ ['Work Order', 'status', '=', 'Completed'], ['Work Order', 'docstatus', '=', 1], - ['Work Order', 'creation', 'between', [year_start_date, year_end_date]] + ['Work Order', 'creation', 'between', [start_date, end_date]] ]), "function": "Count", "is_public": 1, - "label": _("Completed Work Order"), + "label": _("Monthly Completed Work Order"), "show_percentage_stats": 1, - "stats_time_interval": "Monthly" + "stats_time_interval": "Weekly" }, { "doctype": "Number Card", @@ -225,16 +224,19 @@ def get_number_cards(): "is_public": 1, "label": _("Ongoing Job Card"), "show_percentage_stats": 1, - "stats_time_interval": "Monthly" + "stats_time_interval": "Weekly" }, { "doctype": "Number Card", "document_type": "Quality Inspection", - "name": "Total Quality Inspection", - "filters_json": json.dumps([['Quality Inspection', 'docstatus', '=', 1]]), + "name": "Monthly Quality Inspection", + "filters_json": json.dumps([ + ['Quality Inspection', 'docstatus', '=', 1], + ['Quality Inspection', 'creation', 'between', [start_date, end_date]] + ]), "function": "Count", "is_public": 1, - "label": _("Total Quality Inspection"), + "label": _("Monthly Quality Inspection"), "show_percentage_stats": 1, - "stats_time_interval": "Monthly" + "stats_time_interval": "Weekly" }] \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/downtime_entry/downtime_entry.json b/erpnext/manufacturing/doctype/downtime_entry/downtime_entry.json index 9acb4f05133..b301a9ec054 100644 --- a/erpnext/manufacturing/doctype/downtime_entry/downtime_entry.json +++ b/erpnext/manufacturing/doctype/downtime_entry/downtime_entry.json @@ -1,11 +1,13 @@ { "actions": [], "allow_import": 1, + "autoname": "naming_series:", "creation": "2020-04-18 04:50:46.187638", "doctype": "DocType", "editable_grid": 1, "engine": "InnoDB", "field_order": [ + "naming_series", "workstation", "operator", "column_break_4", @@ -78,10 +80,17 @@ "fieldname": "remarks", "fieldtype": "Text", "label": "Remarks" + }, + { + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Naming Series", + "options": "DT-", + "reqd": 1 } ], "links": [], - "modified": "2020-05-19 12:59:37.358483", + "modified": "2020-05-26 22:14:54.479831", "modified_by": "Administrator", "module": "Manufacturing", "name": "Downtime Entry", diff --git a/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.py b/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.py index 2b2be4faa3b..093309a005b 100644 --- a/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.py +++ b/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.py @@ -24,7 +24,12 @@ def get_data(filters): if filters.get("workstation"): query_filters["workstation"] = filters.get("workstation") - return frappe.get_all("Downtime Entry", fields= fields, filters=query_filters) + data = frappe.get_all("Downtime Entry", fields= fields, filters=query_filters) or [] + for d in data: + if d.downtime: + d.downtime = d.downtime / 60 + + return data def get_chart_data(data, columns): labels = sorted(list(set([d.workstation for d in data]))) @@ -44,7 +49,7 @@ def get_chart_data(data, columns): "data": { "labels": labels, "datasets": [ - {"name": "Dataset 1", "values": datasets} + {"name": "Machine Downtime", "values": datasets} ] }, "type": "bar" @@ -88,7 +93,7 @@ def get_columns(filters): "width": 160 }, { - "label": _("Downtime (In Mins)"), + "label": _("Downtime (In Hours)"), "fieldname": "downtime", "fieldtype": "Float", "width": 150 diff --git a/erpnext/manufacturing/report/work_order_summary/work_order_summary.py b/erpnext/manufacturing/report/work_order_summary/work_order_summary.py index bc09ed43354..fb047b230ce 100644 --- a/erpnext/manufacturing/report/work_order_summary/work_order_summary.py +++ b/erpnext/manufacturing/report/work_order_summary/work_order_summary.py @@ -56,7 +56,7 @@ def get_chart_data(data, filters): return get_chart_based_on_qty(data, filters) def get_chart_based_on_status(data): - labels = ["Not Started", "In Process", "Stopped", "Completed"] + labels = ["Completed", "In Process", "Stopped", "Not Started"] status_wise_data = { "Not Started": 0, @@ -66,13 +66,10 @@ def get_chart_based_on_status(data): } for d in data: - if d.status == "In Process" and d.produced_qty: - status_wise_data["Completed"] += d.produced_qty + status_wise_data[d.status] += 1 - status_wise_data[d.status] += d.qty - - values = [status_wise_data["Not Started"], status_wise_data["In Process"], - status_wise_data["Stopped"], status_wise_data["Completed"]] + values = [status_wise_data["Completed"], status_wise_data["In Process"], + status_wise_data["Stopped"], status_wise_data["Not Started"]] chart = { "data": { From e4bbdbbfe1ed5d490f7ce6e46b7bde437c741ccf Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Wed, 27 May 2020 06:17:26 +0000 Subject: [PATCH 201/608] fix: get data from request directly (#21954) --- erpnext/non_profit/doctype/membership/membership.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/non_profit/doctype/membership/membership.py b/erpnext/non_profit/doctype/membership/membership.py index 5a69cdb6ab0..df19995a1c5 100644 --- a/erpnext/non_profit/doctype/membership/membership.py +++ b/erpnext/non_profit/doctype/membership/membership.py @@ -65,7 +65,9 @@ def get_member_based_on_subscription(subscription_id, email): return frappe.get_doc("Member", members[0]['name']) @frappe.whitelist(allow_guest=True) -def trigger_razorpay_subscription(data): +def trigger_razorpay_subscription(*args, **kwargs): + data = frappe.request.get_data() + if isinstance(data, six.string_types): data = json.loads(data) data = frappe._dict(data) From 2a81058f90dde63020eb309648921206d9172ab0 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Wed, 27 May 2020 06:23:48 +0000 Subject: [PATCH 202/608] refactor: project summary report (#21943) * feat: added more filters * feat: show only first 30 projects in chart * Update erpnext/projects/report/project_summary/project_summary.js Co-authored-by: Marica Co-authored-by: Marica --- .../report/project_summary/project_summary.js | 21 ++++++++++++++++++- .../report/project_summary/project_summary.py | 17 ++++++++++----- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/erpnext/projects/report/project_summary/project_summary.js b/erpnext/projects/report/project_summary/project_summary.js index 15367acd7d3..414b7b206a1 100644 --- a/erpnext/projects/report/project_summary/project_summary.js +++ b/erpnext/projects/report/project_summary/project_summary.js @@ -12,12 +12,31 @@ frappe.query_reports["Project Summary"] = { "default": frappe.defaults.get_user_default("Company"), "reqd": 1 }, + { + "fieldname": "is_active", + "label": __("Is Active"), + "fieldtype": "Select", + "options": "\nYes\nNo", + "default": "Yes", + }, { "fieldname": "status", "label": __("Status"), "fieldtype": "Select", - "options": "Open\nComplete\nCancelled", + "options": "\nOpen\nCompleted\nCancelled", "default": "Open" + }, + { + "fieldname": "project_type", + "label": __("Project Type"), + "fieldtype": "Link", + "options": "Project Type" + }, + { + "fieldname": "priority", + "label": __("Priority"), + "fieldtype": "Select", + "options": "\nLow\nMedium\nHigh" } ] }; diff --git a/erpnext/projects/report/project_summary/project_summary.py b/erpnext/projects/report/project_summary/project_summary.py index a20d7f25a3a..ea7f1ab2e77 100644 --- a/erpnext/projects/report/project_summary/project_summary.py +++ b/erpnext/projects/report/project_summary/project_summary.py @@ -9,7 +9,7 @@ def execute(filters=None): columns = get_columns() data = [] - data = frappe.db.get_all("Project", filters=filters, fields=["name", 'status', "percent_complete", "expected_start_date", "expected_end_date"], order_by="expected_end_date") + data = frappe.db.get_all("Project", filters=filters, fields=["name", 'status', "percent_complete", "expected_start_date", "expected_end_date", "project_type"], order_by="expected_end_date") for project in data: project["total_tasks"] = frappe.db.count("Task", filters={"project": project.name}) @@ -30,6 +30,13 @@ def get_columns(): "options": "Project", "width": 200 }, + { + "fieldname": "project_type", + "label": _("Type"), + "fieldtype": "Link", + "options": "Project Type", + "width": 120 + }, { "fieldname": "status", "label": _("Status"), @@ -88,19 +95,19 @@ def get_chart_data(data): return { "data": { - 'labels': labels, + 'labels': labels[:30], 'datasets': [ { "name": "Overdue", - "values": overdue + "values": overdue[:30] }, { "name": "Completed", - "values": completed + "values": completed[:30] }, { "name": "Total Tasks", - "values": total + "values": total[:30] }, ] }, From 01bced8c67313c36232a5e992e9ef5d3d2ea4476 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Wed, 27 May 2020 12:26:54 +0530 Subject: [PATCH 204/608] fix Report not working on parameter Grade (#21951) --- .../hr/report/employee_analytics/employee_analytics.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/erpnext/hr/report/employee_analytics/employee_analytics.py b/erpnext/hr/report/employee_analytics/employee_analytics.py index df64006c1be..8f393889269 100644 --- a/erpnext/hr/report/employee_analytics/employee_analytics.py +++ b/erpnext/hr/report/employee_analytics/employee_analytics.py @@ -26,7 +26,7 @@ def get_columns(): return [ _("Employee") + ":Link/Employee:120", _("Name") + ":Data:200", _("Date of Birth")+ ":Date:100", _("Branch") + ":Link/Branch:120", _("Department") + ":Link/Department:120", - _("Designation") + ":Link/Designation:120", _("Gender") + "::60", _("Company") + ":Link/Company:120" + _("Designation") + ":Link/Designation:120", _("Gender") + "::100", _("Company") + ":Link/Company:120" ] def get_conditions(filters): @@ -43,7 +43,12 @@ def get_employees(filters): gender, company from `tabEmployee` where status = 'Active' %s""" % conditions, as_list=1) def get_parameters(filters): - return frappe.db.sql("""select name from `tab"""+filters.get("parameter")+"""` """, as_list=1) + if filters.get("parameter") == "Grade": + parameter = "Employee Grade" + else: + parameter = filters.get("parameter") + + return frappe.db.sql("""select name from `tab"""+ parameter +"""` """, as_list=1) def get_chart_data(parameters,employees, filters): if not parameters: From ef0026c06f224a486978164325461abb81e32aac Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Wed, 27 May 2020 12:28:11 +0530 Subject: [PATCH 205/608] fix: addtional salary date validation (#21952) --- erpnext/hr/doctype/additional_salary/additional_salary.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/hr/doctype/additional_salary/additional_salary.py b/erpnext/hr/doctype/additional_salary/additional_salary.py index bab6fb545f8..e369ba7cef6 100644 --- a/erpnext/hr/doctype/additional_salary/additional_salary.py +++ b/erpnext/hr/doctype/additional_salary/additional_salary.py @@ -37,7 +37,7 @@ class AdditionalSalary(Document): frappe.throw(_("Payroll date can not be less than employee's joining date.")) elif getdate(self.from_date) < getdate(date_of_joining): frappe.throw(_("From date can not be less than employee's joining date.")) - elif getdate(self.to_date) > getdate(relieving_date): + elif relieving_date and getdate(self.to_date) > getdate(relieving_date): frappe.throw(_("To date can not be greater than employee's relieving date.")) def get_amount(self, sal_start_date, sal_end_date): From a18c896a5641fb74b7cb82bf6808a0b7dcdcfb6d Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 27 May 2020 12:43:44 +0530 Subject: [PATCH 206/608] fix: titles and order of Healthcare Onboarding steps (#21948) * fix(Healthcare): title and order of onboarding steps * refactor: healthcare settings tour --- .../doctype/healthcare_settings/healthcare_settings.js | 10 +++++----- .../module_onboarding/healthcare/healthcare.json | 10 +++++----- .../create_healthcare_practitioner.json} | 8 ++++---- .../explore_clinical_procedure_templates.json | 2 +- .../explore_healthcare_settings.json | 2 +- .../introduction_to_healthcare_practitioner.json} | 6 +++--- 6 files changed, 19 insertions(+), 19 deletions(-) rename erpnext/healthcare/onboarding_step/{create_practitioner/create_practitioner.json => create_healthcare_practitioner/create_healthcare_practitioner.json} (64%) rename erpnext/healthcare/onboarding_step/{setup_schedule_and_employee_for_healthcare_practitioner/setup_schedule_and_employee_for_healthcare_practitioner.json => introduction_to_healthcare_practitioner/introduction_to_healthcare_practitioner.json} (67%) diff --git a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.js b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.js index c266ba86477..cf2276fc07c 100644 --- a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.js +++ b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.js @@ -57,19 +57,19 @@ frappe.tour['Healthcare Settings'] = [ description: __('Checking this will automatically create a Sales Invoice whenever an appointment is booked for a Patient.') }, { - fieldname: 'healthcare_service_items', + fieldname: 'inpatient_visit_charge_item', title: __('Healthcare Service Items'), - description: __('Set up the Healthcare Service Items for billing. Click ') + "here" + __(' to know more') + description: __('You can create a service item for Inpatient Visit Charge and set it here. Similarly, you can set up other Healthcare Service Items for billing in this section. Click ') + "here" + __(' to know more') }, { - fieldname: 'sb_in_ac', + fieldname: 'income_account', title: __('Set up default Accounts for the Healthcare Facility'), description: __('If you wish to override default accounts settings and configure the Income and Receivable accounts for Healthcare, you can do so here.') }, { - fieldname: 'out_patient_sms_alerts', + fieldname: 'send_registration_msg', title: __('Out Patient SMS alerts'), - description: __('You can set up Out Patient SMS alerts here. Click ') + "here" + __(' to know more') + description: __('If you want to send SMS alert on Patient Registration, you can enable this option. Similary, you can set up Out Patient SMS alerts for other functionalities in this section. Click ') + "here" + __(' to know more') } ]; diff --git a/erpnext/healthcare/module_onboarding/healthcare/healthcare.json b/erpnext/healthcare/module_onboarding/healthcare/healthcare.json index db35149f876..3e50726060a 100644 --- a/erpnext/healthcare/module_onboarding/healthcare/healthcare.json +++ b/erpnext/healthcare/module_onboarding/healthcare/healthcare.json @@ -10,7 +10,7 @@ "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/healthcare", "idx": 0, "is_complete": 0, - "modified": "2020-05-19 12:52:09.757729", + "modified": "2020-05-26 23:16:37.603361", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare", @@ -19,14 +19,14 @@ { "step": "Create Patient" }, - { - "step": "Create Practitioner" - }, { "step": "Create Practitioner Schedule" }, { - "step": "Setup Schedule and Employee for Healthcare Practitioner" + "step": "Introduction to Healthcare Practitioner" + }, + { + "step": "Create Healthcare Practitioner" }, { "step": "Explore Healthcare Settings" diff --git a/erpnext/healthcare/onboarding_step/create_practitioner/create_practitioner.json b/erpnext/healthcare/onboarding_step/create_healthcare_practitioner/create_healthcare_practitioner.json similarity index 64% rename from erpnext/healthcare/onboarding_step/create_practitioner/create_practitioner.json rename to erpnext/healthcare/onboarding_step/create_healthcare_practitioner/create_healthcare_practitioner.json index 614b201a58f..c45a347080c 100644 --- a/erpnext/healthcare/onboarding_step/create_practitioner/create_practitioner.json +++ b/erpnext/healthcare/onboarding_step/create_healthcare_practitioner/create_healthcare_practitioner.json @@ -1,6 +1,6 @@ { "action": "Create Entry", - "creation": "2020-05-19 10:39:55.728057", + "creation": "2020-05-19 10:39:55.728058", "docstatus": 0, "doctype": "Onboarding Step", "idx": 0, @@ -8,12 +8,12 @@ "is_mandatory": 1, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-19 12:27:39.851375", + "modified": "2020-05-26 23:16:31.965521", "modified_by": "Administrator", - "name": "Create Practitioner", + "name": "Create Healthcare Practitioner", "owner": "Administrator", "reference_document": "Healthcare Practitioner", "show_full_form": 1, - "title": "Create Practitioner", + "title": "Create Healthcare Practitioner", "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/healthcare/onboarding_step/explore_clinical_procedure_templates/explore_clinical_procedure_templates.json b/erpnext/healthcare/onboarding_step/explore_clinical_procedure_templates/explore_clinical_procedure_templates.json index f0c0f612e18..697b761e528 100644 --- a/erpnext/healthcare/onboarding_step/explore_clinical_procedure_templates/explore_clinical_procedure_templates.json +++ b/erpnext/healthcare/onboarding_step/explore_clinical_procedure_templates/explore_clinical_procedure_templates.json @@ -8,7 +8,7 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-19 11:46:35.085270", + "modified": "2020-05-26 23:10:24.504030", "modified_by": "Administrator", "name": "Explore Clinical Procedure Templates", "owner": "Administrator", diff --git a/erpnext/healthcare/onboarding_step/explore_healthcare_settings/explore_healthcare_settings.json b/erpnext/healthcare/onboarding_step/explore_healthcare_settings/explore_healthcare_settings.json index 2bdab69faa3..b2d5aef4312 100644 --- a/erpnext/healthcare/onboarding_step/explore_healthcare_settings/explore_healthcare_settings.json +++ b/erpnext/healthcare/onboarding_step/explore_healthcare_settings/explore_healthcare_settings.json @@ -8,7 +8,7 @@ "is_mandatory": 1, "is_single": 1, "is_skipped": 0, - "modified": "2020-05-19 12:26:48.682673", + "modified": "2020-05-26 23:10:24.507648", "modified_by": "Administrator", "name": "Explore Healthcare Settings", "owner": "Administrator", diff --git a/erpnext/healthcare/onboarding_step/setup_schedule_and_employee_for_healthcare_practitioner/setup_schedule_and_employee_for_healthcare_practitioner.json b/erpnext/healthcare/onboarding_step/introduction_to_healthcare_practitioner/introduction_to_healthcare_practitioner.json similarity index 67% rename from erpnext/healthcare/onboarding_step/setup_schedule_and_employee_for_healthcare_practitioner/setup_schedule_and_employee_for_healthcare_practitioner.json rename to erpnext/healthcare/onboarding_step/introduction_to_healthcare_practitioner/introduction_to_healthcare_practitioner.json index c5af177e347..fa4c9036d7e 100644 --- a/erpnext/healthcare/onboarding_step/setup_schedule_and_employee_for_healthcare_practitioner/setup_schedule_and_employee_for_healthcare_practitioner.json +++ b/erpnext/healthcare/onboarding_step/introduction_to_healthcare_practitioner/introduction_to_healthcare_practitioner.json @@ -9,12 +9,12 @@ "is_mandatory": 1, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-19 12:26:42.492734", + "modified": "2020-05-26 22:07:07.482530", "modified_by": "Administrator", - "name": "Setup Schedule and Employee for Healthcare Practitioner", + "name": "Introduction to Healthcare Practitioner", "owner": "Administrator", "reference_document": "Healthcare Practitioner", "show_full_form": 0, - "title": "Setup Schedule and Employee for Healthcare Practitioner", + "title": "Introduction to Healthcare Practitioner", "validate_action": 0 } \ No newline at end of file From ed2c1803df5b0640276ff5ed92fb8a8f3480bc8c Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Wed, 27 May 2020 12:46:12 +0530 Subject: [PATCH 207/608] fix: Post Dated unallocated amount not considered in Advance Amount in AR/AP summary (#21837) * fix: Post Dated unallocateed amount not considered in Advance Amount in AR/AP summary report * fix: Add future payment filter in AR/AP summary * fix: Show unallocated future payments only till current creation date * fix: Remove extra query * fix: Remove debug * fix: Condition --- erpnext/accounts/party.py | 8 ++++++-- .../accounts_receivable/accounts_receivable.py | 13 ++++++++++--- .../accounts_receivable_summary.js | 7 ++++++- .../accounts_receivable_summary.py | 2 +- 4 files changed, 23 insertions(+), 7 deletions(-) diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index 528fb4e113a..db91b6696eb 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -602,10 +602,14 @@ def get_party_shipping_address(doctype, name): else: return '' -def get_partywise_advanced_payment_amount(party_type, posting_date = None, company=None): +def get_partywise_advanced_payment_amount(party_type, posting_date = None, future_payment=0, company=None): cond = "1=1" if posting_date: - cond = "posting_date <= '{0}'".format(posting_date) + if future_payment: + cond = "posting_date <= '{0}' OR DATE(creation) <= '{0}' """.format(posting_date) + else: + cond = "posting_date <= '{0}'".format(posting_date) + if company: cond += "and company = '{0}'".format(company) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index a0a1b9783ac..c776d4781e3 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -559,6 +559,14 @@ class ReceivablePayableReport(object): conditions, values = self.prepare_conditions() order_by = self.get_order_by_condition() + if self.filters.show_future_payments: + values.insert(2, self.filters.report_date) + + date_condition = """AND (posting_date <= %s + OR (against_voucher IS NULL AND DATE(creation) <= %s))""" + else: + date_condition = "AND posting_date <=%s" + if self.filters.get(scrub(self.party_type)): select_fields = "debit_in_account_currency as debit, credit_in_account_currency as credit" else: @@ -574,9 +582,8 @@ class ReceivablePayableReport(object): docstatus < 2 and party_type=%s and (party is not null and party != '') - and posting_date <= %s - {1} {2}""" - .format(select_fields, conditions, order_by), values, as_dict=True) + {1} {2} {3}""" + .format(select_fields, date_condition, conditions, order_by), values, as_dict=True) def get_sales_invoices_or_customers_based_on_sales_person(self): if self.filters.get("sales_person"): diff --git a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js index 32ecc63ab33..305cddb102a 100644 --- a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js +++ b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js @@ -111,7 +111,12 @@ frappe.query_reports["Accounts Receivable Summary"] = { "fieldname":"based_on_payment_terms", "label": __("Based On Payment Terms"), "fieldtype": "Check", - } + }, + { + "fieldname":"show_future_payments", + "label": __("Show Future Payments"), + "fieldtype": "Check", + }, ], onload: function(report) { diff --git a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py index aa6b42e89d0..657b3e8f204 100644 --- a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py +++ b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py @@ -33,7 +33,7 @@ class AccountsReceivableSummary(ReceivablePayableReport): self.get_party_total(args) party_advance_amount = get_partywise_advanced_payment_amount(self.party_type, - self.filters.report_date, self.filters.company) or {} + self.filters.report_date, self.filters.show_future_payments, self.filters.company) or {} for party, party_dict in iteritems(self.party_total): if party_dict.outstanding == 0: From 07388495f36c389830277d7a21124593f91d8cb9 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 27 May 2020 12:57:16 +0530 Subject: [PATCH 208/608] fix: label changed in production plan --- .../manufacturing/doctype/production_plan/production_plan.js | 4 ++-- .../report/bom_operations_time/bom_operations_time.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.js b/erpnext/manufacturing/doctype/production_plan/production_plan.js index 64c952b67be..1a64bc5e248 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.js +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.js @@ -201,9 +201,9 @@ frappe.ui.form.on('Production Plan', { title: title, fields: [ { - "fieldtype": "Table MultiSelect", "label": __("Source Warehouses"), + "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", + "description": __("System will pickup the materials from the selected warehouses. If not specified, system will create material request for purchase."), get_query: function () { return { filters: { diff --git a/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.py b/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.py index 1279011b222..e7d92658f7d 100644 --- a/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.py +++ b/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.py @@ -103,7 +103,7 @@ def get_columns(filters): "fieldtype": "Int", "width": 140 }, { - "label": _("Subassembly BOM Count"), + "label": _("Sub-assembly BOM Count"), "fieldname": "used_as_subassembly_items", "fieldtype": "Int", "width": 180 From 0fa1cc647f824cd996823496a098c83011830295 Mon Sep 17 00:00:00 2001 From: Andrew McLeod Date: Wed, 27 May 2020 09:06:35 +0100 Subject: [PATCH 209/608] fix: website_list_for_contact, fix changed frappe.db.exist keyword argument (#21958) --- erpnext/controllers/website_list_for_contact.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/website_list_for_contact.py b/erpnext/controllers/website_list_for_contact.py index ed379389d7e..ecf041efd17 100644 --- a/erpnext/controllers/website_list_for_contact.py +++ b/erpnext/controllers/website_list_for_contact.py @@ -155,7 +155,7 @@ def has_website_permission(doc, ptype, user, verbose=False): return frappe.db.exists(doctype, get_customer_filter(doc, customers)) elif suppliers: fieldname = 'suppliers' if doctype == 'Request for Quotation' else 'supplier' - return frappe.db.exists(doctype, filters={ + return frappe.db.exists(doctype, { 'name': doc.name, fieldname: ["in", suppliers] }) From cb1a910538c6234910a1c1c05ea1cd30adb36272 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 27 May 2020 14:47:46 +0530 Subject: [PATCH 210/608] fix: filters for the manufacturing reports --- .../job_card_summary/job_card_summary.js | 29 ++++++++++++++++--- .../job_card_summary/job_card_summary.py | 29 +++++++++++++++---- .../work_order_summary/work_order_summary.js | 29 ++++++++++++++++--- 3 files changed, 73 insertions(+), 14 deletions(-) diff --git a/erpnext/manufacturing/report/job_card_summary/job_card_summary.js b/erpnext/manufacturing/report/job_card_summary/job_card_summary.js index b7e307183f8..33953b1265e 100644 --- a/erpnext/manufacturing/report/job_card_summary/job_card_summary.js +++ b/erpnext/manufacturing/report/job_card_summary/job_card_summary.js @@ -13,17 +13,38 @@ frappe.query_reports["Job Card Summary"] = { reqd: 1 }, { - label: __("From Date"), + fieldname: "fiscal_year", + label: __("Fiscal Year"), + fieldtype: "Link", + options: "Fiscal Year", + default: frappe.defaults.get_user_default("fiscal_year"), + reqd: 1, + on_change: function(query_report) { + var fiscal_year = query_report.get_values().fiscal_year; + if (!fiscal_year) { + return; + } + frappe.model.with_doc("Fiscal Year", fiscal_year, function(r) { + var fy = frappe.model.get_doc("Fiscal Year", fiscal_year); + frappe.query_report.set_filter_value({ + from_date: fy.year_start_date, + to_date: fy.year_end_date + }); + }); + } + }, + { + label: __("From Posting Date"), fieldname:"from_date", fieldtype: "Date", - default: frappe.datetime.add_months(frappe.datetime.get_today(), -12), + default: frappe.defaults.get_user_default("year_start_date"), reqd: 1 }, { - label: __("To Date"), + label: __("To Posting Datetime"), fieldname:"to_date", fieldtype: "Date", - default: frappe.datetime.get_today(), + default: frappe.defaults.get_user_default("year_end_date"), reqd: 1, }, { diff --git a/erpnext/manufacturing/report/job_card_summary/job_card_summary.py b/erpnext/manufacturing/report/job_card_summary/job_card_summary.py index ae1e4f30469..953d8201a74 100644 --- a/erpnext/manufacturing/report/job_card_summary/job_card_summary.py +++ b/erpnext/manufacturing/report/job_card_summary/job_card_summary.py @@ -15,9 +15,12 @@ def execute(filters=None): return columns, data, None, chart_data def get_data(filters): - query_filters = {"docstatus": ("<", 2)} + query_filters = { + "docstatus": ("=", 1), + "posting_date": ("between", [filters.from_date, filters.to_date]) + } - fields = ["name", "status", "work_order", "production_item", "item_name", + fields = ["name", "status", "work_order", "production_item", "item_name", "posting_date", "total_completed_qty", "workstation", "operation", "employee_name", "total_time_in_mins"] for field in ["work_order", "workstation", "operation", "company"]: @@ -30,12 +33,19 @@ def get_data(filters): if not data: return [] job_cards = [d.name for d in data] + + job_card_time_filter = { + "docstatus": 1, + "parent": ("in", job_cards), + } + job_card_time_details = {} for job_card_data in frappe.get_all("Job Card Time Log", fields=["min(from_time) as from_time", "max(to_time) as to_time", "parent"], - filters={"docstatus": ("<", 2), "parent": ("in", job_cards)}, group_by="parent"): + filters=job_card_time_filter, group_by="parent", debug=1): job_card_time_details[job_card_data.parent] = job_card_data + res = [] for d in data: if d.status == "Material Transferred": d.status = "Open" @@ -43,8 +53,9 @@ def get_data(filters): if job_card_time_details.get(d.name): d.from_time = job_card_time_details.get(d.name).from_time d.to_time = job_card_time_details.get(d.name).to_time + res.append(d) - return data + return res def get_chart_data(job_card_details, filters): labels, periodic_data = prepare_chart_data(job_card_details, filters) @@ -86,10 +97,10 @@ def prepare_chart_data(job_card_details, filters): labels.append(period) for d in job_card_details: - if getdate(d.from_time) >= from_date and getdate(d.to_time) <= end_date: + if getdate(d.posting_date) > from_date and getdate(d.posting_date) <= end_date: status = "Completed" if d.status == "Completed" else "Pending" - if periodic_data.get(status) and periodic_data.get(status).get(period): + if periodic_data.get(status).get(period): periodic_data[status][period] += 1 else: periodic_data[status][period] = 1 @@ -105,6 +116,12 @@ def get_columns(filters): "options": "Job Card", "width": 100 }, + { + "label": _("Posting Date"), + "fieldname": "posting_date", + "fieldtype": "Date", + "width": 100 + }, ] if not filters.get("status"): diff --git a/erpnext/manufacturing/report/work_order_summary/work_order_summary.js b/erpnext/manufacturing/report/work_order_summary/work_order_summary.js index ec9fe35d63a..22928657b9b 100644 --- a/erpnext/manufacturing/report/work_order_summary/work_order_summary.js +++ b/erpnext/manufacturing/report/work_order_summary/work_order_summary.js @@ -13,17 +13,38 @@ frappe.query_reports["Work Order Summary"] = { reqd: 1 }, { - label: __("From Date"), + fieldname: "fiscal_year", + label: __("Fiscal Year"), + fieldtype: "Link", + options: "Fiscal Year", + default: frappe.defaults.get_user_default("fiscal_year"), + reqd: 1, + on_change: function(query_report) { + var fiscal_year = query_report.get_values().fiscal_year; + if (!fiscal_year) { + return; + } + frappe.model.with_doc("Fiscal Year", fiscal_year, function(r) { + var fy = frappe.model.get_doc("Fiscal Year", fiscal_year); + frappe.query_report.set_filter_value({ + from_date: fy.year_start_date, + to_date: fy.year_end_date + }); + }); + } + }, + { + label: __("From Posting Date"), fieldname:"from_date", fieldtype: "Date", - default: frappe.datetime.add_months(frappe.datetime.get_today(), -12), + default: frappe.defaults.get_user_default("year_start_date"), reqd: 1 }, { - label: __("To Date"), + label: __("To Posting Datetime"), fieldname:"to_date", fieldtype: "Date", - default: frappe.datetime.get_today(), + default: frappe.defaults.get_user_default("year_end_date"), reqd: 1, }, { From 45571b99a834642018dd727172ad0efc3fee7851 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 26 May 2020 20:29:08 +0530 Subject: [PATCH 211/608] feat: added tour to manufacturing settings --- .../manufacturing_settings.js | 28 +++++++++++++++++++ .../explore_manufacturing_settings.json | 4 +-- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.js b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.js index ac144e24b10..668e981d188 100644 --- a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.js +++ b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.js @@ -3,3 +3,31 @@ frappe.ui.form.on('Manufacturing Settings', { }); + +frappe.tour["Manufacturing Settings"] = [ + { + fieldname: "material_consumption", + title: __("Allow Multiple Material Consumption"), + description: __("If ticked, multiple materials can be used for a single Work Order. This is useful if one or more time consuming products are being manufactured.") + }, + { + fieldname: "backflush_raw_materials_based_on", + title: __("Backflush Raw Materials"), + description: __("The Stock Entry of type 'Manufacture' is known as backflush. Raw materials being consumed to manufacture finished goods is known as backflushing.

When creating Manufacture Entry, raw-material items are backflushed based on BOM of production item. If you want raw-material items to be backflushed based on Material Transfer entry made against that Work Order instead, then you can set it under this field.") + }, + { + fieldname: "default_wip_warehouse", + title: __("Work In Progress Warehouse"), + description: __("This Warehouse will be auto-updated in the Work In Progress Warehouse field of Work Orders.") + }, + { + fieldname: "default_fg_warehouse", + title: __("Finished Goods Warehouse"), + description: __("This Warehouse will be auto-updated in the Target Warehouse field of Work Order.") + }, + { + fieldname: "update_bom_costs_automatically", + title: __("Update BOM Cost Automatically"), + description: __("If ticked, the BOM cost will be automatically updated based on Valuation Rate / Price List Rate / last purchase rate of raw materials.") + } +]; \ No newline at end of file diff --git a/erpnext/manufacturing/onboarding_step/explore_manufacturing_settings/explore_manufacturing_settings.json b/erpnext/manufacturing/onboarding_step/explore_manufacturing_settings/explore_manufacturing_settings.json index 582aba40d68..7ef202ee4e7 100644 --- a/erpnext/manufacturing/onboarding_step/explore_manufacturing_settings/explore_manufacturing_settings.json +++ b/erpnext/manufacturing/onboarding_step/explore_manufacturing_settings/explore_manufacturing_settings.json @@ -1,5 +1,5 @@ { - "action": "Update Settings", + "action": "Show Form Tour", "creation": "2020-05-19 11:55:11.378374", "docstatus": 0, "doctype": "Onboarding Step", @@ -8,7 +8,7 @@ "is_mandatory": 0, "is_single": 1, "is_skipped": 0, - "modified": "2020-05-19 12:12:28.145366", + "modified": "2020-05-26 20:28:03.558199", "modified_by": "Administrator", "name": "Explore Manufacturing Settings", "owner": "Administrator", From a77032f926fde8244854715f6506e1f081fb3b95 Mon Sep 17 00:00:00 2001 From: Anupam Kumar Date: Wed, 27 May 2020 20:20:20 +0530 Subject: [PATCH 212/608] onbarding steps fix (#21959) --- erpnext/crm/module_onboarding/crm/crm.json | 2 +- .../create_and_send_quotation.json | 6 +++--- erpnext/crm/onboarding_step/create_lead/create_lead.json | 6 +++--- .../create_opportunity/create_opportunity.json | 2 +- .../introduction_to_crm/introduction_to_crm.json | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/erpnext/crm/module_onboarding/crm/crm.json b/erpnext/crm/module_onboarding/crm/crm.json index 694763f7c7f..9b3d91ecee8 100644 --- a/erpnext/crm/module_onboarding/crm/crm.json +++ b/erpnext/crm/module_onboarding/crm/crm.json @@ -16,7 +16,7 @@ "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/CRM", "idx": 0, "is_complete": 0, - "modified": "2020-05-20 12:53:47.029412", + "modified": "2020-05-27 11:33:09.941263", "modified_by": "Administrator", "module": "CRM", "name": "CRM", diff --git a/erpnext/crm/onboarding_step/create_and_send_quotation/create_and_send_quotation.json b/erpnext/crm/onboarding_step/create_and_send_quotation/create_and_send_quotation.json index a6edfd7e53d..9201d77cf87 100644 --- a/erpnext/crm/onboarding_step/create_and_send_quotation/create_and_send_quotation.json +++ b/erpnext/crm/onboarding_step/create_and_send_quotation/create_and_send_quotation.json @@ -8,12 +8,12 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-14 17:30:07.887411", + "modified": "2020-05-27 11:30:28.237263", "modified_by": "Administrator", "name": "Create and Send Quotation", "owner": "Administrator", "reference_document": "Quotation", - "show_full_form": 0, + "show_full_form": 1, "title": "Create and Send Quotation", - "validate_action": 0 + "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/crm/onboarding_step/create_lead/create_lead.json b/erpnext/crm/onboarding_step/create_lead/create_lead.json index 47a45d70a8c..6ff0bd61f12 100644 --- a/erpnext/crm/onboarding_step/create_lead/create_lead.json +++ b/erpnext/crm/onboarding_step/create_lead/create_lead.json @@ -8,12 +8,12 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-14 17:28:36.441387", + "modified": "2020-05-27 11:30:59.493720", "modified_by": "Administrator", "name": "Create Lead", "owner": "Administrator", "reference_document": "Lead", - "show_full_form": 0, + "show_full_form": 1, "title": "Create Lead", - "validate_action": 0 + "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/crm/onboarding_step/create_opportunity/create_opportunity.json b/erpnext/crm/onboarding_step/create_opportunity/create_opportunity.json index 231cf17bb57..9f996d9e2be 100644 --- a/erpnext/crm/onboarding_step/create_opportunity/create_opportunity.json +++ b/erpnext/crm/onboarding_step/create_opportunity/create_opportunity.json @@ -15,5 +15,5 @@ "reference_document": "Opportunity", "show_full_form": 0, "title": "Create Opportunity", - "validate_action": 0 + "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/crm/onboarding_step/introduction_to_crm/introduction_to_crm.json b/erpnext/crm/onboarding_step/introduction_to_crm/introduction_to_crm.json index 552ade0fdda..545a756a596 100644 --- a/erpnext/crm/onboarding_step/introduction_to_crm/introduction_to_crm.json +++ b/erpnext/crm/onboarding_step/introduction_to_crm/introduction_to_crm.json @@ -8,12 +8,12 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-14 17:28:16.448676", + "modified": "2020-05-27 11:28:07.452857", "modified_by": "Administrator", "name": "Introduction to CRM", "owner": "Administrator", "show_full_form": 0, "title": "Introduction to CRM", - "validate_action": 0, + "validate_action": 1, "video_url": "https://www.youtube.com/watch?v=o9XCSZHJfpA" } \ No newline at end of file From d6d84a3c1561195810c6e1da9cdd16ad7ff62b18 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Wed, 27 May 2020 20:21:15 +0530 Subject: [PATCH 213/608] fix: Module Onboarding for HR (#21963) --- .../create_holiday_list/create_holiday_list.json | 4 ++-- .../onboarding_step/create_leave_type/create_leave_type.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/hr/onboarding_step/create_holiday_list/create_holiday_list.json b/erpnext/hr/onboarding_step/create_holiday_list/create_holiday_list.json index 25cb9febd00..208e394560e 100644 --- a/erpnext/hr/onboarding_step/create_holiday_list/create_holiday_list.json +++ b/erpnext/hr/onboarding_step/create_holiday_list/create_holiday_list.json @@ -1,6 +1,6 @@ { "action": "Create Entry", - "creation": "2020-05-14 11:47:34.700174", + "creation": "2020-05-27 11:47:34.700174", "docstatus": 0, "doctype": "Onboarding Step", "idx": 0, @@ -13,7 +13,7 @@ "name": "Create Holiday list", "owner": "Administrator", "reference_document": "Holiday List", - "show_full_form": 0, + "show_full_form": 1, "title": "Create Holiday list", "validate_action": 0 } \ No newline at end of file diff --git a/erpnext/hr/onboarding_step/create_leave_type/create_leave_type.json b/erpnext/hr/onboarding_step/create_leave_type/create_leave_type.json index e8b97c2e5ba..8cbfc5c81f9 100644 --- a/erpnext/hr/onboarding_step/create_leave_type/create_leave_type.json +++ b/erpnext/hr/onboarding_step/create_leave_type/create_leave_type.json @@ -1,6 +1,6 @@ { "action": "Create Entry", - "creation": "2020-05-20 11:17:31.119312", + "creation": "2020-05-27 11:17:31.119312", "docstatus": 0, "doctype": "Onboarding Step", "idx": 0, @@ -13,7 +13,7 @@ "name": "Create Leave Type", "owner": "Administrator", "reference_document": "Leave Type", - "show_full_form": 0, + "show_full_form": 1, "title": "Create Leave Type", "validate_action": 0 } \ No newline at end of file From 873542bc7f57f6627d6a906f133ea179785a3795 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Wed, 27 May 2020 20:26:38 +0530 Subject: [PATCH 214/608] fix: Do not allow backdated stock transactions in previous fiscal year (#21967) --- erpnext/utilities/transaction_base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/utilities/transaction_base.py b/erpnext/utilities/transaction_base.py index ea96503dff9..024aa6f31dd 100644 --- a/erpnext/utilities/transaction_base.py +++ b/erpnext/utilities/transaction_base.py @@ -166,7 +166,7 @@ class TransactionBase(StatusUpdater): last_transaction_time = frappe.db.sql(""" select MAX(timestamp(posting_date, posting_time)) as posting_time from `tabStock Ledger Entry` - where docstatus = 1 and fiscal_year = %s""", (fiscal_year))[0][0] + where docstatus = 1""")[0][0] cur_doc_posting_datetime = "%s %s" % (self.posting_date, self.get("posting_time") or "00:00:00") From 0030b95595e2a74b0167a5e791d62371af4bb24a Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Wed, 27 May 2020 20:27:33 +0530 Subject: [PATCH 215/608] fix: Filtering issues in opneing invoice creation tool (#21969) --- .../opening_invoice_creation_tool.js | 71 ++++++++++++------- 1 file changed, 45 insertions(+), 26 deletions(-) diff --git a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.js b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.js index 4d8da37efe0..699eb08e178 100644 --- a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.js +++ b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.js @@ -11,21 +11,9 @@ frappe.ui.form.on('Opening Invoice Creation Tool', { }; }); - frm.set_query('cost_center', 'invoices', function(doc, cdt, cdn) { - return { - filters: { - 'company': doc.company - } - }; - }); - - frm.set_query('cost_center', function(doc) { - return { - filters: { - 'company': doc.company - } - }; - }); + if (frm.doc.company) { + frm.trigger('setup_company_filters'); + } }, refresh: function(frm) { @@ -51,19 +39,50 @@ frappe.ui.form.on('Opening Invoice Creation Tool', { }); }, - company: function(frm) { - frappe.call({ - method: 'erpnext.accounts.doctype.opening_invoice_creation_tool.opening_invoice_creation_tool.get_temporary_opening_account', - args: { - company: frm.doc.company - }, - callback: (r) => { - if (r.message) { - frm.doc.__onload.temporary_opening_account = r.message; - frm.trigger('update_invoice_table'); + setup_company_filters: function(frm) { + frm.set_query('cost_center', 'invoices', function(doc, cdt, cdn) { + return { + filters: { + 'company': doc.company + } + }; + }); + + frm.set_query('cost_center', function(doc) { + return { + filters: { + 'company': doc.company + } + }; + }); + + frm.set_query('temporary_opening_account', 'invoices', function(doc, cdt, cdn) { + return { + filters: { + 'company': doc.company } } - }) + }); + }, + + company: function(frm) { + if (frm.doc.company) { + + frm.trigger('setup_company_filters'); + + frappe.call({ + method: 'erpnext.accounts.doctype.opening_invoice_creation_tool.opening_invoice_creation_tool.get_temporary_opening_account', + args: { + company: frm.doc.company + }, + callback: (r) => { + if (r.message) { + frm.doc.__onload.temporary_opening_account = r.message; + frm.trigger('update_invoice_table'); + } + } + }) + } }, invoice_type: function(frm) { From a49f045d1435d1fb01547ccba48da4ef00b42710 Mon Sep 17 00:00:00 2001 From: Marica Date: Wed, 27 May 2020 20:28:37 +0530 Subject: [PATCH 216/608] fix: Grammar fixes (#21970) --- erpnext/buying/dashboard_fixtures.py | 6 +++--- erpnext/buying/desk_page/buying/buying.json | 8 ++++---- erpnext/stock/desk_page/stock/stock.json | 10 +++++----- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/erpnext/buying/dashboard_fixtures.py b/erpnext/buying/dashboard_fixtures.py index 0e2f78f3479..186bfb23af9 100644 --- a/erpnext/buying/dashboard_fixtures.py +++ b/erpnext/buying/dashboard_fixtures.py @@ -41,7 +41,7 @@ def get_dashboards(): { "chart": "Top Suppliers", "width": "Full"} ], "cards": [ - { "card": "This Year Purchases"}, + { "card": "Annual Purchase"}, { "card": "Purchase Orders to Receive"}, { "card": "Purchase Orders to Bill"}, { "card": "Active Suppliers"} @@ -142,7 +142,7 @@ def get_charts(): def get_number_cards(): return [ { - "name": "This Year Purchases", + "name": "Annual Purchase", "aggregate_function_based_on": "base_net_total", "doctype": "Number Card", "document_type": "Purchase Order", @@ -155,7 +155,7 @@ def get_number_cards(): ]), "function": "Sum", "is_public": 1, - "label": _("This Year Purchases"), + "label": _("Annual Purchase"), "owner": "Administrator", "show_percentage_stats": 1, "stats_time_interval": "Monthly" diff --git a/erpnext/buying/desk_page/buying/buying.json b/erpnext/buying/desk_page/buying/buying.json index ee18545fd44..d31602b607e 100644 --- a/erpnext/buying/desk_page/buying/buying.json +++ b/erpnext/buying/desk_page/buying/buying.json @@ -55,7 +55,7 @@ "idx": 0, "is_standard": 1, "label": "Buying", - "modified": "2020-05-19 19:44:36.260982", + "modified": "2020-05-27 19:55:22.407528", "modified_by": "Administrator", "module": "Buying", "name": "Buying", @@ -66,7 +66,7 @@ "shortcuts": [ { "color": "#cef6d1", - "format": "{} available", + "format": "{} Available", "label": "Item", "link_to": "Item", "stats_filter": "{\n \"disabled\": 0\n}", @@ -82,7 +82,7 @@ }, { "color": "#ffe8cd", - "format": "{} to Receive", + "format": "{} To Receive", "label": "Purchase Order", "link_to": "Purchase Order", "stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\":[\"in\", [\"To Receive\", \"To Receive and Bill\"]]\n}", @@ -99,7 +99,7 @@ "type": "Report" }, { - "label": "Buying Dashboard", + "label": "Dashboard", "link_to": "Buying", "type": "Dashboard" } diff --git a/erpnext/stock/desk_page/stock/stock.json b/erpnext/stock/desk_page/stock/stock.json index 4506664c1ed..b1835a2adf9 100644 --- a/erpnext/stock/desk_page/stock/stock.json +++ b/erpnext/stock/desk_page/stock/stock.json @@ -58,7 +58,7 @@ "idx": 0, "is_standard": 1, "label": "Stock", - "modified": "2020-05-19 17:36:08.185652", + "modified": "2020-05-27 19:56:28.396962", "modified_by": "Administrator", "module": "Stock", "name": "Stock", @@ -69,7 +69,7 @@ "shortcuts": [ { "color": "#cef6d1", - "format": "{} available", + "format": "{} Available", "label": "Item", "link_to": "Item", "stats_filter": "{\n \"disabled\" : 0\n}", @@ -90,7 +90,7 @@ }, { "color": "#ffe8cd", - "format": "{} to Bill", + "format": "{} To Bill", "label": "Purchase Receipt", "link_to": "Purchase Receipt", "stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\": \"To Bill\"\n}", @@ -98,7 +98,7 @@ }, { "color": "#ffe8cd", - "format": "{} to Bill", + "format": "{} To Bill", "label": "Delivery Note", "link_to": "Delivery Note", "stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\": \"To Bill\"\n}", @@ -115,7 +115,7 @@ "type": "Report" }, { - "label": "Stock Dashboard", + "label": "Dashboard", "link_to": "Stock", "type": "Dashboard" } From 152377f84d7de8b8a0d20d970660ee107d5eca26 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Wed, 27 May 2020 20:29:10 +0530 Subject: [PATCH 217/608] fix: Total for ageing column 121-Above (#21972) --- .../accounts/report/accounts_receivable/accounts_receivable.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index c776d4781e3..d40e58b5287 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -534,7 +534,7 @@ class ReceivablePayableReport(object): def get_ageing_data(self, entry_date, row): # [0-30, 30-60, 60-90, 90-120, 120-above] - row.range1 = row.range2 = row.range3 = row.range4 = range5 = 0.0 + row.range1 = row.range2 = row.range3 = row.range4 = row.range5 = 0.0 if not (self.age_as_on and entry_date): return From 8fcbf1b90e141e59f8072916abe74089497afcd9 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Wed, 27 May 2020 15:01:30 +0000 Subject: [PATCH 218/608] fix: compare start and end time to prevent negative diff (#21974) * fix: compare start and end time to prevent negative diff * feat: parse date when comparing --- erpnext/manufacturing/doctype/job_card/job_card.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py index e43b98aee1a..d97714a23ee 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.py +++ b/erpnext/manufacturing/doctype/job_card/job_card.py @@ -102,8 +102,11 @@ class JobCard(Document): workstation_doc = frappe.get_cached_doc("Workstation", self.workstation) if (not workstation_doc.working_hours or cint(frappe.db.get_single_value("Manufacturing Settings", "allow_overtime"))): - row.remaining_time_in_mins -= time_diff_in_minutes(row.planned_end_time, - row.planned_start_time) + if getdate(row.planned_end_time) < getdate(row.planned_start_time): + row.planned_end_time = add_to_date(row.planned_start_time, minutes=row.time_in_mins) + row.remaining_time_in_mins = 0.0 + else: + row.remaining_time_in_mins -= time_diff_in_minutes(row.planned_end_time, row.planned_start_time) self.update_time_logs(row) return From cb1a7d0fbc54757e6a72d013cd38d4213dd24ae1 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Wed, 27 May 2020 20:36:28 +0530 Subject: [PATCH 219/608] fix: use get_datetime instead of getdate --- erpnext/manufacturing/doctype/job_card/job_card.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py index d97714a23ee..c29d4ba3d5c 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.py +++ b/erpnext/manufacturing/doctype/job_card/job_card.py @@ -102,7 +102,7 @@ class JobCard(Document): workstation_doc = frappe.get_cached_doc("Workstation", self.workstation) if (not workstation_doc.working_hours or cint(frappe.db.get_single_value("Manufacturing Settings", "allow_overtime"))): - if getdate(row.planned_end_time) < getdate(row.planned_start_time): + if get_datetime(row.planned_end_time) < get_datetime(row.planned_start_time): row.planned_end_time = add_to_date(row.planned_start_time, minutes=row.time_in_mins) row.remaining_time_in_mins = 0.0 else: From c2aabb0fbf0efe4491afe68ba1ab13efe4ae0d6f Mon Sep 17 00:00:00 2001 From: Marica Date: Wed, 27 May 2020 20:56:39 +0530 Subject: [PATCH 220/608] fix: Buying Module fixes (#21965) * fix: Buying Module fixes * fix: Delete Report files as well * fix: Add Purchase Order Analysis to Stock & Accounting --- .../desk_page/accounting/accounting.json | 5 +- .../__init__.py | 0 .../purchase_order_items_to_be_billed.js | 8 --- .../purchase_order_items_to_be_billed.json | 33 ------------ .../purchase_order_items_to_be_billed.py | 26 --------- .../module_onboarding/buying/buying.json | 4 +- .../requested_items_to_be_ordered/__init__.py | 0 .../requested_items_to_be_ordered.json | 31 ----------- .../requested_items_to_order.js | 14 ++++- .../requested_items_to_order.py | 54 +++++++++++++------ erpnext/patches.txt | 1 + .../v13_0/delete_old_purchase_reports.py | 15 ++++++ erpnext/stock/desk_page/stock/stock.json | 4 +- .../__init__.py | 0 .../purchase_order_items_to_be_received.json | 34 ------------ .../__init__.py | 0 ..._order_items_to_be_received_or_billed.json | 34 ------------ 17 files changed, 74 insertions(+), 189 deletions(-) delete mode 100644 erpnext/accounts/report/purchase_order_items_to_be_billed/__init__.py delete mode 100644 erpnext/accounts/report/purchase_order_items_to_be_billed/purchase_order_items_to_be_billed.js delete mode 100644 erpnext/accounts/report/purchase_order_items_to_be_billed/purchase_order_items_to_be_billed.json delete mode 100644 erpnext/accounts/report/purchase_order_items_to_be_billed/purchase_order_items_to_be_billed.py delete mode 100644 erpnext/buying/report/requested_items_to_be_ordered/__init__.py delete mode 100644 erpnext/buying/report/requested_items_to_be_ordered/requested_items_to_be_ordered.json create mode 100644 erpnext/patches/v13_0/delete_old_purchase_reports.py delete mode 100644 erpnext/stock/report/purchase_order_items_to_be_received/__init__.py delete mode 100644 erpnext/stock/report/purchase_order_items_to_be_received/purchase_order_items_to_be_received.json delete mode 100644 erpnext/stock/report/purchase_order_items_to_be_received_or_billed/__init__.py delete mode 100644 erpnext/stock/report/purchase_order_items_to_be_received_or_billed/purchase_order_items_to_be_received_or_billed.json diff --git a/erpnext/accounts/desk_page/accounting/accounting.json b/erpnext/accounts/desk_page/accounting/accounting.json index 576d10c0247..42fb9f4f37d 100644 --- a/erpnext/accounts/desk_page/accounting/accounting.json +++ b/erpnext/accounts/desk_page/accounting/accounting.json @@ -18,7 +18,7 @@ { "hidden": 0, "label": "Accounts Payable", - "links": "[\n {\n \"description\": \"Bills raised by Suppliers.\",\n \"label\": \"Purchase Invoice\",\n \"name\": \"Purchase Invoice\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Supplier database.\",\n \"label\": \"Supplier\",\n \"name\": \"Supplier\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Bank/Cash transactions against party or for internal transfer\",\n \"label\": \"Payment Entry\",\n \"name\": \"Payment Entry\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Accounts Payable\",\n \"name\": \"Accounts Payable\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Accounts Payable Summary\",\n \"name\": \"Accounts Payable Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Purchase Register\",\n \"name\": \"Purchase Register\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Item-wise Purchase Register\",\n \"name\": \"Item-wise Purchase Register\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Purchase Order Items To Be Billed\",\n \"name\": \"Purchase Order Items To Be Billed\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Received Items To Be Billed\",\n \"name\": \"Received Items To Be Billed\",\n \"type\": \"report\"\n }\n]" + "links": "[\n {\n \"description\": \"Bills raised by Suppliers.\",\n \"label\": \"Purchase Invoice\",\n \"name\": \"Purchase Invoice\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Supplier database.\",\n \"label\": \"Supplier\",\n \"name\": \"Supplier\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Bank/Cash transactions against party or for internal transfer\",\n \"label\": \"Payment Entry\",\n \"name\": \"Payment Entry\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Accounts Payable\",\n \"name\": \"Accounts Payable\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Accounts Payable Summary\",\n \"name\": \"Accounts Payable Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Purchase Register\",\n \"name\": \"Purchase Register\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Item-wise Purchase Register\",\n \"name\": \"Item-wise Purchase Register\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Order\"\n ],\n \"doctype\": \"Purchase Order\",\n \"is_query_report\": true,\n \"label\": \"Purchase Order Analysis\",\n \"name\": \"Purchase Order Analysis\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Invoice\"\n ],\n \"doctype\": \"Purchase Invoice\",\n \"is_query_report\": true,\n \"label\": \"Received Items To Be Billed\",\n \"name\": \"Received Items To Be Billed\",\n \"type\": \"report\"\n }\n]" }, { "hidden": 0, @@ -94,10 +94,11 @@ "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, + "hide_custom": 0, "idx": 0, "is_standard": 1, "label": "Accounting", - "modified": "2020-05-18 17:27:26.882340", + "modified": "2020-05-27 20:34:50.949772", "modified_by": "Administrator", "module": "Accounts", "name": "Accounting", diff --git a/erpnext/accounts/report/purchase_order_items_to_be_billed/__init__.py b/erpnext/accounts/report/purchase_order_items_to_be_billed/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/erpnext/accounts/report/purchase_order_items_to_be_billed/purchase_order_items_to_be_billed.js b/erpnext/accounts/report/purchase_order_items_to_be_billed/purchase_order_items_to_be_billed.js deleted file mode 100644 index 24c9592c241..00000000000 --- a/erpnext/accounts/report/purchase_order_items_to_be_billed/purchase_order_items_to_be_billed.js +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.query_reports["Purchase Order Items To Be Billed"] = { - "filters": [ - - ] -} diff --git a/erpnext/accounts/report/purchase_order_items_to_be_billed/purchase_order_items_to_be_billed.json b/erpnext/accounts/report/purchase_order_items_to_be_billed/purchase_order_items_to_be_billed.json deleted file mode 100644 index 3645ec02e1d..00000000000 --- a/erpnext/accounts/report/purchase_order_items_to_be_billed/purchase_order_items_to_be_billed.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "add_total_row": 1, - "apply_user_permissions": 1, - "creation": "2013-05-28 15:54:16", - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 3, - "is_standard": "Yes", - "modified": "2017-02-24 20:00:24.302988", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Purchase Order Items To Be Billed", - "owner": "Administrator", - "query": "select \n `tabPurchase Order`.`name` as \"Purchase Order:Link/Purchase Order:120\",\n `tabPurchase Order`.`transaction_date` as \"Date:Date:100\",\n\t`tabPurchase Order`.`supplier` as \"Supplier:Link/Supplier:120\",\n\t`tabPurchase Order`.`supplier_name` as \"Supplier Name::150\",\n\t`tabPurchase Order Item`.`project` as \"Project\",\n\t`tabPurchase Order Item`.item_code as \"Item Code:Link/Item:120\",\n\t`tabPurchase Order Item`.base_amount as \"Amount:Currency:100\",\n\t(`tabPurchase Order Item`.billed_amt * ifnull(`tabPurchase Order`.conversion_rate, 1)) as \"Billed Amount:Currency:100\", \n\t(`tabPurchase Order Item`.base_amount - (`tabPurchase Order Item`.billed_amt * ifnull(`tabPurchase Order`.conversion_rate, 1))) as \"Amount to Bill:Currency:100\",\n\t`tabPurchase Order Item`.item_name as \"Item Name::150\",\n\t`tabPurchase Order Item`.description as \"Description::200\",\n\t`tabPurchase Order`.company as \"Company:Link/Company:\"\nfrom\n\t`tabPurchase Order`, `tabPurchase Order Item`\nwhere\n\t`tabPurchase Order Item`.`parent` = `tabPurchase Order`.`name`\n\tand `tabPurchase Order`.docstatus = 1\n\tand `tabPurchase Order`.status != \"Closed\"\n and `tabPurchase Order Item`.amount > 0\n\tand (`tabPurchase Order Item`.billed_amt * ifnull(`tabPurchase Order`.conversion_rate, 1)) < `tabPurchase Order Item`.base_amount\norder by `tabPurchase Order`.transaction_date asc", - "ref_doctype": "Purchase Invoice", - "report_name": "Purchase Order Items To Be Billed", - "report_type": "Script Report", - "roles": [ - { - "role": "Accounts User" - }, - { - "role": "Purchase User" - }, - { - "role": "Auditor" - }, - { - "role": "Accounts Manager" - } - ] -} \ No newline at end of file diff --git a/erpnext/accounts/report/purchase_order_items_to_be_billed/purchase_order_items_to_be_billed.py b/erpnext/accounts/report/purchase_order_items_to_be_billed/purchase_order_items_to_be_billed.py deleted file mode 100644 index 99d0a368139..00000000000 --- a/erpnext/accounts/report/purchase_order_items_to_be_billed/purchase_order_items_to_be_billed.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - -from __future__ import unicode_literals -import frappe -from frappe import _ -from erpnext.accounts.report.non_billed_report import get_ordered_to_be_billed_data - -def execute(filters=None): - columns = get_column() - args = get_args() - data = get_ordered_to_be_billed_data(args) - return columns, data - -def get_column(): - return [ - _("Purchase Order") + ":Link/Purchase Order:120", _("Status") + "::120", _("Date") + ":Date:100", - _("Suplier") + ":Link/Supplier:120", _("Suplier Name") + "::120", - _("Project") + ":Link/Project:120", _("Item Code") + ":Link/Item:120", - _("Amount") + ":Currency:100", _("Billed Amount") + ":Currency:100", _("Amount to Bill") + ":Currency:100", - _("Item Name") + "::120", _("Description") + "::120", _("Company") + ":Link/Company:120", - ] - -def get_args(): - return {'doctype': 'Purchase Order', 'party': 'supplier', - 'date': 'transaction_date', 'order': 'transaction_date', 'order_by': 'asc'} diff --git a/erpnext/buying/module_onboarding/buying/buying.json b/erpnext/buying/module_onboarding/buying/buying.json index 8c798b34731..8fe2f388b0c 100644 --- a/erpnext/buying/module_onboarding/buying/buying.json +++ b/erpnext/buying/module_onboarding/buying/buying.json @@ -19,7 +19,7 @@ "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/buying", "idx": 0, "is_complete": 0, - "modified": "2020-05-19 20:03:55.776080", + "modified": "2020-05-27 17:17:52.075947", "modified_by": "Administrator", "module": "Buying", "name": "Buying", @@ -49,6 +49,6 @@ ], "subtitle": "Products, Purchases, Analysis and more.", "success_message": "The Buying Module is all set up!", - "title": "Let's Setup the Buying Module.", + "title": "Let's Set Up the Buying Module.", "user_can_dismiss": 1 } \ No newline at end of file diff --git a/erpnext/buying/report/requested_items_to_be_ordered/__init__.py b/erpnext/buying/report/requested_items_to_be_ordered/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/erpnext/buying/report/requested_items_to_be_ordered/requested_items_to_be_ordered.json b/erpnext/buying/report/requested_items_to_be_ordered/requested_items_to_be_ordered.json deleted file mode 100644 index bb112698d32..00000000000 --- a/erpnext/buying/report/requested_items_to_be_ordered/requested_items_to_be_ordered.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "add_total_row": 1, - "creation": "2013-05-13 16:10:02", - "disable_prepared_report": 0, - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 3, - "is_standard": "Yes", - "modified": "2019-04-18 19:02:03.099422", - "modified_by": "Administrator", - "module": "Buying", - "name": "Requested Items To Be Ordered", - "owner": "Administrator", - "prepared_report": 0, - "query": "select \n mr.name as \"Material Request:Link/Material Request:120\",\n\tmr.transaction_date as \"Date:Date:100\",\n\tmr_item.item_code as \"Item Code:Link/Item:120\",\n\tsum(ifnull(mr_item.stock_qty, 0)) as \"Qty:Float:100\",\n\tifnull(mr_item.stock_uom, '') as \"UOM:Link/UOM:100\",\n\tsum(ifnull(mr_item.ordered_qty, 0)) as \"Ordered Qty:Float:100\", \n\t(sum(mr_item.stock_qty) - sum(ifnull(mr_item.ordered_qty, 0))) as \"Qty to Order:Float:100\",\n\tmr_item.item_name as \"Item Name::150\",\n\tmr_item.description as \"Description::200\",\n\tmr.company as \"Company:Link/Company:\"\nfrom\n\t`tabMaterial Request` mr, `tabMaterial Request Item` mr_item\nwhere\n\tmr_item.parent = mr.name\n\tand mr.material_request_type = \"Purchase\"\n\tand mr.docstatus = 1\n\tand mr.status != \"Stopped\"\ngroup by mr.name, mr_item.item_code\nhaving\n\tsum(ifnull(mr_item.ordered_qty, 0)) < sum(ifnull(mr_item.stock_qty, 0))\norder by mr.transaction_date asc", - "ref_doctype": "Purchase Order", - "report_name": "Requested Items To Be Ordered", - "report_type": "Query Report", - "roles": [ - { - "role": "Stock User" - }, - { - "role": "Purchase Manager" - }, - { - "role": "Purchase User" - } - ] - } \ No newline at end of file diff --git a/erpnext/buying/report/requested_items_to_order/requested_items_to_order.js b/erpnext/buying/report/requested_items_to_order/requested_items_to_order.js index 21adb135476..9555e8252a3 100644 --- a/erpnext/buying/report/requested_items_to_order/requested_items_to_order.js +++ b/erpnext/buying/report/requested_items_to_order/requested_items_to_order.js @@ -35,7 +35,7 @@ frappe.query_reports["Requested Items to Order"] = { "fieldtype": "Link", "width": "80", "options": "Material Request", - "get_query": () =>{ + "get_query": () => { return { filters: { "docstatus": 1, @@ -45,6 +45,18 @@ frappe.query_reports["Requested Items to Order"] = { } } }, + { + "fieldname": "item_code", + "label": __("Item"), + "fieldtype": "Link", + "width": "80", + "options": "Item", + "get_query": () => { + return { + query: "erpnext.controllers.queries.item_query" + } + } + }, { "fieldname": "group_by_mr", "label": __("Group by Material Request"), diff --git a/erpnext/buying/report/requested_items_to_order/requested_items_to_order.py b/erpnext/buying/report/requested_items_to_order/requested_items_to_order.py index a021d3c1cab..cca01b104ae 100644 --- a/erpnext/buying/report/requested_items_to_order/requested_items_to_order.py +++ b/erpnext/buying/report/requested_items_to_order/requested_items_to_order.py @@ -44,6 +44,9 @@ def get_conditions(filters): if filters.get("material_request"): conditions += " and mr.name = '{0}'".format(filters.get("material_request")) + if filters.get("item_code"): + conditions += " and mr_item.item_code = '{0}'".format(filters.get("item_code")) + return conditions def get_data(filters, conditions): @@ -74,25 +77,41 @@ def get_data(filters, conditions): return data +def update_qty_columns(row_to_update, data_row): + fields = ["qty", "ordered_qty", "qty_to_order"] + for field in fields: + row_to_update[field] += flt(data_row[field]) + def prepare_data(data, filters): """Prepare consolidated Report data and Chart data""" - material_request_map = {} + material_request_map, item_qty_map = {}, {} for row in data: - if not row["material_request"] in material_request_map: - # create an entry with mr as key - row_copy = copy.deepcopy(row) - material_request_map[row["material_request"]] = row_copy + # item wise map for charts + if not row["item_code"] in item_qty_map: + item_qty_map[row["item_code"]] = { + "qty" : row["qty"], + "ordered_qty" : row["ordered_qty"], + "qty_to_order" : row["qty_to_order"] + } else: - mr_row = material_request_map[row["material_request"]] - mr_row["required_date"] = min(getdate(mr_row["required_date"]), getdate(row["required_date"])) + item_entry = item_qty_map[row["item_code"]] + update_qty_columns(item_entry, row) - #sum numeric rows - fields = ["qty", "ordered_qty", "qty_to_order"] - for field in fields: - mr_row[field] = flt(mr_row[field]) + flt(row[field]) + if filters.get("group_by_mr"): + # consolidated material request map for group by filter + if not row["material_request"] in material_request_map: + # create an entry with mr as key + row_copy = copy.deepcopy(row) + material_request_map[row["material_request"]] = row_copy + else: + mr_row = material_request_map[row["material_request"]] + mr_row["required_date"] = min(getdate(mr_row["required_date"]), getdate(row["required_date"])) - chart_data = prepare_chart_data(material_request_map) + #sum numeric columns + update_qty_columns(mr_row, row) + + chart_data = prepare_chart_data(item_qty_map) if filters.get("group_by_mr"): data =[] @@ -102,12 +121,15 @@ def prepare_data(data, filters): return data, chart_data -def prepare_chart_data(data): +def prepare_chart_data(item_data): labels, qty_to_order, ordered_qty = [], [], [] - for row in data: - mr_row = data[row] - labels.append(mr_row["material_request"]) + if len(item_data) > 30: + item_data = dict(list(item_data.items())[:30]) + + for row in item_data: + mr_row = item_data[row] + labels.append(row) qty_to_order.append(mr_row["qty_to_order"]) ordered_qty.append(mr_row["ordered_qty"]) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 929f8d6b98b..b0b224f084d 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -693,3 +693,4 @@ erpnext.patches.v12_0.update_bom_in_so_mr execute:frappe.delete_doc("Report", "Department Analytics") execute:frappe.rename_doc("Desk Page", "Loan Management", "Loan", force=True) erpnext.patches.v12_0.update_uom_conversion_factor +erpnext.patches.v13_0.delete_old_purchase_reports diff --git a/erpnext/patches/v13_0/delete_old_purchase_reports.py b/erpnext/patches/v13_0/delete_old_purchase_reports.py new file mode 100644 index 00000000000..8271d2e6dce --- /dev/null +++ b/erpnext/patches/v13_0/delete_old_purchase_reports.py @@ -0,0 +1,15 @@ +# Copyright (c) 2019, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals + +import frappe + +def execute(): + reports_to_delete = ["Requested Items To Be Ordered", + "Purchase Order Items To Be Received or Billed","Purchase Order Items To Be Received", + "Purchase Order Items To Be Billed"] + + for report in reports_to_delete: + if frappe.db.exists("Report", report): + frappe.delete_doc("Report", report) \ No newline at end of file diff --git a/erpnext/stock/desk_page/stock/stock.json b/erpnext/stock/desk_page/stock/stock.json index b1835a2adf9..9404292c04f 100644 --- a/erpnext/stock/desk_page/stock/stock.json +++ b/erpnext/stock/desk_page/stock/stock.json @@ -33,7 +33,7 @@ { "hidden": 0, "label": "Key Reports", - "links": "[\n {\n \"dependencies\": [\n \"Item Price\"\n ],\n \"doctype\": \"Item Price\",\n \"is_query_report\": false,\n \"label\": \"Item-wise Price List Rate\",\n \"name\": \"Item-wise Price List Rate\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Stock Entry\"\n ],\n \"doctype\": \"Stock Entry\",\n \"is_query_report\": true,\n \"label\": \"Stock Analytics\",\n \"name\": \"Stock Analytics\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Delivery Note\"\n ],\n \"doctype\": \"Delivery Note\",\n \"is_query_report\": true,\n \"label\": \"Delivery Note Trends\",\n \"name\": \"Delivery Note Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Receipt\"\n ],\n \"doctype\": \"Purchase Receipt\",\n \"is_query_report\": true,\n \"label\": \"Purchase Receipt Trends\",\n \"name\": \"Purchase Receipt Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Delivery Note\"\n ],\n \"doctype\": \"Delivery Note\",\n \"is_query_report\": true,\n \"label\": \"Ordered Items To Be Delivered\",\n \"name\": \"Ordered Items To Be Delivered\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Receipt\"\n ],\n \"doctype\": \"Purchase Receipt\",\n \"is_query_report\": true,\n \"label\": \"Purchase Order Items To Be Received\",\n \"name\": \"Purchase Order Items To Be Received\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Bin\"\n ],\n \"doctype\": \"Bin\",\n \"is_query_report\": true,\n \"label\": \"Item Shortage Report\",\n \"name\": \"Item Shortage Report\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Batch\"\n ],\n \"doctype\": \"Batch\",\n \"is_query_report\": true,\n \"label\": \"Batch-Wise Balance History\",\n \"name\": \"Batch-Wise Balance History\",\n \"type\": \"report\"\n }\n]" + "links": "[\n {\n \"dependencies\": [\n \"Item Price\"\n ],\n \"doctype\": \"Item Price\",\n \"is_query_report\": false,\n \"label\": \"Item-wise Price List Rate\",\n \"name\": \"Item-wise Price List Rate\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Stock Entry\"\n ],\n \"doctype\": \"Stock Entry\",\n \"is_query_report\": true,\n \"label\": \"Stock Analytics\",\n \"name\": \"Stock Analytics\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Delivery Note\"\n ],\n \"doctype\": \"Delivery Note\",\n \"is_query_report\": true,\n \"label\": \"Delivery Note Trends\",\n \"name\": \"Delivery Note Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Receipt\"\n ],\n \"doctype\": \"Purchase Receipt\",\n \"is_query_report\": true,\n \"label\": \"Purchase Receipt Trends\",\n \"name\": \"Purchase Receipt Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Delivery Note\"\n ],\n \"doctype\": \"Delivery Note\",\n \"is_query_report\": true,\n \"label\": \"Ordered Items To Be Delivered\",\n \"name\": \"Ordered Items To Be Delivered\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Order\"\n ],\n \"doctype\": \"Purchase Order\",\n \"is_query_report\": true,\n \"label\": \"Purchase Order Analysis\",\n \"name\": \"Purchase Order Analysis\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Bin\"\n ],\n \"doctype\": \"Bin\",\n \"is_query_report\": true,\n \"label\": \"Item Shortage Report\",\n \"name\": \"Item Shortage Report\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Batch\"\n ],\n \"doctype\": \"Batch\",\n \"is_query_report\": true,\n \"label\": \"Batch-Wise Balance History\",\n \"name\": \"Batch-Wise Balance History\",\n \"type\": \"report\"\n }\n]" }, { "hidden": 0, @@ -58,7 +58,7 @@ "idx": 0, "is_standard": 1, "label": "Stock", - "modified": "2020-05-27 19:56:28.396962", + "modified": "2020-05-27 20:38:25.255323", "modified_by": "Administrator", "module": "Stock", "name": "Stock", diff --git a/erpnext/stock/report/purchase_order_items_to_be_received/__init__.py b/erpnext/stock/report/purchase_order_items_to_be_received/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/erpnext/stock/report/purchase_order_items_to_be_received/purchase_order_items_to_be_received.json b/erpnext/stock/report/purchase_order_items_to_be_received/purchase_order_items_to_be_received.json deleted file mode 100644 index dfaa9ed6cc7..00000000000 --- a/erpnext/stock/report/purchase_order_items_to_be_received/purchase_order_items_to_be_received.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "add_total_row": 1, - "creation": "2013-02-22 18:01:55", - "disable_prepared_report": 0, - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 3, - "is_standard": "Yes", - "modified": "2019-04-01 22:12:05.573343", - "modified_by": "Administrator", - "module": "Stock", - "name": "Purchase Order Items To Be Received", - "owner": "Administrator", - "prepared_report": 0, - "query": "select \n `tabPurchase Order`.`name` as \"Purchase Order:Link/Purchase Order:120\",\n `tabPurchase Order`.`status` as \"Status:Data:120\",\n\t`tabPurchase Order`.`transaction_date` as \"Date:Date:100\",\n\t`tabPurchase Order Item`.`schedule_date` as \"Reqd by Date:Date:110\",\n\t`tabPurchase Order`.`supplier` as \"Supplier:Link/Supplier:120\",\n\t`tabPurchase Order`.`supplier_name` as \"Supplier Name::150\",\n\t`tabPurchase Order Item`.`project` as \"Project\",\n\t`tabPurchase Order Item`.item_code as \"Item Code:Link/Item:120\",\n\t`tabPurchase Order Item`.qty as \"Qty:Float:100\",\n\t`tabPurchase Order Item`.received_qty as \"Received Qty:Float:100\", \n\t(`tabPurchase Order Item`.qty - ifnull(`tabPurchase Order Item`.received_qty, 0)) as \"Qty to Receive:Float:100\",\n `tabPurchase Order Item`.warehouse as \"Warehouse:Link/Warehouse:150\",\n\t`tabPurchase Order Item`.item_name as \"Item Name::150\",\n\t`tabPurchase Order Item`.description as \"Description::200\",\n `tabPurchase Order Item`.brand as \"Brand::100\",\n\t`tabPurchase Order`.`company` as \"Company:Link/Company:\"\nfrom\n\t`tabPurchase Order`, `tabPurchase Order Item`\nwhere\n\t`tabPurchase Order Item`.`parent` = `tabPurchase Order`.`name`\n\tand `tabPurchase Order`.docstatus = 1\n\tand `tabPurchase Order`.status not in (\"Stopped\", \"Closed\")\n\tand ifnull(`tabPurchase Order Item`.received_qty, 0) < ifnull(`tabPurchase Order Item`.qty, 0)\norder by `tabPurchase Order`.transaction_date asc", - "ref_doctype": "Purchase Receipt", - "report_name": "Purchase Order Items To Be Received", - "report_type": "Query Report", - "roles": [ - { - "role": "Stock Manager" - }, - { - "role": "Stock User" - }, - { - "role": "Purchase User" - }, - { - "role": "Accounts User" - } - ] -} \ No newline at end of file diff --git a/erpnext/stock/report/purchase_order_items_to_be_received_or_billed/__init__.py b/erpnext/stock/report/purchase_order_items_to_be_received_or_billed/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/erpnext/stock/report/purchase_order_items_to_be_received_or_billed/purchase_order_items_to_be_received_or_billed.json b/erpnext/stock/report/purchase_order_items_to_be_received_or_billed/purchase_order_items_to_be_received_or_billed.json deleted file mode 100644 index 48c0f423fd9..00000000000 --- a/erpnext/stock/report/purchase_order_items_to_be_received_or_billed/purchase_order_items_to_be_received_or_billed.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "add_total_row": 0, - "creation": "2019-09-16 14:10:33.102865", - "disable_prepared_report": 0, - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 0, - "is_standard": "Yes", - "modified": "2019-09-21 15:19:55.710578", - "modified_by": "Administrator", - "module": "Stock", - "name": "Purchase Order Items To Be Received or Billed", - "owner": "Administrator", - "prepared_report": 0, - "query": "SELECT\n\t`poi_pri`.`purchase_order` as \"Purchase Order:Link/Purchase Order:120\",\n\t`poi_pri`.`status` as \"Status:Data:120\",\n\t`poi_pri`.`transaction_date` as \"Date:Date:100\",\n\t`poi_pri`.`schedule_date` as \"Reqd by Date:Date:110\",\n\t`poi_pri`.`supplier` as \"Supplier:Link/Supplier:120\",\n\t`poi_pri`.`supplier_name` as \"Supplier Name::150\",\n\t`poi_pri`.`item_code` as \"Item Code:Link/Item:120\",\n\t`poi_pri`.`qty` as \"Qty:Float:100\",\n\t`poi_pri`.`base_amount` as \"Base Amount:Currency:100\",\n\t`poi_pri`.`received_qty` as \"Received Qty:Float:100\",\n\t`poi_pri`.`received_amount` as \"Received Qty Amount:Currency:100\",\n\t`poi_pri`.`qty_to_receive` as \"Qty to Receive:Float:100\",\n\t`poi_pri`.`amount_to_be_received` as \"Amount to Receive:Currency:100\",\n\t`poi_pri`.`billed_amount` as \"Billed Amount:Currency:100\",\n\t`poi_pri`.`amount_to_be_billed` as \"Amount To Be Billed:Currency:100\",\n\tSUM(`pii`.`qty`) AS \"Billed Qty:Float:100\",\n\t`poi_pri`.qty - SUM(`pii`.`qty`) AS \"Qty To Be Billed:Float:100\",\n\t`poi_pri`.`warehouse` as \"Warehouse:Link/Warehouse:150\",\n\t`poi_pri`.`item_name` as \"Item Name::150\",\n\t`poi_pri`.`description` as \"Description::200\",\n\t`poi_pri`.`brand` as \"Brand::100\",\n\t`poi_pri`.`project` as \"Project\",\n\t`poi_pri`.`company` as \"Company:Link/Company:\"\nFROM\n\t(SELECT\n\t\t`po`.`name` AS 'purchase_order',\n\t\t`po`.`status`,\n\t\t`po`.`company`,\n\t\t`poi`.`warehouse`,\n\t\t`poi`.`brand`,\n\t\t`poi`.`description`,\n\t\t`po`.`transaction_date`,\n\t\t`poi`.`schedule_date`,\n\t\t`po`.`supplier`,\n\t\t`po`.`supplier_name`,\n\t\t`poi`.`project`,\n\t\t`poi`.`item_code`,\n\t\t`poi`.`item_name`,\n\t\t`poi`.`qty`,\n\t\t`poi`.`base_amount`,\n\t\t`poi`.`received_qty`,\n\t\t(`poi`.billed_amt * ifnull(`po`.conversion_rate, 1)) as billed_amount,\n\t\t(`poi`.base_amount - (`poi`.billed_amt * ifnull(`po`.conversion_rate, 1))) as amount_to_be_billed,\n\t\t`poi`.`qty` - IFNULL(`poi`.`received_qty`, 0) AS 'qty_to_receive',\n\t\t(`poi`.`qty` - IFNULL(`poi`.`received_qty`, 0)) * `poi`.`rate` AS 'amount_to_be_received',\n\t\tSUM(`pri`.`amount`) AS 'received_amount',\n\t\t`poi`.`name` AS 'poi_name',\n\t\t`pri`.`name` AS 'pri_name'\n\tFROM\n\t\t`tabPurchase Order` po\n\t\tLEFT JOIN `tabPurchase Order Item` poi\n\t\tON `poi`.`parent` = `po`.`name`\n\t\tLEFT JOIN `tabPurchase Receipt Item` pri\n\t\tON `pri`.`purchase_order_item` = `poi`.`name`\n\t\t\tAND `pri`.`docstatus`=1\n\tWHERE\n\t\t`po`.`status` not in ('Stopped', 'Closed')\n\t\tAND `po`.`docstatus` = 1\n\t\tAND IFNULL(`poi`.`received_qty`, 0) < IFNULL(`poi`.`qty`, 0)\n\tGROUP BY `poi`.`name`\n\tORDER BY `po`.`transaction_date` ASC\n\t) poi_pri\n\tLEFT JOIN `tabPurchase Invoice Item` pii\n\tON `pii`.`po_detail` = `poi_pri`.`poi_name`\n\t\tAND `pii`.`docstatus`=1\nGROUP BY `poi_pri`.`poi_name`", - "ref_doctype": "Purchase Order", - "report_name": "Purchase Order Items To Be Received or Billed", - "report_type": "Query Report", - "roles": [ - { - "role": "Purchase Manager" - }, - { - "role": "Purchase User" - }, - { - "role": "Stock User" - }, - { - "role": "Stock Manager" - } - ] -} \ No newline at end of file From 7ac731dd4f212c404a0099aa97fb51824e9d4ce6 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Wed, 27 May 2020 21:53:17 +0530 Subject: [PATCH 221/608] fix: raw material warehouse in Production Planning Report (#21982) --- .../production_planning_report/production_planning_report.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/manufacturing/report/production_planning_report/production_planning_report.py b/erpnext/manufacturing/report/production_planning_report/production_planning_report.py index b5e6c6fc853..5ac3923187a 100644 --- a/erpnext/manufacturing/report/production_planning_report/production_planning_report.py +++ b/erpnext/manufacturing/report/production_planning_report/production_planning_report.py @@ -220,6 +220,9 @@ class ProductionPlanReport(object): if item_details: warehouses = [item_details["default_warehouse"]] + if self.filters.raw_material_warehouse: + warehouses = get_child_warehouses(self.filters.raw_material_warehouse) + d.remaining_qty = d.required_qty self.pick_materials_from_warehouses(d, data, warehouses) From 3da51984f2a31fe6e43fcb91a9b8162f262ca66f Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 25 May 2020 21:46:04 +0530 Subject: [PATCH 222/608] chore: Add Import Supplier Invoice to Menu --- erpnext/config/buying.py | 21 +++++++- .../import_supplier_invoice.json | 48 +++++++++++++++++-- 2 files changed, 64 insertions(+), 5 deletions(-) diff --git a/erpnext/config/buying.py b/erpnext/config/buying.py index 16b49a1e578..b06bb76ca82 100644 --- a/erpnext/config/buying.py +++ b/erpnext/config/buying.py @@ -1,8 +1,9 @@ from __future__ import unicode_literals +import frappe from frappe import _ def get_data(): - return [ + config = [ { "label": _("Purchasing"), "icon": "fa fa-star", @@ -243,3 +244,21 @@ def get_data(): }, ] + + regional = { + "label": _("Regional"), + "items": [ + { + "type": "doctype", + "name": "Import Supplier Invoice", + "description": _("Import Italian Supplier Invoice."), + "onboard": 1, + } + ] + } + + countries = frappe.get_all("Company", fields="country") + countries = [country["country"] for country in countries] + if "Italy" in countries: + config.append(regional) + return config \ No newline at end of file diff --git a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.json b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.json index 59e955c23f4..6b45841b465 100644 --- a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.json +++ b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.json @@ -1,5 +1,4 @@ { - "actions": [], "creation": "2019-10-15 12:33:21.845329", "doctype": "DocType", "editable_grid": 1, @@ -92,13 +91,54 @@ "label": "Upload XML Invoices" } ], - "links": [], - "modified": "2019-12-10 16:37:26.793398", + "modified": "2020-05-25 21:32:49.064579", "modified_by": "Administrator", "module": "Regional", "name": "Import Supplier Invoice", "owner": "Administrator", - "permissions": [], + "permissions": [ + { + "create": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts User", + "share": 1, + "write": 1 + }, + { + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Purchase User" + }, + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts Manager", + "share": 1, + "write": 1 + }, + { + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Auditor" + }, + { + "permlevel": 1, + "read": 1, + "role": "Accounts Manager", + "write": 1 + } + ], "sort_field": "modified", "sort_order": "DESC", "track_changes": 1 From 29d7a7ea05cc5a0ed65b4252acad7c698a05fdef Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 27 May 2020 21:53:01 +0530 Subject: [PATCH 223/608] fix: Added permission via regional setup and patch --- erpnext/patches.txt | 1 + ...ian_import_supplier_invoice_permissions.py | 12 +++++ .../import_supplier_invoice.json | 44 +------------------ erpnext/regional/italy/setup.py | 20 +++++++++ 4 files changed, 34 insertions(+), 43 deletions(-) create mode 100644 erpnext/patches/v12_0/set_italian_import_supplier_invoice_permissions.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index b0b224f084d..b0421f43c64 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -694,3 +694,4 @@ execute:frappe.delete_doc("Report", "Department Analytics") execute:frappe.rename_doc("Desk Page", "Loan Management", "Loan", force=True) erpnext.patches.v12_0.update_uom_conversion_factor erpnext.patches.v13_0.delete_old_purchase_reports +erpnext.patches.v12_0.set_italian_import_supplier_invoice_permissions \ No newline at end of file diff --git a/erpnext/patches/v12_0/set_italian_import_supplier_invoice_permissions.py b/erpnext/patches/v12_0/set_italian_import_supplier_invoice_permissions.py new file mode 100644 index 00000000000..a6011c4dace --- /dev/null +++ b/erpnext/patches/v12_0/set_italian_import_supplier_invoice_permissions.py @@ -0,0 +1,12 @@ +# Copyright (c) 2017, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe +from erpnext.regional.italy.setup import add_permissions + +def execute(): + countries = frappe.get_all("Company", fields="country") + countries = [country["country"] for country in countries] + if "Italy" in countries: + add_permissions() \ No newline at end of file diff --git a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.json b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.json index 6b45841b465..c1680c4b492 100644 --- a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.json +++ b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.json @@ -96,49 +96,7 @@ "module": "Regional", "name": "Import Supplier Invoice", "owner": "Administrator", - "permissions": [ - { - "create": 1, - "email": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts User", - "share": 1, - "write": 1 - }, - { - "email": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Purchase User" - }, - { - "create": 1, - "delete": 1, - "email": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts Manager", - "share": 1, - "write": 1 - }, - { - "email": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Auditor" - }, - { - "permlevel": 1, - "read": 1, - "role": "Accounts Manager", - "write": 1 - } - ], + "permissions": [], "sort_field": "modified", "sort_order": "DESC", "track_changes": 1 diff --git a/erpnext/regional/italy/setup.py b/erpnext/regional/italy/setup.py index 2d0ad66b0a0..6ab73413df2 100644 --- a/erpnext/regional/italy/setup.py +++ b/erpnext/regional/italy/setup.py @@ -7,11 +7,13 @@ from __future__ import unicode_literals import frappe from frappe import _ from frappe.custom.doctype.custom_field.custom_field import create_custom_fields +from frappe.permissions import add_permission, update_permission_property from erpnext.regional.italy import fiscal_regimes, tax_exemption_reasons, mode_of_payment_codes, vat_collectability_options def setup(company=None, patch=True): make_custom_fields() setup_report() + add_permissions() def make_custom_fields(update=True): invoice_item_fields = [ @@ -200,3 +202,21 @@ def setup_report(): dict(role='Accounts Manager') ] )).insert() + +def add_permissions(): + doctype = 'Import Supplier Invoice' + add_permission(doctype, 'All', 0) + + for role in ('Accounts Manager', 'Accounts User','Purchase User', 'Auditor'): + add_permission(doctype, role, 0) + update_permission_property(doctype, role, 0, 'print', 1) + update_permission_property(doctype, role, 0, 'report', 1) + + if role in ('Accounts Manager', 'Accounts User'): + update_permission_property(doctype, role, 0, 'write', 1) + update_permission_property(doctype, role, 0, 'create', 1) + + update_permission_property(doctype, 'Accounts Manager', 0, 'delete', 1) + add_permission(doctype, 'Accounts Manager', 1) + update_permission_property(doctype, 'Accounts Manager', 1, 'write', 1) + update_permission_property(doctype, 'Accounts Manager', 1, 'create', 1) \ No newline at end of file From 50956aa29edf635f3d1db06bb055383f85ab85c4 Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 27 May 2020 22:24:39 +0530 Subject: [PATCH 224/608] chore: Added to v13 desk under Regional Link Card --- erpnext/buying/desk_page/buying/buying.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/erpnext/buying/desk_page/buying/buying.json b/erpnext/buying/desk_page/buying/buying.json index d31602b607e..31b26d4ff89 100644 --- a/erpnext/buying/desk_page/buying/buying.json +++ b/erpnext/buying/desk_page/buying/buying.json @@ -34,6 +34,11 @@ "hidden": 0, "label": "Other Reports", "links": "[\n {\n \"is_query_report\": true,\n \"label\": \"Items To Be Requested\",\n \"name\": \"Items To Be Requested\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Item-wise Purchase History\",\n \"name\": \"Item-wise Purchase History\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Subcontracted Raw Materials To Be Transferred\",\n \"name\": \"Subcontracted Raw Materials To Be Transferred\",\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Subcontracted Item To Be Received\",\n \"name\": \"Subcontracted Item To Be Received\",\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Quoted Item Comparison\",\n \"name\": \"Quoted Item Comparison\",\n \"onboard\": 1,\n \"reference_doctype\": \"Supplier Quotation\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Material Requests for which Supplier Quotations are not created\",\n \"name\": \"Material Requests for which Supplier Quotations are not created\",\n \"reference_doctype\": \"Material Request\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Supplier Addresses And Contacts\",\n \"name\": \"Address And Contacts\",\n \"reference_doctype\": \"Address\",\n \"route_options\": {\n \"party_type\": \"Supplier\"\n },\n \"type\": \"report\"\n }\n]" + }, + { + "hidden": 0, + "label": "Regional", + "links": "[\n {\n \"description\": \"Import Italian Purchase Invoices\",\n \"label\": \"Import Supplier Invoice\",\n \"name\": \"Import Supplier Invoice\",\n \"type\": \"doctype\"\n } \n]" } ], "cards_label": "Masters & Reports ", @@ -55,7 +60,7 @@ "idx": 0, "is_standard": 1, "label": "Buying", - "modified": "2020-05-27 19:55:22.407528", + "modified": "2020-05-27 22:23:38.529720", "modified_by": "Administrator", "module": "Buying", "name": "Buying", From 33a08f1ea710ac0f24fa4406f3455942e6d32bb3 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 27 May 2020 22:44:50 +0530 Subject: [PATCH 225/608] fix: TDS computation summary report --- .../tax_withholding_category/tax_withholding_category.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py index 4d43919f62e..83d7967f793 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -180,7 +180,7 @@ def get_advance_vouchers(suppliers, fiscal_year=None, company=None, from_date=No if company: condition += "and company =%s" % (company) if from_date and to_date: - condition += "and posting_date between %s and %s" % (company, from_date, to_date) + condition += "and posting_date between %s and %s" % (from_date, to_date) ## Appending the same supplier again if length of suppliers list is 1 ## since tuple of single element list contains None, For example ('Test Supplier 1', ) From 234a55c6bc28ed1950d286182e70ded5eb7048e7 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 28 May 2020 09:55:24 +0530 Subject: [PATCH 226/608] refactor: showing the order's data for past period --- .../exponential_smoothing_forecasting.py | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py b/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py index 1b49df6af87..cac80677297 100644 --- a/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py +++ b/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py @@ -21,8 +21,6 @@ class ExponentialSmoothingForecast(object): if value.get(period.key) and not forecast_data: value[forecast_key] = flt(value.get("avg", 0)) or flt(value.get(period.key)) - # will be use to forecaset next period - forecast_data.append([value.get(period.key), value.get(forecast_key)]) elif forecast_data: previous_period_data = forecast_data[-1] value[forecast_key] = (previous_period_data[1] + @@ -31,6 +29,10 @@ class ExponentialSmoothingForecast(object): ) ) + if value.get(forecast_key): + # will be use to forecaset next period + forecast_data.append([value.get(period.key), value.get(forecast_key)]) + class ForecastingReport(ExponentialSmoothingForecast): def __init__(self, filters=None): self.filters = frappe._dict(filters or {}) @@ -78,7 +80,9 @@ class ForecastingReport(ExponentialSmoothingForecast): list_of_period_value = [value.get(p.key, 0) for p in self.period_list] if list_of_period_value: - value["avg"] = sum(list_of_period_value) / len(list_of_period_value) + total_qty = [1 for d in list_of_period_value if d] + if total_qty: + value["avg"] = flt(sum(list_of_period_value)) / flt(sum(total_qty)) def get_data_for_forecast(self): cond = "" @@ -152,15 +156,19 @@ class ForecastingReport(ExponentialSmoothingForecast): "width": 130 }] - width = 150 if self.filters.periodicity in ['Yearly', "Half-Yearly", "Quarterly"] else 100 + width = 180 if self.filters.periodicity in ['Yearly', "Half-Yearly", "Quarterly"] else 100 for period in self.period_list: if (self.filters.periodicity in ['Yearly', "Half-Yearly", "Quarterly"] or period.from_date >= getdate(self.filters.from_date)): - forecast_key = 'forecast_' + period.key + forecast_key = period.key + label = _(period.label) + if period.from_date >= getdate(self.filters.from_date): + forecast_key = 'forecast_' + period.key + label = _(period.label) + " " + _("(Forecast)") columns.append({ - "label": _(period.label), + "label": label, "fieldname": forecast_key, "fieldtype": self.fieldtype, "width": width, From 5226403b6297faf550c71b4c0ecfc58f6466d6e1 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 28 May 2020 10:10:22 +0530 Subject: [PATCH 227/608] refactor: display draft job card as Open job card --- .../job_card_summary/job_card_summary.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/erpnext/manufacturing/report/job_card_summary/job_card_summary.py b/erpnext/manufacturing/report/job_card_summary/job_card_summary.py index 953d8201a74..b1bff3500c6 100644 --- a/erpnext/manufacturing/report/job_card_summary/job_card_summary.py +++ b/erpnext/manufacturing/report/job_card_summary/job_card_summary.py @@ -16,7 +16,7 @@ def execute(filters=None): def get_data(filters): query_filters = { - "docstatus": ("=", 1), + "docstatus": ("<", 2), "posting_date": ("between", [filters.from_date, filters.to_date]) } @@ -35,7 +35,7 @@ def get_data(filters): job_cards = [d.name for d in data] job_card_time_filter = { - "docstatus": 1, + "docstatus": ("<", 2), "parent": ("in", job_cards), } @@ -47,27 +47,28 @@ def get_data(filters): res = [] for d in data: - if d.status == "Material Transferred": + if d.status != "Completed": d.status = "Open" if job_card_time_details.get(d.name): d.from_time = job_card_time_details.get(d.name).from_time d.to_time = job_card_time_details.get(d.name).to_time - res.append(d) + + res.append(d) return res def get_chart_data(job_card_details, filters): labels, periodic_data = prepare_chart_data(job_card_details, filters) - pending, completed = [], [] + open_job_cards, completed = [], [] datasets = [] for d in labels: - pending.append(periodic_data.get("Pending").get(d)) + open_job_cards.append(periodic_data.get("Open").get(d)) completed.append(periodic_data.get("Completed").get(d)) - datasets.append({"name": "Pending", "values": pending}) + datasets.append({"name": "Open", "values": open_job_cards}) datasets.append({"name": "Completed", "values": completed}) chart = { @@ -84,7 +85,7 @@ def prepare_chart_data(job_card_details, filters): labels = [] periodic_data = { - "Pending": {}, + "Open": {}, "Completed": {} } @@ -98,7 +99,7 @@ def prepare_chart_data(job_card_details, filters): for d in job_card_details: if getdate(d.posting_date) > from_date and getdate(d.posting_date) <= end_date: - status = "Completed" if d.status == "Completed" else "Pending" + status = "Completed" if d.status == "Completed" else "Open" if periodic_data.get(status).get(period): periodic_data[status][period] += 1 From f7beeff2d8633538372a9bf992d9249869428d1f Mon Sep 17 00:00:00 2001 From: Michelle Alva <50285544+michellealva@users.noreply.github.com> Date: Thu, 28 May 2020 11:05:19 +0530 Subject: [PATCH 228/608] fix: change description for field (#21995) grammatical fixes for description of Default Customer --- .../doctype/shopify_settings/shopify_settings.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json index 8f1b746e5b6..d1e5f40d6c1 100644 --- a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json +++ b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json @@ -133,7 +133,7 @@ "label": "Customer Settings" }, { - "description": "If Shopify not contains a customer in Order, then while syncing Orders, the system will consider default customer for order", + "description": "If Shopify does not have a customer in the order, then while syncing the orders, the system will consider the default customer for the order", "fieldname": "default_customer", "fieldtype": "Link", "label": "Default Customer", @@ -277,4 +277,4 @@ ], "sort_field": "modified", "sort_order": "DESC" -} \ No newline at end of file +} From 596b3dbeeed513a1d6f945bacde9c872c25ebcd6 Mon Sep 17 00:00:00 2001 From: totolouis Date: Wed, 27 May 2020 22:51:03 -0700 Subject: [PATCH 229/608] fix: translation from HTML code to apostrophe For French translation, the apostrophe was in HTML code and thus is not displayed correctly. Issue reported a few times: https://discuss.erpnext.com/t/apostrophe-is-showed-as-39-is-not-interpreted/49954/3 and https://discuss.erpnext.com/t/mega-thread-version-12-release-bugs/50987/49. --- erpnext/translations/fr.csv | 1334 +++++++++++++++++------------------ 1 file changed, 667 insertions(+), 667 deletions(-) diff --git a/erpnext/translations/fr.csv b/erpnext/translations/fr.csv index 7960941e9eb..b0fbf37a59e 100644 --- a/erpnext/translations/fr.csv +++ b/erpnext/translations/fr.csv @@ -18,9 +18,9 @@ 90-Above,90-Dessus, A Customer Group exists with same name please change the Customer name or rename the Customer Group,"Un Groupe de Clients existe avec le même nom, veuillez changer le nom du Client ou renommer le Groupe de Clients", A Default Service Level Agreement already exists.,Un accord de niveau de service par défaut existe déjà., -A Lead requires either a person's name or an organization's name,Un responsable requiert le nom d'une personne ou le nom d'une organisation, +A Lead requires either a person's name or an organization's name,Un responsable requiert le nom d'une personne ou le nom d'une organisation, A customer with the same name already exists,Un client avec un nom identique existe déjà, -A question must have more than one options,Une question doit avoir plus d'une option, +A question must have more than one options,Une question doit avoir plus d'une option, A qustion must have at least one correct options,Une qustion doit avoir au moins une des options correctes, A {0} exists between {1} and {2} (,Un {0} existe entre {1} et {2} (, A4,A4, @@ -30,7 +30,7 @@ Abbr can not be blank or space,Abré. ne peut être vide ou contenir un espace, Abbreviation already used for another company,Abréviation déjà utilisée pour une autre société, Abbreviation cannot have more than 5 characters,L'abbréviation ne peut pas avoir plus de 5 caractères, Abbreviation is mandatory,Abréviation est obligatoire, -About the Company,À propos de l'entreprise, +About the Company,À propos de l'entreprise, About your company,A propos de votre entreprise, Above,Au-dessus, Absent,Absent, @@ -100,7 +100,7 @@ Active,actif, Active Leads / Customers,Prospects / Clients Actifs, Activity Cost exists for Employee {0} against Activity Type - {1},Des Coûts d'Activité existent pour l'Employé {0} pour le Type d'Activité - {1}, Activity Cost per Employee,Coût de l'Activité par Employé, -Activity Type,Type d'activité, +Activity Type,Type d'activité, Actual Cost,Prix actuel, Actual Delivery Date,Date de livraison réelle, Actual Qty,Quantité Réelle, @@ -129,7 +129,7 @@ Add Timesheets,Ajouter des feuilles de temps, Add Timeslots,Ajouter des Créneaux, Add Users to Marketplace,Ajouter des utilisateurs à la Marketplace, Add a new address,ajouter une nouvelle adresse, -Add cards or custom sections on homepage,Ajouter des cartes ou des sections personnalisées sur la page d'accueil, +Add cards or custom sections on homepage,Ajouter des cartes ou des sections personnalisées sur la page d'accueil, Add more items or open full form,Ajouter plus d'articles ou ouvrir le formulaire complet, Add notes,Ajouter des notes, Add the rest of your organization as your users. You can also add invite Customers to your portal by adding them from Contacts,Ajouter le reste de votre organisation en tant qu'utilisateurs. Vous pouvez aussi inviter des Clients sur votre portail en les ajoutant depuis les Contacts, @@ -196,7 +196,7 @@ All communications including and above this shall be moved into the new Issue,"T All items have already been invoiced,Tous les articles ont déjà été facturés, All items have already been transferred for this Work Order.,Tous les articles ont déjà été transférés pour cet ordre de travail., All other ITC,Tous les autres CTI, -All the mandatory Task for employee creation hasn't been done yet.,Toutes les tâches obligatoires pour la création d'employés n'ont pas encore été effectuées., +All the mandatory Task for employee creation hasn't been done yet.,Toutes les tâches obligatoires pour la création d'employés n'ont pas encore été effectuées., All these items have already been invoiced,Tous ces articles ont déjà été facturés, Allocate Payment Amount,Allouer le montant du paiement, Allocated Amount,Montant alloué, @@ -206,7 +206,7 @@ Allow Delete,Autoriser la suppression, Already record exists for the item {0},L'enregistrement existe déjà pour l'article {0}, "Already set default in pos profile {0} for user {1}, kindly disabled default","Déjà défini par défaut dans le profil pdv {0} pour l'utilisateur {1}, veuillez désactiver la valeur par défaut", Alternate Item,Article alternatif, -Alternative item must not be same as item code,L'article alternatif ne doit pas être le même que le code article, +Alternative item must not be same as item code,L'article alternatif ne doit pas être le même que le code article, Amended From,Modifié depuis, Amount,Montant, Amount After Depreciation,Montant après amortissement, @@ -221,14 +221,14 @@ Amount {0} {1} {2} {3},Montant {0} {1} {2} {3}, Amt,Nb, "An Item Group exists with same name, please change the item name or rename the item group","Un Groupe d'Article existe avec le même nom, veuillez changer le nom de l'article ou renommer le groupe d'article", An academic term with this 'Academic Year' {0} and 'Term Name' {1} already exists. Please modify these entries and try again.,Une période universitaire avec cette 'Année Universitaire' {0} et 'Nom de la Période' {1} existe déjà. Veuillez modifier ces entrées et essayer à nouveau., -An error occurred during the update process,Une erreur s'est produite lors du processus de mise à jour, +An error occurred during the update process,Une erreur s'est produite lors du processus de mise à jour, "An item exists with same name ({0}), please change the item group name or rename the item","Un article existe avec le même nom ({0}), veuillez changer le nom du groupe d'article ou renommer l'article", Analyst,Analyste, Analytics,Analytique, Annual Billing: {0},Facturation Annuelle : {0}, Annual Salary,Salaire annuel, Anonymous,Anonyme, -Another Budget record '{0}' already exists against {1} '{2}' and account '{3}' for fiscal year {4},Un autre enregistrement Budget '{0}' existe déjà pour {1} '{2}' et pour le compte '{3}' pour l'exercice {4}., +Another Budget record '{0}' already exists against {1} '{2}' and account '{3}' for fiscal year {4},Un autre enregistrement Budget '{0}' existe déjà pour {1} '{2}' et pour le compte '{3}' pour l'exercice {4}., Another Period Closing Entry {0} has been made after {1},Une autre Entrée de Clôture de Période {0} a été faite après {1}, Another Sales Person {0} exists with the same Employee id,Un autre Commercial {0} existe avec le même ID d'Employé, Antibiotic,Antibiotique, @@ -260,11 +260,11 @@ Approving User cannot be same as user the rule is Applicable To,L'Utilisateur Ap "Apps using current key won't be able to access, are you sure?","Les applications utilisant la clé actuelle ne pourront plus y accéder, êtes-vous sûr?", Are you sure you want to cancel this appointment?,Êtes-vous sûr de vouloir annuler ce rendez-vous?, Arrear,Arriéré, -As Examiner,En tant qu'examinateur, +As Examiner,En tant qu'examinateur, As On Date,Comme à la date, As Supervisor,En tant que superviseur, As per rules 42 & 43 of CGST Rules,Conformément aux règles 42 et 43 des règles de la CGST, -As per section 17(5),Conformément à l'article 17 (5), +As per section 17(5),Conformément à l'article 17 (5), As per your assigned Salary Structure you cannot apply for benefits,La struture salariale qui vous a été assignée ne vous permet pas de demander des avantages sociaux, Assessment,Évaluation, Assessment Criteria,Critères d'Évaluation, @@ -273,7 +273,7 @@ Assessment Group: ,Groupe d'Évaluation:, Assessment Plan,Plan d'Évaluation, Assessment Plan Name,Nom du Plan d'Évaluation, Assessment Report,Rapport d'Évaluation, -Assessment Reports,Rapports d'évaluation, +Assessment Reports,Rapports d'évaluation, Assessment Result,Résultat de l'Évaluation, Assessment Result record {0} already exists.,Le Résultat d'Évaluation {0} existe déjà., Asset,Atout, @@ -329,7 +329,7 @@ Available Selling,Vente disponible, Available for use date is required,La date de mise en service est nécessaire, Available slots,Créneaux Disponibles, Available {0},Disponible {0}, -Available-for-use Date should be after purchase date,La date de disponibilité devrait être postérieure à la date d'achat, +Available-for-use Date should be after purchase date,La date de disponibilité devrait être postérieure à la date d'achat, Average Age,Âge moyen, Average Rate,Prix moyen, Avg Daily Outgoing,Moy Quotidienne Sortante, @@ -384,7 +384,7 @@ Batch Name,Nom du lot, Batch No,N° du Lot, Batch number is mandatory for Item {0},Le numéro de lot est obligatoire pour l'Article {0}, Batch {0} of Item {1} has expired.,Lot {0} de l'Article {1} a expiré., -Batch {0} of Item {1} is disabled.,Le lot {0} de l'élément {1} est désactivé., +Batch {0} of Item {1} is disabled.,Le lot {0} de l'élément {1} est désactivé., Batch: ,Lot:, Batches,Lots, Become a Seller,Devenir vendeur, @@ -399,20 +399,20 @@ Billed,Facturé, Billed Amount,Montant facturé, Billing,Facturation, Billing Address,Adresse de facturation, -Billing Address is same as Shipping Address,L'adresse de facturation est identique à l'adresse de livraison, +Billing Address is same as Shipping Address,L'adresse de facturation est identique à l'adresse de livraison, Billing Amount,Montant de Facturation, Billing Status,Statut de la Facturation, Billing currency must be equal to either default company's currency or party account currency,La devise de facturation doit être égale à la devise de la société par défaut ou à la devise du compte du partenaire, Bills raised by Suppliers.,Factures émises par des Fournisseurs., Bills raised to Customers.,Factures émises pour des Clients., Biotechnology,Biotechnologie, -Birthday Reminder,Rappel d'anniversaire, +Birthday Reminder,Rappel d'anniversaire, Black,Noir, Blanket Orders from Costumers.,Commandes provisoires de clients., Block Invoice,Bloquer la facture, Boms,Listes de Matériaux, Bonus Payment Date cannot be a past date,La date de paiement du bonus ne peut pas être une date passée, -Both Trial Period Start Date and Trial Period End Date must be set,La date de début de la période d'essai et la date de fin de la période d'essai doivent être définies, +Both Trial Period Start Date and Trial Period End Date must be set,La date de début de la période d'essai et la date de fin de la période d'essai doivent être définies, Both Warehouse must belong to same Company,Les deux Entrepôt doivent appartenir à la même Société, Branch,Branche, Broadcasting,Radio/Télévision, @@ -429,7 +429,7 @@ Business Development Manager,Directeur Commercial, Buy,Acheter, Buying,Achat, Buying Amount,Montant d'Achat, -Buying Price List,Liste de prix d'achat, +Buying Price List,Liste de prix d'achat, Buying Rate,Prix d'achat, "Buying must be checked, if Applicable For is selected as {0}","Achat doit être vérifié, si Applicable Pour {0} est sélectionné", By {0},Par {0}, @@ -447,7 +447,7 @@ Campaign,Campagne, Can be approved by {0},Peut être approuvé par {0}, "Can not filter based on Account, if grouped by Account","Impossible de filtrer sur le Compte , si les lignes sont regroupées par Compte", "Can not filter based on Voucher No, if grouped by Voucher","Impossible de filtrer sur la base du N° de Coupon, si les lignes sont regroupées par Coupon", -"Can not mark Inpatient Record Discharged, there are Unbilled Invoices {0}","Impossible de marquer le dossier d'hospitalisation déchargé, il existe des factures non facturées {0}", +"Can not mark Inpatient Record Discharged, there are Unbilled Invoices {0}","Impossible de marquer le dossier d'hospitalisation déchargé, il existe des factures non facturées {0}", Can only make payment against unbilled {0},Le paiement n'est possible qu'avec les {0} non facturés, Can refer row only if the charge type is 'On Previous Row Amount' or 'Previous Row Total',Peut se référer à ligne seulement si le type de charge est 'Montant de la ligne précedente' ou 'Total des lignes précedente', "Can't change valuation method, as there are transactions against some items which does not have it's own valuation method","Impossible de modifier la méthode de valorisation, car il existe des transactions sur certains articles ne possèdant pas leur propre méthode de valorisation", @@ -455,17 +455,17 @@ Can't create standard criteria. Please rename the criteria,Impossible de créer Cancel,Annuler, Cancel Material Visit {0} before cancelling this Warranty Claim,Annuler la Visite Matérielle {0} avant d'annuler cette Réclamation de Garantie, Cancel Material Visits {0} before cancelling this Maintenance Visit,Annuler les Visites Matérielles {0} avant d'annuler cette Visite de Maintenance, -Cancel Subscription,Annuler l'abonnement, +Cancel Subscription,Annuler l'abonnement, Cancel the journal entry {0} first,Annuler d'abord l'écriture de journal {0}, Canceled,Annulé, "Cannot Submit, Employees left to mark attendance","Ne peut pas être soumis, certains employés n'ont pas pas validé leurs feuilles de présence", Cannot be a fixed asset item as Stock Ledger is created.,Ne peut pas être un article immobilisé car un Journal de Stock a été créé., Cannot cancel because submitted Stock Entry {0} exists,Impossible d'annuler car l'Écriture de Stock soumise {0} existe, Cannot cancel transaction for Completed Work Order.,Impossible d'annuler la transaction lorsque l'ordre de travail est terminé., -Cannot cancel {0} {1} because Serial No {2} does not belong to the warehouse {3},Impossible d'annuler {0} {1} car le numéro de série {2} n'appartient pas à l'entrepôt {3}, +Cannot cancel {0} {1} because Serial No {2} does not belong to the warehouse {3},Impossible d'annuler {0} {1} car le numéro de série {2} n'appartient pas à l'entrepôt {3}, Cannot change Attributes after stock transaction. Make a new Item and transfer stock to the new Item,Impossible de modifier les attributs après des mouvements de stock. Faites un nouvel article et transférez la quantité en stock au nouvel article, Cannot change Fiscal Year Start Date and Fiscal Year End Date once the Fiscal Year is saved.,Impossible de modifier les dates de début et de fin d'exercice une fois que l'exercice est enregistré., -Cannot change Service Stop Date for item in row {0},Impossible de modifier la date d'arrêt du service pour l'élément de la ligne {0}, +Cannot change Service Stop Date for item in row {0},Impossible de modifier la date d'arrêt du service pour l'élément de la ligne {0}, Cannot change Variant properties after stock transaction. You will have to make a new Item to do this.,Impossible de modifier les propriétés de variante après une transaction de stock. Vous devrez créer un nouvel article pour pouvoir le faire., "Cannot change company's default currency, because there are existing transactions. Transactions must be cancelled to change the default currency.","Impossible de changer la devise par défaut de la société, parce qu'il y a des opérations existantes. Les transactions doivent être annulées pour changer la devise par défaut.", Cannot change status as student {0} is linked with student application {1},Impossible de changer le statut car l'étudiant {0} est lié à la candidature de l'étudiant {1}, @@ -479,7 +479,7 @@ Cannot deduct when category is for 'Valuation' or 'Valuation and Total',Déducti Cannot deduct when category is for 'Valuation' or 'Vaulation and Total',Vous ne pouvez pas déduire lorsqu'une catégorie est pour 'Évaluation' ou 'Évaluation et Total', "Cannot delete Serial No {0}, as it is used in stock transactions","Impossible de supprimer les N° de série {0}, s'ils sont dans les mouvements de stock", Cannot enroll more than {0} students for this student group.,Inscription de plus de {0} étudiants impossible pour ce groupe d'étudiants., -Cannot find Item with this barcode,Impossible de trouver l'article avec ce code à barres, +Cannot find Item with this barcode,Impossible de trouver l'article avec ce code à barres, Cannot find active Leave Period,Impossible de trouver une période de congés active, Cannot produce more Item {0} than Sales Order quantity {1},Impossible de produire plus d'Article {0} que la quantité {1} du Bon de Commande, Cannot promote Employee with status Left,"Impossible de promouvoir un employé avec le statut ""Parti""", @@ -538,7 +538,7 @@ Cheques Required,Chèques requis, Cheques and Deposits incorrectly cleared,Chèques et Dépôts incorrectement compensés, Child Item should not be a Product Bundle. Please remove item `{0}` and save,Le sous-article ne doit pas être un ensemble de produit. S'il vous plaît retirer l'article `{0}` et sauver, Child Task exists for this Task. You can not delete this Task.,Une tâche enfant existe pour cette tâche. Vous ne pouvez pas supprimer cette tâche., -Child nodes can be only created under 'Group' type nodes,Les noeuds enfants peuvent être créés uniquement dans les nÅ“uds de type 'Groupe', +Child nodes can be only created under 'Group' type nodes,Les noeuds enfants peuvent être créés uniquement dans les nÅ“uds de type 'Groupe', Child warehouse exists for this warehouse. You can not delete this warehouse.,Un entrepôt enfant existe pour cet entrepôt. Vous ne pouvez pas supprimer cet entrepôt., Circular Reference Error,Erreur de référence circulaire, City,Ville, @@ -578,11 +578,11 @@ Commission rate cannot be greater than 100,Taux de commission ne peut pas être Community Forum,Forum de la communauté, Company (not Customer or Supplier) master.,Données de base de la Société (ni les Clients ni les Fournisseurs), Company Abbreviation,Abréviation de la Société, -Company Abbreviation cannot have more than 5 characters,L'abréviation de l'entreprise ne peut pas comporter plus de 5 caractères, +Company Abbreviation cannot have more than 5 characters,L'abréviation de l'entreprise ne peut pas comporter plus de 5 caractères, Company Name,Nom de la Société, Company Name cannot be Company,Nom de la Société ne peut pas être Company, Company currencies of both the companies should match for Inter Company Transactions.,Les devises des deux sociétés doivent correspondre pour les transactions inter-sociétés., -Company is manadatory for company account,La société est le maître d'Å“uvre du compte d'entreprise, +Company is manadatory for company account,La société est le maître d'Å“uvre du compte d'entreprise, Company name not same,Le nom de la société n'est pas identique, Company {0} does not exist,Société {0} n'existe pas, "Company, Payment Account, From Date and To Date is mandatory","Société, compte de paiement, date de début et date de fin sont obligatoires", @@ -653,7 +653,7 @@ Could not submit some Salary Slips,Les fiches de paie n'ont pas pu être soumise Country wise default Address Templates,Modèles d'Adresse par défaut en fonction du pays, Course,Cours, Course Code: ,Code du Cours:, -Course Enrollment {0} does not exists,L'inscription au cours {0} n'existe pas, +Course Enrollment {0} does not exists,L'inscription au cours {0} n'existe pas, Course Schedule,Horaire du cours, Course: ,Cours:, Cr,Cr, @@ -677,7 +677,7 @@ Create Leads,Créer des Prospects, Create Maintenance Visit,Créer une visite de maintenance, Create Material Request,Créer une demande de matériel, Create Multiple,Créer plusieurs, -Create Opening Sales and Purchase Invoices,Créer des factures d'ouverture et des factures d'achat, +Create Opening Sales and Purchase Invoices,Créer des factures d'ouverture et des factures d'achat, Create Payment Entries,Créer des entrées de paiement, Create Payment Entry,Créer une entrée de paiement, Create Print Format,Créer Format d'Impression, @@ -691,7 +691,7 @@ Create Sales Order,Créer une commande client, Create Sales Orders to help you plan your work and deliver on-time,Créez des commandes pour vous aider à planifier votre travail et à livrer à temps, Create Sample Retention Stock Entry,Créer un échantillon de stock de rétention, Create Student,Créer un étudiant, -Create Student Batch,Créer un lot d'étudiants, +Create Student Batch,Créer un lot d'étudiants, Create Student Groups,Créer des Groupes d'Étudiants, Create Supplier Quotation,Créer une offre fournisseur, Create Tax Template,Créer un modèle de taxe, @@ -706,11 +706,11 @@ Create customer quotes,Créer les devis client, Create rules to restrict transactions based on values.,Créer des règles pour restreindre les transactions basées sur les valeurs ., Created By,Établi par, Created {0} scorecards for {1} between: ,{0} fiches d'évaluations créées pour {1} entre:, -Creating Company and Importing Chart of Accounts,Création d'une société et importation d'un plan comptable, +Creating Company and Importing Chart of Accounts,Création d'une société et importation d'un plan comptable, Creating Fees,Création d'Honoraires, Creating Payment Entries......,Créer des écritures de paiement..., Creating Salary Slips...,Création des fiches de paie en cours..., -Creating student groups,Créer des groupes d'étudiants, +Creating student groups,Créer des groupes d'étudiants, Creating {0} Invoice,Création de {0} facture, Credit,Crédit, Credit ({0}),Crédit ({0}), @@ -763,7 +763,7 @@ Customer required for 'Customerwise Discount',Client requis pour appliquer une ' Customer {0} does not belong to project {1},Le Client {0} ne fait pas parti du projet {1}, Customer {0} is created.,Le client {0} est créé., Customers in Queue,Clients dans la File d'Attente, -Customize Homepage Sections,Personnaliser les sections de la page d'accueil, +Customize Homepage Sections,Personnaliser les sections de la page d'accueil, Customizing Forms,Personnalisation des formulaires, Daily Project Summary for {0},Récapitulatif quotidien du projet pour {0}, Daily Reminders,Rappels quotidiens, @@ -817,7 +817,7 @@ Del,Supp, Delay in payment (Days),Retard de paiement (jours), Delete all the Transactions for this Company,Supprimer toutes les transactions pour cette société, Delete permanently?,Supprimer définitivement ?, -Deletion is not permitted for country {0},La suppression n'est pas autorisée pour le pays {0}, +Deletion is not permitted for country {0},La suppression n'est pas autorisée pour le pays {0}, Delivered,Livré, Delivered Amount,Montant Livré, Delivered Qty,Qté Livrée, @@ -842,13 +842,13 @@ Depreciation Eliminated due to disposal of assets,Amortissement Eliminé en rais Depreciation Entry,Ecriture d’Amortissement, Depreciation Method,Méthode d'Amortissement, Depreciation Row {0}: Depreciation Start Date is entered as past date,Ligne de d'amortissement {0}: La date de début de l'amortissement est dans le passé, -Depreciation Row {0}: Expected value after useful life must be greater than or equal to {1},Ligne d'amortissement {0}: la valeur attendue après la durée de vie utile doit être supérieure ou égale à {1}, +Depreciation Row {0}: Expected value after useful life must be greater than or equal to {1},Ligne d'amortissement {0}: la valeur attendue après la durée de vie utile doit être supérieure ou égale à {1}, Depreciation Row {0}: Next Depreciation Date cannot be before Available-for-use Date,Ligne d'amortissement {0}: La date d'amortissement suivante ne peut pas être antérieure à la date de mise en service, -Depreciation Row {0}: Next Depreciation Date cannot be before Purchase Date,Ligne d'amortissement {0}: la date d'amortissement suivante ne peut pas être antérieure à la date d'achat, +Depreciation Row {0}: Next Depreciation Date cannot be before Purchase Date,Ligne d'amortissement {0}: la date d'amortissement suivante ne peut pas être antérieure à la date d'achat, Designer,Designer, Detailed Reason,Raison détaillée, Details,Détails, -Details of Outward Supplies and inward supplies liable to reverse charge,Détails des livraisons sortantes et des livraisons entrantes susceptibles d'inverser la charge, +Details of Outward Supplies and inward supplies liable to reverse charge,Détails des livraisons sortantes et des livraisons entrantes susceptibles d'inverser la charge, Details of the operations carried out.,Détails des opérations effectuées., Diagnosis,Diagnostique, Did not find any item called {0},N'a pas trouvé d'élément appelé {0}, @@ -872,7 +872,7 @@ Discount amount cannot be greater than 100%,Le montant de la réduction ne peut Discount must be less than 100,La remise doit être inférieure à 100, Diseases & Fertilizers,Maladies et engrais, Dispatch,Envoi, -Dispatch Notification,Notification d'expédition, +Dispatch Notification,Notification d'expédition, Dispatch State,Statut de l'expédition, Distance,Distance, Distribution,Distribution, @@ -900,7 +900,7 @@ Draft,Brouillon, Drop Ship,Expédition Directe, Drug,Médicament, Due / Reference Date cannot be after {0},Date d’échéance / de référence ne peut pas être après le {0}, -Due Date cannot be before Posting / Supplier Invoice Date,La date d'échéance ne peut pas être antérieure à la date de comptabilisation / facture fournisseur, +Due Date cannot be before Posting / Supplier Invoice Date,La date d'échéance ne peut pas être antérieure à la date de comptabilisation / facture fournisseur, Due Date is mandatory,La Date d’Échéance est obligatoire, Duplicate Entry. Please check Authorization Rule {0},Écriture en double. Merci de vérifier la Règle d’Autorisation {0}, Duplicate Serial No entered for Item {0},Dupliquer N° de Série pour l'Article {0}, @@ -920,7 +920,7 @@ Earnest Money,Arrhes, Earning,Revenus, Edit,modifier, Edit Publishing Details,Modifier les détails de publication, -"Edit in full page for more options like assets, serial nos, batches etc.","Modifier en pleine page pour plus d'options comme les actifs, les numéros de série, les lots, etc.", +"Edit in full page for more options like assets, serial nos, batches etc.","Modifier en pleine page pour plus d'options comme les actifs, les numéros de série, les lots, etc.", Education,Éducation, Either location or employee must be required,La localisation ou l'employé sont requis, Either target qty or target amount is mandatory,Soit la qté cible soit le montant cible est obligatoire, @@ -935,16 +935,16 @@ Email Address,Adresse électronique, Email Digest: ,Compte Rendu par Email :, Email Reminders will be sent to all parties with email contacts,Les rappels par emails seront envoyés à toutes les parties avec des contacts ayant une adresse email, Email Sent,Email envoyé, -Email Template,Modèle d'email, +Email Template,Modèle d'email, Email not found in default contact,Email non trouvé dans le contact par défaut, Email sent to supplier {0},Email envoyé au fournisseur {0}, Email sent to {0},Email envoyé à {0}, Employee,Employé, -Employee A/C Number,Numéro de l'employé, +Employee A/C Number,Numéro de l'employé, Employee Advances,Avances versées aux employés, Employee Benefits,Avantages de l'Employé, Employee Grade,Echelon des employés, -Employee ID,Numéro d'employé, +Employee ID,Numéro d'employé, Employee Lifecycle,Cycle de vie des employés, Employee Name,Nom de l'Employé, Employee Promotion cannot be submitted before Promotion Date ,La promotion ne peut être soumise avant la date de promotion, @@ -952,9 +952,9 @@ Employee Referral,Recommandations, Employee Transfer cannot be submitted before Transfer Date ,Le transfert ne peut pas être soumis avant la date de transfert, Employee cannot report to himself.,L'employé ne peut pas rendre de compte à lui-même., Employee relieved on {0} must be set as 'Left',Employé dégagé de {0} doit être défini comme 'Gauche', -Employee status cannot be set to 'Left' as following employees are currently reporting to this employee: ,Le statut d'employé ne peut pas être défini sur 'Gauche' car les employés suivants sont actuellement rattachés à cet employé:, +Employee status cannot be set to 'Left' as following employees are currently reporting to this employee: ,Le statut d'employé ne peut pas être défini sur 'Gauche' car les employés suivants sont actuellement rattachés à cet employé:, Employee {0} already submited an apllication {1} for the payroll period {2},L'employé {0} a déjà envoyé une demande {1} pour la période de calcul de paie {2}, -Employee {0} has already applied for {1} between {2} and {3} : ,L'employé {0} a déjà postulé pour {1} entre {2} et {3}:, +Employee {0} has already applied for {1} between {2} and {3} : ,L'employé {0} a déjà postulé pour {1} entre {2} et {3}:, Employee {0} has already applied for {1} on {2} : ,L'employé {0} a déjà postulé pour {1} le {2}:, Employee {0} has no maximum benefit amount,L'employé {0} n'a pas de montant maximal d'avantages sociaux, Employee {0} is not active or does not exist,"L'employé {0} n'est pas actif, ou n'existe pas", @@ -971,7 +971,7 @@ End Date cannot be before Start Date.,La date de fin ne peut pas être antérieu End Year,Année de Fin, End Year cannot be before Start Year,L'Année de Fin ne peut pas être avant l'Année de Début, End on,Termine le, -End time cannot be before start time,L'heure de fin ne peut pas être avant l'heure de début, +End time cannot be before start time,L'heure de fin ne peut pas être avant l'heure de début, Ends On date cannot be before Next Contact Date.,La date de fin ne peut pas être avant la prochaine date de contact, Energy,Énergie, Engineer,Ingénieur, @@ -1021,7 +1021,7 @@ Expense Claims,Notes de Frais, Expense account is mandatory for item {0},Compte de charge est obligatoire pour l'article {0}, Expense or Difference account is mandatory for Item {0} as it impacts overall stock value,Compte de Charge et d'Écarts est obligatoire pour objet {0} car il impacte la valeur globale des actions, Expenses,Charges, -Expenses Included In Asset Valuation,Dépenses incluses dans l'évaluation de l'actif, +Expenses Included In Asset Valuation,Dépenses incluses dans l'évaluation de l'actif, Expenses Included In Valuation,Charges Incluses dans la Valorisation, Expired Batches,Lots expirés, Expires On,Expire le, @@ -1034,7 +1034,7 @@ Extra Small,Très Petit, Fail,Échec, Failed,Échoué, Failed to create website,Échec de la création du site Web, -Failed to install presets,Échec de l'installation des préréglages, +Failed to install presets,Échec de l'installation des préréglages, Failed to login,Échec de la connexion, Failed to setup company,Échec de la configuration de la société, Failed to setup defaults,Échec de la configuration par défaut, @@ -1066,16 +1066,16 @@ Financial Statements,États financiers, Financial Year,Exercice Financier, Finish,terminer, Finished Good,Produit fini, -Finished Good Item Code,Code d'article fini, +Finished Good Item Code,Code d'article fini, Finished Goods,Produits finis, Finished Item {0} must be entered for Manufacture type entry,Le Produit Fini {0} doit être saisi pour une écriture de type Production, Finished product quantity {0} and For Quantity {1} cannot be different,La quantité de produit fini {0} et Pour la quantité {1} ne peut pas être différente, First Name,Prénom, -"Fiscal Regime is mandatory, kindly set the fiscal regime in the company {0}","Le régime fiscal est obligatoire, veuillez définir le régime fiscal de l'entreprise {0}", +"Fiscal Regime is mandatory, kindly set the fiscal regime in the company {0}","Le régime fiscal est obligatoire, veuillez définir le régime fiscal de l'entreprise {0}", Fiscal Year,Exercice fiscal, -Fiscal Year End Date should be one year after Fiscal Year Start Date,La date de fin d'exercice doit être un an après la date de début d'exercice, +Fiscal Year End Date should be one year after Fiscal Year Start Date,La date de fin d'exercice doit être un an après la date de début d'exercice, Fiscal Year Start Date and Fiscal Year End Date are already set in Fiscal Year {0},La Date de Début et la Date de Fin de l'Exercice Fiscal sont déjà définies dans l'Année Fiscale {0}, -Fiscal Year Start Date should be one year earlier than Fiscal Year End Date,La date de début d'exercice doit être un an plus tôt que la date de fin d'exercice, +Fiscal Year Start Date should be one year earlier than Fiscal Year End Date,La date de début d'exercice doit être un an plus tôt que la date de fin d'exercice, Fiscal Year {0} does not exist,Exercice Fiscal {0} n'existe pas, Fiscal Year {0} is required,Exercice Fiscal {0} est nécessaire, Fiscal Year {0} not found,Exercice Fiscal {0} introuvable, @@ -1086,8 +1086,8 @@ Fixed Assets,Actifs Immobilisés, Following Material Requests have been raised automatically based on Item's re-order level,Les Demandes de Matériel suivantes ont été créées automatiquement sur la base du niveau de réapprovisionnement de l’Article, Following accounts might be selected in GST Settings:,Les comptes suivants peuvent être sélectionnés dans les paramètres GST:, Following course schedules were created,Les horaires de cours suivants ont été créés, -Following item {0} is not marked as {1} item. You can enable them as {1} item from its Item master,L'élément suivant {0} n'est pas marqué comme élément {1}. Vous pouvez les activer en tant qu'élément {1} à partir de sa fiche article., -Following items {0} are not marked as {1} item. You can enable them as {1} item from its Item master,Les éléments suivants {0} ne sont pas marqués comme {1} élément. Vous pouvez les activer en tant qu'élément {1} à partir de sa fiche article., +Following item {0} is not marked as {1} item. You can enable them as {1} item from its Item master,L'élément suivant {0} n'est pas marqué comme élément {1}. Vous pouvez les activer en tant qu'élément {1} à partir de sa fiche article., +Following items {0} are not marked as {1} item. You can enable them as {1} item from its Item master,Les éléments suivants {0} ne sont pas marqués comme {1} élément. Vous pouvez les activer en tant qu'élément {1} à partir de sa fiche article., Food,Alimentation, "Food, Beverage & Tobacco","Alimentation, boissons et tabac", For,Pour, @@ -1099,14 +1099,14 @@ For Warehouse,Pour l’Entrepôt, For Warehouse is required before Submit,Pour l’Entrepôt est requis avant de Soumettre, "For an item {0}, quantity must be negative number","Pour l'article {0}, la quantité doit être un nombre négatif", "For an item {0}, quantity must be positive number","Pour un article {0}, la quantité doit être un nombre positif", -"For job card {0}, you can only make the 'Material Transfer for Manufacture' type stock entry","Pour la carte de travail {0}, vous pouvez uniquement saisir une entrée de stock de type "Transfert d'article pour fabrication".", +"For job card {0}, you can only make the 'Material Transfer for Manufacture' type stock entry","Pour la carte de travail {0}, vous pouvez uniquement saisir une entrée de stock de type "Transfert d'article pour fabrication".", "For row {0} in {1}. To include {2} in Item rate, rows {3} must also be included","Pour la ligne {0} dans {1}. Pour inclure {2} dans le prix de l'Article, les lignes {3} doivent également être incluses", For row {0}: Enter Planned Qty,Pour la ligne {0}: entrez la quantité planifiée, "For {0}, only credit accounts can be linked against another debit entry","Pour {0}, seuls les comptes de crédit peuvent être liés avec une autre écriture de débit", "For {0}, only debit accounts can be linked against another credit entry","Pour {0}, seuls les comptes de débit peuvent être liés avec une autre écriture de crédit", Form View,Vue de Formulaire, Forum Activity,Activité du forum, -Free item code is not selected,Le code d'article gratuit n'est pas sélectionné, +Free item code is not selected,Le code d'article gratuit n'est pas sélectionné, Freight and Forwarding Charges,Frais de Fret et d'Expédition, Frequency,Fréquence, Friday,Vendredi, @@ -1122,7 +1122,7 @@ From Date {0} cannot be after employee's relieving Date {1},La date de début {0 From Date {0} cannot be before employee's joining Date {1},La date de départ {0} ne peut pas être antérieure à la date d'arrivée de l'employé {1}, From Datetime,A partir du (Date et Heure), From Delivery Note,Du Bon de Livraison, -From Fiscal Year,À partir de l'année fiscale, +From Fiscal Year,À partir de l'année fiscale, From GSTIN,GSTIN (Origine), From Party Name,Nom du tiers (Origine), From Pin Code,Code postal (Origine), @@ -1132,7 +1132,7 @@ From State,Etat (Origine), From Time,Horaire de Début, From Time Should Be Less Than To Time,Du temps devrait être moins que du temps, From Time cannot be greater than To Time.,L’Horaire Initial ne peut pas être postérieur à l’Horaire Final, -"From a supplier under composition scheme, Exempt and Nil rated","De la part d'un fournisseur sous schéma de composition, coté Exempt et Nil", +"From a supplier under composition scheme, Exempt and Nil rated","De la part d'un fournisseur sous schéma de composition, coté Exempt et Nil", From and To dates required,Les date Du et Au sont requises, From date can not be less than employee's joining date,La date de départ ne peut être antérieure à la date d'arrivée de l'employé, From value must be less than to value in row {0},De la valeur doit être inférieure à la valeur de la ligne {0}, @@ -1184,7 +1184,7 @@ Goals cannot be empty,Les objectifs ne peuvent pas être vides, Goods In Transit,Les marchandises en transit, Goods Transferred,Marchandises transférées, Goods and Services Tax (GST India),Taxe sur les Biens et Services (GST India), -Goods are already received against the outward entry {0},Les marchandises sont déjà reçues pour l'entrée sortante {0}, +Goods are already received against the outward entry {0},Les marchandises sont déjà reçues pour l'entrée sortante {0}, Government,Gouvernement, Grand Total,Total TTC, Grant,Subvention, @@ -1229,11 +1229,11 @@ Health Care,Soins de santé, Healthcare,Santé, Healthcare (beta),Santé (beta), Healthcare Practitioner,Praticien de la santé, -Healthcare Practitioner not available on {0},Le praticien de la santé n'est pas disponible le {0}, -Healthcare Practitioner {0} not available on {1},Le praticien de la santé {0} n'est pas disponible le {1}, +Healthcare Practitioner not available on {0},Le praticien de la santé n'est pas disponible le {0}, +Healthcare Practitioner {0} not available on {1},Le praticien de la santé {0} n'est pas disponible le {1}, Healthcare Service Unit,Service de soins de santé, Healthcare Service Unit Tree,Arbre des services de soins de santé, -Healthcare Service Unit Type,Type d'unité de service de soins de santé, +Healthcare Service Unit Type,Type d'unité de service de soins de santé, Healthcare Services,Services de santé, Healthcare Settings,Paramètres de santé, Hello,Bonjour, @@ -1244,7 +1244,7 @@ Hold,Tenir, Hold Invoice,Facture en attente, Holiday,Vacances, Holiday List,Liste de vacances, -Hotel Rooms of type {0} are unavailable on {1},Les chambres d'hôtel de type {0} sont indisponibles le {1}, +Hotel Rooms of type {0} are unavailable on {1},Les chambres d'hôtel de type {0} sont indisponibles le {1}, Hotels,Hôtels, Hourly,Horaire, Hours,Heures, @@ -1272,7 +1272,7 @@ Ignore Existing Ordered Qty,Ignorer la quantité commandée existante, Image,Image, Image View,Voir l'Image, Import Data,Importer des données, -Import Day Book Data,Données du journal d'importation, +Import Day Book Data,Données du journal d'importation, Import Log,Journal d'import, Import Master Data,Importer des données de base, Import Successfull,Importation réussie, @@ -1308,14 +1308,14 @@ Indirect Income,Revenu indirect, Individual,Individuel, Ineligible ITC,CTI non éligible, Initiated,Initié, -Inpatient Record,Dossier d'hospitalisation, +Inpatient Record,Dossier d'hospitalisation, Insert,Insérer, Installation Note,Note d'Installation, Installation Note {0} has already been submitted,Note d'Installation {0} à déjà été sousmise, Installation date cannot be before delivery date for Item {0},Date d'installation ne peut pas être avant la date de livraison pour l'Article {0}, Installing presets,Installation des réglages, Institute Abbreviation,Abréviation de l'Institut, -Institute Name,Nom de l'Institut, +Institute Name,Nom de l'Institut, Instructor,Instructeur, Insufficient Stock,Stock insuffisant, Insurance Start date should be less than Insurance End date,Date de Début d'Assurance devrait être antérieure à la Date de Fin d'Assurance, @@ -1331,8 +1331,8 @@ Invalid Attribute,Attribut invalide, Invalid Blanket Order for the selected Customer and Item,Commande avec limites non valide pour le client et l'article sélectionnés, Invalid Company for Inter Company Transaction.,Société non valide pour une transaction inter-sociétés., Invalid GSTIN! A GSTIN must have 15 characters.,GSTIN invalide! Un GSTIN doit comporter 15 caractères., -Invalid GSTIN! First 2 digits of GSTIN should match with State number {0}.,GSTIN invalide! Les deux premiers chiffres de GSTIN doivent correspondre au numéro d'état {0}., -Invalid GSTIN! The input you've entered doesn't match the format of GSTIN.,GSTIN invalide! L'entrée que vous avez entrée ne correspond pas au format de GSTIN., +Invalid GSTIN! First 2 digits of GSTIN should match with State number {0}.,GSTIN invalide! Les deux premiers chiffres de GSTIN doivent correspondre au numéro d'état {0}., +Invalid GSTIN! The input you've entered doesn't match the format of GSTIN.,GSTIN invalide! L'entrée que vous avez entrée ne correspond pas au format de GSTIN., Invalid Posting Time,Heure de publication non valide, Invalid attribute {0} {1},Attribut invalide {0} {1}, Invalid quantity specified for item {0}. Quantity should be greater than 0.,Quantité spécifiée invalide pour l'élément {0}. Quantité doit être supérieur à 0., @@ -1351,12 +1351,12 @@ Invoice Posting Date,Date d’Envois de la Facture, Invoice Type,Type de facture, Invoice already created for all billing hours,Facture déjà créée pour toutes les heures facturées, Invoice can't be made for zero billing hour,La facture ne peut pas être faite pour une heure facturée à zéro, -Invoice {0} no longer exists,La facture {0} n'existe plus, +Invoice {0} no longer exists,La facture {0} n'existe plus, Invoiced,Facturé, Invoiced Amount,Montant facturé, Invoices,Factures, Invoices for Costumers.,Factures pour les clients., -Inward Supplies(liable to reverse charge,Approvisionnement entrant (susceptible d'inverser la charge, +Inward Supplies(liable to reverse charge,Approvisionnement entrant (susceptible d'inverser la charge, Inward supplies from ISD,Approvisionnement entrant de la DSI, Inward supplies liable to reverse charge (other than 1 & 2 above),Approvisionnements entrants susceptibles d’être dédouanés (autres que 1 et 2 ci-dessus), Is Active,Est Active, @@ -1383,17 +1383,17 @@ Item Description,Description de l'Article, Item Group,Groupe d'Article, Item Group Tree,Arborescence de Groupe d'Article, Item Group not mentioned in item master for item {0},Le Groupe d'Articles n'est pas mentionné dans la fiche de l'article pour l'article {0}, -Item Name,Nom de l'article, +Item Name,Nom de l'article, Item Price added for {0} in Price List {1},Prix de l'Article ajouté pour {0} dans la Liste de Prix {1}, -"Item Price appears multiple times based on Price List, Supplier/Customer, Currency, Item, UOM, Qty and Dates.","Le prix de l'article apparaît plusieurs fois en fonction de la liste de prix, du fournisseur / client, de la devise, de l'article, de l'unité de mesure, de la quantité et des dates.", +"Item Price appears multiple times based on Price List, Supplier/Customer, Currency, Item, UOM, Qty and Dates.","Le prix de l'article apparaît plusieurs fois en fonction de la liste de prix, du fournisseur / client, de la devise, de l'article, de l'unité de mesure, de la quantité et des dates.", Item Price updated for {0} in Price List {1},Prix de l'Article mis à jour pour {0} dans la Liste des Prix {1}, -Item Row {0}: {1} {2} does not exist in above '{1}' table,Ligne d'objet {0}: {1} {2} n'existe pas dans la table '{1}' ci-dessus, +Item Row {0}: {1} {2} does not exist in above '{1}' table,Ligne d'objet {0}: {1} {2} n'existe pas dans la table '{1}' ci-dessus, Item Tax Row {0} must have account of type Tax or Income or Expense or Chargeable,La Ligne de Taxe d'Article {0} doit indiquer un compte de type Taxes ou Produit ou Charge ou Facturable, -Item Template,Modèle d'article, +Item Template,Modèle d'article, Item Variant Settings,Paramètres de Variante d'Article, Item Variant {0} already exists with same attributes,La Variante de l'Article {0} existe déjà avec les mêmes caractéristiques, Item Variants,Variantes de l'Article, -Item Variants updated,Variantes d'article mises à jour, +Item Variants updated,Variantes d'article mises à jour, Item has variants.,L'article a des variantes., Item must be added using 'Get Items from Purchase Receipts' button,L'article doit être ajouté à l'aide du bouton 'Obtenir des éléments de Reçus d'Achat', Item or Warehouse for row {0} does not match Material Request,L'Article ou l'Entrepôt pour la ligne {0} ne correspond pas avec la Requête de Matériel, @@ -1422,12 +1422,12 @@ Item {0} not found in 'Raw Materials Supplied' table in Purchase Order {1},Artic Item {0}: Ordered qty {1} cannot be less than minimum order qty {2} (defined in Item).,L'article {0} : Qté commandée {1} ne peut pas être inférieure à la qté de commande minimum {2} (défini dans l'Article)., Item: {0} does not exist in the system,Article : {0} n'existe pas dans le système, Items,Articles, -Items Filter,Filtre d'articles, +Items Filter,Filtre d'articles, Items and Pricing,Articles et prix, Items for Raw Material Request,Articles pour demande de matière première, Job Card,Carte de travail, Job Description,Description de l'Emploi, -Job Offer,Offre d'emploi, +Job Offer,Offre d'emploi, Job card {0} created,Job card {0} créée, Jobs,Emplois, Join,Joindre, @@ -1455,7 +1455,7 @@ Last Communication Date,Date de la Dernière Communication, Last Name,Nom de famille, Last Order Amount,Montant de la Dernière Commande, Last Order Date,Date de la dernière commande, -Last Purchase Price,Dernier prix d'achat, +Last Purchase Price,Dernier prix d'achat, Last Purchase Rate,Dernier Prix d'Achat, Latest,Dernier, Latest price updated in all BOMs,Prix les plus récents mis à jour dans toutes les LDMs, @@ -1479,7 +1479,7 @@ Leave Type {0} cannot be carry-forwarded,Le Type de Congé {0} ne peut pas être Leave Type {0} is not encashable,Le type de congé {0} n'est pas encaissable, Leave Without Pay,Congé Sans Solde, Leave and Attendance,Congés et Présences, -Leave application {0} already exists against the student {1},Laisser l'application {0} existe déjà pour l'étudiant {1}, +Leave application {0} already exists against the student {1},Laisser l'application {0} existe déjà pour l'étudiant {1}, "Leave cannot be allocated before {0}, as leave balance has already been carry-forwarded in the future leave allocation record {1}","Congé ne peut être alloué avant le {0}, car le solde de congés a déjà été reporté dans la feuille d'allocation de congés futurs {1}", "Leave cannot be applied/cancelled before {0}, as leave balance has already been carry-forwarded in the future leave allocation record {1}","Congé ne peut être demandé / annulé avant le {0}, car le solde de congés a déjà été reporté dans la feuille d'allocation de congés futurs {1}", Leave of type {0} cannot be longer than {1},Les Congés de type {0} ne peuvent pas être plus long que {1}, @@ -1543,7 +1543,7 @@ Maintenance Visit {0} must be cancelled before cancelling this Sales Order,La Vi Maintenance start date can not be before delivery date for Serial No {0},La date de début d'entretien ne peut pas être antérieure à la date de livraison pour le N° de Série {0}, Make,Faire, Make Payment,Faire un Paiement, -Make project from a template.,Faire un projet à partir d'un modèle., +Make project from a template.,Faire un projet à partir d'un modèle., Making Stock Entries,Faire des Écritures de Stock, Male,Masculin, Manage Customer Group Tree.,Gérer l'Arborescence des Groupes de Clients., @@ -1593,16 +1593,16 @@ Material Request {0} submitted.,Demande de matériel {0} soumise., Material Transfer,Transfert de matériel, Material Transferred,Matériel transféré, Material to Supplier,Du Matériel au Fournisseur, -Max Exemption Amount cannot be greater than maximum exemption amount {0} of Tax Exemption Category {1},Le montant maximal de l'exemption ne peut pas dépasser le montant maximal de l'exonération {0} de la catégorie d'exonération fiscale {1}., +Max Exemption Amount cannot be greater than maximum exemption amount {0} of Tax Exemption Category {1},Le montant maximal de l'exemption ne peut pas dépasser le montant maximal de l'exonération {0} de la catégorie d'exonération fiscale {1}., Max benefits should be greater than zero to dispense benefits,Les prestations sociales maximales doivent être supérieures à zéro pour être calculées, Max discount allowed for item: {0} is {1}%,Réduction max autorisée pour l'article : {0} est de {1} %, Max: {0},Max : {0}, -Maximum Samples - {0} can be retained for Batch {1} and Item {2}.,Maximum d'échantillons - {0} peut être conservé pour le lot {1} et l'article {2}., -Maximum Samples - {0} have already been retained for Batch {1} and Item {2} in Batch {3}.,Nombre maximum d'échantillons - {0} ont déjà été conservés pour le lot {1} et l'article {2} dans le lot {3}., +Maximum Samples - {0} can be retained for Batch {1} and Item {2}.,Maximum d'échantillons - {0} peut être conservé pour le lot {1} et l'article {2}., +Maximum Samples - {0} have already been retained for Batch {1} and Item {2} in Batch {3}.,Nombre maximum d'échantillons - {0} ont déjà été conservés pour le lot {1} et l'article {2} dans le lot {3}., Maximum amount eligible for the component {0} exceeds {1},Le montant maximal éligible pour le composant {0} dépasse {1}, Maximum benefit amount of component {0} exceeds {1},La quantité maximale de prestations sociales du composant {0} dépasse {1}, Maximum benefit amount of employee {0} exceeds {1},Le montant maximal des prestations sociales de l'employé {0} dépasse {1}, -Maximum discount for Item {0} is {1}%,La remise maximale pour l'article {0} est {1}%, +Maximum discount for Item {0} is {1}%,La remise maximale pour l'article {0} est {1}%, Maximum leave allowed in the leave type {0} is {1},La durée maximale autorisée pour le type de congé {0} est {1}, Medical,Médical, Medical Code,Code médical, @@ -1616,9 +1616,9 @@ Member ID,ID du membre, Member Name,Nom de membre, Member information.,Informations sur le membre, Membership,Adhésion, -Membership Details,Détails de l'adhésion, -Membership ID,ID d'adhésion, -Membership Type,Type d'adhésion, +Membership Details,Détails de l'adhésion, +Membership ID,ID d'adhésion, +Membership Type,Type d'adhésion, Memebership Details,Détails de l'adhésion, Memebership Type Details,Détails du type d'adhésion, Merge,Fusionner, @@ -1636,8 +1636,8 @@ Min Qty can not be greater than Max Qty,Qté Min ne peut pas être supérieure Minimum Lead Age (Days),Âge Minimum du Prospect (Jours), Miscellaneous Expenses,Charges Diverses, Missing Currency Exchange Rates for {0},Taux de Change Manquant pour {0}, -Missing email template for dispatch. Please set one in Delivery Settings.,Modèle de courrier électronique manquant pour l'envoi. Veuillez en définir un dans les paramètres de livraison., -"Missing value for Password, API Key or Shopify URL","Valeur manquante pour le mot de passe, la clé API ou l'URL Shopify", +Missing email template for dispatch. Please set one in Delivery Settings.,Modèle de courrier électronique manquant pour l'envoi. Veuillez en définir un dans les paramètres de livraison., +"Missing value for Password, API Key or Shopify URL","Valeur manquante pour le mot de passe, la clé API ou l'URL Shopify", Mode of Payment,Moyen de paiement, Mode of Payments,Mode de paiement, Mode of Transport,Mode de transport, @@ -1651,7 +1651,7 @@ Monthly Distribution,Répartition Mensuelle, Monthly Repayment Amount cannot be greater than Loan Amount,Montant du Remboursement Mensuel ne peut pas être supérieur au Montant du Prêt, More,Plus, More Information,Informations Complémentaires, -More than one selection for {0} not allowed,Plus d'une sélection pour {0} non autorisée, +More than one selection for {0} not allowed,Plus d'une sélection pour {0} non autorisée, More...,Plus..., Motion Picture & Video,Cinéma & Vidéo, Move,mouvement, @@ -1719,7 +1719,7 @@ Next,Suivant, Next Contact By cannot be same as the Lead Email Address,Prochain Contact Par ne peut être identique à l’Adresse Email du Prospect, Next Contact Date cannot be in the past,La Date de Prochain Contact ne peut pas être dans le passé, Next Steps,Prochaines étapes, -No Action,Pas d'action, +No Action,Pas d'action, No Customers yet!,Pas encore de clients!, No Data,Aucune Donnée, No Delivery Note selected for Customer {},Aucun bon de livraison sélectionné pour le client {}, @@ -1737,11 +1737,11 @@ No Permission,Aucune autorisation, No Quote,Aucun Devis, No Remarks,Aucune Remarque, No Result to submit,Aucun résultat à soumettre, -No Salary Structure assigned for Employee {0} on given date {1},Aucune structure de salaire attribuée à l'employé {0} à la date donnée {1}, +No Salary Structure assigned for Employee {0} on given date {1},Aucune structure de salaire attribuée à l'employé {0} à la date donnée {1}, No Staffing Plans found for this Designation,Aucun plan de dotation trouvé pour cette désignation, No Student Groups created.,Aucun Groupe d'Étudiants créé., No Students in,Aucun étudiant dans, -No Tax Withholding data found for the current Fiscal Year.,Aucune donnée de retenue d'impôt trouvée pour l'exercice en cours., +No Tax Withholding data found for the current Fiscal Year.,Aucune donnée de retenue d'impôt trouvée pour l'exercice en cours., No Work Orders created,Aucun ordre de travail créé, No accounting entries for the following warehouses,Pas d’écritures comptables pour les entrepôts suivants, No active or default Salary Structure found for employee {0} for the given dates,Aucune Structure de Salaire active ou par défaut trouvée pour employé {0} pour les dates données, @@ -1753,12 +1753,12 @@ No description given,Aucune Description, No employees for the mentioned criteria,Aucun employé pour les critères mentionnés, No gain or loss in the exchange rate,Aucun gain ou perte dans le taux de change, No items listed,Aucun article référencé, -No items to be received are overdue,Aucun article à recevoir n'est en retard, +No items to be received are overdue,Aucun article à recevoir n'est en retard, No material request created,Aucune demande de matériel créée, No more updates,Pas de mise à jour supplémentaire, -No of Interactions,Nombre d'interactions, -No of Shares,Nombre d'actions, -No pending Material Requests found to link for the given items.,Aucune demande de matériel en attente n'a été trouvée pour créer un lien vers les articles donnés., +No of Interactions,Nombre d'interactions, +No of Shares,Nombre d'actions, +No pending Material Requests found to link for the given items.,Aucune demande de matériel en attente n'a été trouvée pour créer un lien vers les articles donnés., No products found,Aucun produit trouvé, No products found.,Aucun produit trouvé., No record found,Aucun Enregistrement Trouvé, @@ -1769,7 +1769,7 @@ No salary slip found to submit for the above selected criteria OR salary slip al No tasks,Aucune tâche, No time sheets,Aucunes feuilles de temps, No values,Pas de valeurs, -No {0} found for Inter Company Transactions.,Aucun {0} n'a été trouvé pour les transactions inter-sociétés., +No {0} found for Inter Company Transactions.,Aucun {0} n'a été trouvé pour les transactions inter-sociétés., Non GST Inward Supplies,Fournitures entrantes non liées à la TPS, Non Profit,À But Non Lucratif, Non Profit (beta),Association (bêta), @@ -1792,7 +1792,7 @@ Not eligible for the admission in this program as per DOB,Non admissible à l'ad Not items found,Pas d'objets trouvés, Not permitted for {0},Non autorisé pour {0}, "Not permitted, configure Lab Test Template as required","Non autorisé, veuillez configurer le modèle de test de laboratoire", -Not permitted. Please disable the Service Unit Type,Pas permis. Veuillez désactiver le type d'unité de service, +Not permitted. Please disable the Service Unit Type,Pas permis. Veuillez désactiver le type d'unité de service, Note: Due / Reference Date exceeds allowed customer credit days by {0} day(s),Remarque : Date de Référence / d’Échéance dépasse le nombre de jours de crédit client autorisé de {0} jour(s), Note: Item {0} entered multiple times,Remarque : Article {0} saisi plusieurs fois, Note: Payment Entry will not be created since 'Cash or Bank Account' was not specified,Remarque : Écriture de Paiement ne sera pas créée car le compte 'Compte Bancaire ou de Caisse' n'a pas été spécifié, @@ -1801,7 +1801,7 @@ Note: There is not enough leave balance for Leave Type {0},Remarque : Le solde d Note: This Cost Center is a Group. Cannot make accounting entries against groups.,Remarque : Ce Centre de Coûts est un Groupe. Vous ne pouvez pas faire des écritures comptables sur des groupes., Note: {0},Note : {0}, Notes,Remarques, -Nothing is included in gross,Rien n'est inclus dans le brut, +Nothing is included in gross,Rien n'est inclus dans le brut, Nothing more to show.,Rien de plus à montrer., Nothing to change,Rien à changer, Notice Period,Période de préavis, @@ -1824,8 +1824,8 @@ Online,En ligne, Online Auctions,Enchères en ligne, Only Leave Applications with status 'Approved' and 'Rejected' can be submitted,Seules les Demandes de Congés avec le statut 'Appouvée' ou 'Rejetée' peuvent être soumises, "Only the Student Applicant with the status ""Approved"" will be selected in the table below.",Seul les candidatures étudiantes avec le statut «Approuvé» seront sélectionnées dans le tableau ci-dessous., -Only users with {0} role can register on Marketplace,Seuls les utilisateurs ayant le rôle {0} peuvent s'inscrire sur Marketplace, -Only {0} in stock for item {1},Seulement {0} en stock pour l'article {1}, +Only users with {0} role can register on Marketplace,Seuls les utilisateurs ayant le rôle {0} peuvent s'inscrire sur Marketplace, +Only {0} in stock for item {1},Seulement {0} en stock pour l'article {1}, Open BOM {0},Ouvrir LDM {0}, Open Item {0},Ouvrir l'Article {0}, Open Notifications,Notifications ouvertes, @@ -1837,13 +1837,13 @@ Opening (Dr),Ouverture (Dr), Opening Accounting Balance,Solde d'Ouverture de Comptabilité, Opening Accumulated Depreciation,Amortissement Cumulé d'Ouverture, Opening Accumulated Depreciation must be less than equal to {0},Amortissement Cumulé d'Ouverture doit être inférieur ou égal à {0}, -Opening Balance,Solde d'ouverture, +Opening Balance,Solde d'ouverture, Opening Balance Equity,Ouverture de la Balance des Capitaux Propres, Opening Date and Closing Date should be within same Fiscal Year,Date d'Ouverture et Date de Clôture devraient être dans le même Exercice, Opening Date should be before Closing Date,Date d'Ouverture devrait être antérieure à la Date de Clôture, Opening Entry Journal,Ecriture de journal d'ouverture, -Opening Invoice Creation Tool,Ouverture de l'outil de création de facture, -Opening Invoice Item,Ouverture d'un poste de facture, +Opening Invoice Creation Tool,Ouverture de l'outil de création de facture, +Opening Invoice Item,Ouverture d'un poste de facture, Opening Invoices,Ouverture des factures, Opening Invoices Summary,Ouverture des factures Résumé, Opening Qty,Quantité d'Ouverture, @@ -1861,7 +1861,7 @@ Opp/Lead %,Opp / Prospect %, Opportunities,Opportunités, Opportunities by lead source,Opportunités par source de plomb, Opportunity,Opportunité, -Opportunity Amount,Montant de l'opportunité, +Opportunity Amount,Montant de l'opportunité, Optional Holiday List not set for leave period {0},Une liste de vacances facultative n'est pas définie pour la période de congé {0}, "Optional. Sets company's default currency, if not specified.","Optionnel. Défini la devise par défaut de l'entreprise, si non spécifié.", Optional. This setting will be used to filter in various transactions.,Facultatif. Ce paramètre sera utilisé pour filtrer différentes transactions., @@ -1925,7 +1925,7 @@ Party Type and Party is mandatory for {0} account,Le type de tiers et le tiers s Party Type is mandatory,Type de Tiers Obligatoire, Party is mandatory,Le Tiers est obligatoire, Password,Mot de passe, -Password policy for Salary Slips is not set,La politique de mot de passe pour les bulletins de salaire n'est pas définie, +Password policy for Salary Slips is not set,La politique de mot de passe pour les bulletins de salaire n'est pas définie, Past Due Date,Date d'échéance dépassée, Patient,Patient, Patient Appointment,Rendez-vous patient, @@ -1971,7 +1971,7 @@ Payments,Paiements, Payroll,Paie, Payroll Number,Numéro de paie, Payroll Payable,Paie à Payer, -Payroll date can not be less than employee's joining date,La date de paie ne peut être inférieure à la date d'adhésion de l'employé, +Payroll date can not be less than employee's joining date,La date de paie ne peut être inférieure à la date d'adhésion de l'employé, Payslip,Fiche de paie, Pending Activities,Activités en attente, Pending Amount,Montant en attente, @@ -1994,17 +1994,17 @@ Physician,Médecin, Piecework,Travail à la pièce, Pin Code,Code PIN, Pincode,Code Postal, -Place Of Supply (State/UT),Lieu d'approvisionnement (State / UT), +Place Of Supply (State/UT),Lieu d'approvisionnement (State / UT), Place Order,Passer la commande, Plan Name,Nom du plan, Plan for maintenance visits.,Plan pour les visites de maintenance., Planned Qty,Qté Planifiée, -"Planned Qty: Quantity, for which, Work Order has been raised, but is pending to be manufactured.",Qté prévue: quantité pour laquelle l'ordre de travail a été créé mais sa fabrication est en attente., +"Planned Qty: Quantity, for which, Work Order has been raised, but is pending to be manufactured.",Qté prévue: quantité pour laquelle l'ordre de travail a été créé mais sa fabrication est en attente., Planning,Planification, Plants and Machineries,Usines et Machines, Please Set Supplier Group in Buying Settings.,Veuillez définir un groupe de fournisseurs par défaut dans les paramètres d'achat., -Please add a Temporary Opening account in Chart of Accounts,Veuillez ajouter un compte d'ouverture temporaire dans le plan comptable, -Please add the account to root level Company - ,S'il vous plaît ajouter le compte au niveau racine Société -, +Please add a Temporary Opening account in Chart of Accounts,Veuillez ajouter un compte d'ouverture temporaire dans le plan comptable, +Please add the account to root level Company - ,S'il vous plaît ajouter le compte au niveau racine Société -, Please add the remaining benefits {0} to any of the existing component,Veuillez ajouter les prestations restantes {0} à l'un des composants existants, Please check Multi Currency option to allow accounts with other currency,Veuillez vérifier l'option Multi-Devises pour permettre les comptes avec une autre devise, Please click on 'Generate Schedule',"Veuillez cliquer sur ""Générer calendrier''", @@ -2013,7 +2013,7 @@ Please click on 'Generate Schedule' to get schedule,Veuillez cliquer sur ‘Gén Please confirm once you have completed your training,Veuillez confirmer une fois que vous avez terminé votre formation, Please contact to the user who have Sales Master Manager {0} role,Veuillez contactez l'utilisateur qui a le rôle de Directeur des Ventes {0}, Please create Customer from Lead {0},Veuillez créer un client à partir du prospect {0}, -Please create purchase receipt or purchase invoice for the item {0},Veuillez créer un reçu d'achat ou une facture d'achat pour l'article {0}, +Please create purchase receipt or purchase invoice for the item {0},Veuillez créer un reçu d'achat ou une facture d'achat pour l'article {0}, Please define grade for Threshold 0%,Veuillez définir une note pour le Seuil 0%, Please enable Applicable on Booking Actual Expenses,Veuillez activer l'option : Applicable sur la base de l'enregistrement des dépenses réelles, Please enable Applicable on Purchase Order and Applicable on Booking Actual Expenses,Veuillez activer les options : Applicable sur la base des bons de commande d'achat et Applicable sur la base des bons de commande d'achat, @@ -2056,11 +2056,11 @@ Please enter repayment Amount,Veuillez entrer le Montant de remboursement, Please enter valid Financial Year Start and End Dates,Veuillez entrer des Dates de Début et de Fin d’Exercice Comptable valides, Please enter valid email address,Entrez une adresse email valide, Please enter {0} first,Veuillez d’abord entrer {0}, -Please fill in all the details to generate Assessment Result.,Veuillez renseigner tous les détails pour générer le résultat de l'évaluation., +Please fill in all the details to generate Assessment Result.,Veuillez renseigner tous les détails pour générer le résultat de l'évaluation., Please identify/create Account (Group) for type - {0},Veuillez identifier / créer un compte (groupe) pour le type - {0}, Please identify/create Account (Ledger) for type - {0},Veuillez identifier / créer un compte (grand livre) pour le type - {0}, Please input all required Result Value(s),Veuillez entrer toutes les valeurs de résultat requises, -Please login as another user to register on Marketplace,Veuillez vous connecter en tant qu'autre utilisateur pour vous inscrire sur Marketplace, +Please login as another user to register on Marketplace,Veuillez vous connecter en tant qu'autre utilisateur pour vous inscrire sur Marketplace, Please make sure you really want to delete all the transactions for this company. Your master data will remain as it is. This action cannot be undone.,Veuillez vous assurer que vous voulez vraiment supprimer tous les transactions de cette société. Vos données de base resteront intactes. Cette action ne peut être annulée., Please mention Basic and HRA component in Company,Veuillez mentionner les composants Basic et HRA dans la société, Please mention Round Off Account in Company,Veuillez indiquer le Compte d’Arrondi de la Société, @@ -2085,10 +2085,10 @@ Please select Company and Designation,Veuillez sélectionner la société et la Please select Company and Party Type first,Veuillez d’abord sélectionner une Société et le Type de Tiers, Please select Company and Posting Date to getting entries,Veuillez sélectionner la société et la date de comptabilisation pour obtenir les écritures, Please select Company first,Veuillez d’abord sélectionner une Société, -Please select Completion Date for Completed Asset Maintenance Log,Veuillez sélectionner la date d'achèvement pour le journal de maintenance des actifs terminé, -Please select Completion Date for Completed Repair,Veuillez sélectionner la date d'achèvement pour la réparation terminée, +Please select Completion Date for Completed Asset Maintenance Log,Veuillez sélectionner la date d'achèvement pour le journal de maintenance des actifs terminé, +Please select Completion Date for Completed Repair,Veuillez sélectionner la date d'achèvement pour la réparation terminée, Please select Course,Veuillez sélectionner un cours, -Please select Drug,S'il vous plaît sélectionnez Drug, +Please select Drug,S'il vous plaît sélectionnez Drug, Please select Employee,Veuillez sélectionner un employé, Please select Employee Record first.,Veuillez d’abord sélectionner le Dossier de l'Employé., Please select Existing Company for creating Chart of Accounts,Veuillez sélectionner une Société Existante pour créer un Plan de Compte, @@ -2102,7 +2102,7 @@ Please select Posting Date before selecting Party,Veuillez sélectionner la Date Please select Posting Date first,Veuillez d’abord sélectionner la Date de Comptabilisation, Please select Price List,Veuillez sélectionner une Liste de Prix, Please select Program,Veuillez sélectionner un programme, -Please select Qty against item {0},Veuillez sélectionner Qté par rapport à l'élément {0}, +Please select Qty against item {0},Veuillez sélectionner Qté par rapport à l'élément {0}, Please select Sample Retention Warehouse in Stock Settings first,Veuillez d'abord définir un entrepôt de stockage des échantillons dans les paramètres de stock, Please select Start Date and End Date for Item {0},Veuillez sélectionner la Date de Début et Date de Fin pour l'Article {0}, Please select Student Admission which is mandatory for the paid student applicant,Veuillez sélectionner obligatoirement une Admission d'Étudiant pour la candidature étudiante payée, @@ -2111,7 +2111,7 @@ Please select a Batch for Item {0}. Unable to find a single batch that fulfills Please select a Company,Veuillez sélectionner une Société, Please select a batch,Veuillez sélectionner un lot, Please select a csv file,Veuillez sélectionner un fichier csv, -Please select a customer,S'il vous plaît sélectionner un client, +Please select a customer,S'il vous plaît sélectionner un client, Please select a field to edit from numpad,Veuillez sélectionner un champ à modifier sur le pavé numérique, Please select a table,Veuillez sélectionner une table, Please select a valid Date,Veuillez sélectionner une date valide, @@ -2127,7 +2127,7 @@ Please select month and year,Veuillez sélectionner le mois et l'année, Please select prefix first,Veuillez d’abord sélectionner un préfixe, Please select the Company,Veuillez sélectionner la société, Please select the Company first,Veuillez sélectionner la Société en premier, -Please select the Multiple Tier Program type for more than one collection rules.,Veuillez sélectionner le type de programme à plusieurs niveaux pour plus d'une règle de collecte., +Please select the Multiple Tier Program type for more than one collection rules.,Veuillez sélectionner le type de programme à plusieurs niveaux pour plus d'une règle de collecte., Please select the assessment group other than 'All Assessment Groups',Sélectionnez un groupe d'évaluation autre que «Tous les Groupes d'Évaluation», Please select the document type first,Veuillez d’abord sélectionner le type de document, Please select weekly off day,Veuillez sélectionnez les jours de congé hebdomadaires, @@ -2149,9 +2149,9 @@ Please set Number of Depreciations Booked,Veuillez définir le Nombre d’Amorti Please set Unrealized Exchange Gain/Loss Account in Company {0},Veuillez définir un compte de gain / perte de change non réalisé pour la société {0}, Please set User ID field in an Employee record to set Employee Role,Veuillez définir le champ ID de l'Utilisateur dans un dossier Employé pour définir le Rôle de l’Employés, Please set a default Holiday List for Employee {0} or Company {1},Veuillez définir une Liste de Vacances par défaut pour l'Employé {0} ou la Société {1}, -Please set account in Warehouse {0},Veuillez définir un compte dans l'entrepôt {0}, +Please set account in Warehouse {0},Veuillez définir un compte dans l'entrepôt {0}, Please set an active menu for Restaurant {0},Veuillez définir un menu actif pour le restaurant {0}, -Please set associated account in Tax Withholding Category {0} against Company {1},Veuillez définir le compte associé dans la catégorie de retenue d'impôt {0} contre la société {1}., +Please set associated account in Tax Withholding Category {0} against Company {1},Veuillez définir le compte associé dans la catégorie de retenue d'impôt {0} contre la société {1}., Please set at least one row in the Taxes and Charges Table,Veuillez définir au moins une ligne dans le tableau des taxes et des frais., Please set default Cash or Bank account in Mode of Payment {0},Veuillez définir un compte de Caisse ou de Banque par défaut pour le Mode de Paiement {0}, Please set default account in Salary Component {0},Veuillez définir le compte par défaut dans la Composante Salariale {0}, @@ -2161,17 +2161,17 @@ Please set default template for Leave Approval Notification in HR Settings.,Veui Please set default template for Leave Status Notification in HR Settings.,Veuillez définir un modèle par défaut pour la notification de statut de congés dans les paramètres RH., Please set default {0} in Company {1},Veuillez définir {0} par défaut dans la Société {1}, Please set filter based on Item or Warehouse,Veuillez définir un filtre basé sur l'Article ou l'Entrepôt, -Please set leave policy for employee {0} in Employee / Grade record,Veuillez définir la politique de congé pour l'employé {0} dans le dossier Employé / Grade, +Please set leave policy for employee {0} in Employee / Grade record,Veuillez définir la politique de congé pour l'employé {0} dans le dossier Employé / Grade, Please set recurring after saving,Veuillez définir la récurrence après avoir sauvegardé, Please set the Company,Veuillez définir la Société, -Please set the Customer Address,Veuillez définir l'adresse du client, +Please set the Customer Address,Veuillez définir l'adresse du client, Please set the Date Of Joining for employee {0},Veuillez définir la Date d'Embauche pour l'employé {0}, Please set the Default Cost Center in {0} company.,Veuillez définir un centre de coûts par défaut pour la société {0}., Please set the Email ID for the Student to send the Payment Request,Configurez l'ID de courrier électronique pour que l'Élève envoie la Demande de Paiement, Please set the Item Code first,Veuillez définir le Code d'Article en premier, Please set the Payment Schedule,Veuillez définir le calendrier de paiement, Please set the series to be used.,Veuillez définir la série à utiliser., -Please set {0} for address {1},Définissez {0} pour l'adresse {1}., +Please set {0} for address {1},Définissez {0} pour l'adresse {1}., Please setup Students under Student Groups,Veuillez configurer les Étudiants sous des groupes d'Étudiants, Please share your feedback to the training by clicking on 'Training Feedback' and then 'New',"Partagez vos commentaires sur la formation en cliquant sur 'Retour d'Expérience de la formation', puis 'Nouveau'", Please specify Company,Veuillez spécifier la Société, @@ -2227,13 +2227,13 @@ Pricing Rule,Règle de tarification, Pricing Rule {0} is updated,La règle de tarification {0} est mise à jour, Pricing Rules are further filtered based on quantity.,Les Règles de Tarification sont d'avantage filtrés en fonction de la quantité., Primary,Primaire, -Primary Address Details,Détails de l'adresse principale, +Primary Address Details,Détails de l'adresse principale, Primary Contact Details,Détails du contact principal, Principal Amount,Montant Principal, -Print Format,Format d'impression, +Print Format,Format d'impression, Print IRS 1099 Forms,Imprimer les formulaires IRS 1099, Print Report Card,Imprimer le rapport, -Print Settings,Paramètres d'impression, +Print Settings,Paramètres d'impression, Print and Stationery,Impression et Papeterie, Print settings updated in respective print format,Paramètres d'impression mis à jour avec le format d'impression indiqué, Print taxes with zero amount,Impression de taxes avec un montant nul, @@ -2261,7 +2261,7 @@ Profit and Loss,Pertes et Profits, Profit for the year,Bénéfice de l'exercice, Program,Programme, Program in the Fee Structure and Student Group {0} are different.,Le programme dans la structure d'honoraires et le groupe d'étudiants {0} sont différents., -Program {0} does not exist.,Le programme {0} n'existe pas., +Program {0} does not exist.,Le programme {0} n'existe pas., Program: ,Programme:, Progress % for a task cannot be more than 100.,% de Progression pour une tâche ne peut pas être supérieur à 100., Project Collaboration Invitation,Invitation de Collaboration à un Projet, @@ -2301,7 +2301,7 @@ Purchase Order,Bon de commande, Purchase Order Amount,Bon de commande, Purchase Order Amount(Company Currency),Montant du bon de commande (devise de la société), Purchase Order Date,Date du bon de commande, -Purchase Order Items not received on time,Articles de commande d'achat non reçus à temps, +Purchase Order Items not received on time,Articles de commande d'achat non reçus à temps, Purchase Order number required for Item {0},Numéro de Bon de Commande requis pour l'Article {0}, Purchase Order to Payment,Du Bon de Commande au Paiement, Purchase Order {0} is not submitted,Le Bon de Commande {0} n’est pas soumis, @@ -2324,7 +2324,7 @@ Quality,Qualité, Quality Action,Action Qualité, Quality Goal.,Objectif de qualité., Quality Inspection,Inspection de la Qualité, -Quality Inspection: {0} is not submitted for the item: {1} in row {2},Contrôle qualité: {0} n'est pas soumis pour l'élément: {1} à la ligne {2}., +Quality Inspection: {0} is not submitted for the item: {1} in row {2},Contrôle qualité: {0} n'est pas soumis pour l'élément: {1} à la ligne {2}., Quality Management,Gestion de la qualité, Quality Meeting,Réunion de qualité, Quality Procedure,Procédure de qualité, @@ -2371,12 +2371,12 @@ Reading Uploaded File,Lecture du fichier téléchargé, Real Estate,Immobilier, Reason For Putting On Hold,Raison de la mise en attente, Reason for Hold,Raison de tenir, -Reason for hold: ,Raison de l'attente:, +Reason for hold: ,Raison de l'attente:, Receipt,Reçu, Receipt document must be submitted,Le reçu doit être soumis, Receivable,Créance, Receivable Account,Compte Débiteur, -Receive at Warehouse Entry,Recevoir à l'entrée de l'entrepôt, +Receive at Warehouse Entry,Recevoir à l'entrée de l'entrepôt, Received,Reçu, Received On,Reçu le, Received Quantity,Quantité reçue, @@ -2402,7 +2402,7 @@ Reference No.,Numéro de référence, Reference Number,Numéro de réference, Reference Owner,Responsable de la Référence, Reference Type,Type de référence, -"Reference: {0}, Item Code: {1} and Customer: {2}","Référence: {0}, Code de l'article: {1} et Client: {2}", +"Reference: {0}, Item Code: {1} and Customer: {2}","Référence: {0}, Code de l'article: {1} et Client: {2}", References,Références, Refresh Token,Jeton de Rafraîchissement, Region,Région, @@ -2413,7 +2413,7 @@ Related,en relation, Relation with Guardian1,Relation avec Tuteur1, Relation with Guardian2,Relation avec Tuteur2, Release Date,Date de la fin de mise en attente, -Reload Linked Analysis,Recharger l'analyse liée, +Reload Linked Analysis,Recharger l'analyse liée, Remaining,Restant, Remaining Balance,Solde restant, Remarks,Remarques, @@ -2460,16 +2460,16 @@ Reserved Qty,Qté Réservées, Reserved Qty for Production,Qté Réservée pour la Production, Reserved Qty for Production: Raw materials quantity to make manufacturing items.,Qté réservée à la production: quantité de matières premières permettant de fabriquer des articles de fabrication., "Reserved Qty: Quantity ordered for sale, but not delivered.","Réservés Quantité: Quantité de commande pour la vente , mais pas livré .", -Reserved Warehouse is mandatory for Item {0} in Raw Materials supplied,L'entrepôt réservé est obligatoire pour l'article {0} dans les matières premières fournies, +Reserved Warehouse is mandatory for Item {0} in Raw Materials supplied,L'entrepôt réservé est obligatoire pour l'article {0} dans les matières premières fournies, Reserved for manufacturing,Réservé pour la production, Reserved for sale,Réservé à la vente, Reserved for sub contracting,Réservé à la sous-traitance, Resistant,Résistant, -Resolve error and upload again.,Résoudre l'erreur et télécharger à nouveau., +Resolve error and upload again.,Résoudre l'erreur et télécharger à nouveau., Response,Réponse, Responsibilities,Responsabilités, Rest Of The World,Reste du monde, -Restart Subscription,Redémarrer l'abonnement, +Restart Subscription,Redémarrer l'abonnement, Restaurant,Restaurant, Result Date,Date de résultat, Result already Submitted,Résultat déjà soumis, @@ -2479,13 +2479,13 @@ Retail & Wholesale,Vente de Détail & en Gros, Retail Operations,Opérations de détail, Retained Earnings,Bénéfices Non Répartis, Retention Stock Entry,Entrée de stock de rétention, -Retention Stock Entry already created or Sample Quantity not provided,Saisie de stock de rétention déjà créée ou quantité d'échantillon non fournie, +Retention Stock Entry already created or Sample Quantity not provided,Saisie de stock de rétention déjà créée ou quantité d'échantillon non fournie, Return,Retour, Return / Credit Note,Retour / Note de crédit, Return / Debit Note,Retour / Note de Débit, Returns,Retours, Reverse Journal Entry,Ecriture de journal de contre-passation, -Review Invitation Sent,Examiner l'invitation envoyée, +Review Invitation Sent,Examiner l'invitation envoyée, Review and Action,Révision et action, Role,Rôle, Rooms Booked,Chambres réservées, @@ -2506,10 +2506,10 @@ Row # {0}: Serial No is mandatory,Ligne # {0} : N° de série est obligatoire, Row # {0}: Serial No {1} does not match with {2} {3},Ligne # {0} : N° de série {1} ne correspond pas à {2} {3}, Row #{0} (Payment Table): Amount must be negative,Row # {0} (Table de paiement): le montant doit être négatif, Row #{0} (Payment Table): Amount must be positive,Ligne #{0} (Table de paiement): Le montant doit être positif, -Row #{0}: Account {1} does not belong to company {2},Ligne # {0}: le compte {1} n'appartient pas à la société {2}, +Row #{0}: Account {1} does not belong to company {2},Ligne # {0}: le compte {1} n'appartient pas à la société {2}, Row #{0}: Allocated Amount cannot be greater than outstanding amount.,Ligne # {0}: montant attribué ne peut pas être supérieur au montant en souffrance., "Row #{0}: Asset {1} cannot be submitted, it is already {2}","Ligne #{0} : L’Actif {1} ne peut pas être soumis, il est déjà {2}", -Row #{0}: Cannot set Rate if amount is greater than billed amount for Item {1}.,Ligne n ° {0}: impossible de définir le tarif si le montant est supérieur au montant facturé pour l'élément {1}., +Row #{0}: Cannot set Rate if amount is greater than billed amount for Item {1}.,Ligne n ° {0}: impossible de définir le tarif si le montant est supérieur au montant facturé pour l'élément {1}., Row #{0}: Clearance date {1} cannot be before Cheque Date {2},Ligne #{0} : Date de compensation {1} ne peut pas être antérieure à la Date du Chèque {2}, Row #{0}: Duplicate entry in References {1} {2},Ligne # {0}: entrée en double dans les références {1} {2}, Row #{0}: Expected Delivery Date cannot be before Purchase Order Date,Ligne {0}: la date de livraison prévue ne peut pas être avant la date de commande, @@ -2527,15 +2527,15 @@ Row #{0}: Rejected Qty can not be entered in Purchase Return,Ligne #{0} : Qté R Row #{0}: Rejected Warehouse is mandatory against rejected Item {1},Ligne #{0} : Entrepôt de Rejet est obligatoire pour l’Article rejeté {1}, Row #{0}: Reqd by Date cannot be before Transaction Date,La ligne # {0}: Reqd par date ne peut pas être antérieure à la date de la transaction, Row #{0}: Set Supplier for item {1},Ligne #{0} : Définir Fournisseur pour l’article {1}, -Row #{0}: Status must be {1} for Invoice Discounting {2},Ligne n ° {0}: l'état doit être {1} pour l'actualisation de facture {2}., +Row #{0}: Status must be {1} for Invoice Discounting {2},Ligne n ° {0}: l'état doit être {1} pour l'actualisation de facture {2}., "Row #{0}: The batch {1} has only {2} qty. Please select another batch which has {3} qty available or split the row into multiple rows, to deliver/issue from multiple batches","Ligne # {0}: Le lot {1} n'a que {2} qté(s). Veuillez sélectionner un autre lot contenant {3} qtés disponible ou diviser la rangée en plusieurs lignes, pour livrer / émettre à partir de plusieurs lots", Row #{0}: Timings conflicts with row {1},Ligne #{0}: Minutage en conflit avec la ligne {1}, Row #{0}: {1} can not be negative for item {2},Ligne #{0} : {1} ne peut pas être négatif pour l’article {2}, Row No {0}: Amount cannot be greater than Pending Amount against Expense Claim {1}. Pending Amount is {2},Ligne N° {0}: Le montant ne peut être supérieur au Montant en Attente pour la Note de Frais {1}. Le Montant en Attente est de {2}, -Row {0} : Operation is required against the raw material item {1},Ligne {0}: l'opération est requise pour l'article de matière première {1}, +Row {0} : Operation is required against the raw material item {1},Ligne {0}: l'opération est requise pour l'article de matière première {1}, Row {0}# Allocated amount {1} cannot be greater than unclaimed amount {2},La ligne {0} # Montant alloué {1} ne peut pas être supérieure au montant non réclamé {2}, -Row {0}# Item {1} cannot be transferred more than {2} against Purchase Order {3},La ligne {0} # article {1} ne peut pas être transférée plus de {2} par commande d'achat {3}, -Row {0}# Paid Amount cannot be greater than requested advance amount,La ligne {0} # Montant payé ne peut pas être supérieure au montant de l'avance demandée, +Row {0}# Item {1} cannot be transferred more than {2} against Purchase Order {3},La ligne {0} # article {1} ne peut pas être transférée plus de {2} par commande d'achat {3}, +Row {0}# Paid Amount cannot be greater than requested advance amount,La ligne {0} # Montant payé ne peut pas être supérieure au montant de l'avance demandée, Row {0}: Activity Type is mandatory.,Ligne {0} : Le Type d'Activité est obligatoire., Row {0}: Advance against Customer must be credit,Ligne {0} : L’Avance du Client doit être un crédit, Row {0}: Advance against Supplier must be debit,Ligne {0} : L’Avance du Fournisseur doit être un débit, @@ -2548,7 +2548,7 @@ Row {0}: Cost center is required for an item {1},Ligne {0}: le Centre de Coûts Row {0}: Credit entry can not be linked with a {1},Ligne {0} : L’Écriture de crédit ne peut pas être liée à un {1}, Row {0}: Currency of the BOM #{1} should be equal to the selected currency {2},Ligne {0} : La devise de la LDM #{1} doit être égale à la devise sélectionnée {2}, Row {0}: Debit entry can not be linked with a {1},Ligne {0} : L’Écriture de Débit ne peut pas être lié à un {1}, -Row {0}: Depreciation Start Date is required,Ligne {0}: la date de début de l'amortissement est obligatoire, +Row {0}: Depreciation Start Date is required,Ligne {0}: la date de début de l'amortissement est obligatoire, Row {0}: Enter location for the asset item {1},Ligne {0}: entrez la localisation de l'actif {1}, Row {0}: Exchange Rate is mandatory,Ligne {0} : Le Taux de Change est obligatoire, Row {0}: Expected Value After Useful Life must be less than Gross Purchase Amount,Ligne {0}: la valeur attendue après la durée de vie utile doit être inférieure au montant brut de l'achat, @@ -2562,19 +2562,19 @@ Row {0}: Party / Account does not match with {1} / {2} in {3} {4},Ligne {0} : Ti Row {0}: Party Type and Party is required for Receivable / Payable account {1},Ligne {0} : Le Type de Tiers et le Tiers sont requis pour le compte Débiteur / Créditeur {1}, Row {0}: Payment against Sales/Purchase Order should always be marked as advance,Ligne {0} : Paiements contre Commandes Client / Fournisseur doivent toujours être marqués comme des avances, Row {0}: Please check 'Is Advance' against Account {1} if this is an advance entry.,Ligne {0} : Veuillez vérifier 'Est Avance' sur le compte {1} si c'est une avance., -Row {0}: Please set at Tax Exemption Reason in Sales Taxes and Charges,Ligne {0}: Définissez le motif d'exemption de taxe dans les taxes de vente et les frais., +Row {0}: Please set at Tax Exemption Reason in Sales Taxes and Charges,Ligne {0}: Définissez le motif d'exemption de taxe dans les taxes de vente et les frais., Row {0}: Please set the Mode of Payment in Payment Schedule,Ligne {0}: Veuillez définir le mode de paiement dans le calendrier de paiement., Row {0}: Please set the correct code on Mode of Payment {1},Ligne {0}: définissez le code correct sur le mode de paiement {1}., Row {0}: Qty is mandatory,Ligne {0} : Qté obligatoire, -Row {0}: Quality Inspection rejected for item {1},Ligne {0}: le contrôle qualité a été rejeté pour l'élément {1}., +Row {0}: Quality Inspection rejected for item {1},Ligne {0}: le contrôle qualité a été rejeté pour l'élément {1}., Row {0}: UOM Conversion Factor is mandatory,Ligne {0} : Facteur de Conversion LDM est obligatoire, -Row {0}: select the workstation against the operation {1},Ligne {0}: sélectionnez le poste de travail en fonction de l'opération {1}, +Row {0}: select the workstation against the operation {1},Ligne {0}: sélectionnez le poste de travail en fonction de l'opération {1}, Row {0}: {1} Serial numbers required for Item {2}. You have provided {3}.,Ligne {0}: {1} Numéros de série requis pour l'article {2}. Vous en avez fourni {3}., -Row {0}: {1} is required to create the Opening {2} Invoices,La ligne {0}: {1} est requise pour créer les factures d'ouverture {2}, +Row {0}: {1} is required to create the Opening {2} Invoices,La ligne {0}: {1} est requise pour créer les factures d'ouverture {2}, Row {0}: {1} must be greater than 0,Ligne {0}: {1} doit être supérieure à 0, Row {0}: {1} {2} does not match with {3},Ligne {0} : {1} {2} ne correspond pas à {3}, Row {0}:Start Date must be before End Date,Ligne {0} : La Date de Début doit être avant la Date de Fin, -Rows with duplicate due dates in other rows were found: {0},Des lignes avec des dates d'échéance en double dans les autres lignes ont été trouvées: {0}, +Rows with duplicate due dates in other rows were found: {0},Des lignes avec des dates d'échéance en double dans les autres lignes ont été trouvées: {0}, Rules for adding shipping costs.,Règles pour l'ajout de frais de port., Rules for applying pricing and discount.,Règles pour l’application des tarifs et des remises., S.O. No.,S.O. N°., @@ -2586,10 +2586,10 @@ Salary Slip ID,ID Fiche de Paie, Salary Slip of employee {0} already created for this period,Fiche de Paie de l'employé {0} déjà créée pour cette période, Salary Slip of employee {0} already created for time sheet {1},Fiche de Paie de l'employé {0} déjà créée pour la feuille de temps {1}, Salary Slip submitted for period from {0} to {1},Fiche de paie soumise pour la période du {0} au {1}, -Salary Structure Assignment for Employee already exists,La structure de la structure salariale pour l'employé existe déjà, +Salary Structure Assignment for Employee already exists,La structure de la structure salariale pour l'employé existe déjà, Salary Structure Missing,Grille des Salaires Manquante, -Salary Structure must be submitted before submission of Tax Ememption Declaration,La structure salariale doit être soumise avant la soumission de la déclaration d'émigration fiscale, -Salary Structure not found for employee {0} and date {1},Structure de salaire non trouvée pour l'employé {0} et la date {1}, +Salary Structure must be submitted before submission of Tax Ememption Declaration,La structure salariale doit être soumise avant la soumission de la déclaration d'émigration fiscale, +Salary Structure not found for employee {0} and date {1},Structure de salaire non trouvée pour l'employé {0} et la date {1}, Salary Structure should have flexible benefit component(s) to dispense benefit amount,La structure salariale devrait comporter une ou plusieurs composantes de prestation sociales variables pour la distribution du montant de la prestation, "Salary already processed for period between {0} and {1}, Leave application period cannot be between this date range.","Salaire déjà traité pour la période entre {0} et {1}, La période de demande de congé ne peut pas être entre cette plage de dates.", Sales,Ventes, @@ -2626,7 +2626,7 @@ Same item cannot be entered multiple times.,Le même article ne peut pas être e Same supplier has been entered multiple times,Le même fournisseur a été saisi plusieurs fois, Sample,Échantillon, Sample Collection,Collecte d'Échantillons, -Sample quantity {0} cannot be more than received quantity {1},La quantité d'échantillon {0} ne peut pas dépasser la quantité reçue {1}, +Sample quantity {0} cannot be more than received quantity {1},La quantité d'échantillon {0} ne peut pas dépasser la quantité reçue {1}, Sanctioned,Sanctionné, Sanctioned Amount,Montant Approuvé, Sanctioned Amount cannot be greater than Claim Amount in Row {0}.,Le Montant Approuvé ne peut pas être supérieur au Montant Réclamé à la ligne {0}., @@ -2636,12 +2636,12 @@ Saved,Enregistré, Saving {0},Enregistrement {0}, Scan Barcode,Scan Code Barre, Schedule,Calendrier, -Schedule Admission,Calendrier d'admission, +Schedule Admission,Calendrier d'admission, Schedule Course,Cours Calendrier, Schedule Date,Date du Calendrier, Schedule Discharge,Décharge horaire, Scheduled,Prévu, -Scheduled Upto,Programmé jusqu'à, +Scheduled Upto,Programmé jusqu'à, "Schedules for {0} overlaps, do you want to proceed after skiping overlaped slots ?","Les plannings pour {0} se chevauchent, voulez-vous continuer sans prendre en compte les créneaux qui se chevauchent ?", Score cannot be greater than Maximum Score,Score ne peut pas être supérieure à Score maximum, Score must be less than or equal to 5,Score doit être inférieur ou égal à 5, @@ -2666,7 +2666,7 @@ See past orders,Voir les commandes passées, See past quotations,Voir les citations passées, Select,Sélectionner, Select Alternate Item,Sélectionnez un autre élément, -Select Attribute Values,Sélectionner les valeurs d'attribut, +Select Attribute Values,Sélectionner les valeurs d'attribut, Select BOM,Sélectionner LDM, Select BOM and Qty for Production,Sélectionner la LDM et la Qté pour la Production, "Select BOM, Qty and For Warehouse","Sélectionner une nomenclature, une quantité et un entrepôt", @@ -2703,7 +2703,7 @@ Select or add new customer,Sélectionner ou ajoutez nouveau client, Select students manually for the Activity based Group,Sélectionner les élèves manuellement pour un Groupe basé sur l'Activité, Select the customer or supplier.,Veuillez sélectionner le client ou le fournisseur., Select the nature of your business.,Sélectionner la nature de votre entreprise., -Select the program first,Sélectionnez d'abord le programme, +Select the program first,Sélectionnez d'abord le programme, Select to add Serial Number.,Sélectionnez pour ajouter un numéro de série., Select your Domains,Sélectionnez vos domaines, Selected Price List should have buying and selling fields checked.,La liste de prix sélectionnée doit avoir les champs d'achat et de vente cochés., @@ -2724,7 +2724,7 @@ Sent,Envoyé, Serial #,Série #, Serial No and Batch,N° de Série et lot, Serial No is mandatory for Item {0},N° de Série est obligatoire pour l'Article {0}, -Serial No {0} does not belong to Batch {1},Le numéro de série {0} n'appartient pas au lot {1}, +Serial No {0} does not belong to Batch {1},Le numéro de série {0} n'appartient pas au lot {1}, Serial No {0} does not belong to Delivery Note {1},N° de Série {0} ne fait pas partie du Bon de Livraison {1}, Serial No {0} does not belong to Item {1},N° de Série {0} n'appartient pas à l'Article {1}, Serial No {0} does not belong to Warehouse {1},N° de Série {0} ne fait pas partie de l’Entrepôt {1}, @@ -2753,11 +2753,11 @@ Service Expense,Frais de service, Service Level Agreement,Contrat de niveau de service, Service Level Agreement.,Contrat de niveau de service., Service Level.,Niveau de service., -Service Stop Date cannot be after Service End Date,La date d'arrêt du service ne peut pas être postérieure à la date de fin du service, -Service Stop Date cannot be before Service Start Date,La date d'arrêt du service ne peut pas être antérieure à la date de début du service, +Service Stop Date cannot be after Service End Date,La date d'arrêt du service ne peut pas être postérieure à la date de fin du service, +Service Stop Date cannot be before Service Start Date,La date d'arrêt du service ne peut pas être antérieure à la date de début du service, Services,Services, "Set Default Values like Company, Currency, Current Fiscal Year, etc.","Définir les Valeurs par Défaut comme : Societé, Devise, Exercice Actuel, etc...", -Set Details,Détails de l'ensemble, +Set Details,Détails de l'ensemble, Set New Release Date,Définir la nouvelle date de fin de mise en attente, Set Project and all Tasks to status {0}?,Définir le projet et toutes les tâches sur le statut {0}?, Set Status,Définir le statut, @@ -2769,15 +2769,15 @@ Set as Lost,Définir comme perdu, Set as Open,Définir comme ouvert, Set default inventory account for perpetual inventory,Configurer le compte d'inventaire par défaut pour l'inventaire perpétuel, Set default mode of payment,Définir le mode de paiement par défaut, -Set this if the customer is a Public Administration company.,Définissez cette option si le client est une société d'administration publique., -Set {0} in asset category {1} or company {2},Définissez {0} dans la catégorie d'actifs {1} ou la société {2}, +Set this if the customer is a Public Administration company.,Définissez cette option si le client est une société d'administration publique., +Set {0} in asset category {1} or company {2},Définissez {0} dans la catégorie d'actifs {1} ou la société {2}, "Setting Events to {0}, since the Employee attached to the below Sales Persons does not have a User ID{1}","Définir les Événements à {0}, puisque l'employé attaché au Commercial ci-dessous n'a pas d'ID Utilisateur {1}", Setting defaults,Définition des valeurs par défaut, Setting up Email,Configurer l'Email, Setting up Email Account,Configuration du Compte Email, Setting up Employees,Configuration des Employés, Setting up Taxes,Configuration des Impôts, -Setting up company,Création d'entreprise, +Setting up company,Création d'entreprise, Settings,Paramètres, "Settings for online shopping cart such as shipping rules, price list etc.","Paramètres du panier tels que les règles de livraison, liste de prix, etc.", Settings for website homepage,Paramètres de la page d'accueil du site, @@ -2799,9 +2799,9 @@ Ship To State,Ship To State, Shipments,Livraisons, Shipping,livraison, Shipping Address,Adresse de livraison, -"Shipping Address does not have country, which is required for this Shipping Rule","L'adresse de livraison n'a pas de pays, ce qui est requis pour cette règle d'expédition", -Shipping rule only applicable for Buying,Règle d'expédition applicable uniquement pour l'achat, -Shipping rule only applicable for Selling,Règle d'expédition applicable uniquement pour la vente, +"Shipping Address does not have country, which is required for this Shipping Rule","L'adresse de livraison n'a pas de pays, ce qui est requis pour cette règle d'expédition", +Shipping rule only applicable for Buying,Règle d'expédition applicable uniquement pour l'achat, +Shipping rule only applicable for Selling,Règle d'expédition applicable uniquement pour la vente, Shopify Supplier,Fournisseur Shopify, Shopping Cart,Panier, Shopping Cart Settings,Paramètres du panier, @@ -2809,9 +2809,9 @@ Short Name,Nom court, Shortage Qty,Qté de Pénurie, Show Completed,Montrer terminé, Show Cumulative Amount,Afficher le montant cumulatif, -Show Employee,Afficher l'employé, +Show Employee,Afficher l'employé, Show Open,Afficher ouverte, -Show Opening Entries,Afficher les entrées d'ouverture, +Show Opening Entries,Afficher les entrées d'ouverture, Show Payment Details,Afficher les détails du paiement, Show Return Entries,Afficher les entrées de retour, Show Salary Slip,Afficher la Fiche de Salaire, @@ -2827,7 +2827,7 @@ Silt,Limon, Single Variant,Variante unique, Single unit of an Item.,Seule unité d'un Article., "Skipping Leave Allocation for the following employees, as Leave Allocation records already exists against them. {0}","Attribution des congés de congé pour les employés suivants, car des dossiers de répartition des congés existent déjà contre eux. {0}", -"Skipping Salary Structure Assignment for the following employees, as Salary Structure Assignment records already exists against them. {0}","Ignorer l'affectation de structure salariale pour les employés suivants, car des enregistrements d'affectation de structure salariale existent déjà pour eux. {0}", +"Skipping Salary Structure Assignment for the following employees, as Salary Structure Assignment records already exists against them. {0}","Ignorer l'affectation de structure salariale pour les employés suivants, car des enregistrements d'affectation de structure salariale existent déjà pour eux. {0}", Slideshow,Diaporama, Slots for {0} are not added to the schedule,Les créneaux pour {0} ne sont pas ajoutés à l'agenda, Small,Petit, @@ -2860,9 +2860,9 @@ Standard Buying,Achat standard, Standard Selling,Vente standard, Standard contract terms for Sales or Purchase.,Termes contractuels standards pour Ventes ou Achats, Start Date,Date de début, -Start Date of Agreement can't be greater than or equal to End Date.,La date de début de l'accord ne peut être supérieure ou égale à la date de fin., +Start Date of Agreement can't be greater than or equal to End Date.,La date de début de l'accord ne peut être supérieure ou égale à la date de fin., Start Year,Année de début, -"Start and end dates not in a valid Payroll Period, cannot calculate {0}","Les dates de début et de fin ne faisant pas partie d'une période de paie valide, impossible de calculer {0}", +"Start and end dates not in a valid Payroll Period, cannot calculate {0}","Les dates de début et de fin ne faisant pas partie d'une période de paie valide, impossible de calculer {0}", "Start and end dates not in a valid Payroll Period, cannot calculate {0}.","Les dates de début et de fin ne figurant pas dans une période de paie valide, le système ne peut pas calculer {0}.", Start date should be less than end date for Item {0},La date de début doit être antérieure à la date de fin pour l'Article {0}, Start date should be less than end date for task {0},La date de début doit être inférieure à la date de fin de la tâche {0}, @@ -2919,17 +2919,17 @@ Student Group,Groupe Étudiant, Student Group Strength,Force du Groupe d'Étudiant, Student Group is already updated.,Le Groupe d'Étudiants est déjà mis à jour., Student Group or Course Schedule is mandatory,Le Ggroupe d'Étudiants ou le Calendrier des Cours est obligatoire, -Student Group: ,Groupe d'étudiants:, +Student Group: ,Groupe d'étudiants:, Student ID,Carte d'Étudiant, -Student ID: ,Carte d'étudiant:, +Student ID: ,Carte d'étudiant:, Student LMS Activity,Activité LMS des étudiants, Student Mobile No.,N° de Mobile de l'Étudiant, Student Name,Nom de l'Étudiant, -Student Name: ,Nom d'étudiant:, +Student Name: ,Nom d'étudiant:, Student Report Card,Carte d'étudiant, Student is already enrolled.,L'étudiant est déjà inscrit., Student {0} - {1} appears Multiple times in row {2} & {3},Étudiant {0} - {1} apparaît Plusieurs fois dans la ligne {2} & {3}, -Student {0} does not belong to group {1},L'élève {0} n'appartient pas au groupe {1}, +Student {0} does not belong to group {1},L'élève {0} n'appartient pas au groupe {1}, Student {0} exist against student applicant {1},Étudiant {0} existe pour la candidature d'un étudiant {1}, "Students are at the heart of the system, add all your students","Les étudiants sont au cÅ“ur du système, ajouter tous vos étudiants", Sub Assemblies,Sous-Ensembles, @@ -2977,10 +2977,10 @@ Supplier Warehouse mandatory for sub-contracted Purchase Receipt,Entrepôt Fourn Supplier database.,Base de données fournisseurs., Supplier {0} not found in {1},Fournisseur {0} introuvable dans {1}, Supplier(s),Fournisseur(s), -Supplies made to UIN holders,Fournitures faites aux titulaires de l'UIN, +Supplies made to UIN holders,Fournitures faites aux titulaires de l'UIN, Supplies made to Unregistered Persons,Fournitures faites à des personnes non inscrites, Suppliies made to Composition Taxable Persons,Suppleies à des personnes assujetties à la composition, -Supply Type,Type d'approvisionnement, +Supply Type,Type d'approvisionnement, Support,Soutien, Support Analytics,Analyse du Support, Support Settings,Paramètres du Support, @@ -3014,9 +3014,9 @@ Tax Rate,Taux d'Imposition, Tax Rule Conflicts with {0},Règle de Taxation est en Conflit avec {0}, Tax Rule for transactions.,Règle de Taxation pour les transactions., Tax Template is mandatory.,Un Modèle de Taxe est obligatoire., -Tax Withholding rates to be applied on transactions.,Taux de retenue d'impôt à appliquer aux transactions., +Tax Withholding rates to be applied on transactions.,Taux de retenue d'impôt à appliquer aux transactions., Tax template for buying transactions.,Modèle de taxe pour les opérations d’achat., -Tax template for item tax rates.,Modèle de taxe pour les taux de taxe d'article., +Tax template for item tax rates.,Modèle de taxe pour les taux de taxe d'article., Tax template for selling transactions.,Modèle de taxe pour les opérations de vente., Taxable Amount,Montant Taxable, Taxes,Taxes, @@ -3044,7 +3044,7 @@ Thank you for your business!,Merci pour votre entreprise !, The 'From Package No.' field must neither be empty nor it's value less than 1.,Le champ 'N° de Paquet' ne doit pas être vide ni sa valeur être inférieure à 1., The Brand,La marque, The Item {0} cannot have Batch,L'Article {0} ne peut être en Lot, -The Loyalty Program isn't valid for the selected company,Le programme de fidélité n'est pas valable pour la société sélectionnée, +The Loyalty Program isn't valid for the selected company,Le programme de fidélité n'est pas valable pour la société sélectionnée, The Payment Term at row {0} is possibly a duplicate.,Le délai de paiement à la ligne {0} est probablement un doublon., The Term End Date cannot be earlier than the Term Start Date. Please correct the dates and try again.,La Date de Fin de Terme ne peut pas être antérieure à la Date de Début de Terme. Veuillez corriger les dates et essayer à nouveau., The Term End Date cannot be later than the Year End Date of the Academic Year to which the term is linked (Academic Year {}). Please correct the dates and try again.,La Date de Fin de Terme ne peut pas être postérieure à la Date de Fin de l'Année Académique à laquelle le terme est lié (Année Académique {}). Veuillez corriger les dates et essayer à nouveau., @@ -3064,21 +3064,21 @@ The payment gateway account in plan {0} is different from the payment gateway ac The request for quotation can be accessed by clicking on the following link,La demande de devis peut être consultée en cliquant sur le lien suivant, The selected BOMs are not for the same item,Les LDMs sélectionnées ne sont pas pour le même article, The selected item cannot have Batch,L’article sélectionné ne peut pas avoir de Lot, -The seller and the buyer cannot be the same,Le vendeur et l'acheteur ne peuvent pas être les mêmes, -The shareholder does not belong to this company,L'actionnaire n'appartient pas à cette société, +The seller and the buyer cannot be the same,Le vendeur et l'acheteur ne peuvent pas être les mêmes, +The shareholder does not belong to this company,L'actionnaire n'appartient pas à cette société, The shares already exist,Les actions existent déjà, The shares don't exist with the {0},Les actions n'existent pas pour {0}, -"The task has been enqueued as a background job. In case there is any issue on processing in background, the system will add a comment about the error on this Stock Reconciliation and revert to the Draft stage","La tâche a été mise en file d'attente en tant que tâche en arrière-plan. En cas de problème de traitement en arrière-plan, le système ajoute un commentaire concernant l'erreur sur ce rapprochement des stocks et revient au stade de brouillon.", +"The task has been enqueued as a background job. In case there is any issue on processing in background, the system will add a comment about the error on this Stock Reconciliation and revert to the Draft stage","La tâche a été mise en file d'attente en tant que tâche en arrière-plan. En cas de problème de traitement en arrière-plan, le système ajoute un commentaire concernant l'erreur sur ce rapprochement des stocks et revient au stade de brouillon.", "Then Pricing Rules are filtered out based on Customer, Customer Group, Territory, Supplier, Supplier Type, Campaign, Sales Partner etc.","Les Règles de Tarification sont ensuite filtrées en fonction des Clients, des Groupes de Clients, des Régions, des Fournisseurs, des Groupes de Fournisseurs, des Campagnes, des Partenaires Commerciaux, etc.", "There are inconsistencies between the rate, no of shares and the amount calculated","Il existe des incohérences entre le prix unitaire, le nombre d'actions et le montant calculé", There are more holidays than working days this month.,Il y a plus de vacances que de jours travaillés ce mois-ci., There can be multiple tiered collection factor based on the total spent. But the conversion factor for redemption will always be same for all the tier.,Il peut y avoir plusieurs facteurs de collecte hiérarchisés en fonction du total dépensé. Mais le facteur de conversion pour l'échange sera toujours le même pour tous les niveaux., There can only be 1 Account per Company in {0} {1},Il ne peut y avoir qu’un Compte par Société dans {0} {1}, "There can only be one Shipping Rule Condition with 0 or blank value for ""To Value""","Il ne peut y avoir qu’une Condition de Règle de Livraison avec 0 ou une valeur vide pour « A la Valeur""", -There is no leave period in between {0} and {1},Il n'y a pas de période de congé entre {0} et {1}, +There is no leave period in between {0} and {1},Il n'y a pas de période de congé entre {0} et {1}, There is not enough leave balance for Leave Type {0},Il n'y a pas assez de solde de congés pour les Congés de Type {0}, There is nothing to edit.,Il n'y a rien à modifier., -There isn't any item variant for the selected item,Il n'y a pas de variante d'article pour l'article sélectionné, +There isn't any item variant for the selected item,Il n'y a pas de variante d'article pour l'article sélectionné, "There seems to be an issue with the server's GoCardless configuration. Don't worry, in case of failure, the amount will get refunded to your account.","Il semble y avoir un problème avec la configuration de GoCardless sur le serveur. Ne vous inquiétez pas, en cas d'échec, le montant sera remboursé sur votre compte.", There were errors creating Course Schedule,Des erreurs se sont produites lors de la création du programme, There were errors.,Il y a eu des erreurs., @@ -3108,7 +3108,7 @@ This is based on transactions against this Healthcare Practitioner.,Ce graphique This is based on transactions against this Patient. See timeline below for details,Ceci est basé sur les transactions de ce patient. Voir la chronologie ci-dessous pour plus de détails, This is based on transactions against this Sales Person. See timeline below for details,Ceci est basé sur les transactions contre ce vendeur. Voir la chronologie ci-dessous pour plus de détails, This is based on transactions against this Supplier. See timeline below for details,Basé sur les transactions avec ce fournisseur. Voir la chronologie ci-dessous pour plus de détails, -This will submit Salary Slips and create accrual Journal Entry. Do you want to proceed?,Cela permettra de soumettre des bulletins de salaire et de créer une écriture de journal d'accumulation. Voulez-vous poursuivre?, +This will submit Salary Slips and create accrual Journal Entry. Do you want to proceed?,Cela permettra de soumettre des bulletins de salaire et de créer une écriture de journal d'accumulation. Voulez-vous poursuivre?, This {0} conflicts with {1} for {2} {3},Ce {0} est en conflit avec {1} pour {2} {3}, Time Sheet for manufacturing.,Feuille de Temps pour la production., Time Tracking,Suivi du temps, @@ -3135,7 +3135,7 @@ To Date should be within the Fiscal Year. Assuming To Date = {0},La Date Finale To Datetime,À la Date, To Deliver,À Livrer, To Deliver and Bill,À Livrer et Facturer, -To Fiscal Year,À l'année fiscale, +To Fiscal Year,À l'année fiscale, To GSTIN,GSTIN (Destination), To Party Name,Nom du tiers (Destination), To Pin Code,Code postal (Destination), @@ -3151,7 +3151,7 @@ To date can not greater than employee's relieving date,La date de fin ne peut pa "To filter based on Party, select Party Type first","Pour filtrer en fonction du Tiers, sélectionnez d’abord le Type de Tiers", "To get the best out of ERPNext, we recommend that you take some time and watch these help videos.","Pour tirer le meilleur parti d’ERPNext, nous vous recommandons de prendre un peu de temps et de regarder ces vidéos d'aide.", "To include tax in row {0} in Item rate, taxes in rows {1} must also be included","Pour inclure la taxe de la ligne {0} dans le prix de l'Article, les taxes des lignes {1} doivent également être incluses", -To make Customer based incentive schemes.,Faire des programmes d'incitation basés sur le client., +To make Customer based incentive schemes.,Faire des programmes d'incitation basés sur le client., "To merge, following properties must be same for both items","Pour fusionner, les propriétés suivantes doivent être les mêmes pour les deux articles", "To not apply Pricing Rule in a particular transaction, all applicable Pricing Rules should be disabled.","Pour ne pas appliquer la Règle de Tarification dans une transaction particulière, toutes les Règles de Tarification applicables doivent être désactivées.", "To set this Fiscal Year as Default, click on 'Set as Default'","Pour définir cet Exercice Fiscal par défaut, cliquez sur ""Définir par défaut""", @@ -3202,9 +3202,9 @@ Total Unpaid: {0},Total des Impayés : {0}, Total Variance,Variance totale, Total Weightage of all Assessment Criteria must be 100%,Le total des pondérations de tous les Critères d'Évaluation doit être égal à 100%, Total advance ({0}) against Order {1} cannot be greater than the Grand Total ({2}),Avance totale ({0}) pour la Commande {1} ne peut pas être supérieure au Total Général ({2}), -Total advance amount cannot be greater than total claimed amount,Le montant total de l'avance ne peut être supérieur au montant total réclamé, +Total advance amount cannot be greater than total claimed amount,Le montant total de l'avance ne peut être supérieur au montant total réclamé, Total advance amount cannot be greater than total sanctioned amount,Le montant total de l'avance ne peut être supérieur au montant total approuvé, -Total allocated leaves are more days than maximum allocation of {0} leave type for employee {1} in the period,Le nombre total de congés alloués est supérieur de plusieurs jours à l'allocation maximale du type de congé {0} pour l'employé {1} au cours de la période, +Total allocated leaves are more days than maximum allocation of {0} leave type for employee {1} in the period,Le nombre total de congés alloués est supérieur de plusieurs jours à l'allocation maximale du type de congé {0} pour l'employé {1} au cours de la période, Total allocated leaves are more than days in the period,Le Total des feuilles attribuées est supérieur au nombre de jours dans la période, Total allocated percentage for sales team should be 100,Pourcentage total attribué à l'équipe commerciale devrait être de 100, Total cannot be zero,Total ne peut pas être zéro, @@ -3303,18 +3303,18 @@ User Forum,Forum de l'Utilisateur, User ID,ID de l'Utilisateur, User ID not set for Employee {0},ID de l'Utilisateur non défini pour l'Employé {0}, User Remark,Remarque de l'Utilisateur, -User has not applied rule on the invoice {0},L'utilisateur n'a pas appliqué la règle sur la facture {0}, -User {0} already exists,L'utilisateur {0} existe déjà, +User has not applied rule on the invoice {0},L'utilisateur n'a pas appliqué la règle sur la facture {0}, +User {0} already exists,L'utilisateur {0} existe déjà, User {0} created,Utilisateur {0} créé, User {0} does not exist,Utilisateur {0} n'existe pas, -User {0} doesn't have any default POS Profile. Check Default at Row {1} for this User.,L'utilisateur {0} n'a aucun profil POS par défaut. Vérifiez par défaut à la ligne {1} pour cet utilisateur., +User {0} doesn't have any default POS Profile. Check Default at Row {1} for this User.,L'utilisateur {0} n'a aucun profil POS par défaut. Vérifiez par défaut à la ligne {1} pour cet utilisateur., User {0} is already assigned to Employee {1},Utilisateur {0} est déjà attribué à l'Employé {1}, -User {0} is already assigned to Healthcare Practitioner {1},L'utilisateur {0} est déjà attribué à un professionnel de la santé {1}, +User {0} is already assigned to Healthcare Practitioner {1},L'utilisateur {0} est déjà attribué à un professionnel de la santé {1}, Users,Utilisateurs, Utility Expenses,Frais de Services d'Utilité Publique, Valid From Date must be lesser than Valid Upto Date.,La date de début de validité doit être inférieure à la date de mise en service valide., Valid Till,Valable Jusqu'au, -Valid from and valid upto fields are mandatory for the cumulative,Les champs valides à partir de et valables jusqu'à sont obligatoires pour le cumulatif., +Valid from and valid upto fields are mandatory for the cumulative,Les champs valides à partir de et valables jusqu'à sont obligatoires pour le cumulatif., Valid from date must be less than valid upto date,La date de début de validité doit être inférieure à la date de validité, Valid till date cannot be before transaction date,La date de validité ne peut pas être avant la date de transaction, Validity,Validité, @@ -3327,7 +3327,7 @@ Value Proposition,Proposition de valeur, Value for Attribute {0} must be within the range of {1} to {2} in the increments of {3} for Item {4},Valeur pour l'attribut {0} doit être dans la gamme de {1} à {2} dans les incréments de {3} pour le poste {4}, Value missing,Valeur manquante, Value must be between {0} and {1},La valeur doit être comprise entre {0} et {1}., -"Values of exempt, nil rated and non-GST inward supplies","Valeurs des fournitures importées exonérées, assorties d'une cote zéro et non liées à la TPS", +"Values of exempt, nil rated and non-GST inward supplies","Valeurs des fournitures importées exonérées, assorties d'une cote zéro et non liées à la TPS", Variable,Variable, Variance,Variance, Variance ({}),Variance ({}), @@ -3335,7 +3335,7 @@ Variant,Variante, Variant Attributes,Attributs Variant, Variant Based On cannot be changed,Les variantes basées sur ne peuvent pas être modifiées, Variant Details Report,Rapport détaillé des variantes, -Variant creation has been queued.,La création de variantes a été placée en file d'attente., +Variant creation has been queued.,La création de variantes a été placée en file d'attente., Vehicle Expenses,Frais de véhicule, Vehicle No,N° du Véhicule, Vehicle Type,Type de véhicule, @@ -3354,7 +3354,7 @@ Visit report for maintenance call.,Rapport de visite pour appel de maintenance, Visit the forums,Visitez les forums, Vital Signs,Signes vitaux, Volunteer,Bénévole, -Volunteer Type information.,Volontaire Type d'information., +Volunteer Type information.,Volontaire Type d'information., Volunteer information.,Informations sur le bénévolat, Voucher #,Référence #, Voucher No,N° de Référence, @@ -3367,7 +3367,7 @@ Warehouse is mandatory,L'entrepôt est obligatoire, Warehouse is mandatory for stock Item {0} in row {1},L’entrepôt est obligatoire pour l'Article du stock {0} dans la ligne {1}, Warehouse not found in the system,L'entrepôt n'a pas été trouvé dans le système, "Warehouse required at Row No {0}, please set default warehouse for the item {1} for the company {2}",Entrepôt requis à la ligne n ° {0}. Veuillez définir un entrepôt par défaut pour l'article {1} et la société {2}, -Warehouse required for stock Item {0},Magasin requis pour l'article en stock {0}, +Warehouse required for stock Item {0},Magasin requis pour l'article en stock {0}, Warehouse {0} can not be deleted as quantity exists for Item {1},L'entrepôt {0} ne peut pas être supprimé car il existe une quantité pour l'Article {1}, Warehouse {0} does not belong to company {1},L'entrepôt {0} n'appartient pas à la société {1}, Warehouse {0} does not exist,L'entrepôt {0} n'existe pas, @@ -3442,13 +3442,13 @@ You cannot credit and debit same account at the same time,Vous ne pouvez pas cr You cannot delete Fiscal Year {0}. Fiscal Year {0} is set as default in Global Settings,Vous ne pouvez pas supprimer l'exercice fiscal {0}. L'exercice fiscal {0} est défini par défaut dans les Paramètres Globaux, You cannot delete Project Type 'External',Vous ne pouvez pas supprimer le Type de Projet 'Externe', You cannot edit root node.,Vous ne pouvez pas modifier le nÅ“ud racine., -You cannot restart a Subscription that is not cancelled.,Vous ne pouvez pas redémarrer un abonnement qui n'est pas annulé., +You cannot restart a Subscription that is not cancelled.,Vous ne pouvez pas redémarrer un abonnement qui n'est pas annulé., You don't have enought Loyalty Points to redeem,Vous n'avez pas assez de points de fidélité à échanger, -You have already assessed for the assessment criteria {}.,Vous avez déjà évalué les critères d'évaluation {}., +You have already assessed for the assessment criteria {}.,Vous avez déjà évalué les critères d'évaluation {}., You have already selected items from {0} {1},Vous avez déjà choisi des articles de {0} {1}, You have been invited to collaborate on the project: {0},Vous avez été invité à collaborer sur le projet : {0}, You have entered duplicate items. Please rectify and try again.,Vous avez entré un doublon. Veuillez rectifier et essayer à nouveau., -You need to be a user other than Administrator with System Manager and Item Manager roles to register on Marketplace.,Vous devez être un utilisateur autre que l'administrateur avec les rôles System Manager et Item Manager pour vous inscrire sur Marketplace., +You need to be a user other than Administrator with System Manager and Item Manager roles to register on Marketplace.,Vous devez être un utilisateur autre que l'administrateur avec les rôles System Manager et Item Manager pour vous inscrire sur Marketplace., You need to be a user with System Manager and Item Manager roles to add users to Marketplace.,Vous devez être un utilisateur doté de rôles System Manager et Item Manager pour ajouter des utilisateurs à Marketplace., You need to be a user with System Manager and Item Manager roles to register on Marketplace.,Vous devez être un utilisateur avec des rôles System Manager et Item Manager pour vous inscrire sur Marketplace., You need to be logged in to access this page,Vous devez être connecté pour pouvoir accéder à cette page, @@ -3512,11 +3512,11 @@ on,sur, {0} is mandatory. Maybe Currency Exchange record is not created for {1} to {2}.,{0} est obligatoire. Peut-être qu’un enregistrement de Taux de Change n'est pas créé pour {1} et {2}., {0} is not a stock Item,{0} n'est pas un Article de stock, {0} is not a valid Batch Number for Item {1},{0} n'est pas un Numéro de Lot valide pour l’Article {1}, -{0} is not added in the table,{0} n'est pas ajouté dans la table, -{0} is not in Optional Holiday List,{0} n'est pas dans la liste des jours fériés facultatifs, -{0} is not in a valid Payroll Period,{0} n'est pas dans une période de paie valide, +{0} is not added in the table,{0} n'est pas ajouté dans la table, +{0} is not in Optional Holiday List,{0} n'est pas dans la liste des jours fériés facultatifs, +{0} is not in a valid Payroll Period,{0} n'est pas dans une période de paie valide, {0} is now the default Fiscal Year. Please refresh your browser for the change to take effect.,{0} est désormais l’Exercice par défaut. Veuillez actualiser la page pour que les modifications soient prises en compte., -{0} is on hold till {1},{0} est en attente jusqu'à {1}, +{0} is on hold till {1},{0} est en attente jusqu'à {1}, {0} item found.,{0} élément trouvé., {0} items found.,{0} éléments trouvés., {0} items in progress,{0} articles en cours, @@ -3525,8 +3525,8 @@ on,sur, {0} must be negative in return document,{0} doit être négatif dans le document de retour, {0} must be submitted,{0} doit être soumis, {0} not allowed to transact with {1}. Please change the Company.,{0} n'est pas autorisé à traiter avec {1}. Veuillez changer la société., -{0} not found for item {1},{0} introuvable pour l'élément {1}, -{0} parameter is invalid,Le paramètre {0} n'est pas valide, +{0} not found for item {1},{0} introuvable pour l'élément {1}, +{0} parameter is invalid,Le paramètre {0} n'est pas valide, {0} payment entries can not be filtered by {1},{0} écritures de paiement ne peuvent pas être filtrées par {1}, {0} should be a value between 0 and 100,{0} devrait être une valeur comprise entre 0 et 100, {0} units of [{1}](#Form/Item/{1}) found in [{2}](#Form/Warehouse/{2}),{0} unités de [{1}] (#Formulaire/Article/{1}) trouvées dans [{2}] (#Formulaire/Entrepôt/{2}), @@ -3536,7 +3536,7 @@ on,sur, {0} variants created.,{0} variantes créées., {0} {1} created,{0} {1} créé, {0} {1} does not exist,{0} {1} n'existe pas, -{0} {1} does not exist.,{0} {1} n'existe pas, +{0} {1} does not exist.,{0} {1} n'existe pas, {0} {1} has been modified. Please refresh.,{0} {1} a été modifié. Veuillez actualiser., {0} {1} has not been submitted so the action cannot be completed,"{0} {1} n'a pas été soumis, donc l'action ne peut pas être complétée", "{0} {1} is associated with {2}, but Party Account is {3}","{0} {1} est associé à {2}, mais le compte tiers est {3}", @@ -3548,8 +3548,8 @@ on,sur, {0} {1} is frozen,{0} {1} est gelée, {0} {1} is fully billed,{0} {1} est entièrement facturé, {0} {1} is not active,{0} {1} n'est pas actif, -{0} {1} is not associated with {2} {3},{0} {1} n'est pas associé à {2} {3}, -{0} {1} is not present in the parent company,{0} {1} n'est pas présent dans la société mère, +{0} {1} is not associated with {2} {3},{0} {1} n'est pas associé à {2} {3}, +{0} {1} is not present in the parent company,{0} {1} n'est pas présent dans la société mère, {0} {1} is not submitted,{0} {1} n'a pas été soumis, {0} {1} is {2},{0} {1} est {2}, {0} {1} must be submitted,{0} {1} doit être soumis, @@ -3608,28 +3608,28 @@ or,ou, Ageing Range 4,Gamme de vieillissement 4, Allocated amount cannot be greater than unadjusted amount,Le montant alloué ne peut être supérieur au montant non ajusté, Allocated amount cannot be negative,Le montant alloué ne peut être négatif, -"Difference Account must be a Asset/Liability type account, since this Stock Entry is an Opening Entry","Le compte d'écart doit être un compte de type actif / passif, car cette entrée de stock est une entrée d'ouverture.", +"Difference Account must be a Asset/Liability type account, since this Stock Entry is an Opening Entry","Le compte d'écart doit être un compte de type actif / passif, car cette entrée de stock est une entrée d'ouverture.", Error in some rows,Erreur dans certaines lignes, Import Successful,Importation réussie, -Please save first,S'il vous plaît enregistrer en premier, -Price not found for item {0} in price list {1},Prix non trouvé pour l'article {0} dans la liste de prix {1}, -Warehouse Type,Type d'entrepôt, -'Date' is required,'Date' est requis, +Please save first,S'il vous plaît enregistrer en premier, +Price not found for item {0} in price list {1},Prix non trouvé pour l'article {0} dans la liste de prix {1}, +Warehouse Type,Type d'entrepôt, +'Date' is required,'Date' est requis, Benefit,Avantage, Budgets,Budgets, Bundle Qty,Quantité de paquet, Company GSTIN,GSTIN de la Société, -Company field is required,Le champ de l'entreprise est obligatoire, +Company field is required,Le champ de l'entreprise est obligatoire, Creating Dimensions...,Créer des dimensions ..., Duplicate entry against the item code {0} and manufacturer {1},Dupliquer la saisie par rapport au code article {0} et au fabricant {1}, Import Chart Of Accounts from CSV / Excel files,Importer un graphique des comptes à partir de fichiers CSV / Excel, -Invalid GSTIN! The input you've entered doesn't match the GSTIN format for UIN Holders or Non-Resident OIDAR Service Providers,GSTIN invalide! L'entrée que vous avez entrée ne correspond pas au format GSTIN pour les titulaires d'un UIN ou les fournisseurs de services OIDAR non résidents, +Invalid GSTIN! The input you've entered doesn't match the GSTIN format for UIN Holders or Non-Resident OIDAR Service Providers,GSTIN invalide! L'entrée que vous avez entrée ne correspond pas au format GSTIN pour les titulaires d'un UIN ou les fournisseurs de services OIDAR non résidents, Invoice Grand Total,Total général de la facture, Last carbon check date cannot be a future date,La date du dernier bilan carbone ne peut pas être une date future, Make Stock Entry,Faire une entrée de stock, Quality Feedback,Commentaires sur la qualité, Quality Feedback Template,Modèle de commentaires sur la qualité, -Rules for applying different promotional schemes.,Règles d'application de différents programmes promotionnels., +Rules for applying different promotional schemes.,Règles d'application de différents programmes promotionnels., Shift,Décalage, Show {0},Montrer {0}, "Special Characters except ""-"", ""#"", ""."", ""/"", ""{"" and ""}"" not allowed in naming series","Caractères spéciaux sauf "-", "#", ".", "/", "{" Et "}" non autorisés dans les séries de nommage", @@ -3649,7 +3649,7 @@ No data to export,Aucune donnée à exporter, Print Heading,Imprimer Titre, Video,Vidéo, % Of Grand Total,% Du grand total, -'employee_field_value' and 'timestamp' are required.,'employee_field_value' et 'timestamp' sont obligatoires., +'employee_field_value' and 'timestamp' are required.,'employee_field_value' et 'timestamp' sont obligatoires., Company is a mandatory filter.,La société est un filtre obligatoire., From Date is a mandatory filter.,De la date est un filtre obligatoire., From Time cannot be later than To Time for {0},From Time ne peut pas être postérieur à To Time pour {0}, @@ -3657,13 +3657,13 @@ Video,Vidéo, A new appointment has been created for you with {0},Un nouveau rendez-vous a été créé pour vous avec {0}, Account Value,Valeur du compte, Account is mandatory to get payment entries,Le compte est obligatoire pour obtenir les entrées de paiement, -Account is not set for the dashboard chart {0},Le compte n'est pas défini pour le graphique du tableau de bord {0}, +Account is not set for the dashboard chart {0},Le compte n'est pas défini pour le graphique du tableau de bord {0}, Account {0} does not belong to company {1},Compte {0} n'appartient pas à la société {1}, -Account {0} does not exists in the dashboard chart {1},Le compte {0} n'existe pas dans le graphique du tableau de bord {1}, +Account {0} does not exists in the dashboard chart {1},Le compte {0} n'existe pas dans le graphique du tableau de bord {1}, Account: {0} is capital Work in progress and can not be updated by Journal Entry,Compte: {0} est un travail capital et ne peut pas être mis à jour par une écriture au journal., -Account: {0} is not permitted under Payment Entry,Compte: {0} n'est pas autorisé sous Saisie du paiement., +Account: {0} is not permitted under Payment Entry,Compte: {0} n'est pas autorisé sous Saisie du paiement., Accounting Dimension {0} is required for 'Balance Sheet' account {1}.,La dimension de comptabilité {0} est requise pour le compte "Bilan" {1}., -Accounting Dimension {0} is required for 'Profit and Loss' account {1}.,La dimension de comptabilité {0} est requise pour le compte 'Bénéfices et pertes' {1}., +Accounting Dimension {0} is required for 'Profit and Loss' account {1}.,La dimension de comptabilité {0} est requise pour le compte 'Bénéfices et pertes' {1}., Accounting Masters,Maîtres Comptables, Accounting Period overlaps with {0},La période comptable chevauche avec {0}, Activity,Activité, @@ -3672,14 +3672,14 @@ Add Child,Ajouter une Sous-Catégorie, Add Loan Security,Ajouter une garantie de prêt, Add Multiple,Ajout Multiple, Add Participants,Ajouter des participants, -Add to Featured Item,Ajouter à l'article en vedette, +Add to Featured Item,Ajouter à l'article en vedette, Add your review,Ajouter votre avis, Add/Edit Coupon Conditions,Ajouter / Modifier les conditions du coupon, Added to Featured Items,Ajouté aux articles en vedette, Added {0} ({1}),Ajouté {0} ({1}), Address Line 1,Adresse Ligne 1, Addresses,Adresses, -Admission End Date should be greater than Admission Start Date.,La date de fin d'admission doit être supérieure à la date de début d'admission., +Admission End Date should be greater than Admission Start Date.,La date de fin d'admission doit être supérieure à la date de début d'admission., Against Loan,Contre le prêt, Against Loan:,Contre le prêt:, All,Tout, @@ -3693,35 +3693,35 @@ Applied Coupon Code,Code de coupon appliqué, Apply Coupon Code,Appliquer le code de coupon, Appointment Booking,Prise de rendez-vous, "As there are existing transactions against item {0}, you can not change the value of {1}","Comme il existe des transactions avec l'article {0}, vous ne pouvez pas changer la valeur de {1}", -Asset Id,ID d'actif, -Asset Value,Valeur d'actif, -Asset Value Adjustment cannot be posted before Asset's purchase date {0}.,L'ajustement de la valeur de l'actif ne peut pas être enregistré avant la date d'achat de l'actif {0} ., -Asset {0} does not belongs to the custodian {1},L'élément {0} n'appartient pas au dépositaire {1}, -Asset {0} does not belongs to the location {1},L'élément {0} n'appartient pas à l'emplacement {1}, +Asset Id,ID d'actif, +Asset Value,Valeur d'actif, +Asset Value Adjustment cannot be posted before Asset's purchase date {0}.,L'ajustement de la valeur de l'actif ne peut pas être enregistré avant la date d'achat de l'actif {0} ., +Asset {0} does not belongs to the custodian {1},L'élément {0} n'appartient pas au dépositaire {1}, +Asset {0} does not belongs to the location {1},L'élément {0} n'appartient pas à l'emplacement {1}, At least one of the Applicable Modules should be selected,Au moins un des modules applicables doit être sélectionné, Atleast one asset has to be selected.,Au moins un actif doit être sélectionné., Attendance Marked,Présence marquée, Attendance has been marked as per employee check-ins,La présence a été marquée selon les enregistrements des employés, Authentication Failed,Authentification échouée, Automatic Reconciliation,Rapprochement automatique, -Available For Use Date,Date d'utilisation disponible, +Available For Use Date,Date d'utilisation disponible, Available Stock,Stock disponible, "Available quantity is {0}, you need {1}",La quantité disponible est {0}. Vous avez besoin de {1}., BOM 1,BOM 1, BOM 2,BOM 2, BOM Comparison Tool,Outil de comparaison de nomenclature, BOM recursion: {0} cannot be child of {1},Récursion de nomenclature: {0} ne peut pas être enfant de {1}, -BOM recursion: {0} cannot be parent or child of {1},Récursion de nomenclature: {0} ne peut pas être le parent ou l'enfant de {1}, +BOM recursion: {0} cannot be parent or child of {1},Récursion de nomenclature: {0} ne peut pas être le parent ou l'enfant de {1}, Back to Home,De retour à la maison, Back to Messages,Retour aux messages, -Bank Data mapper doesn't exist,Bank Data Mapper n'existe pas, +Bank Data mapper doesn't exist,Bank Data Mapper n'existe pas, Bank Details,Coordonnées bancaires, -Bank account '{0}' has been synchronized,Le compte bancaire '{0}' a été synchronisé, -Bank account {0} already exists and could not be created again,Le compte bancaire {0} existe déjà et n'a pas pu être créé à nouveau., +Bank account '{0}' has been synchronized,Le compte bancaire '{0}' a été synchronisé, +Bank account {0} already exists and could not be created again,Le compte bancaire {0} existe déjà et n'a pas pu être créé à nouveau., Bank accounts added,Comptes bancaires ajoutés, -Batch no is required for batched item {0},Le numéro de lot est requis pour l'article en lot {0}., +Batch no is required for batched item {0},Le numéro de lot est requis pour l'article en lot {0}., Billing Date,Date de facturation, -Billing Interval Count cannot be less than 1,Le nombre d'intervalles de facturation ne peut pas être inférieur à 1, +Billing Interval Count cannot be less than 1,Le nombre d'intervalles de facturation ne peut pas être inférieur à 1, Blue,Bleu, Book,Livre, Book Appointment,Prendre rendez-vous, @@ -3730,18 +3730,18 @@ Browse,Feuilleter, Call Connected,Appel connecté, Call Disconnected,Appel déconnecté, Call Missed,Appel manqué, -Call Summary,Résumé d'appel, -Call Summary Saved,Résumé de l'appel enregistré, +Call Summary,Résumé d'appel, +Call Summary Saved,Résumé de l'appel enregistré, Cancelled,Annulé, -Cannot Calculate Arrival Time as Driver Address is Missing.,Impossible de calculer l'heure d'arrivée car l'adresse du conducteur est manquante., -Cannot Optimize Route as Driver Address is Missing.,Impossible d'optimiser l'itinéraire car l'adresse du pilote est manquante., +Cannot Calculate Arrival Time as Driver Address is Missing.,Impossible de calculer l'heure d'arrivée car l'adresse du conducteur est manquante., +Cannot Optimize Route as Driver Address is Missing.,Impossible d'optimiser l'itinéraire car l'adresse du pilote est manquante., "Cannot Unpledge, loan security value is greater than the repaid amount","Impossible de désengager, la valeur de la garantie de prêt est supérieure au montant remboursé", -Cannot complete task {0} as its dependant task {1} are not ccompleted / cancelled.,Impossible de terminer la tâche {0} car sa tâche dépendante {1} n'est pas terminée / annulée., -Cannot create loan until application is approved,Impossible de créer un prêt tant que la demande n'est pas approuvée, +Cannot complete task {0} as its dependant task {1} are not ccompleted / cancelled.,Impossible de terminer la tâche {0} car sa tâche dépendante {1} n'est pas terminée / annulée., +Cannot create loan until application is approved,Impossible de créer un prêt tant que la demande n'est pas approuvée, Cannot find a matching Item. Please select some other value for {0}.,Impossible de trouver un article similaire. Veuillez sélectionner une autre valeur pour {0}., "Cannot overbill for Item {0} in row {1} more than {2}. To allow over-billing, please set allowance in Accounts Settings","La surfacturation pour le poste {0} dans la ligne {1} ne peut pas dépasser {2}. Pour autoriser la surfacturation, définissez la provision dans les paramètres du compte.", Cannot unpledge more than {0} qty of {0},Impossible de retirer plus de {0} quantité de {0}, -"Capacity Planning Error, planned start time can not be same as end time","Erreur de planification de capacité, l'heure de début prévue ne peut pas être identique à l'heure de fin", +"Capacity Planning Error, planned start time can not be same as end time","Erreur de planification de capacité, l'heure de début prévue ne peut pas être identique à l'heure de fin", Categories,Catégories, Changes in {0},Changements dans {0}, Chart,Graphique, @@ -3751,26 +3751,26 @@ Close,Fermer, Communication,la communication, Compact Item Print,Impression de l'Article Compacté, Company,Société, -Company of asset {0} and purchase document {1} doesn't matches.,La société de l'actif {0} et le document d'achat {1} ne correspondent pas., +Company of asset {0} and purchase document {1} doesn't matches.,La société de l'actif {0} et le document d'achat {1} ne correspondent pas., Compare BOMs for changes in Raw Materials and Operations,Comparer les nomenclatures aux modifications apportées aux matières premières et aux opérations, Compare List function takes on list arguments,La fonction de comparaison de liste accepte les arguments de liste, Complete,Terminé, Completed,Terminé, Completed Quantity,Quantité terminée, -Connect your Exotel Account to ERPNext and track call logs,Connectez votre compte Exotel à ERPNext et suivez les journaux d'appels, +Connect your Exotel Account to ERPNext and track call logs,Connectez votre compte Exotel à ERPNext et suivez les journaux d'appels, Connect your bank accounts to ERPNext,Connectez vos comptes bancaires à ERPNext, Contact Seller,Contacter le vendeur, Continue,Continuer, -Cost Center: {0} does not exist,Centre de coûts: {0} n'existe pas, +Cost Center: {0} does not exist,Centre de coûts: {0} n'existe pas, Couldn't Set Service Level Agreement {0}.,Impossible de définir le contrat de service {0}., Country,Pays, Country Code in File does not match with country code set up in the system,Le code de pays dans le fichier ne correspond pas au code de pays configuré dans le système, Create New Contact,Créer un nouveau contact, Create New Lead,Créer une nouvelle piste, Create Pick List,Créer une liste de choix, -Create Quality Inspection for Item {0},Créer un contrôle qualité pour l'article {0}, +Create Quality Inspection for Item {0},Créer un contrôle qualité pour l'article {0}, Creating Accounts...,Création de comptes ..., -Creating bank entries...,Création d'entrées bancaires ..., +Creating bank entries...,Création d'entrées bancaires ..., Creating {0},Création de {0}, Credit limit is already defined for the Company {0},La limite de crédit est déjà définie pour la société {0}., Ctrl + Enter to submit,Ctrl + Entrée pour soumettre, @@ -3782,7 +3782,7 @@ Customize,Personnaliser, Daily,Quotidien, Date,Date, Date Range,Intervalle de Date, -Date of Birth cannot be greater than Joining Date.,La date de naissance ne peut pas être supérieure à la date d'adhésion., +Date of Birth cannot be greater than Joining Date.,La date de naissance ne peut pas être supérieure à la date d'adhésion., Dear,Cher/Chère, Default,Par Défaut, Define coupon codes.,Définissez les codes promo., @@ -3808,40 +3808,40 @@ Due Date,Date d'Échéance, Duplicate,Dupliquer, Duplicate Project with Tasks,Projet en double avec tâches, Duplicate project has been created,Un projet en double a été créé, -E-Way Bill JSON can only be generated from a submitted document,E-Way Bill JSON ne peut être généré qu'à partir d'un document soumis, -E-Way Bill JSON can only be generated from submitted document,E-Way Bill JSON ne peut être généré qu'à partir du document soumis, +E-Way Bill JSON can only be generated from a submitted document,E-Way Bill JSON ne peut être généré qu'à partir d'un document soumis, +E-Way Bill JSON can only be generated from submitted document,E-Way Bill JSON ne peut être généré qu'à partir du document soumis, E-Way Bill JSON cannot be generated for Sales Return as of now,La facture e-Way JSON ne peut pas être générée pour le retour de vente à partir de maintenant, -ERPNext could not find any matching payment entry,ERPNext n'a trouvé aucune entrée de paiement correspondante, +ERPNext could not find any matching payment entry,ERPNext n'a trouvé aucune entrée de paiement correspondante, Earliest Age,Âge le plus précoce, Edit Details,Modifier les détails, Edit Profile,Modifier le Profil, -Either GST Transporter ID or Vehicle No is required if Mode of Transport is Road,Un numéro d'identification de transporteur ou un numéro de véhicule est requis si le mode de transport est la route., +Either GST Transporter ID or Vehicle No is required if Mode of Transport is Road,Un numéro d'identification de transporteur ou un numéro de véhicule est requis si le mode de transport est la route., Email,Email, Email Campaigns,Campagnes de courrier électronique, -Employee ID is linked with another instructor,L'ID de l'employé est lié à un autre instructeur, +Employee ID is linked with another instructor,L'ID de l'employé est lié à un autre instructeur, Employee Tax and Benefits,Impôt et avantages sociaux des employés, -Employee is required while issuing Asset {0},L'employé est requis lors de l'émission de l'actif {0}, -Employee {0} does not belongs to the company {1},L'employé {0} n'appartient pas à l'entreprise {1}, +Employee is required while issuing Asset {0},L'employé est requis lors de l'émission de l'actif {0}, +Employee {0} does not belongs to the company {1},L'employé {0} n'appartient pas à l'entreprise {1}, Enable Auto Re-Order,Activer la re-commande automatique, -End Date of Agreement can't be less than today.,La date de fin de l'accord ne peut être inférieure à celle d'aujourd'hui., +End Date of Agreement can't be less than today.,La date de fin de l'accord ne peut être inférieure à celle d'aujourd'hui., End Time,Heure de fin, Energy Point Leaderboard,Point de classement énergétique, Enter API key in Google Settings.,Entrez la clé API dans les paramètres Google., Enter Supplier,Entrez le fournisseur, Enter Value,Entrez une valeur, -Entity Type,Type d'entité, +Entity Type,Type d'entité, Error,Erreur, Error in Exotel incoming call,Erreur dans un appel entrant Exotel, Error: {0} is mandatory field,Erreur: {0} est un champ obligatoire, -Event Link,Lien d'événement, -Exception occurred while reconciling {0},Une exception s'est produite lors de la réconciliation {0}, -Expected and Discharge dates cannot be less than Admission Schedule date,Les dates prévues et de sortie ne peuvent pas être inférieures à la date du calendrier d'admission, -Expire Allocation,Expiration de l'allocation, +Event Link,Lien d'événement, +Exception occurred while reconciling {0},Une exception s'est produite lors de la réconciliation {0}, +Expected and Discharge dates cannot be less than Admission Schedule date,Les dates prévues et de sortie ne peuvent pas être inférieures à la date du calendrier d'admission, +Expire Allocation,Expiration de l'allocation, Expired,Expiré, Export,Exporter, Export not allowed. You need {0} role to export.,Pas autorisé à exporter. Vous devez avoir le rôle {0} pour exporter., -Failed to add Domain,Impossible d'ajouter le domaine, -Fetch Items from Warehouse,Récupérer des articles de l'entrepôt, +Failed to add Domain,Impossible d'ajouter le domaine, +Fetch Items from Warehouse,Récupérer des articles de l'entrepôt, Fetching...,Aller chercher..., Field,Champ, File Manager,Gestionnaire de fichiers, @@ -3852,13 +3852,13 @@ Finished Qty,Quantité finie, Fleet Management,Gestion de flotte, Following fields are mandatory to create address:,Les champs suivants sont obligatoires pour créer une adresse:, For Month,Pour mois, -"For item {0} at row {1}, count of serial numbers does not match with the picked quantity","Pour l'élément {0} à la ligne {1}, le nombre de numéros de série ne correspond pas à la quantité sélectionnée.", -For operation {0}: Quantity ({1}) can not be greter than pending quantity({2}),Pour l'opération {0}: la quantité ({1}) ne peut pas être supérieure à la quantité en attente ({2}), -For quantity {0} should not be greater than work order quantity {1},Pour la quantité {0} ne doit pas être supérieure à la quantité d'ordre de travail {1}, +"For item {0} at row {1}, count of serial numbers does not match with the picked quantity","Pour l'élément {0} à la ligne {1}, le nombre de numéros de série ne correspond pas à la quantité sélectionnée.", +For operation {0}: Quantity ({1}) can not be greter than pending quantity({2}),Pour l'opération {0}: la quantité ({1}) ne peut pas être supérieure à la quantité en attente ({2}), +For quantity {0} should not be greater than work order quantity {1},Pour la quantité {0} ne doit pas être supérieure à la quantité d'ordre de travail {1}, Free item not set in the pricing rule {0},Article gratuit non défini dans la règle de tarification {0}, From Date and To Date are Mandatory,La date de début et la date de fin sont obligatoires, From date can not be greater than than To date,La date de début ne peut être supérieure à la date de fin, -From employee is required while receiving Asset {0} to a target location,De l'employé est requis lors de la réception de l'actif {0} vers un emplacement cible, +From employee is required while receiving Asset {0} to a target location,De l'employé est requis lors de la réception de l'actif {0} vers un emplacement cible, Fuel Expense,Frais de carburant, Future Payment Amount,Montant du paiement futur, Future Payment Ref,Paiement futur Ref, @@ -3880,27 +3880,27 @@ Help Article,Article d’Aide, "Helps you keep tracks of Contracts based on Supplier, Customer and Employee","Vous aide à garder une trace des contrats en fonction du fournisseur, client et employé", Helps you manage appointments with your leads,Vous aide à gérer les rendez-vous avec vos prospects, Home,Accueil, -IBAN is not valid,IBAN n'est pas valide, +IBAN is not valid,IBAN n'est pas valide, Import Data from CSV / Excel files.,Importer des données à partir de fichiers CSV / Excel, In Progress,En cours, Incoming call from {0},Appel entrant du {0}, Incorrect Warehouse,Entrepôt incorrect, Interest Amount is mandatory,Le montant des intérêts est obligatoire, Intermediate,Intermédiaire, -Invalid Barcode. There is no Item attached to this barcode.,Code à barres invalide. Il n'y a pas d'article attaché à ce code à barres., -Invalid credentials,les informations d'identification invalides, +Invalid Barcode. There is no Item attached to this barcode.,Code à barres invalide. Il n'y a pas d'article attaché à ce code à barres., +Invalid credentials,les informations d'identification invalides, Invite as User,Inviter en tant qu'Utilisateur, -Issue Priority.,Priorité d'émission., +Issue Priority.,Priorité d'émission., Issue Type.,Type de probleme., "It seems that there is an issue with the server's stripe configuration. In case of failure, the amount will get refunded to your account.","Il semble qu'il y a un problème avec la configuration de Stripe sur le serveur. En cas d'erreur, le montant est remboursé sur votre compte.", Item Reported,Article rapporté, -Item listing removed,Liste d'articles supprimée, -Item quantity can not be zero,La quantité d'article ne peut être nulle, +Item listing removed,Liste d'articles supprimée, +Item quantity can not be zero,La quantité d'article ne peut être nulle, Item taxes updated,Taxes sur les articles mises à jour, Item {0}: {1} qty produced. ,Article {0}: {1} quantité produite., Items are required to pull the raw materials which is associated with it.,Les articles sont nécessaires pour extraire les matières premières qui lui sont associées., -Joining Date can not be greater than Leaving Date,La date d'adhésion ne peut pas être supérieure à la date de départ, -Lab Test Item {0} already exist,L'élément de test en laboratoire {0} existe déjà, +Joining Date can not be greater than Leaving Date,La date d'adhésion ne peut pas être supérieure à la date de départ, +Lab Test Item {0} already exist,L'élément de test en laboratoire {0} existe déjà, Last Issue,Dernier numéro, Latest Age,Dernier âge, Leave application is linked with leave allocations {0}. Leave application cannot be set as leave without pay,La demande de congé est liée aux allocations de congé {0}. Demande de congé ne peut pas être défini comme congé sans solde, @@ -3910,7 +3910,7 @@ Liabilities,Passifs, Loading...,Chargement en Cours ..., Loan Amount exceeds maximum loan amount of {0} as per proposed securities,Le montant du prêt dépasse le montant maximal du prêt de {0} selon les titres proposés, Loan Applications from customers and employees.,Demandes de prêt des clients et des employés., -Loan Disbursement,Déboursement de l'emprunt, +Loan Disbursement,Déboursement de l'emprunt, Loan Processes,Processus de prêt, Loan Security,Sécurité des prêts, Loan Security Pledge,Garantie de prêt, @@ -3922,7 +3922,7 @@ Loan Security Price,Prix de la sécurité du prêt, Loan Security Price overlapping with {0},Le prix du titre de crédit se chevauche avec {0}, Loan Security Unpledge,Désengagement de garantie de prêt, Loan Security Value,Valeur de la sécurité du prêt, -Loan Type for interest and penalty rates,Type de prêt pour les taux d'intérêt et de pénalité, +Loan Type for interest and penalty rates,Type de prêt pour les taux d'intérêt et de pénalité, Loan amount cannot be greater than {0},Le montant du prêt ne peut pas être supérieur à {0}, Loan is mandatory,Le prêt est obligatoire, Loans,Les prêts, @@ -3951,17 +3951,17 @@ New Payment,Nouveau paiement, New release date should be in the future,La nouvelle date de sortie devrait être dans le futur, Newsletter,Newsletter, No Account matched these filters: {},Aucun compte ne correspond à ces filtres: {}, -No Employee found for the given employee field value. '{}': {},Aucun employé trouvé pour la valeur de champ d'employé donnée. '{}': {}, -No Leaves Allocated to Employee: {0} for Leave Type: {1},Aucun congé attribué à l'employé: {0} pour le type de congé: {1}, +No Employee found for the given employee field value. '{}': {},Aucun employé trouvé pour la valeur de champ d'employé donnée. '{}': {}, +No Leaves Allocated to Employee: {0} for Leave Type: {1},Aucun congé attribué à l'employé: {0} pour le type de congé: {1}, No communication found.,Aucune communication trouvée., -No correct answer is set for {0},Aucune réponse correcte n'est définie pour {0}., +No correct answer is set for {0},Aucune réponse correcte n'est définie pour {0}., No description,Pas de description, -No issue has been raised by the caller.,Aucun problème n'a été soulevé par l'appelant., +No issue has been raised by the caller.,Aucun problème n'a été soulevé par l'appelant., No items to publish,Aucun élément à publier, No outstanding invoices found,Aucune facture en attente trouvée, -No outstanding invoices found for the {0} {1} which qualify the filters you have specified.,Aucune facture en attente n'a été trouvée pour le {0} {1} qui qualifie les filtres que vous avez spécifiés., +No outstanding invoices found for the {0} {1} which qualify the filters you have specified.,Aucune facture en attente n'a été trouvée pour le {0} {1} qui qualifie les filtres que vous avez spécifiés., No outstanding invoices require exchange rate revaluation,Aucune facture en attente ne nécessite une réévaluation du taux de change, -No reviews yet,Pas encore d'avis, +No reviews yet,Pas encore d'avis, No views yet,Pas encore de vue, Non stock items,Articles hors stock, Not Allowed,Non Autorisé, @@ -3970,25 +3970,25 @@ Not permitted. Please disable the Lab Test Template,Pas permis. Veuillez désact Note,Note, Notes: ,Remarques :, Offline,Hors ligne, -On Converting Opportunity,Sur l'opportunité de conversion, +On Converting Opportunity,Sur l'opportunité de conversion, On Purchase Order Submission,Sur soumission de commande, On Sales Order Submission,Envoi de commande client, On Task Completion,En fin de tâche, On {0} Creation,Sur {0} Creation, Only .csv and .xlsx files are supported currently,Seuls les fichiers .csv et .xlsx sont actuellement pris en charge., -Only expired allocation can be cancelled,Seule l'allocation expirée peut être annulée, +Only expired allocation can be cancelled,Seule l'allocation expirée peut être annulée, Only users with the {0} role can create backdated leave applications,Seuls les utilisateurs avec le rôle {0} peuvent créer des demandes de congé antidatées, Open,Ouvert, Open Contact,Contact ouvert, Open Lead,Ouvrir le fil, Opening and Closing,Ouverture et fermeture, -Operating Cost as per Work Order / BOM,Coût d'exploitation selon l'ordre de travail / nomenclature, +Operating Cost as per Work Order / BOM,Coût d'exploitation selon l'ordre de travail / nomenclature, Order Amount,Montant de la commande, Page {0} of {1},Page {0} sur {1}, Paid amount cannot be less than {0},Le montant payé ne peut pas être inférieur à {0}, Parent Company must be a group company,La société mère doit être une société du groupe, Passing Score value should be between 0 and 100,La note de passage doit être comprise entre 0 et 100, -Password policy cannot contain spaces or simultaneous hyphens. The format will be restructured automatically,La politique de mot de passe ne peut pas contenir d'espaces ni de traits d'union simultanés. Le format sera restructuré automatiquement, +Password policy cannot contain spaces or simultaneous hyphens. The format will be restructured automatically,La politique de mot de passe ne peut pas contenir d'espaces ni de traits d'union simultanés. Le format sera restructuré automatiquement, Patient History,Histoire du patient, Pause,Pause, Pay,Payer, @@ -4001,39 +4001,39 @@ Period based On,Période basée sur, Perpetual inventory required for the company {0} to view this report.,Inventaire permanent requis pour que la société {0} puisse consulter ce rapport., Phone,Téléphone, Pick List,Liste de sélection, -Plaid authentication error,Erreur d'authentification du plaid, +Plaid authentication error,Erreur d'authentification du plaid, Plaid public token error,Erreur de jeton public Plaid, Plaid transactions sync error,Erreur de synchronisation des transactions plaid, -Please check the error log for details about the import errors,Veuillez consulter le journal des erreurs pour plus de détails sur les erreurs d'importation., +Please check the error log for details about the import errors,Veuillez consulter le journal des erreurs pour plus de détails sur les erreurs d'importation., Please click on the following link to set your new password,Veuillez cliquer sur le lien suivant pour définir votre nouveau mot de passe, -Please create DATEV Settings for Company {}.,Veuillez créer les paramètres DATEV pour l'entreprise {} ., -Please create adjustment Journal Entry for amount {0} ,Veuillez créer une écriture de journal d'ajustement pour le montant {0}, +Please create DATEV Settings for Company {}.,Veuillez créer les paramètres DATEV pour l'entreprise {} ., +Please create adjustment Journal Entry for amount {0} ,Veuillez créer une écriture de journal d'ajustement pour le montant {0}, Please do not create more than 500 items at a time,Ne créez pas plus de 500 objets à la fois., -Please enter Difference Account or set default Stock Adjustment Account for company {0},Veuillez saisir un compte d'écart ou définir un compte d'ajustement de stock par défaut pour la société {0}, -Please enter GSTIN and state for the Company Address {0},Veuillez saisir GSTIN et indiquer l'adresse de la société {0}., -Please enter Item Code to get item taxes,Veuillez entrer le code de l'article pour obtenir les taxes sur les articles, +Please enter Difference Account or set default Stock Adjustment Account for company {0},Veuillez saisir un compte d'écart ou définir un compte d'ajustement de stock par défaut pour la société {0}, +Please enter GSTIN and state for the Company Address {0},Veuillez saisir GSTIN et indiquer l'adresse de la société {0}., +Please enter Item Code to get item taxes,Veuillez entrer le code de l'article pour obtenir les taxes sur les articles, Please enter Warehouse and Date,Veuillez entrer entrepôt et date, -Please enter coupon code !!,S'il vous plaît entrer le code coupon !!, -Please enter the designation,S'il vous plaît entrer la désignation, +Please enter coupon code !!,S'il vous plaît entrer le code coupon !!, +Please enter the designation,S'il vous plaît entrer la désignation, Please enter valid coupon code !!,Veuillez entrer un code de coupon valide !!, -Please login as a Marketplace User to edit this item.,Veuillez vous connecter en tant qu'utilisateur Marketplace pour modifier cet article., -Please login as a Marketplace User to report this item.,Veuillez vous connecter en tant qu'utilisateur de la Marketplace pour signaler cet élément., +Please login as a Marketplace User to edit this item.,Veuillez vous connecter en tant qu'utilisateur Marketplace pour modifier cet article., +Please login as a Marketplace User to report this item.,Veuillez vous connecter en tant qu'utilisateur de la Marketplace pour signaler cet élément., Please select Template Type to download template,Veuillez sélectionner le type de modèle pour télécharger le modèle, -Please select Applicant Type first,Veuillez d'abord sélectionner le type de demandeur, -Please select Customer first,S'il vous plaît sélectionnez d'abord le client, -Please select Item Code first,Veuillez d'abord sélectionner le code d'article, +Please select Applicant Type first,Veuillez d'abord sélectionner le type de demandeur, +Please select Customer first,S'il vous plaît sélectionnez d'abord le client, +Please select Item Code first,Veuillez d'abord sélectionner le code d'article, Please select Loan Type for company {0},Veuillez sélectionner le type de prêt pour la société {0}, Please select a Delivery Note,Veuillez sélectionner un bon de livraison, -Please select a Sales Person for item: {0},Veuillez sélectionner un commercial pour l'article: {0}, +Please select a Sales Person for item: {0},Veuillez sélectionner un commercial pour l'article: {0}, Please select another payment method. Stripe does not support transactions in currency '{0}',Veuillez sélectionner une autre méthode de paiement. Stripe ne prend pas en charge les transactions en devise '{0}', -Please select the customer.,S'il vous plaît sélectionner le client., +Please select the customer.,S'il vous plaît sélectionner le client., Please set a Supplier against the Items to be considered in the Purchase Order.,Veuillez définir un fournisseur par rapport aux articles à prendre en compte dans le bon de commande., Please set account heads in GST Settings for Compnay {0},Définissez les en-têtes de compte dans les paramètres de la TPS pour le service {0}., Please set an email id for the Lead {0},Veuillez définir un identifiant de messagerie pour le prospect {0}., -Please set default UOM in Stock Settings,Veuillez définir l'UdM par défaut dans les paramètres de stock, -Please set filter based on Item or Warehouse due to a large amount of entries.,Veuillez définir le filtre en fonction de l'article ou de l'entrepôt en raison d'une grande quantité d'entrées., +Please set default UOM in Stock Settings,Veuillez définir l'UdM par défaut dans les paramètres de stock, +Please set filter based on Item or Warehouse due to a large amount of entries.,Veuillez définir le filtre en fonction de l'article ou de l'entrepôt en raison d'une grande quantité d'entrées., Please set up the Campaign Schedule in the Campaign {0},Configurez le calendrier de la campagne dans la campagne {0}., -Please set valid GSTIN No. in Company Address for company {0},Veuillez définir un numéro GSTIN valide dans l'adresse de l'entreprise pour l'entreprise {0}, +Please set valid GSTIN No. in Company Address for company {0},Veuillez définir un numéro GSTIN valide dans l'adresse de l'entreprise pour l'entreprise {0}, Please set {0},Veuillez définir {0},customer Please setup a default bank account for company {0},Veuillez configurer un compte bancaire par défaut pour la société {0}., Please specify,Veuillez spécifier, @@ -4048,29 +4048,29 @@ Processing XML Files,Traitement des fichiers XML, Profitability,Rentabilité, Project,Projet, Proposed Pledges are mandatory for secured Loans,Les engagements proposés sont obligatoires pour les prêts garantis, -Provide the academic year and set the starting and ending date.,Indiquez l'année universitaire et définissez la date de début et de fin., +Provide the academic year and set the starting and ending date.,Indiquez l'année universitaire et définissez la date de début et de fin., Public token is missing for this bank,Un jeton public est manquant pour cette banque, Publish,Publier, Publish 1 Item,Publier 1 élément, Publish Items,Publier des articles, -Publish More Items,Publier plus d'articles, +Publish More Items,Publier plus d'articles, Publish Your First Items,Publiez vos premiers articles, Publish {0} Items,Publier {0} éléments, Published Items,Articles publiés, -Purchase Invoice cannot be made against an existing asset {0},La facture d'achat ne peut pas être effectuée sur un élément existant {0}, -Purchase Invoices,Factures d'achat, +Purchase Invoice cannot be made against an existing asset {0},La facture d'achat ne peut pas être effectuée sur un élément existant {0}, +Purchase Invoices,Factures d'achat, Purchase Orders,Acheter en ligne, Purchase Receipt doesn't have any Item for which Retain Sample is enabled.,Le reçu d’achat ne contient aucun élément pour lequel Conserver échantillon est activé., Purchase Return,Retour d'Achat, Qty of Finished Goods Item,Quantité de produits finis, Qty or Amount is mandatroy for loan security,La quantité ou le montant est obligatoire pour la garantie de prêt, -Quality Inspection required for Item {0} to submit,Inspection de qualité requise pour que l'élément {0} soit envoyé, +Quality Inspection required for Item {0} to submit,Inspection de qualité requise pour que l'élément {0} soit envoyé, Quantity to Manufacture,Quantité à fabriquer, -Quantity to Manufacture can not be zero for the operation {0},La quantité à fabriquer ne peut pas être nulle pour l'opération {0}, +Quantity to Manufacture can not be zero for the operation {0},La quantité à fabriquer ne peut pas être nulle pour l'opération {0}, Quarterly,Trimestriel, Queued,File d'Attente, Quick Entry,Écriture Rapide, -Quiz {0} does not exist,Le questionnaire {0} n'existe pas, +Quiz {0} does not exist,Le questionnaire {0} n'existe pas, Quotation Amount,Montant du devis, Rate or Discount is required for the price discount.,Le taux ou la remise est requis pour la remise de prix., Reason,Raison, @@ -4081,7 +4081,7 @@ Recruitment,Recrutement, Red,rouge, Refreshing,Rafraîchissant, Release date must be in the future,La date de sortie doit être dans le futur, -Relieving Date must be greater than or equal to Date of Joining,La date de libération doit être supérieure ou égale à la date d'adhésion, +Relieving Date must be greater than or equal to Date of Joining,La date de libération doit être supérieure ou égale à la date d'adhésion, Rename,Renommer, Rename Not Allowed,Renommer non autorisé, Repayment Method is mandatory for term loans,La méthode de remboursement est obligatoire pour les prêts à terme, @@ -4090,70 +4090,70 @@ Report Item,Élément de rapport, Report this Item,Signaler cet article, Reserved Qty for Subcontract: Raw materials quantity to make subcontracted items.,Quantité réservée pour la sous-traitance: quantité de matières premières pour fabriquer des articles sous-traités., Reset,Réinitialiser, -Reset Service Level Agreement,Réinitialiser l'accord de niveau de service, -Resetting Service Level Agreement.,Réinitialisation de l'accord de niveau de service., -Response Time for {0} at index {1} can't be greater than Resolution Time.,Le temps de réponse pour {0} à l'index {1} ne peut pas être supérieur au temps de résolution., +Reset Service Level Agreement,Réinitialiser l'accord de niveau de service, +Resetting Service Level Agreement.,Réinitialisation de l'accord de niveau de service., +Response Time for {0} at index {1} can't be greater than Resolution Time.,Le temps de réponse pour {0} à l'index {1} ne peut pas être supérieur au temps de résolution., Return amount cannot be greater unclaimed amount,Le montant du retour ne peut pas être supérieur au montant non réclamé, Review,La revue, Room,Chambre, Room Type,Type de chambre, Row # ,Ligne #, -Row #{0}: Accepted Warehouse and Supplier Warehouse cannot be same,Ligne # {0}: l'entrepôt accepté et l'entrepôt fournisseur ne peuvent pas être identiques, -Row #{0}: Cannot delete item {1} which has already been billed.,Ligne # {0}: impossible de supprimer l'élément {1} qui a déjà été facturé., -Row #{0}: Cannot delete item {1} which has already been delivered,Ligne # {0}: impossible de supprimer l'élément {1} qui a déjà été livré, -Row #{0}: Cannot delete item {1} which has already been received,Ligne # {0}: impossible de supprimer l'élément {1} qui a déjà été reçu, -Row #{0}: Cannot delete item {1} which has work order assigned to it.,Ligne # {0}: impossible de supprimer l'élément {1} auquel un bon de travail est affecté., -Row #{0}: Cannot delete item {1} which is assigned to customer's purchase order.,Ligne # {0}: impossible de supprimer l'article {1} affecté à la commande d'achat du client., -Row #{0}: Cannot select Supplier Warehouse while suppling raw materials to subcontractor,Ligne # {0}: Impossible de sélectionner l'entrepôt fournisseur lors de la fourniture de matières premières au sous-traitant, -Row #{0}: Cost Center {1} does not belong to company {2},Ligne # {0}: le centre de coûts {1} n'appartient pas à l'entreprise {2}, -Row #{0}: Operation {1} is not completed for {2} qty of finished goods in Work Order {3}. Please update operation status via Job Card {4}.,Ligne n ° {0}: l'opération {1} n'est pas terminée pour {2} quantité de produits finis dans l'ordre de travail {3}. Veuillez mettre à jour le statut de l'opération via la carte de travail {4}., +Row #{0}: Accepted Warehouse and Supplier Warehouse cannot be same,Ligne # {0}: l'entrepôt accepté et l'entrepôt fournisseur ne peuvent pas être identiques, +Row #{0}: Cannot delete item {1} which has already been billed.,Ligne # {0}: impossible de supprimer l'élément {1} qui a déjà été facturé., +Row #{0}: Cannot delete item {1} which has already been delivered,Ligne # {0}: impossible de supprimer l'élément {1} qui a déjà été livré, +Row #{0}: Cannot delete item {1} which has already been received,Ligne # {0}: impossible de supprimer l'élément {1} qui a déjà été reçu, +Row #{0}: Cannot delete item {1} which has work order assigned to it.,Ligne # {0}: impossible de supprimer l'élément {1} auquel un bon de travail est affecté., +Row #{0}: Cannot delete item {1} which is assigned to customer's purchase order.,Ligne # {0}: impossible de supprimer l'article {1} affecté à la commande d'achat du client., +Row #{0}: Cannot select Supplier Warehouse while suppling raw materials to subcontractor,Ligne # {0}: Impossible de sélectionner l'entrepôt fournisseur lors de la fourniture de matières premières au sous-traitant, +Row #{0}: Cost Center {1} does not belong to company {2},Ligne # {0}: le centre de coûts {1} n'appartient pas à l'entreprise {2}, +Row #{0}: Operation {1} is not completed for {2} qty of finished goods in Work Order {3}. Please update operation status via Job Card {4}.,Ligne n ° {0}: l'opération {1} n'est pas terminée pour {2} quantité de produits finis dans l'ordre de travail {3}. Veuillez mettre à jour le statut de l'opération via la carte de travail {4}., Row #{0}: Payment document is required to complete the transaction,Ligne n ° {0}: Un document de paiement est requis pour effectuer la transaction., -Row #{0}: Serial No {1} does not belong to Batch {2},Ligne # {0}: le numéro de série {1} n'appartient pas au lot {2}, +Row #{0}: Serial No {1} does not belong to Batch {2},Ligne # {0}: le numéro de série {1} n'appartient pas au lot {2}, Row #{0}: Service End Date cannot be before Invoice Posting Date,Ligne # {0}: la date de fin du service ne peut pas être antérieure à la date de validation de la facture, Row #{0}: Service Start Date cannot be greater than Service End Date,Ligne # {0}: la date de début du service ne peut pas être supérieure à la date de fin du service, Row #{0}: Service Start and End Date is required for deferred accounting,Ligne # {0}: la date de début et de fin du service est requise pour la comptabilité différée, -Row {0}: Invalid Item Tax Template for item {1},Ligne {0}: modèle de taxe sur les articles non valide pour l'article {1}, -Row {0}: Quantity not available for {4} in warehouse {1} at posting time of the entry ({2} {3}),Ligne {0}: quantité non disponible pour {4} dans l'entrepôt {1} au moment de la comptabilisation de l'entrée ({2} {3})., -Row {0}: user has not applied the rule {1} on the item {2},Ligne {0}: l'utilisateur n'a pas appliqué la règle {1} sur l'élément {2}, -Row {0}:Sibling Date of Birth cannot be greater than today.,Ligne {0}: la date de naissance du frère ou de la sÅ“ur ne peut pas être supérieure à celle d'aujourd'hui., +Row {0}: Invalid Item Tax Template for item {1},Ligne {0}: modèle de taxe sur les articles non valide pour l'article {1}, +Row {0}: Quantity not available for {4} in warehouse {1} at posting time of the entry ({2} {3}),Ligne {0}: quantité non disponible pour {4} dans l'entrepôt {1} au moment de la comptabilisation de l'entrée ({2} {3})., +Row {0}: user has not applied the rule {1} on the item {2},Ligne {0}: l'utilisateur n'a pas appliqué la règle {1} sur l'élément {2}, +Row {0}:Sibling Date of Birth cannot be greater than today.,Ligne {0}: la date de naissance du frère ou de la sÅ“ur ne peut pas être supérieure à celle d'aujourd'hui., Row({0}): {1} is already discounted in {2},Ligne ({0}): {1} est déjà réduit dans {2}., Rows Added in {0},Lignes ajoutées dans {0}, Rows Removed in {0},Lignes supprimées dans {0}, Sanctioned Amount limit crossed for {0} {1},Montant sanctionné dépassé pour {0} {1}, -Sanctioned Loan Amount already exists for {0} against company {1},Le montant du prêt sanctionné existe déjà pour {0} contre l'entreprise {1}, +Sanctioned Loan Amount already exists for {0} against company {1},Le montant du prêt sanctionné existe déjà pour {0} contre l'entreprise {1}, Save,sauvegarder, -Save Item,Enregistrer l'élément, +Save Item,Enregistrer l'élément, Saved Items,Articles sauvegardés, -Scheduled and Admitted dates can not be less than today,Les dates prévues et admises ne peuvent être inférieures à celles d'aujourd'hui, +Scheduled and Admitted dates can not be less than today,Les dates prévues et admises ne peuvent être inférieures à celles d'aujourd'hui, Search Items ...,Rechercher des articles ..., Search for a payment,Rechercher un paiement, -Search for anything ...,Rechercher n'importe quoi ..., +Search for anything ...,Rechercher n'importe quoi ..., Search results for,Résultats de recherche pour, Select All,Sélectionner Tout, Select Difference Account,Sélectionnez compte différentiel, Select a Default Priority.,Sélectionnez une priorité par défaut., Select a Supplier from the Default Supplier List of the items below.,Sélectionnez un fournisseur dans la liste des fournisseurs par défaut des éléments ci-dessous., Select a company,Sélectionnez une entreprise, -Select finance book for the item {0} at row {1},Sélectionnez le livre de financement pour l'élément {0} à la ligne {1}., +Select finance book for the item {0} at row {1},Sélectionnez le livre de financement pour l'élément {0} à la ligne {1}., Select only one Priority as Default.,Sélectionnez une seule priorité par défaut., Seller Information,Information du vendeur, Send,Envoyer, Send a message,Envoyer un message, Sending,Envoi, -Sends Mails to lead or contact based on a Campaign schedule,Envoie des courriers à diriger ou à contacter en fonction d'un calendrier de campagne, +Sends Mails to lead or contact based on a Campaign schedule,Envoie des courriers à diriger ou à contacter en fonction d'un calendrier de campagne, Serial Number Created,Numéro de série créé, Serial Numbers Created,Numéros de série créés, -Serial no(s) required for serialized item {0},N ° de série requis pour l'article sérialisé {0}, +Serial no(s) required for serialized item {0},N ° de série requis pour l'article sérialisé {0}, Series,Séries, Server Error,erreur du serveur, -Service Level Agreement has been changed to {0}.,L'accord de niveau de service a été remplacé par {0}., -Service Level Agreement tracking is not enabled.,Le suivi des accords de niveau de service n'est pas activé., -Service Level Agreement was reset.,L'accord de niveau de service a été réinitialisé., -Service Level Agreement with Entity Type {0} and Entity {1} already exists.,L'accord de niveau de service avec le type d'entité {0} et l'entité {1} existe déjà., +Service Level Agreement has been changed to {0}.,L'accord de niveau de service a été remplacé par {0}., +Service Level Agreement tracking is not enabled.,Le suivi des accords de niveau de service n'est pas activé., +Service Level Agreement was reset.,L'accord de niveau de service a été réinitialisé., +Service Level Agreement with Entity Type {0} and Entity {1} already exists.,L'accord de niveau de service avec le type d'entité {0} et l'entité {1} existe déjà., Set,Définir, Set Meta Tags,Définir les balises méta, -Set Response Time and Resolution for Priority {0} at index {1}.,Définissez le temps de réponse et la résolution pour la priorité {0} à l'index {1}., -Set {0} in company {1},Définissez {0} dans l'entreprise {1}, +Set Response Time and Resolution for Priority {0} at index {1}.,Définissez le temps de réponse et la résolution pour la priorité {0} à l'index {1}., +Set {0} in company {1},Définissez {0} dans l'entreprise {1}, Setup,Configuration, Setup Wizard,Assistant de configuration, Shift Management,Gestion des quarts, @@ -4163,10 +4163,10 @@ Show Sales Person,Afficher le vendeur, Show Stock Ageing Data,Afficher les données sur le vieillissement des stocks, Show Warehouse-wise Stock,Afficher le stock entre les magasins, Size,Taille, -Something went wrong while evaluating the quiz.,Quelque chose s'est mal passé lors de l'évaluation du quiz., +Something went wrong while evaluating the quiz.,Quelque chose s'est mal passé lors de l'évaluation du quiz., "Sorry,coupon code are exhausted","Désolé, le code de coupon est épuisé", "Sorry,coupon code validity has expired","Désolé, la validité du code promo a expiré", -"Sorry,coupon code validity has not started","Désolé, la validité du code promo n'a pas commencé", +"Sorry,coupon code validity has not started","Désolé, la validité du code promo n'a pas commencé", Sr,Sr, Start,Démarrer, Start Date cannot be before the current date,La date de début ne peut pas être antérieure à la date du jour, @@ -4178,32 +4178,32 @@ Stock Entry has been already created against this Pick List,Une entrée de stock Stock Ledger ID,ID du registre des stocks, Stock Value ({0}) and Account Balance ({1}) are out of sync for account {2} and it's linked warehouses.,La valeur du stock ({0}) et le solde du compte ({1}) ne sont pas synchronisés pour le compte {2} et ses entrepôts liés., Stores - {0},Magasins - {0}, -Student with email {0} does not exist,Étudiant avec le courrier électronique {0} n'existe pas, +Student with email {0} does not exist,Étudiant avec le courrier électronique {0} n'existe pas, Submit Review,Poster un commentaire, Submitted,Soumis, Supplier Addresses And Contacts,Adresses et contacts des fournisseurs, Synchronize this account,Synchroniser ce compte, Tag,Étiquette, -Target Location is required while receiving Asset {0} from an employee,L'emplacement cible est requis lors de la réception de l'élément {0} d'un employé, -Target Location is required while transferring Asset {0},L'emplacement cible est requis lors du transfert de l'élément {0}, -Target Location or To Employee is required while receiving Asset {0},L'emplacement cible ou l'employé est requis lors de la réception de l'élément {0}, +Target Location is required while receiving Asset {0} from an employee,L'emplacement cible est requis lors de la réception de l'élément {0} d'un employé, +Target Location is required while transferring Asset {0},L'emplacement cible est requis lors du transfert de l'élément {0}, +Target Location or To Employee is required while receiving Asset {0},L'emplacement cible ou l'employé est requis lors de la réception de l'élément {0}, Task's {0} End Date cannot be after Project's End Date.,La date de fin {0} de la tâche ne peut pas être postérieure à la date de fin du projet., Task's {0} Start Date cannot be after Project's End Date.,La date de début {0} de la tâche ne peut pas être postérieure à la date de fin du projet., Tax Account not specified for Shopify Tax {0},Compte de taxe non spécifié pour Shopify Tax {0}, Tax Total,Total de la taxe, Template,Modèle, -The Campaign '{0}' already exists for the {1} '{2}',La campagne '{0}' existe déjà pour le {1} '{2}'., +The Campaign '{0}' already exists for the {1} '{2}',La campagne '{0}' existe déjà pour le {1} '{2}'., The difference between from time and To Time must be a multiple of Appointment,La différence entre from time et To Time doit être un multiple de Appointment, -The field Asset Account cannot be blank,Le champ Compte d'actif ne peut pas être vide, +The field Asset Account cannot be blank,Le champ Compte d'actif ne peut pas être vide, The field Equity/Liability Account cannot be blank,Le champ Compte d’équité / de responsabilité ne peut pas être vide, The following serial numbers were created:

{0},Les numéros de série suivants ont été créés:

{0}, -The parent account {0} does not exists in the uploaded template,Le compte parent {0} n'existe pas dans le modèle téléchargé, +The parent account {0} does not exists in the uploaded template,Le compte parent {0} n'existe pas dans le modèle téléchargé, The question cannot be duplicate,La question ne peut pas être dupliquée, -The selected payment entry should be linked with a creditor bank transaction,L'entrée de paiement sélectionnée doit être liée à une transaction bancaire créditrice, -The selected payment entry should be linked with a debtor bank transaction,L'entrée de paiement sélectionnée doit être liée à une transaction bancaire débitrice, +The selected payment entry should be linked with a creditor bank transaction,L'entrée de paiement sélectionnée doit être liée à une transaction bancaire créditrice, +The selected payment entry should be linked with a debtor bank transaction,L'entrée de paiement sélectionnée doit être liée à une transaction bancaire débitrice, The total allocated amount ({0}) is greated than the paid amount ({1}).,Le montant total alloué ({0}) est supérieur au montant payé ({1})., The value {0} is already assigned to an exisiting Item {2}.,La valeur {0} est déjà attribuée à un élément existant {2}., -There are no vacancies under staffing plan {0},Il n'y a pas de postes vacants dans le plan de dotation en personnel {0}, +There are no vacancies under staffing plan {0},Il n'y a pas de postes vacants dans le plan de dotation en personnel {0}, This Service Level Agreement is specific to Customer {0},Cet accord de niveau de service est spécifique au client {0}., This action will unlink this account from any external service integrating ERPNext with your bank accounts. It cannot be undone. Are you certain ?,Cette action dissociera ce compte de tout service externe intégrant ERPNext avec vos comptes bancaires. Ça ne peut pas être défait. Êtes-vous sûr ?, This bank account is already synchronized,Ce compte bancaire est déjà synchronisé, @@ -4219,7 +4219,7 @@ Title,Titre, To date needs to be before from date,À ce jour doit être avant la date du, Total,Total, Total Early Exits,Total des sorties anticipées, -Total Late Entries,Nombre total d'entrées en retard, +Total Late Entries,Nombre total d'entrées en retard, Total Payment Request amount cannot be greater than {0} amount,Le montant total de la demande de paiement ne peut être supérieur à {0}., Total payments amount can't be greater than {},Le montant total des paiements ne peut être supérieur à {}, Totals,Totaux, @@ -4230,8 +4230,8 @@ Transport Receipt No and Date are mandatory for your chosen Mode of Transport,Le Tuesday,Mardi, Type,Type, Unable to find Salary Component {0},Impossible de trouver la composante salaire {0}, -Unable to find the time slot in the next {0} days for the operation {1}.,Impossible de trouver l'intervalle de temps dans les {0} jours suivants pour l'opération {1}., -Unable to update remote activity,Impossible de mettre à jour l'activité à distance, +Unable to find the time slot in the next {0} days for the operation {1}.,Impossible de trouver l'intervalle de temps dans les {0} jours suivants pour l'opération {1}., +Unable to update remote activity,Impossible de mettre à jour l'activité à distance, Unknown Caller,Appelant inconnu, Unlink external integrations,Dissocier les intégrations externes, Unmarked Attendance for days,Présence non marquée pendant des jours, @@ -4241,7 +4241,7 @@ Unsupported GST Category for E-Way Bill JSON generation,Catégorie GST non prise Update,Mettre à Jour, Update Details,Détails de mise à jour, Update Taxes for Items,Mettre à jour les taxes pour les articles, -"Upload a bank statement, link or reconcile a bank account","Télécharger un relevé bancaire, un lien ou un rapprochement d'un compte bancaire", +"Upload a bank statement, link or reconcile a bank account","Télécharger un relevé bancaire, un lien ou un rapprochement d'un compte bancaire", Upload a statement,Télécharger une déclaration, Use a name that is different from previous project name,Utilisez un nom différent du nom du projet précédent, User {0} is disabled,Utilisateur {0} est désactivé, @@ -4249,7 +4249,7 @@ Users and Permissions,Utilisateurs et autorisations, Vacancies cannot be lower than the current openings,Les postes vacants ne peuvent pas être inférieurs aux ouvertures actuelles, Valid From Time must be lesser than Valid Upto Time.,La période de validité doit être inférieure à la durée de validité., Valuation Rate required for Item {0} at row {1},Taux de valorisation requis pour le poste {0} à la ligne {1}, -"Valuation rate not found for the Item {0}, which is required to do accounting entries for {1} {2}. If the item is transacting as a zero valuation rate item in the {1}, please mention that in the {1} Item table. Otherwise, please create an incoming stock transaction for the item or mention valuation rate in the Item record, and then try submiting / cancelling this entry.","Taux de valorisation non trouvé pour le poste {0}, nécessaire pour effectuer les écritures comptables pour {1} {2}. Si l'élément effectue une transaction en tant qu'élément à taux d'évaluation zéro dans {1}, veuillez l'indiquer dans le tableau {1} Article. Sinon, créez une transaction de stock entrante pour l'article ou indiquez le taux de valorisation dans l'enregistrement de l'article, puis essayez de soumettre / annuler cette entrée.", +"Valuation rate not found for the Item {0}, which is required to do accounting entries for {1} {2}. If the item is transacting as a zero valuation rate item in the {1}, please mention that in the {1} Item table. Otherwise, please create an incoming stock transaction for the item or mention valuation rate in the Item record, and then try submiting / cancelling this entry.","Taux de valorisation non trouvé pour le poste {0}, nécessaire pour effectuer les écritures comptables pour {1} {2}. Si l'élément effectue une transaction en tant qu'élément à taux d'évaluation zéro dans {1}, veuillez l'indiquer dans le tableau {1} Article. Sinon, créez une transaction de stock entrante pour l'article ou indiquez le taux de valorisation dans l'enregistrement de l'article, puis essayez de soumettre / annuler cette entrée.", Values Out Of Sync,Valeurs désynchronisées, Vehicle Type is required if Mode of Transport is Road,Le type de véhicule est requis si le mode de transport est la route, Vendor Name,Nom du vendeur, @@ -4261,21 +4261,21 @@ Warehouse,Entrepôt, Warehouse not found against the account {0},Entrepôt introuvable sur le compte {0}, Welcome to {0},Bienvenue sur {0}, Why do think this Item should be removed?,Pourquoi pensez-vous que cet élément devrait être supprimé?, -Work Order {0}: Job Card not found for the operation {1},Bon de travail {0}: carte de travail non trouvée pour l'opération {1}, +Work Order {0}: Job Card not found for the operation {1},Bon de travail {0}: carte de travail non trouvée pour l'opération {1}, Workday {0} has been repeated.,La journée de travail {0} a été répétée., XML Files Processed,Fichiers XML traités, Year,Année, Yearly,Annuel, You,Vous, -You are not allowed to enroll for this course,Vous n'êtes pas autorisé à vous inscrire à ce cours, -You are not enrolled in program {0},Vous n'êtes pas inscrit au programme {0}, -You can Feature upto 8 items.,Vous pouvez présenter jusqu'à 8 éléments., +You are not allowed to enroll for this course,Vous n'êtes pas autorisé à vous inscrire à ce cours, +You are not enrolled in program {0},Vous n'êtes pas inscrit au programme {0}, +You can Feature upto 8 items.,Vous pouvez présenter jusqu'à 8 éléments., You can also copy-paste this link in your browser,Vous pouvez également copier-coller ce lien dans votre navigateur, -You can publish upto 200 items.,Vous pouvez publier jusqu'à 200 articles., -You can't create accounting entries in the closed accounting period {0},Vous ne pouvez pas créer d'écritures comptables dans la période comptable clôturée {0}., +You can publish upto 200 items.,Vous pouvez publier jusqu'à 200 articles., +You can't create accounting entries in the closed accounting period {0},Vous ne pouvez pas créer d'écritures comptables dans la période comptable clôturée {0}., You have to enable auto re-order in Stock Settings to maintain re-order levels.,Vous devez activer la re-commande automatique dans les paramètres de stock pour maintenir les niveaux de ré-commande., You must be a registered supplier to generate e-Way Bill,Vous devez être un fournisseur enregistré pour générer une facture électronique, -You need to login as a Marketplace User before you can add any reviews.,Vous devez vous connecter en tant qu'utilisateur de la Marketplace avant de pouvoir ajouter des critiques., +You need to login as a Marketplace User before you can add any reviews.,Vous devez vous connecter en tant qu'utilisateur de la Marketplace avant de pouvoir ajouter des critiques., Your Featured Items,Vos articles en vedette, Your Items,Vos articles, Your Profile,Votre profil, @@ -4291,9 +4291,9 @@ woocommerce - {0},woocommerce - {0}, {0} bank transaction(s) created and {1} errors,{0} transaction (s) bancaire (s) créée (s) et {1} erreur (s), {0} can not be greater than {1},{0} ne peut pas être supérieur à {1}, {0} conversations,{0} conversations, -{0} is not a company bank account,{0} n'est pas un compte bancaire d'entreprise, -{0} is not a group node. Please select a group node as parent cost center,{0} n'est pas un nœud de groupe. Veuillez sélectionner un nœud de groupe comme centre de coûts parent, -{0} is not the default supplier for any items.,{0} n'est le fournisseur par défaut d'aucun élément., +{0} is not a company bank account,{0} n'est pas un compte bancaire d'entreprise, +{0} is not a group node. Please select a group node as parent cost center,{0} n'est pas un nœud de groupe. Veuillez sélectionner un nœud de groupe comme centre de coûts parent, +{0} is not the default supplier for any items.,{0} n'est le fournisseur par défaut d'aucun élément., {0} is required,{0} est nécessaire, {0} units of {1} is not available.,{0} unités de {1} ne sont pas disponibles., {0}: {1} must be less than {2},{0}: {1} doit être inférieur à {2}, @@ -4329,7 +4329,7 @@ Loan Amount is mandatory,Le montant du prêt est obligatoire, Mode Of Payment,Mode de Paiement, No students Found,Aucun étudiant trouvé, Not in Stock,En Rupture de Stock, -Please select a Customer,S'il vous plaît sélectionner un client, +Please select a Customer,S'il vous plaît sélectionner un client, Printed On,Imprimé sur, Received From,Reçu de, Sales Person,Vendeur, @@ -4339,7 +4339,7 @@ Write Off,Reprise, Email Id,Identifiant Email, No,Non, Reference Doctype,DocType de la Référence, -User Id,Identifiant d'utilisateur, +User Id,Identifiant d'utilisateur, Yes,Oui, Actual ,Réel, Add to cart,Ajouter au Panier, @@ -4370,7 +4370,7 @@ Open Projects ,Projets ouverts, Open To Do ,ToDo ouvertes, Operation Id,ID de l'Opération, Partially ordered,Partiellement Ordonné, -Please select company first,Sélectionnez d'abord l'entreprise, +Please select company first,Sélectionnez d'abord l'entreprise, Please select patient,Veuillez sélectionner un patient, Printed On ,Imprimé sur, Projected qty,Qté Projetée, @@ -4378,7 +4378,7 @@ Sales person,Vendeur, Serial No {0} Created,N° de Série {0} créé, Set as default,Définir par défaut, Source Location is required for the Asset {0},La localisation source est requis pour l'actif {0}, -Tax Id,Numéro d'identification fiscale, +Tax Id,Numéro d'identification fiscale, To Time,Horaire de Fin, To date cannot be before from date,À ce jour ne peut pas être antérieure à la date du, Total Taxable value,Valeur taxable totale, @@ -4394,19 +4394,19 @@ to,à, Cards,Cartes, Percentage,Pourcentage, Failed to setup defaults for country {0}. Please contact support@erpnext.com,Échec de la configuration des paramètres par défaut pour le pays {0}. Veuillez contacter support@erpnext.com, -Row #{0}: Item {1} is not a Serialized/Batched Item. It cannot have a Serial No/Batch No against it.,Ligne # {0}: l'article {1} n'est pas un article sérialisé / en lot. Il ne peut pas avoir de numéro de série / de lot contre lui., +Row #{0}: Item {1} is not a Serialized/Batched Item. It cannot have a Serial No/Batch No against it.,Ligne # {0}: l'article {1} n'est pas un article sérialisé / en lot. Il ne peut pas avoir de numéro de série / de lot contre lui., Please set {0},Veuillez définir {0}, Please set {0},Veuillez définir {0},supplier Draft,Brouillon,"docstatus,=,0" Cancelled,Annulé,"docstatus,=,2" -Please setup Instructor Naming System in Education > Education Settings,Veuillez configurer le système de dénomination de l'instructeur dans Éducation> Paramètres de l'éducation, +Please setup Instructor Naming System in Education > Education Settings,Veuillez configurer le système de dénomination de l'instructeur dans Éducation> Paramètres de l'éducation, Please set Naming Series for {0} via Setup > Settings > Naming Series,Veuillez définir la série de noms pour {0} via Configuration> Paramètres> Série de noms, -UOM Conversion factor ({0} -> {1}) not found for item: {2},Facteur de conversion UdM ({0} -> {1}) introuvable pour l'article: {2}, -Item Code > Item Group > Brand,Code article> Groupe d'articles> Marque, +UOM Conversion factor ({0} -> {1}) not found for item: {2},Facteur de conversion UdM ({0} -> {1}) introuvable pour l'article: {2}, +Item Code > Item Group > Brand,Code article> Groupe d'articles> Marque, Customer > Customer Group > Territory,Client> Groupe de clients> Territoire, Supplier > Supplier Type,Fournisseur> Type de fournisseur, Please setup Employee Naming System in Human Resource > HR Settings,Veuillez configurer le système de dénomination des employés dans Ressources humaines> Paramètres RH, -Please setup numbering series for Attendance via Setup > Numbering Series,Veuillez configurer la série de numérotation pour l'assistance via Configuration> Série de numérotation, +Please setup numbering series for Attendance via Setup > Numbering Series,Veuillez configurer la série de numérotation pour l'assistance via Configuration> Série de numérotation, Purchase Order Required,Bon de Commande Requis, Purchase Receipt Required,Reçu d’Achat Requis, Requested,Demandé, @@ -4462,7 +4462,7 @@ Accounts Frozen Upto,Comptes Gelés Jusqu'au, "Accounting entry frozen up to this date, nobody can do / modify entry except role specified below.","Les écritures comptables sont gelées jusqu'à cette date, personne ne peut ajouter / modifier les entrées sauf les rôles spécifiés ci-dessous.", Role Allowed to Set Frozen Accounts & Edit Frozen Entries,Rôle Autorisé à Geler des Comptes & à Éditer des Écritures Gelées, Users with this role are allowed to set frozen accounts and create / modify accounting entries against frozen accounts,Les utilisateurs ayant ce rôle sont autorisés à définir les comptes gelés et à créer / modifier des écritures comptables sur des comptes gelés, -Determine Address Tax Category From,Déterminer la catégorie de taxe d'adresse à partir de, +Determine Address Tax Category From,Déterminer la catégorie de taxe d'adresse à partir de, Address used to determine Tax Category in transactions.,Adresse utilisée pour déterminer la catégorie de taxe dans les transactions., Over Billing Allowance (%),Frais de facturation excédentaires (%), Percentage you are allowed to bill more against the amount ordered. For example: If the order value is $100 for an item and tolerance is set as 10% then you are allowed to bill for $110.,"Pourcentage vous êtes autorisé à facturer plus par rapport au montant commandé. Par exemple: Si la valeur de la commande est de 100 USD pour un article et que la tolérance est définie sur 10%, vous êtes autorisé à facturer 110 USD.", @@ -4471,14 +4471,14 @@ Role that is allowed to submit transactions that exceed credit limits set.,Rôle Check Supplier Invoice Number Uniqueness,Vérifiez l'Unicité du Numéro de Facture du Fournisseur, Make Payment via Journal Entry,Effectuer un Paiement par une Écriture de Journal, Unlink Payment on Cancellation of Invoice,Délier Paiement à l'Annulation de la Facture, -Unlink Advance Payment on Cancelation of Order,Dissocier le paiement anticipé lors de l'annulation d'une commande, +Unlink Advance Payment on Cancelation of Order,Dissocier le paiement anticipé lors de l'annulation d'une commande, Book Asset Depreciation Entry Automatically,Comptabiliser les Entrées de Dépréciation d'Actifs Automatiquement, Allow Cost Center In Entry of Balance Sheet Account,Autoriser le centre de coûts en saisie du compte de bilan, Automatically Add Taxes and Charges from Item Tax Template,Ajouter automatiquement des taxes et des frais à partir du modèle de taxe à la pièce, Automatically Fetch Payment Terms,Récupérer automatiquement les conditions de paiement, Show Inclusive Tax In Print,Afficher la taxe inclusive en impression, Show Payment Schedule in Print,Afficher le calendrier de paiement dans Imprimer, -Currency Exchange Settings,Paramètres d'échange de devises, +Currency Exchange Settings,Paramètres d'échange de devises, Allow Stale Exchange Rates,Autoriser les Taux de Change Existants, Stale Days,Journées Passées, Report Settings,Paramètres de rapport, @@ -4489,20 +4489,20 @@ Branch Code,Code de la branche, Address and Contact,Adresse et Contact, Address HTML,Adresse HTML, Contact HTML,HTML du Contact, -Data Import Configuration,Configuration de l'importation de données, +Data Import Configuration,Configuration de l'importation de données, Bank Transaction Mapping,Cartographie des transactions bancaires, -Plaid Access Token,Jeton d'accès plaid, -Company Account,Compte d'entreprise, +Plaid Access Token,Jeton d'accès plaid, +Company Account,Compte d'entreprise, Account Subtype,Sous-type de compte, Is Default Account,Est un compte par défaut, -Is Company Account,Est le compte de l'entreprise, +Is Company Account,Est le compte de l'entreprise, Party Details,Parti Détails, Account Details,Détails du compte, IBAN,IBAN, Bank Account No,No de compte bancaire, -Integration Details,Détails d'intégration, -Integration ID,ID d'intégration, -Last Integration Date,Dernière date d'intégration, +Integration Details,Détails d'intégration, +Integration ID,ID d'intégration, +Last Integration Date,Dernière date d'intégration, Change this date manually to setup the next synchronization start date,Modifiez cette date manuellement pour définir la prochaine date de début de la synchronisation., Mask,Masque, Bank Guarantee,Garantie Bancaire, @@ -4609,7 +4609,7 @@ POS-CLO-,POS-CLO-, Custody,Garde, Net Amount,Montant Net, Cashier Closing Payments,Paiements de clôture du caissier, -Import Chart of Accounts from a csv file,Importer un tableau de comptes à partir d'un fichier csv, +Import Chart of Accounts from a csv file,Importer un tableau de comptes à partir d'un fichier csv, Attach custom Chart of Accounts file,Joindre un fichier de plan comptable personnalisé, Chart Preview,Aperçu du graphique, Chart Tree,Arbre à cartes, @@ -4641,7 +4641,7 @@ lft,Lft, rgt,rgt, Coupon Code,Code de coupon, Coupon Name,Nom du coupon, -"e.g. ""Summer Holiday 2019 Offer 20""",ex. "Offre vacances d'été 2019 20", +"e.g. ""Summer Holiday 2019 Offer 20""",ex. "Offre vacances d'été 2019 20", Coupon Type,Type de coupon, Promotional,Promotionnel, Gift Card,Carte cadeau, @@ -4692,9 +4692,9 @@ Bank Charges Account,Compte de frais bancaires, Accounts Receivable Credit Account,Compte de crédit débiteur, Accounts Receivable Discounted Account,Compte escompté des comptes débiteurs, Accounts Receivable Unpaid Account,Comptes débiteurs non payés, -Item Tax Template,Modèle de taxe d'article, -Tax Rates,Les taux d'imposition, -Item Tax Template Detail,Détail du modèle de taxe d'article, +Item Tax Template,Modèle de taxe d'article, +Tax Rates,Les taux d'imposition, +Item Tax Template Detail,Détail du modèle de taxe d'article, Entry Type,Type d'Écriture, Inter Company Journal Entry,Ecriture de journal inter-sociétés, Bank Entry,Écriture Bancaire, @@ -4730,7 +4730,7 @@ Debit in Company Currency,Débit en Devise Société, Credit in Company Currency,Crédit dans la Devise de la Société, Payroll Entry,Entrée de la paie, Employee Advance,Avance versée aux employés, -Reference Due Date,Date d'échéance de référence, +Reference Due Date,Date d'échéance de référence, Loyalty Program Tier,Echelon de programme de fidélité, Redeem Against,Échanger contre, Expiry Date,Date d'expiration, @@ -4748,8 +4748,8 @@ Collection Rules,Règles de collecte, Redemption,Echange, Conversion Factor,Facteur de Conversion, 1 Loyalty Points = How much base currency?,1 point de fidélité = Quel montant en devise de base ?, -Expiry Duration (in days),Durée d'expiration (en jours), -Help Section,Section d'aide, +Expiry Duration (in days),Durée d'expiration (en jours), +Help Section,Section d'aide, Loyalty Program Help,Aide au programme de fidélité, Loyalty Program Collection,Collecte du programme de fidélité, Tier Name,Nom de l'échelon, @@ -4767,17 +4767,17 @@ Monthly Distribution Percentage,Pourcentage de Répartition Mensuelle, Percentage Allocation,Allocation en Pourcentage, Create Missing Party,Créer les tiers manquants, Create missing customer or supplier.,Créer les clients ou les fournisseurs manquant, -Opening Invoice Creation Tool Item,Ouverture d'un outil de création de facture, +Opening Invoice Creation Tool Item,Ouverture d'un outil de création de facture, Temporary Opening Account,Compte temporaire d'ouverture, Party Account,Compte de Tiers, Type of Payment,Type de Paiement, ACC-PAY-.YYYY.-,ACC-PAY-YYYY.-, Receive,Recevoir, Internal Transfer,Transfert Interne, -Payment Order Status,État de l'ordre de paiement, +Payment Order Status,État de l'ordre de paiement, Payment Ordered,Paiement commandé, Payment From / To,Paiement De / À, -Company Bank Account,Compte bancaire de l'entreprise, +Company Bank Account,Compte bancaire de l'entreprise, Party Bank Account,Compte bancaire du parti, Account Paid From,Compte Payé Du, Account Paid To,Compte Payé Au, @@ -4802,8 +4802,8 @@ Payment Gateway Account,Compte Passerelle de Paiement, Payment Account,Compte de Paiement, Default Payment Request Message,Message de Demande de Paiement par Défaut, PMO-,PMO-, -Payment Order Type,Type d'ordre de paiement, -Payment Order Reference,Référence de l'ordre de paiement, +Payment Order Type,Type d'ordre de paiement, +Payment Order Reference,Référence de l'ordre de paiement, Bank Account Details,Détails de compte en banque, Payment Reconciliation,Réconciliation des Paiements, Receivable / Payable Account,Compte Débiteur / Créditeur, @@ -4822,14 +4822,14 @@ Payment Reconciliation Payment,Paiement de Réconciliation des Paiements, Reference Row,Ligne de Référence, Allocated amount,Montant alloué, Payment Request Type,Type de demande de paiement, -Outward,À l'extérieur, -Inward,Vers l'intérieur, +Outward,À l'extérieur, +Inward,Vers l'intérieur, ACC-PRQ-.YYYY.-,ACC-PRQ-.AAAA.-, Transaction Details,détails de la transaction, Amount in customer's currency,Montant dans la devise du client, Is a Subscription,Est un abonnement, Transaction Currency,Devise de la Transaction, -Subscription Plans,Plans d'abonnement, +Subscription Plans,Plans d'abonnement, SWIFT Number,Numéro SWIFT, Recipient Message And Payment Details,Message du Destinataire et Détails de Paiement, Make Sales Invoice,Faire des Factures de Vente, @@ -4840,7 +4840,7 @@ Payment Schedule,Calendrier de paiement, Invoice Portion,Pourcentage de facturation, Payment Amount,Montant du paiement, Payment Term Name,Nom du terme de paiement, -Due Date Based On,Date d'échéance basée sur, +Due Date Based On,Date d'échéance basée sur, Day(s) after invoice date,Jour (s) après la date de la facture, Day(s) after the end of the invoice month,Jour (s) après la fin du mois de facture, Month(s) after the end of the invoice month,Mois (s) après la fin du mois de la facture, @@ -4859,12 +4859,12 @@ Update Stock,Mettre à Jour le Stock, Ignore Pricing Rule,Ignorez Règle de Prix, Allow user to edit Rate,Autoriser l'utilisateur à modifier le Taux, Allow user to edit Discount,Autoriser l'utilisateur à modifier la remise, -Allow Print Before Pay,Autoriser l'impression avant la paie, +Allow Print Before Pay,Autoriser l'impression avant la paie, Display Items In Stock,Afficher les articles en stock, Applicable for Users,Applicable aux Utilisateurs, Sales Invoice Payment,Paiement de la Facture de Vente, -Item Groups,Groupes d'articles, -Only show Items from these Item Groups,Afficher uniquement les éléments de ces groupes d'éléments, +Item Groups,Groupes d'articles, +Only show Items from these Item Groups,Afficher uniquement les éléments de ces groupes d'éléments, Customer Groups,Groupes de Clients, Only show Customer of these Customer Groups,Afficher uniquement les clients de ces groupes de clients, Print Format for Online,Format d'Impression en Ligne, @@ -4878,8 +4878,8 @@ POS Profile User,Utilisateur du profil PDV, Use POS in Offline Mode,Utiliser PDV en Mode Hors-Ligne, Apply On,Appliquer Sur, Price or Product Discount,Prix ou remise de produit, -Apply Rule On Item Code,Appliquer la règle sur le code d'article, -Apply Rule On Item Group,Appliquer une règle sur un groupe d'articles, +Apply Rule On Item Code,Appliquer la règle sur le code d'article, +Apply Rule On Item Group,Appliquer une règle sur un groupe d'articles, Apply Rule On Brand,Appliquer la règle sur la marque, Mixed Conditions,Conditions mixtes, Conditions will be applied on all the selected items combined. ,Des conditions seront appliquées sur tous les éléments sélectionnés combinés., @@ -4906,7 +4906,7 @@ Product Discount Scheme,Schéma de remise de produit, Same Item,Même article, Free Item,Article gratuit, Threshold for Suggestion,Seuil de suggestion, -System will notify to increase or decrease quantity or amount ,Le système notifiera d'augmenter ou de diminuer la quantité ou le montant, +System will notify to increase or decrease quantity or amount ,Le système notifiera d'augmenter ou de diminuer la quantité ou le montant, "Higher the number, higher the priority","Plus le nombre est grand, plus la priorité est haute", Apply Multiple Pricing Rules,Appliquer plusieurs règles de tarification, Apply Discount on Rate,Appliquer une réduction sur le taux, @@ -4917,7 +4917,7 @@ Promotional Scheme Id,Id de schéma promotionnel, Promotional Scheme,Schéma promotionnel, Pricing Rule Brand,Marque de règle de tarification, Pricing Rule Detail,Détail de la règle de tarification, -Child Docname,Nom de l'enfant, +Child Docname,Nom de l'enfant, Rule Applied,Règle appliquée, Pricing Rule Item Code,Règle de tarification Article Code, Pricing Rule Item Group,Groupe de postes de règle de tarification, @@ -4933,7 +4933,7 @@ Tax Withholding Category,Catégorie de taxation à la source, Edit Posting Date and Time,Modifier la Date et l'Heure de la Publication, Is Paid,Est Payé, Is Return (Debit Note),Est une note de débit, -Apply Tax Withholding Amount,Appliquer le montant de la retenue d'impôt, +Apply Tax Withholding Amount,Appliquer le montant de la retenue d'impôt, Accounting Dimensions ,Dimensions comptables, Supplier Invoice Details,Détails de la Facture du Fournisseur, Supplier Invoice Date,Date de la Facture du Fournisseur, @@ -5021,7 +5021,7 @@ Is Fixed Asset,Est Immobilisation, Asset Location,Localisation de l'actif, Deferred Expense,Frais différés, Deferred Expense Account,Compte de dépenses différées, -Service Stop Date,Date d'arrêt du service, +Service Stop Date,Date d'arrêt du service, Enable Deferred Expense,Activer les frais reportés, Service Start Date,Date de début du service, Service End Date,Date de fin du service, @@ -5029,8 +5029,8 @@ Allow Zero Valuation Rate,Autoriser un Taux de Valorisation Égal à Zéro, Item Tax Rate,Taux de la Taxe sur l'Article, Tax detail table fetched from item master as a string and stored in this field.\nUsed for Taxes and Charges,La table de détails de taxe est récupérée depuis les données de base de l'article comme une chaîne de caractères et stockée dans ce champ. Elle est utilisée pour les Taxes et Frais., Purchase Order Item,Article du Bon de Commande, -Purchase Receipt Detail,Détail du reçu d'achat, -Item Weight Details,Détails du poids de l'article, +Purchase Receipt Detail,Détail du reçu d'achat, +Item Weight Details,Détails du poids de l'article, Weight Per Unit,Poids par unité, Total Weight,Poids total, Weight UOM,UDM de Poids, @@ -5042,7 +5042,7 @@ Add or Deduct,Ajouter ou Déduire, Deduct,Déduire, On Previous Row Amount,Le Montant de la Rangée Précédente, On Previous Row Total,Le Total de la Rangée Précédente, -On Item Quantity,Sur quantité d'article, +On Item Quantity,Sur quantité d'article, Reference Row #,Ligne de Référence #, Is this Tax included in Basic Rate?,Cette Taxe est-elle incluse dans le Taux de Base ?, "If checked, the tax amount will be considered as already included in the Print Rate / Print Amount","Si cochée, le montant de la taxe sera considéré comme déjà inclus dans le Taux d'Impression / Prix d'Impression", @@ -5104,7 +5104,7 @@ Qty as per Stock UOM,Qté par UDM du Stock, Discount and Margin,Remise et Marge, Rate With Margin,Tarif Avec Marge, Discount (%) on Price List Rate with Margin,Remise (%) sur le Tarif de la Liste de Prix avec la Marge, -Rate With Margin (Company Currency),Taux avec marge (devise de l'entreprise), +Rate With Margin (Company Currency),Taux avec marge (devise de l'entreprise), Delivered By Supplier,Livré par le Fournisseur, Deferred Revenue,Produits comptabilisés d'avance, Deferred Revenue Account,Compte de produits comptabilisés d'avance, @@ -5134,7 +5134,7 @@ From Folio No,Du No de Folio, To Shareholder,A l'actionnaire, To Folio No,Au N. de Folio, Equity/Liability Account,Compte de capitaux propres / passif, -Asset Account,Compte d'actif, +Asset Account,Compte d'actif, (including),(compris), ACC-SH-.YYYY.-,ACC-SH-.YYYY.-, Folio no.,No. de Folio, @@ -5143,7 +5143,7 @@ Hidden list maintaining the list of contacts linked to Shareholder,Liste cachée Specify conditions to calculate shipping amount,Spécifier les conditions pour calculer le montant de la livraison, Shipping Rule Label,Étiquette de la Règle de Livraison, example: Next Day Shipping,Exemple : Livraison le Jour Suivant, -Shipping Rule Type,Type de règle d'expédition, +Shipping Rule Type,Type de règle d'expédition, Shipping Account,Compte de Livraison, Calculate Based On,Calculer en fonction de, Fixed,Fixé, @@ -5157,11 +5157,11 @@ A condition for a Shipping Rule,Une condition pour une Règle de Livraison, From Value,De la Valeur, To Value,Valeur Finale, Shipping Rule Country,Pays de la Règle de Livraison, -Subscription Period,Période d'abonnement, -Subscription Start Date,Date de début de l'abonnement, -Cancelation Date,Date d'annulation, -Trial Period Start Date,Date de début de la période d'essai, -Trial Period End Date,Date de fin de la période d'évaluation, +Subscription Period,Période d'abonnement, +Subscription Start Date,Date de début de l'abonnement, +Cancelation Date,Date d'annulation, +Trial Period Start Date,Date de début de la période d'essai, +Trial Period End Date,Date de fin de la période d'évaluation, Current Invoice Start Date,Date de début de la facture en cours, Current Invoice End Date,Date de fin de la facture en cours, Days Until Due,Jours avant échéance, @@ -5172,17 +5172,17 @@ Plans,Plans, Discounts,Réductions, Additional DIscount Percentage,Pourcentage de réduction supplémentaire, Additional DIscount Amount,Montant de la Remise Supplémentaire, -Subscription Invoice,Facture d'abonnement, -Subscription Plan,Plan d'abonnement, +Subscription Invoice,Facture d'abonnement, +Subscription Plan,Plan d'abonnement, Price Determination,Détermination du prix, Fixed rate,Taux fixe, Based on price list,Sur la base de la liste de prix, Cost,Coût, Billing Interval,Intervalle de facturation, -Billing Interval Count,Nombre d'intervalles de facturation, -"Number of intervals for the interval field e.g if Interval is 'Days' and Billing Interval Count is 3, invoices will be generated every 3 days","Nombre d'intervalles pour le champ d'intervalle, par exemple si Intervalle est "Jours" et si le décompte d'intervalle de facturation est 3, les factures seront générées tous les 3 jours", +Billing Interval Count,Nombre d'intervalles de facturation, +"Number of intervals for the interval field e.g if Interval is 'Days' and Billing Interval Count is 3, invoices will be generated every 3 days","Nombre d'intervalles pour le champ d'intervalle, par exemple si Intervalle est "Jours" et si le décompte d'intervalle de facturation est 3, les factures seront générées tous les 3 jours", Payment Plan,Plan de paiement, -Subscription Plan Detail,Détail du plan d'abonnement, +Subscription Plan Detail,Détail du plan d'abonnement, Plan,Plan, Subscription Settings,Paramètres des Abonnements, Grace Period,Période de grâce, @@ -5200,25 +5200,25 @@ Billing Country,Pays de Facturation, Shipping City,Ville de Livraison, Shipping County,Comté de Livraison, Shipping State,État de livraison, -Shipping Zipcode,Code postal d'expédition, +Shipping Zipcode,Code postal d'expédition, Shipping Country,Pays de Livraison, Tax Withholding Account,Compte de taxation à la source, -Tax Withholding Rates,Taux de retenue d'impôt, +Tax Withholding Rates,Taux de retenue d'impôt, Rates,Prix, -Tax Withholding Rate,Taux de retenue d'impôt, +Tax Withholding Rate,Taux de retenue d'impôt, Single Transaction Threshold,Seuil de transaction unique, Cumulative Transaction Threshold,Seuil de transaction cumulatif, -Agriculture Analysis Criteria,Critères d'analyse de l'agriculture, +Agriculture Analysis Criteria,Critères d'analyse de l'agriculture, Linked Doctype,Doctype lié, -Water Analysis,Analyse de l'eau, +Water Analysis,Analyse de l'eau, Soil Analysis,Analyse du sol, Plant Analysis,Analyse des plantes, Fertilizer,Engrais, Soil Texture,Texture du sol, Weather,Météo, -Agriculture Manager,Directeur de l'agriculture, +Agriculture Manager,Directeur de l'agriculture, Agriculture User,Agriculteur, -Agriculture Task,Tâche d'agriculture, +Agriculture Task,Tâche d'agriculture, Start Day,Date de début, End Day,Jour de fin, Holiday Management,Gestion des vacances, @@ -5231,9 +5231,9 @@ Crop Name,Nom de la culture, Scientific Name,Nom scientifique, "You can define all the tasks which need to carried out for this crop here. The day field is used to mention the day on which the task needs to be carried out, 1 being the 1st day, etc.. ","Vous pouvez définir ici toutes les tâches à effectuer pour cette culture. Le champ de jour est utilisé pour mentionner le jour où la tâche doit être effectuée, 1 étant le 1er jour, etc.", Crop Spacing,Espacement des cultures, -Crop Spacing UOM,UOM d'espacement des cultures, +Crop Spacing UOM,UOM d'espacement des cultures, Row Spacing,Écartement des rangs, -Row Spacing UOM,UOM d'espacement des lignes, +Row Spacing UOM,UOM d'espacement des lignes, Perennial,Vivace, Biennial,Biennal, Planting UOM,Unité de mesure de la plantation, @@ -5248,7 +5248,7 @@ A link to all the Locations in which the Crop is growing,Lien vers tous les lieu This will be day 1 of the crop cycle,Ce sera le jour 1 du cycle de la culture, ISO 8601 standard,Norme ISO 8601, Cycle Type,Type de cycle, -Less than a year,Moins d'un an, +Less than a year,Moins d'un an, The minimum length between each plant in the field for optimum growth,La longueur minimale entre chaque plante sur le terrain pour une croissance optimale, The minimum distance between rows of plants for optimum growth,La distance minimale entre les rangées de plantes pour une croissance optimale, Detected Diseases,Maladies détectées, @@ -5260,18 +5260,18 @@ Tasks Created,Tâches créées, Common Name,Nom commun, Treatment Task,Tâche de traitement, Treatment Period,Période de traitement, -Fertilizer Name,Nom de l'engrais, +Fertilizer Name,Nom de l'engrais, Density (if liquid),Densité (si liquide), -Fertilizer Contents,Contenu de l'engrais, -Fertilizer Content,Contenu d'engrais, +Fertilizer Contents,Contenu de l'engrais, +Fertilizer Content,Contenu d'engrais, Linked Plant Analysis,Analyse des plantes liées, Linked Soil Analysis,Analyse de sol liée, Linked Soil Texture,Texture de sol liée, Collection Datetime,Date et heure du prélèvement, Laboratory Testing Datetime,Date et heure du test de laboratoire, Result Datetime,Date et heure du résultat, -Plant Analysis Criterias,Critères d'analyse des plantes, -Plant Analysis Criteria,Critères d'analyse des plantes, +Plant Analysis Criterias,Critères d'analyse des plantes, +Plant Analysis Criteria,Critères d'analyse des plantes, Minimum Permissible Value,Valeur minimale autorisée, Maximum Permissible Value,Valeur maximale autorisée, Ca/K,Ca / K, @@ -5279,31 +5279,31 @@ Ca/Mg,Ca / Mg, Mg/K,Mg / K, (Ca+Mg)/K,(Ca + Mg) / K, Ca/(K+Ca+Mg),Ca / (K + Ca + Mg), -Soil Analysis Criterias,Critères d'analyse des sols, -Soil Analysis Criteria,Critères d'analyse des sols, +Soil Analysis Criterias,Critères d'analyse des sols, +Soil Analysis Criteria,Critères d'analyse des sols, Soil Type,Le type de sol, Loamy Sand,Sable limoneux, Sandy Loam,Limon sableux, Loam,Terreau, Silt Loam,Limon fin, Sandy Clay Loam,Limon argilo-sableux, -Clay Loam,Terreau d'argile, +Clay Loam,Terreau d'argile, Silty Clay Loam,Limon argileux fin, Sandy Clay,Argile sableuse, Silty Clay,Argile limoneuse, -Clay Composition (%),Composition d'argile (%), +Clay Composition (%),Composition d'argile (%), Sand Composition (%),Composition de sable (%), Silt Composition (%),Composition de limon (%), Ternary Plot,Tracé ternaire, Soil Texture Criteria,Critères de texture du sol, -Type of Sample,Type d'échantillon, +Type of Sample,Type d'échantillon, Container,Récipient, Origin,Origine, Collection Temperature ,Température de collecte, Storage Temperature,Température de stockage, Appearance,Apparence, Person Responsible,Personne responsable, -Water Analysis Criteria,Critères d'analyse de l'eau, +Water Analysis Criteria,Critères d'analyse de l'eau, Weather Parameter,Paramètre météo, ACC-ASS-.YYYY.-,ACC-ASS-YYYY.-, Asset Owner,Propriétaire de l'Actif, @@ -5313,7 +5313,7 @@ Disposal Date,Date d’Élimination, Journal Entry for Scrap,Écriture de Journal pour la Mise au Rebut, Available-for-use Date,Date de mise en service, Calculate Depreciation,Calculer la dépréciation, -Allow Monthly Depreciation,Autoriser l'amortissement mensuel, +Allow Monthly Depreciation,Autoriser l'amortissement mensuel, Number of Depreciations Booked,Nombre d’Amortissements Comptabilisés, Finance Books,Livres comptables, Straight Line,Linéaire, @@ -5328,17 +5328,17 @@ Depreciation Schedules,Calendriers d'Amortissement, Policy number,Numéro de politique, Insurer,Assureur, Insured value,Valeur assurée, -Insurance Start Date,Date de début de l'assurance, -Insurance End Date,Date de fin de l'assurance, +Insurance Start Date,Date de début de l'assurance, +Insurance End Date,Date de fin de l'assurance, Comprehensive Insurance,Assurance complète, Maintenance Required,Maintenance requise, -Check if Asset requires Preventive Maintenance or Calibration,Vérifier si l'actif nécessite une maintenance préventive ou un étalonnage, +Check if Asset requires Preventive Maintenance or Calibration,Vérifier si l'actif nécessite une maintenance préventive ou un étalonnage, Booked Fixed Asset,Actif immobilisé comptabilisé, -Purchase Receipt Amount,Montant du reçu d'achat, +Purchase Receipt Amount,Montant du reçu d'achat, Default Finance Book,Livre comptable par défaut, Quality Manager,Responsable Qualité, Asset Category Name,Nom de Catégorie d'Actif, -Depreciation Options,Options d'amortissement, +Depreciation Options,Options d'amortissement, Enable Capital Work in Progress Accounting,Activer la comptabilité des immobilisations en cours, Finance Book Detail,Détails du livre comptable, Asset Category Account,Compte de Catégorie d'Actif, @@ -5348,9 +5348,9 @@ Depreciation Expense Account,Compte de Dotations aux Amortissement, Capital Work In Progress Account,Compte d'immobilisation en cours, Asset Finance Book,Livre comptable d'actifs, Written Down Value,Valeur comptable nette, -Depreciation Start Date,Date de début de l'amortissement, +Depreciation Start Date,Date de début de l'amortissement, Expected Value After Useful Life,Valeur Attendue Après Utilisation Complète, -Rate of Depreciation,Taux d'amortissement, +Rate of Depreciation,Taux d'amortissement, In Percentage,En pourcentage, Select Serial No,Veuillez sélectionner le numéro de série, Maintenance Team,Équipe de maintenance, @@ -5369,43 +5369,43 @@ Preventive Maintenance,Maintenance préventive, Calibration,Étalonnage, 2 Yearly,2 ans, Certificate Required,Certificat requis, -Next Due Date,prochaine date d'échéance, -Last Completion Date,Dernière date d'achèvement, +Next Due Date,prochaine date d'échéance, +Last Completion Date,Dernière date d'achèvement, Asset Maintenance Team,Équipe de Maintenance des Actifs, -Maintenance Team Name,Nom de l'équipe de maintenance, -Maintenance Team Members,Membres de l'équipe de maintenance, +Maintenance Team Name,Nom de l'équipe de maintenance, +Maintenance Team Members,Membres de l'équipe de maintenance, Purpose,Objet, Stock Manager,Responsable des Stocks, -Asset Movement Item,Élément de mouvement d'actif, +Asset Movement Item,Élément de mouvement d'actif, Source Location,Localisation source, From Employee,De l'Employé, Target Location,Localisation cible, -To Employee,À l'employé, +To Employee,À l'employé, Asset Repair,Réparation d'Actif, ACC-ASR-.YYYY.-,ACC-ASR-.AAAA.-, -Failure Date,Date d'échec, +Failure Date,Date d'échec, Assign To Name,Attribuer au nom, Repair Status,État de réparation, Error Description,Erreur de description, -Downtime,Temps d'arrêt, +Downtime,Temps d'arrêt, Repair Cost,Coût de réparation, Manufacturing Manager,Responsable de Production, -Current Asset Value,Valeur actuelle de l'actif, -New Asset Value,Nouvelle valeur de l'actif, +Current Asset Value,Valeur actuelle de l'actif, +New Asset Value,Nouvelle valeur de l'actif, Make Depreciation Entry,Créer une Écriture d'Amortissement, Finance Book Id,Identifiant du livre comptable, Location Name,Nom du lieux, Parent Location,Localisation parente, Is Container,Est le contenant, -Check if it is a hydroponic unit,Vérifiez s'il s'agit d'une unité hydroponique, -Location Details,Détails de l'emplacement, +Check if it is a hydroponic unit,Vérifiez s'il s'agit d'une unité hydroponique, +Location Details,Détails de l'emplacement, Latitude,Latitude, Longitude,Longitude, Area,Région, Area UOM,Unité de mesure de la surface, Tree Details,Détails de l’Arbre, -Maintenance Team Member,Membre de l'équipe de maintenance, -Team Member,Membre de l'équipe, +Maintenance Team Member,Membre de l'équipe de maintenance, +Team Member,Membre de l'équipe, Maintenance Role,Rôle de maintenance, Buying Settings,Paramètres d'Achat, Settings for Buying Module,Paramètres du Module Achat, @@ -5428,7 +5428,7 @@ Customer Contact Email,Email Contact Client, Set Target Warehouse,Définir le magasin cible, Supply Raw Materials,Fournir les Matières Premières, Purchase Order Pricing Rule,Règle de tarification des bons de commande, -Set Reserve Warehouse,Définir l'entrepôt de réserve, +Set Reserve Warehouse,Définir l'entrepôt de réserve, In Words will be visible once you save the Purchase Order.,En Toutes Lettres. Sera visible une fois que vous enregistrerez le Bon de Commande., Advance Paid,Avance Payée, % Billed,% Facturé, @@ -5535,23 +5535,23 @@ Parameter Name,Nom du Paramètre, Supplier Scorecard Standing,Classement de la Fiche d'Évaluation Fournisseur, Notify Other,Notifier Autre, Supplier Scorecard Variable,Variable de la Fiche d'Évaluation Fournisseur, -Call Log,Journal d'appel, +Call Log,Journal d'appel, Received By,Reçu par, -Caller Information,Informations sur l'appelant, +Caller Information,Informations sur l'appelant, Contact Name,Nom du Contact, Lead Name,Nom du Prospect, Ringing,Sonnerie, Missed,Manqué, -Call Duration in seconds,Durée d'appel en secondes, -Recording URL,URL d'enregistrement, +Call Duration in seconds,Durée d'appel en secondes, +Recording URL,URL d'enregistrement, Communication Medium,Moyen de Communication, Communication Medium Type,Type de support de communication, Voice,Voix, Catch All,Attraper tout, -"If there is no assigned timeslot, then communication will be handled by this group","S'il n'y a pas d'intervalle de temps attribué, la communication sera gérée par ce groupe.", +"If there is no assigned timeslot, then communication will be handled by this group","S'il n'y a pas d'intervalle de temps attribué, la communication sera gérée par ce groupe.", Timeslots,Tranches de temps, Communication Medium Timeslot,Période de communication moyenne, -Employee Group,Groupe d'employés, +Employee Group,Groupe d'employés, Appointment,Rendez-Vous, Scheduled Time,Heure prévue, Unverified,Non vérifié, @@ -5563,18 +5563,18 @@ Appointment With,Rendez-vous avec, Calendar Event,Événement de calendrier, Appointment Booking Settings,Paramètres de réservation de rendez-vous, Enable Appointment Scheduling,Activer la planification des rendez-vous, -Agent Details,Détails de l'agent, +Agent Details,Détails de l'agent, Availability Of Slots,Disponibilité des emplacements, Number of Concurrent Appointments,Nombre de rendez-vous simultanés, Agents,Agents, Appointment Details,Détails du rendez-vous, Appointment Duration (In Minutes),Durée du rendez-vous (en minutes), Notify Via Email,Avertir par e-mail, -Notify customer and agent via email on the day of the appointment.,Avertissez le client et l'agent par e-mail le jour du rendez-vous., -Number of days appointments can be booked in advance,Nombre de jours de rendez-vous peuvent être réservés à l'avance, +Notify customer and agent via email on the day of the appointment.,Avertissez le client et l'agent par e-mail le jour du rendez-vous., +Number of days appointments can be booked in advance,Nombre de jours de rendez-vous peuvent être réservés à l'avance, Success Settings,Paramètres de réussite, Success Redirect URL,URL de redirection réussie, -"Leave blank for home.\nThis is relative to site URL, for example ""about"" will redirect to ""https://yoursitename.com/about""","Laissez vide pour la maison. Ceci est relatif à l'URL du site, par exemple "about" redirigera vers "https://yoursitename.com/about"", +"Leave blank for home.\nThis is relative to site URL, for example ""about"" will redirect to ""https://yoursitename.com/about""","Laissez vide pour la maison. Ceci est relatif à l'URL du site, par exemple "about" redirigera vers "https://yoursitename.com/about"", Appointment Booking Slots,Horaires de prise de rendez-vous, From Time ,Horaire de Début, Campaign Email Schedule,Calendrier des e-mails de campagne, @@ -5595,17 +5595,17 @@ Signed On,Signé le, Contract Details,Détails du contrat, Contract Template,Modèle de contrat, Contract Terms,Termes du contrat, -Fulfilment Details,Détails de l'exécution, +Fulfilment Details,Détails de l'exécution, Requires Fulfilment,Nécessite des conditions, -Fulfilment Deadline,Délai d'exécution, -Fulfilment Terms,Conditions d'exécution, -Contract Fulfilment Checklist,Liste de vérification de l'exécution des contrats, +Fulfilment Deadline,Délai d'exécution, +Fulfilment Terms,Conditions d'exécution, +Contract Fulfilment Checklist,Liste de vérification de l'exécution des contrats, Requirement,Obligations, Contract Terms and Conditions,Termes et conditions du contrat, -Fulfilment Terms and Conditions,Termes et conditions d'exécution, +Fulfilment Terms and Conditions,Termes et conditions d'exécution, Contract Template Fulfilment Terms,Conditions d'exécution du modèle de contrat, Email Campaign,Campagne Email, -Email Campaign For ,Campagne d'email pour, +Email Campaign For ,Campagne d'email pour, Lead is an Organization,Le prospect est une organisation, CRM-LEAD-.YYYY.-,CRM-LEAD-.YYYY.-, Person Name,Nom de la Personne, @@ -5668,7 +5668,7 @@ Examiner Name,Nom de l'Examinateur, Supervisor,Superviseur, Supervisor Name,Nom du Superviseur, Evaluate,Évaluer, -Maximum Assessment Score,Score d'évaluation maximale, +Maximum Assessment Score,Score d'évaluation maximale, Assessment Plan Criteria,Critères du Plan d'Évaluation, Maximum Score,Score Maximum, Total Score,Score Total, @@ -5684,10 +5684,10 @@ Course Name,Nom du Cours, Topics,Les sujets, Hero Image,Image de héros, Default Grading Scale,Échelle de Notation par Défault, -Education Manager,Gestionnaire de l'éducation, +Education Manager,Gestionnaire de l'éducation, Course Activity,Activité de cours, Course Enrollment,Inscription au cours, -Activity Date,Date d'activité, +Activity Date,Date d'activité, Course Assessment Criteria,Critères d'Évaluation du Cours, Weightage,Poids, Course Content,Le contenu des cours, @@ -5703,7 +5703,7 @@ Course End Date,Date de Fin du Cours, Course Topic,Sujet du cours, Topic,Sujet, Topic Name,Nom du Sujet, -Education Settings,Paramètres d'éducation, +Education Settings,Paramètres d'éducation, Current Academic Year,Année Académique Actuelle, Current Academic Term,Terme Académique Actuel, Attendance Freeze Date,Date du Gel des Présences, @@ -5712,7 +5712,7 @@ Validate Batch for Students in Student Group,Valider le Lot pour les Étudiants Validate Enrolled Course for Students in Student Group,Valider le Cours Inscrit pour les Étudiants en Groupe Étudiant, "For Course based Student Group, the Course will be validated for every Student from the enrolled Courses in Program Enrollment.","Pour un groupe étudiant basé sur un cours, le cours sera validé pour chaque élève inscrit aux cours du programme.", Make Academic Term Mandatory,Faire un terme académique obligatoire, -"If enabled, field Academic Term will be Mandatory in Program Enrollment Tool.","Si cette option est activée, le champ Période académique sera obligatoire dans l'outil d'inscription au programme.", +"If enabled, field Academic Term will be Mandatory in Program Enrollment Tool.","Si cette option est activée, le champ Période académique sera obligatoire dans l'outil d'inscription au programme.", Instructor Records to be created by,Les Enregistrements de l'Instructeur seront créés par, Employee Number,Numéro d'Employé, LMS Settings,Paramètres LMS, @@ -5760,7 +5760,7 @@ Guardian Interest,Part du Tuteur, Interest,Intérêt, Guardian Student,Tuteur de l'Étudiant, EDU-INS-.YYYY.-,EDU-INS-YYYY.-, -Instructor Log,Journal de l'instructeur, +Instructor Log,Journal de l'instructeur, Other details,Autres Détails, Option,Option, Is Correct,Est correct, @@ -5768,15 +5768,15 @@ Program Name,Nom du Programme, Program Abbreviation,Abréviation du Programme, Courses,Cours, Is Published,Est publié, -Allow Self Enroll,Autoriser l'auto-inscription, +Allow Self Enroll,Autoriser l'auto-inscription, Is Featured,Est en vedette, -Intro Video,Vidéo d'introduction, +Intro Video,Vidéo d'introduction, Program Course,Cours du Programme, School House,Maison de l'École, Boarding Student,Enregistrement Étudiant, Check this if the Student is residing at the Institute's Hostel.,Vérifiez si l'Étudiant réside à la Résidence de l'Institut., Walking,En Marchant, -Institute's Bus,Bus de l'Institut, +Institute's Bus,Bus de l'Institut, Public Transport,Transports Publics, Self-Driving Vehicle,Véhicule Autonome, Pick/Drop by Guardian,Déposé/Récupéré par le Tuteur, @@ -5787,7 +5787,7 @@ Program Enrollment Tool,Outil d’Inscription au Programme, Get Students From,Obtenir les Étudiants De, Student Applicant,Candidature Étudiante, Get Students,Obtenir les Étudiants, -Enrollment Details,Détails d'inscription, +Enrollment Details,Détails d'inscription, New Program,Nouveau Programme, New Student Batch,Nouveau groupe d'étudiants, Enroll Students,Inscrire des Étudiants, @@ -5846,7 +5846,7 @@ Admission Start Date,Date de Début de l'Admission, Admission End Date,Date de Fin de l'Admission, Publish on website,Publier sur le site web, Eligibility and Details,Admissibilité et Détails, -Student Admission Program,Programme d'admission des étudiants, +Student Admission Program,Programme d'admission des étudiants, Minimum Age,Âge Minimum, Maximum Age,Âge Maximum, Application Fee,Frais de Dossier, @@ -5883,14 +5883,14 @@ Will show the student as Present in Student Monthly Attendance Report,Affichera Student Log,Journal des Étudiants, Academic,Académique, Achievement,Réalisation, -Student Report Generation Tool,Outil de génération de rapports d'étudiants, -Include All Assessment Group,Inclure tout le groupe d'évaluation, +Student Report Generation Tool,Outil de génération de rapports d'étudiants, +Include All Assessment Group,Inclure tout le groupe d'évaluation, Show Marks,Afficher les notes, Add letterhead,Ajouter un en-tête, Print Section,Section d'impression, Total Parents Teacher Meeting,Total des réunions parents/professeur, Attended by Parents,Les parents ont participé, -Assessment Terms,Conditions d'évaluation, +Assessment Terms,Conditions d'évaluation, Student Sibling,Frère et Sœur de l'Étudiant, Studying in Same Institute,Étudier au même Institut, Student Siblings,Frères et Sœurs de l'Étudiants, @@ -5898,10 +5898,10 @@ Topic Content,Contenu du sujet, Amazon MWS Settings,Paramètres Amazon MWS, ERPNext Integrations,Intégrations ERPNext, Enable Amazon,Activer Amazon, -MWS Credentials,Informations d'identification MWS, +MWS Credentials,Informations d'identification MWS, Seller ID,ID du vendeur, -AWS Access Key ID,ID de clé d'accès AWS, -MWS Auth Token,Jeton d'authentification MWS, +AWS Access Key ID,ID de clé d'accès AWS, +MWS Auth Token,Jeton d'authentification MWS, Market Place ID,Identifiant de la place du marché, AU,AU, BR,BR, @@ -5919,12 +5919,12 @@ Market Place Account Group,Groupe de comptes de marché, After Date,Après la date, Amazon will synch data updated after this date,Amazon synchronisera les données mises à jour après cette date, Get financial breakup of Taxes and charges data by Amazon ,Obtenez la répartition financière des taxes et des données de facturation par Amazon, -Click this button to pull your Sales Order data from Amazon MWS.,Cliquez sur ce bouton pour extraire vos données de commande client d'Amazon MWS., +Click this button to pull your Sales Order data from Amazon MWS.,Cliquez sur ce bouton pour extraire vos données de commande client d'Amazon MWS., Check this to enable a scheduled Daily synchronization routine via scheduler,Cochez cette case pour activer une routine de synchronisation quotidienne programmée via le planificateur, Max Retry Limit,Max Retry Limit, Exotel Settings,Paramètres Exotel, Account SID,Compte SID, -API Token,Jeton d'API, +API Token,Jeton d'API, GoCardless Mandate,Mandat GoCardless, Mandate,Mandat, GoCardless Customer,Client GoCardless, @@ -5939,15 +5939,15 @@ Plaid Environment,Environnement écossais, sandbox,bac à sable, development,développement, QuickBooks Migrator,QuickBooks Migrator, -Application Settings,Paramètres de l'application, +Application Settings,Paramètres de l'application, Token Endpoint,Point de terminaison de jeton, Scope,Portée, -Authorization Settings,Paramètres d'autorisation, +Authorization Settings,Paramètres d'autorisation, Authorization Endpoint,Autorisation Endpoint, -Authorization URL,URL d'autorisation, +Authorization URL,URL d'autorisation, Quickbooks Company ID,ID Quickbooks de la société, Company Settings,des paramètres de l'entreprise, -Default Shipping Account,Compte d'expédition par défaut, +Default Shipping Account,Compte d'expédition par défaut, Default Warehouse,Entrepôt par Défaut, Default Cost Center,Centre de Coûts par Défaut, Undeposited Funds Account,Compte de fonds non déposés, @@ -5956,7 +5956,7 @@ Request Data,Données de la requête, Shopify Settings,Paramètres de Shopify, status html,Statut, Enable Shopify,Activer Shopify, -App Type,Type d'application, +App Type,Type d'application, Last Sync Datetime,Dernière date de synchronisation, Shop URL,URL de la boutique, eg: frappe.myshopify.com,par exemple: frappe.myshopify.com, @@ -6004,15 +6004,15 @@ Secret,Secret, API consumer key,Clé de consommateur API, API consumer secret,Secret de consommateur API, Tax Account,Compte de taxes, -Freight and Forwarding Account,Compte de fret et d'expédition, +Freight and Forwarding Account,Compte de fret et d'expédition, Creation User,Création utilisateur, "The user that will be used to create Customers, Items and Sales Orders. This user should have the relevant permissions.","Utilisateur qui sera utilisé pour créer des clients, des articles et des commandes clients. Cet utilisateur doit avoir les autorisations appropriées.", -"This warehouse will be used to create Sales Orders. The fallback warehouse is ""Stores"".",Cet entrepôt sera utilisé pour créer des commandes client. L'entrepôt de secours est "Stores"., +"This warehouse will be used to create Sales Orders. The fallback warehouse is ""Stores"".",Cet entrepôt sera utilisé pour créer des commandes client. L'entrepôt de secours est "Stores"., "The fallback series is ""SO-WOO-"".",La série de repli est "SO-WOO-"., This company will be used to create Sales Orders.,Cette société sera utilisée pour créer des commandes client., Delivery After (Days),Livraison après (jours), -This is the default offset (days) for the Delivery Date in Sales Orders. The fallback offset is 7 days from the order placement date.,Il s'agit du décalage par défaut (jours) pour la date de livraison dans les commandes client. La compensation de repli est de 7 jours à compter de la date de passation de la commande., -"This is the default UOM used for items and Sales orders. The fallback UOM is ""Nos"".",Il s'agit de l'UOM par défaut utilisée pour les articles et les commandes clients. La MOU de repli est "Nos"., +This is the default offset (days) for the Delivery Date in Sales Orders. The fallback offset is 7 days from the order placement date.,Il s'agit du décalage par défaut (jours) pour la date de livraison dans les commandes client. La compensation de repli est de 7 jours à compter de la date de passation de la commande., +"This is the default UOM used for items and Sales orders. The fallback UOM is ""Nos"".",Il s'agit de l'UOM par défaut utilisée pour les articles et les commandes clients. La MOU de repli est "Nos"., Endpoints,Points de terminaison, Endpoint,Point de terminaison, Antibiotic Name,Nom de l'Antibiotique, @@ -6056,14 +6056,14 @@ Charges,Charges, Default Currency,Devise par Défaut, Healthcare Schedule Time Slot,Horaire horaire, Parent Service Unit,Service parent, -Service Unit Type,Type d'unité de service, +Service Unit Type,Type d'unité de service, Allow Appointments,Autoriser les rendez-vous, Allow Overlap,Autoriser le chevauchement, Inpatient Occupancy,Occupation des patients hospitalisés, -Occupancy Status,Statut d'occupation, +Occupancy Status,Statut d'occupation, Vacant,Vacant, Occupied,Occupé, -Item Details,Détails d'article, +Item Details,Détails d'article, UOM Conversion in Hours,Conversion UOM en heures, Rate / UOM,Taux / UM, Change in Item,Modification dans l'Article, @@ -6089,7 +6089,7 @@ Reminder Message,Message de Rappel, Remind Before,Rappeler Avant, Laboratory Settings,Paramètres de laboratoire, Employee name and designation in print,Nom et désignation de l'employé sur l'imprimé, -Custom Signature in Print,Signature personnalisée dans l'impression, +Custom Signature in Print,Signature personnalisée dans l'impression, Laboratory SMS Alerts,Alertes SMS de laboratoire, Check In,Arrivée, Check Out,Départ, @@ -6146,7 +6146,7 @@ Require Result Value,Nécessite la Valeur du Résultat, Normal Test Template,Modèle de Test Normal, Patient Demographics,Démographie du Patient, HLC-PAT-.YYYY.-,HLC-PAT-. AAAA.-, -Inpatient Status,Statut d'hospitalisation, +Inpatient Status,Statut d'hospitalisation, Personal and Social History,Antécédents Personnels et Sociaux, Marital Status,État Civil, Married,Marié, @@ -6164,7 +6164,7 @@ Other Risk Factors,Autres facteurs de risque, Patient Details,Détails du patient, Additional information regarding the patient,Informations complémentaires concernant le patient, Patient Age,Âge du patient, -More Info,Plus d'infos, +More Info,Plus d'infos, Referring Practitioner,Praticien référant, Reminded,Rappelé, Parameters,Paramètres, @@ -6181,7 +6181,7 @@ Spouse,Époux, Family,Famille, Schedule Name,Nom du calendrier, Time Slots,Créneaux Horaires, -Practitioner Service Unit Schedule,Horaire de l'unité de service du praticien, +Practitioner Service Unit Schedule,Horaire de l'unité de service du praticien, Procedure Name,Nom de la procédure, Appointment Booked,Rendez-vous pris, Procedure Created,Procédure créée, @@ -6217,25 +6217,25 @@ One Sided,Une face, Blood Pressure (systolic),Pression Artérielle (Systolique), Blood Pressure (diastolic),Pression Artérielle (Diastolique), Blood Pressure,Pression Artérielle, -"Normal resting blood pressure in an adult is approximately 120 mmHg systolic, and 80 mmHg diastolic, abbreviated ""120/80 mmHg""","La tension artérielle normale chez un adulte est d'environ 120 mmHg systolique et 80 mmHg diastolique, abrégé "120/80 mmHg"", +"Normal resting blood pressure in an adult is approximately 120 mmHg systolic, and 80 mmHg diastolic, abbreviated ""120/80 mmHg""","La tension artérielle normale chez un adulte est d'environ 120 mmHg systolique et 80 mmHg diastolique, abrégé "120/80 mmHg"", Nutrition Values,Valeurs Nutritionnelles, Height (In Meter),Hauteur (en Mètres), Weight (In Kilogram),Poids (En Kilogramme), BMI,IMC, -Hotel Room,Chambre d'hôtel, -Hotel Room Type,Type de chambre d'hôtel, +Hotel Room,Chambre d'hôtel, +Hotel Room Type,Type de chambre d'hôtel, Capacity,Capacité, Extra Bed Capacity,Capacité de lits supplémentaire, -Hotel Manager,Directeur de l'hôtel, +Hotel Manager,Directeur de l'hôtel, Hotel Room Amenity,Équipement de la chambre d'hôtel, Billable,Facturable, Hotel Room Package,Forfait de la chambre d'hôtel, Amenities,Équipements, -Hotel Room Pricing,Prix de la chambre d'hôtel, +Hotel Room Pricing,Prix de la chambre d'hôtel, Hotel Room Pricing Item,Article de prix de la chambre d'hôtel, Hotel Room Pricing Package,Forfait de prix de la chambre d'hôtel, Hotel Room Reservation,Réservation de la chambre d'hôtel, -Guest Name,Nom de l'invité, +Guest Name,Nom de l'invité, Late Checkin,Arrivée tardive, Booked,Réservé, Hotel Reservation User,Utilisateur chargé des réservations d'hôtel, @@ -6264,7 +6264,7 @@ Closing Notes,Notes de clôture, Appointment Letter content,Contenu de la lettre de nomination, Appraisal,Estimation, HR-APR-.YY.-.MM.,HR-APR-.YY.-.MM., -Appraisal Template,Modèle d'évaluation, +Appraisal Template,Modèle d'évaluation, For Employee Name,Nom de l'Employé, Goals,Objectifs, Calculate Total Score,Calculer le Résultat Total, @@ -6322,7 +6322,7 @@ Applicable for external driver,Applicable pour pilote externe, Cellphone Number,Numéro de téléphone portable, License Details,Détails de la licence, License Number,Numéro de licence, -Issuing Date,Date d'émission, +Issuing Date,Date d'émission, Driving License Categories,Catégories de permis de conduire, Driving License Category,Catégorie de permis de conduire, Fleet Manager,Gestionnaire de Flotte, @@ -6330,7 +6330,7 @@ Driver licence class,Classe de permis de conduire, HR-EMP-,HR-EMP-, Employment Type,Type d'Emploi, Emergency Contact,Contact en cas d'Urgence, -Emergency Contact Name,Nom à contacter en cas d'urgence, +Emergency Contact Name,Nom à contacter en cas d'urgence, Emergency Phone,Téléphone d'Urgence, ERPNext User,Utilisateur ERPNext, "System User (login) ID. If set, it will become default for all HR forms.","L'ID (de connexion) de l'Utilisateur Système. S'il est défini, il deviendra la valeur par défaut pour tous les formulaires des RH.", @@ -6346,7 +6346,7 @@ Department and Grade,Département et échelon, Reports to,Rapports À, Attendance and Leave Details,Détails de présence et de congés, Leave Policy,Politique de congé, -Attendance Device ID (Biometric/RF tag ID),Périphérique d'assistance (identifiant d'étiquette biométrique / RF), +Attendance Device ID (Biometric/RF tag ID),Périphérique d'assistance (identifiant d'étiquette biométrique / RF), Applicable Holiday List,Liste de Vacances Valable, Default Shift,Décalage par défaut, Salary Details,Détails du salaire, @@ -6397,7 +6397,7 @@ HR-EAD-.YYYY.-,HR-EAD-.YYYY.-, Due Advance Amount,Montant de l'avance dû, Returned Amount,Montant retourné, Claimed,Réclamé, -Advance Account,Compte d'avances, +Advance Account,Compte d'avances, Employee Attendance Tool,Outil de Gestion des Présences des Employés, Unmarked Attendance,Participation Non Marquée, Employees HTML,Employés HTML, @@ -6409,7 +6409,7 @@ Remaining Benefits (Yearly),Prestations sociales restantes (par année), Payroll Period,Période de paie, Benefits Applied,Prestations demandées, Dispensed Amount (Pro-rated),Montant distribué (au prorata), -Employee Benefit Application Detail,Détail de la demande d'avantages sociaux, +Employee Benefit Application Detail,Détail de la demande d'avantages sociaux, Earning Component,Composante de revenu, Pay Against Benefit Claim,Payer la demande de prestations, Max Benefit Amount,Montant maximal des prestations, @@ -6420,15 +6420,15 @@ Claim Benefit For,Demande de prestations pour, Max Amount Eligible,Montant maximum admissible, Expense Proof,Preuves de dépenses, Employee Boarding Activity,Activité d'intégration des nouveaux employés, -Activity Name,Nom de l'activité, +Activity Name,Nom de l'activité, Task Weight,Poids de la Tâche, -Required for Employee Creation,Obligatoire pour la création d'un employé, +Required for Employee Creation,Obligatoire pour la création d'un employé, Applicable in the case of Employee Onboarding,Applicable dans le cas de l'accueil des nouveaux employés, Employee Checkin,Enregistrement des employés, Log Type,Type de journal, OUT,EN DEHORS, Location / Device ID,Emplacement / ID de périphérique, -Skip Auto Attendance,Ignorer l'assistance automatique, +Skip Auto Attendance,Ignorer l'assistance automatique, Shift Start,Début de quart, Shift End,Fin de quart, Shift Actual Start,Décalage début effectif, @@ -6445,7 +6445,7 @@ Employee External Work History,Antécédents Professionnels de l'Employé, Total Experience,Expérience Totale, Default Leave Policy,Politique de congés par défaut, Default Salary Structure,Structure salariale par défaut, -Employee Group Table,Table de groupe d'employés, +Employee Group Table,Table de groupe d'employés, ERPNext User ID,ID utilisateur ERPNext, Employee Health Insurance,Assurance maladie des employés, Health Insurance Name,Nom de l'assurance santé, @@ -6465,34 +6465,34 @@ Employee Property History,Historique des propriétés des champs de la fiche emp Employee Separation,Départ des employés, Employee Separation Template,Modèle de départ des employés, Exit Interview Summary,Récapitulatif de l'entretien de sortie, -Employee Skill,Compétence de l'employé, +Employee Skill,Compétence de l'employé, Proficiency,Compétence, -Evaluation Date,Date d'évaluation, +Evaluation Date,Date d'évaluation, Employee Skill Map,Carte de compétences des employés, Employee Skills,Compétences des employés, Trainings,Des formations, Employee Tax Exemption Category,Catégorie d'exemption de taxe des employés, -Max Exemption Amount,Montant maximum d'exemption, +Max Exemption Amount,Montant maximum d'exemption, Employee Tax Exemption Declaration,Déclaration d'exemption de taxe, Declarations,Déclarations, Total Declared Amount,Montant total déclaré, Total Exemption Amount,Montant total de l'exonération, Employee Tax Exemption Declaration Category,Catégorie de déclaration d'exemption de taxe, -Exemption Sub Category,Sous-catégorie d'exemption, -Exemption Category,Catégorie d'exemption, +Exemption Sub Category,Sous-catégorie d'exemption, +Exemption Category,Catégorie d'exemption, Maximum Exempted Amount,Montant maximum exonéré, Declared Amount,Montant Déclaré, Employee Tax Exemption Proof Submission,Soumission d'une preuve d'exemption de taxe, Submission Date,Date de soumission, -Tax Exemption Proofs,Preuves d'exonération fiscale, +Tax Exemption Proofs,Preuves d'exonération fiscale, Total Actual Amount,Montant total total, Employee Tax Exemption Proof Submission Detail,Détails de la soumission de preuve d'exemption de taxe, -Maximum Exemption Amount,Montant maximum d'exemption, +Maximum Exemption Amount,Montant maximum d'exemption, Type of Proof,Type de preuve, Actual Amount,Montant actuel, Employee Tax Exemption Sub Category,Sous-catégorie d'exemption de taxe, Tax Exemption Category,Catégorie d'exonération fiscale, -Employee Training,Entrainement d'employé, +Employee Training,Entrainement d'employé, Training Date,Date de formation, Employee Transfer,Transfert des employés, Transfer Date,Date de transfert, @@ -6505,7 +6505,7 @@ Employee Transfer Property,Propriété des champs pour le transfert des employé HR-EXP-.YYYY.-,HR-EXP-. AAAA.-, Expense Taxes and Charges,Frais et taxes, Total Sanctioned Amount,Montant Total Validé, -Total Advance Amount,Montant total de l'avance, +Total Advance Amount,Montant total de l'avance, Total Claimed Amount,Montant Total Réclamé, Total Amount Reimbursed,Montant Total Remboursé, Vehicle Log,Journal du Véhicule, @@ -6540,7 +6540,7 @@ Include holidays in Total no. of Working Days,Inclure les vacances dans le nombr Email Salary Slip to Employee,Envoyer la Fiche de Paie à l'Employé par Mail, Emails salary slip to employee based on preferred email selected in Employee,Envoi des fiches de paie à l'employé par Email en fonction de l'email sélectionné dans la fiche Employé, Encrypt Salary Slips in Emails,Crypter les bulletins de salaire dans les courriels, -"The salary slip emailed to the employee will be password protected, the password will be generated based on the password policy.",La fiche de salaire envoyée à l'employé par courrier électronique sera protégée par un mot de passe. Le mot de passe sera généré en fonction de la politique de mot de passe., +"The salary slip emailed to the employee will be password protected, the password will be generated based on the password policy.",La fiche de salaire envoyée à l'employé par courrier électronique sera protégée par un mot de passe. Le mot de passe sera généré en fonction de la politique de mot de passe., Password Policy,Politique de mot de passe, Example: SAL-{first_name}-{date_of_birth.year}
This will generate a password like SAL-Jane-1972,Exemple: SAL- {prenom} - {date_naissance.année}
Cela générera un mot de passe comme SAL-Jane-1972, Leave Settings,Paramètres des Congés, @@ -6551,10 +6551,10 @@ Leave Approver Mandatory In Leave Application,Approbateur de congés obligatoire Show Leaves Of All Department Members In Calendar,Afficher les congés de tous les membres du département dans le calendrier, Auto Leave Encashment,Auto Leave Encashment, Restrict Backdated Leave Application,Restreindre la demande de congé antidaté, -Hiring Settings,Paramètres d'embauche, -Check Vacancies On Job Offer Creation,Vérifier les offres d'emploi lors de la création d'une offre d'emploi, -Identification Document Type,Type de document d'identification, -Standard Tax Exemption Amount,Montant de l'exemption fiscale standard, +Hiring Settings,Paramètres d'embauche, +Check Vacancies On Job Offer Creation,Vérifier les offres d'emploi lors de la création d'une offre d'emploi, +Identification Document Type,Type de document d'identification, +Standard Tax Exemption Amount,Montant de l'exemption fiscale standard, Taxable Salary Slabs,Paliers de salaire imposables, Applicant for a Job,Candidat à un Emploi, Accepted,Accepté, @@ -6606,11 +6606,11 @@ Leave Block List Date,Date de la Liste de Blocage des Congés, Block Date,Bloquer la Date, Leave Control Panel,Quitter le Panneau de Configuration, Select Employees,Sélectionner les Employés, -Employment Type (optional),Type d'emploi (facultatif), +Employment Type (optional),Type d'emploi (facultatif), Branch (optional),Branche (optionnel), Department (optional),Département (optionnel), Designation (optional),Désignation (facultatif), -Employee Grade (optional),Grade d'employé (facultatif), +Employee Grade (optional),Grade d'employé (facultatif), Employee (optional),Employé (facultatif), Allocate Leaves,Allouer des feuilles, Carry Forward,Reporter, @@ -6642,8 +6642,8 @@ Maximum Carry Forwarded Leaves,Nombre maximal de congés reportés, Expire Carry Forwarded Leaves (Days),Expirer les congés reportés (jours), Calculated in days,Calculé en jours, Encashment,Encaissement, -Allow Encashment,Autoriser l'encaissement, -Encashment Threshold Days,Jours de seuil d'encaissement, +Allow Encashment,Autoriser l'encaissement, +Encashment Threshold Days,Jours de seuil d'encaissement, Earned Leave,Congés acquis, Is Earned Leave,Est un congé acquis, Earned Leave Frequency,Fréquence d'acquisition des congés, @@ -6653,7 +6653,7 @@ Payroll Frequency,Fréquence de la Paie, Fortnightly,Bimensuel, Bimonthly,Bimensuel, Employees,Employés, -Number Of Employees,Nombre d'employés, +Number Of Employees,Nombre d'employés, Employee Details,Détails des employés, Validate Attendance,Valider la présence, Salary Slip Based on Timesheet,Fiche de Paie basée sur la Feuille de Temps, @@ -6673,9 +6673,9 @@ Abbr,Abré, Depends on Payment Days,Dépend des jours de paiement, Is Tax Applicable,Est taxable, Variable Based On Taxable Salary,Variable basée sur le salaire imposable, -Round to the Nearest Integer,Arrondir à l'entier le plus proche, +Round to the Nearest Integer,Arrondir à l'entier le plus proche, Statistical Component,Composante Statistique, -"If selected, the value specified or calculated in this component will not contribute to the earnings or deductions. However, it's value can be referenced by other components that can be added or deducted. ","Si cette option est sélectionnée, la valeur spécifiée ou calculée dans ce composant ne contribuera pas aux gains ou aux déductions. Cependant, sa valeur peut être référencée par d'autres composants qui peuvent être ajoutés ou déduits.", +"If selected, the value specified or calculated in this component will not contribute to the earnings or deductions. However, it's value can be referenced by other components that can be added or deducted. ","Si cette option est sélectionnée, la valeur spécifiée ou calculée dans ce composant ne contribuera pas aux gains ou aux déductions. Cependant, sa valeur peut être référencée par d'autres composants qui peuvent être ajoutés ou déduits.", Flexible Benefits,Avantages sociaux variables, Is Flexible Benefit,Est un avantage flexible, Max Benefit Amount (Yearly),Montant maximum des prestations sociales (annuel), @@ -6703,7 +6703,7 @@ Earnings,Bénéfices, Deductions,Déductions, Employee Loan,Prêt Employé, Total Principal Amount,Montant total du capital, -Total Interest Amount,Montant total de l'intérêt, +Total Interest Amount,Montant total de l'intérêt, Total Loan Repayment,Total de Remboursement du Prêt, net pay info,Info de salaire net, Gross Pay - Total Deduction - Loan Repayment,Salaire Brut - Déductions Totales - Remboursement de Prêt, @@ -6719,17 +6719,17 @@ Shift Assignment,Affectation de quart, Shift Type,Type de quart, Shift Request,Demande de quart, Enable Auto Attendance,Activer la présence automatique, -Mark attendance based on 'Employee Checkin' for Employees assigned to this shift.,Marquez la présence sur la base de 'Enregistrement des employés' pour les employés affectés à ce poste., +Mark attendance based on 'Employee Checkin' for Employees assigned to this shift.,Marquez la présence sur la base de 'Enregistrement des employés' pour les employés affectés à ce poste., Auto Attendance Settings,Paramètres de présence automatique, Determine Check-in and Check-out,Déterminer les entrées et les sorties, Alternating entries as IN and OUT during the same shift,Alterner les entrées comme IN et OUT pendant le même quart, -Strictly based on Log Type in Employee Checkin,Strictement basé sur le type de journal dans l'enregistrement des employés, +Strictly based on Log Type in Employee Checkin,Strictement basé sur le type de journal dans l'enregistrement des employés, Working Hours Calculation Based On,Calcul des heures de travail basé sur, First Check-in and Last Check-out,Premier enregistrement et dernier départ, Every Valid Check-in and Check-out,Chaque enregistrement valide et check-out, -Begin check-in before shift start time (in minutes),Commencez l'enregistrement avant l'heure de début du poste (en minutes), -The time before the shift start time during which Employee Check-in is considered for attendance.,Heure avant l'heure de début du quart pendant laquelle l'enregistrement des employés est pris en compte pour la présence., -Allow check-out after shift end time (in minutes),Autoriser le départ après l'heure de fin du quart (en minutes), +Begin check-in before shift start time (in minutes),Commencez l'enregistrement avant l'heure de début du poste (en minutes), +The time before the shift start time during which Employee Check-in is considered for attendance.,Heure avant l'heure de début du quart pendant laquelle l'enregistrement des employés est pris en compte pour la présence., +Allow check-out after shift end time (in minutes),Autoriser le départ après l'heure de fin du quart (en minutes), Time after the end of shift during which check-out is considered for attendance.,Heure après la fin du quart de travail au cours de laquelle la prise en charge est prise en compte., Working Hours Threshold for Half Day,Seuil des heures de travail pour une demi-journée, Working hours below which Half Day is marked. (Zero to disable),Heures de travail en dessous desquelles la demi-journée est marquée. (Zéro à désactiver), @@ -6738,14 +6738,14 @@ Working hours below which Absent is marked. (Zero to disable),Heures de travail Process Attendance After,Processus de présence après, Attendance will be marked automatically only after this date.,La participation sera automatiquement marquée après cette date., Last Sync of Checkin,Dernière synchronisation de Checkin, -Last Known Successful Sync of Employee Checkin. Reset this only if you are sure that all Logs are synced from all the locations. Please don't modify this if you are unsure.,Dernière synchronisation réussie de l'enregistrement des employés. Réinitialisez cette opération uniquement si vous êtes certain que tous les journaux sont synchronisés à partir de tous les emplacements. S'il vous plaît ne modifiez pas cela si vous n'êtes pas sûr., -Grace Period Settings For Auto Attendance,Paramètres de période de grâce pour l'assistance automatique, -Enable Entry Grace Period,Activer la période de grâce d'entrée, +Last Known Successful Sync of Employee Checkin. Reset this only if you are sure that all Logs are synced from all the locations. Please don't modify this if you are unsure.,Dernière synchronisation réussie de l'enregistrement des employés. Réinitialisez cette opération uniquement si vous êtes certain que tous les journaux sont synchronisés à partir de tous les emplacements. S'il vous plaît ne modifiez pas cela si vous n'êtes pas sûr., +Grace Period Settings For Auto Attendance,Paramètres de période de grâce pour l'assistance automatique, +Enable Entry Grace Period,Activer la période de grâce d'entrée, Late Entry Grace Period,Délai de grâce pour entrée tardive, -The time after the shift start time when check-in is considered as late (in minutes).,L'heure après l'heure de début du quart de travail où l'enregistrement est considéré comme tardif (en minutes)., +The time after the shift start time when check-in is considered as late (in minutes).,L'heure après l'heure de début du quart de travail où l'enregistrement est considéré comme tardif (en minutes)., Enable Exit Grace Period,Activer la période de grâce de sortie, Early Exit Grace Period,Période de grâce de sortie anticipée, -The time before the shift end time when check-out is considered as early (in minutes).,L'heure avant l'heure de fin du quart de travail au moment du départ est considérée comme précoce (en minutes)., +The time before the shift end time when check-out is considered as early (in minutes).,L'heure avant l'heure de fin du quart de travail au moment du départ est considérée comme précoce (en minutes)., Skill Name,Nom de la compétence, Staffing Plan Details,Détails du plan de dotation, Staffing Plan Detail,Détail du plan de dotation, @@ -6797,8 +6797,8 @@ Travel Advance Required,Avance de déplacement requise, Departure Datetime,Date/Heure de départ, Arrival Datetime,Date/Heure d'arrivée, Lodging Required,Hébergement requis, -Preferred Area for Lodging,Zone préférée pour l'hébergement, -Check-in Date,Date d'arrivée, +Preferred Area for Lodging,Zone préférée pour l'hébergement, +Check-in Date,Date d'arrivée, Check-out Date,Date de départ, Travel Request,Demande de déplacement, Travel Type,Type de déplacement, @@ -6808,15 +6808,15 @@ Travel Funding,Financement du déplacement, Require Full Funding,Nécessite un financement complet, Fully Sponsored,Entièrement commandité, "Partially Sponsored, Require Partial Funding","Partiellement sponsorisé, nécessite un financement partiel", -Copy of Invitation/Announcement,Copie de l'invitation / annonce, +Copy of Invitation/Announcement,Copie de l'invitation / annonce, "Details of Sponsor (Name, Location)","Détails du commanditaire (nom, lieu)", Identification Document Number,Numéro du document d'identification, Any other details,Tout autre détail, Costing Details,Détails des coûts, Costing,Coût, -Event Details,Détails de l'évènement, -Name of Organizer,Nom de l'organisateur, -Address of Organizer,Adresse de l'organisateur, +Event Details,Détails de l'évènement, +Name of Organizer,Nom de l'organisateur, +Address of Organizer,Adresse de l'organisateur, Travel Request Costing,Coût de la demande de déplacement, Expense Type,Type de dépense, Sponsored Amount,Montant sponsorisé, @@ -6865,14 +6865,14 @@ Inspection,Inspection, Mileage,Kilométrage, Hub Tracked Item,Article suivi sur le Hub, Hub Node,Noeud du Hub, -Image List,Liste d'images, +Image List,Liste d'images, Item Manager,Gestionnaire d'Article, Hub User,Utilisateur du hub, Hub Password,Mot de passe Hub, Hub Users,Utilisateurs du Hub, Marketplace Settings,Paramètres du marché, Disable Marketplace,Désactiver le marché, -Marketplace URL (to hide and update label),URL du marché (pour masquer et mettre à jour l'étiquette), +Marketplace URL (to hide and update label),URL du marché (pour masquer et mettre à jour l'étiquette), Registered,Inscrit, Sync in Progress,Synchronisation en cours, Hub Seller Name,Nom du vendeur, @@ -6946,8 +6946,8 @@ Unpledge Time,Désengager le temps, Unpledge Type,Type de désengagement, Loan Name,Nom du Prêt, Rate of Interest (%) Yearly,Taux d'Intérêt (%) Annuel, -Penalty Interest Rate (%) Per Day,Taux d'intérêt de pénalité (%) par jour, -Penalty Interest Rate is levied on the pending interest amount on a daily basis in case of delayed repayment ,Le taux d'intérêt de pénalité est prélevé quotidiennement sur le montant des intérêts en attente en cas de retard de remboursement, +Penalty Interest Rate (%) Per Day,Taux d'intérêt de pénalité (%) par jour, +Penalty Interest Rate is levied on the pending interest amount on a daily basis in case of delayed repayment ,Le taux d'intérêt de pénalité est prélevé quotidiennement sur le montant des intérêts en attente en cas de retard de remboursement, Grace Period in Days,Délai de grâce en jours, Pledge,Gage, Post Haircut Amount,Montant de la coupe de cheveux, @@ -6961,7 +6961,7 @@ Loan Repayment Entry,Entrée de remboursement de prêt, Sanctioned Loan Amount,Montant du prêt sanctionné, Sanctioned Amount Limit,Limite de montant sanctionnée, Unpledge,Désengager, -Against Pledge,Contre l'engagement, +Against Pledge,Contre l'engagement, Haircut,la Coupe de cheveux, MAT-MSH-.YYYY.-,MAT-MSH-YYYY.-, Generate Schedule,Créer un Échéancier, @@ -7002,7 +7002,7 @@ Transfer Material Against,Transférer du matériel contre, Routing,Routage, Materials,Matériels, Quality Inspection Required,Inspection de qualité requise, -Quality Inspection Template,Modèle d'inspection de la qualité, +Quality Inspection Template,Modèle d'inspection de la qualité, Scrap,Mettre au Rebut, Scrap Items,Mettre au Rebut des Articles, Operating Cost,Coût d'Exploitation, @@ -7012,7 +7012,7 @@ Operating Cost (Company Currency),Coût d'Exploitation (Devise Société), Raw Material Cost (Company Currency),Coût de la matière première (devise de la société), Scrap Material Cost(Company Currency),Coût de Mise au Rebut des Matériaux (Devise Société), Total Cost,Coût Total, -Total Cost (Company Currency),Coût total (devise de l'entreprise), +Total Cost (Company Currency),Coût total (devise de l'entreprise), Materials Required (Exploded),Matériel Requis (Éclaté), Exploded Items,Articles éclatés, Item Image (if not slideshow),Image de l'Article (si ce n'est diaporama), @@ -7023,9 +7023,9 @@ Show Operations,Afficher Opérations, Website Description,Description du Site Web, BOM Explosion Item,Article Eclaté LDM, Qty Consumed Per Unit,Qté Consommée Par Unité, -Include Item In Manufacturing,Inclure l'article dans la fabrication, +Include Item In Manufacturing,Inclure l'article dans la fabrication, BOM Item,Article LDM, -Item operation,Opération de l'article, +Item operation,Opération de l'article, Rate & Amount,Taux et Montant, Basic Rate (Company Currency),Taux de Base (Devise de la Société ), Scrap %,% de Rebut, @@ -7118,24 +7118,24 @@ Product Bundle Item,Article d'un Ensemble de Produits, Production Plan Material Request,Demande de Matériel du Plan de Production, Production Plan Sales Order,Commande Client du Plan de Production, Sales Order Date,Date de la Commande Client, -Routing Name,Nom d'acheminement, +Routing Name,Nom d'acheminement, MFG-WO-.YYYY.-,MFG-WO-.YYYY.-, Item To Manufacture,Article à produire, Material Transferred for Manufacturing,Matériel Transféré pour la Production, Manufactured Qty,Qté Produite, Use Multi-Level BOM,Utiliser LDM à Plusieurs Niveaux, Plan material for sub-assemblies,Plan de matériaux pour les sous-ensembles, -Skip Material Transfer to WIP Warehouse,Ignorer le transfert de matériel vers l'entrepôt WIP, +Skip Material Transfer to WIP Warehouse,Ignorer le transfert de matériel vers l'entrepôt WIP, Check if material transfer entry is not required,Vérifiez si une un transfert de matériel n'est pas requis, -Backflush Raw Materials From Work-in-Progress Warehouse,Rembourrage des matières premières dans l'entrepôt de travaux en cours, +Backflush Raw Materials From Work-in-Progress Warehouse,Rembourrage des matières premières dans l'entrepôt de travaux en cours, Update Consumed Material Cost In Project,Mettre à jour le coût des matières consommées dans le projet, Warehouses,Entrepôts, -This is a location where raw materials are available.,C'est un endroit où les matières premières sont disponibles., +This is a location where raw materials are available.,C'est un endroit où les matières premières sont disponibles., Work-in-Progress Warehouse,Entrepôt des Travaux en Cours, -This is a location where operations are executed.,Il s'agit d'un emplacement où les opérations sont exécutées., -This is a location where final product stored.,Il s'agit d'un emplacement où le produit final est stocké., +This is a location where operations are executed.,Il s'agit d'un emplacement où les opérations sont exécutées., +This is a location where final product stored.,Il s'agit d'un emplacement où le produit final est stocké., Scrap Warehouse,Entrepôt de Rebut, -This is a location where scraped materials are stored.,Il s'agit d'un emplacement où les matériaux raclés sont stockés., +This is a location where scraped materials are stored.,Il s'agit d'un emplacement où les matériaux raclés sont stockés., Required Items,Articles Requis, Actual Start Date,Date de Début Réelle, Planned End Date,Date de Fin Prévue, @@ -7205,13 +7205,13 @@ Grant Description,Description de la subvention, Requested Amount,Quantité exigée, Has any past Grant Record,A obtenu des bourses par le passé, Show on Website,Afficher sur le site Web, -Assessment Mark (Out of 10),Note d'évaluation (sur 10), -Assessment Manager,Gestionnaire d'évaluation, +Assessment Mark (Out of 10),Note d'évaluation (sur 10), +Assessment Manager,Gestionnaire d'évaluation, Email Notification Sent,Notification par e-mail envoyée, NPO-MEM-.YYYY.-,NPO-MEM-YYYY.-, -Membership Expiry Date,Date d'expiration de l'adhésion, +Membership Expiry Date,Date d'expiration de l'adhésion, Non Profit Member,Membre de l'association, -Membership Status,Statut d'adhésion, +Membership Status,Statut d'adhésion, Member Since,Membre depuis, Volunteer Name,Nom du bénévole, Volunteer Type,Type de bénévole, @@ -7227,12 +7227,12 @@ Volunteer Skills,Compétences bénévoles, Volunteer Skill,Compétence bénévole, Homepage,Page d'Accueil, Hero Section Based On,Section de héros basée sur, -Homepage Section,Section de la page d'accueil, +Homepage Section,Section de la page d'accueil, Hero Section,Section de héros, Tag Line,Ligne de Tag, Company Tagline for website homepage,Slogan de la Société pour la page d'accueil du site web, Company Description for website homepage,Description de la Société pour la page d'accueil du site web, -Homepage Slideshow,Diaporama de la page d'accueil, +Homepage Slideshow,Diaporama de la page d'accueil, "URL for ""All Products""","URL pour ""Tous les Produits""", Products to be shown on website homepage,Produits destinés à être affichés sur la page d’accueil du site web, Homepage Featured Product,Produit Présenté sur la Page d'Accueil, @@ -7249,12 +7249,12 @@ Subtitle,Sous-titre, Products Settings,Paramètres des Produits, Home Page is Products,La Page d'Accueil est Produits, "If checked, the Home page will be the default Item Group for the website","Si cochée, la page d'Accueil pour le site sera le Groupe d'Article par défaut", -Show Availability Status,Afficher l'état de la disponibilité, +Show Availability Status,Afficher l'état de la disponibilité, Product Page,Page produit, Products per Page,Produits par page, Enable Field Filters,Activer les filtres de champ, -Item Fields,Champs de l'article, -Enable Attribute Filters,Activer les filtres d'attributs, +Item Fields,Champs de l'article, +Enable Attribute Filters,Activer les filtres d'attributs, Attributes,Attributs, Hide Variants,Masquer les variantes, Website Attribute,Attribut de site Web, @@ -7391,7 +7391,7 @@ October,octobre, November,novembre, December,décembre, JSON Output,Sortie JSON, -Invoices with no Place Of Supply,Factures sans lieu d'approvisionnement, +Invoices with no Place Of Supply,Factures sans lieu d'approvisionnement, Import Supplier Invoice,Importer la facture fournisseur, Invoice Series,Série de factures, Upload XML Invoices,Télécharger des factures XML, @@ -7424,7 +7424,7 @@ SAL-CAM-.YYYY.-,SAL-CAM-YYYY.-, Campaign Schedules,Horaires de campagne, Buyer of Goods and Services.,Acheteur des Biens et Services., CUST-.YYYY.-,CUST-.YYYY.-, -Default Company Bank Account,Compte bancaire d'entreprise par défaut, +Default Company Bank Account,Compte bancaire d'entreprise par défaut, From Lead,Du Prospect, Account Manager,Gestionnaire de compte, Default Price List,Liste des Prix par Défaut, @@ -7466,7 +7466,7 @@ POS Closing Voucher Details,Détail du bon de clôture du PDV, Collected Amount,Montant collecté, Expected Amount,Montant prévu, POS Closing Voucher Invoices,Factures du bon de clôture du PDV, -Quantity of Items,Quantité d'articles, +Quantity of Items,Quantité d'articles, POS Closing Voucher Taxes,Taxes du bon de clotûre du PDV, "Aggregate group of **Items** into another **Item**. This is useful if you are bundling a certain **Items** into a package and you maintain stock of the packed **Items** and not the aggregate **Item**. \n\nThe package **Item** will have ""Is Stock Item"" as ""No"" and ""Is Sales Item"" as ""Yes"".\n\nFor Example: If you are selling Laptops and Backpacks separately and have a special price if the customer buys both, then the Laptop + Backpack will be a new Product Bundle Item.\n\nNote: BOM = Bill of Materials","Regroupement d' **Articles** dans un autre **Article**. Ceci est utile si vous regroupez certains **Articles** dans un lot et que vous maintenez l'inventaire des **Articles** du lot et non de l'**Article** composé. L'**Article** composé aura ""Article En Stock"" à ""Non"" et ""Article À Vendre"" à ""Oui"". Exemple : Si vous vendez des Ordinateurs Portables et Sacs à Dos séparément et qu'il y a un prix spécial si le client achète les deux, alors l'Ordinateur Portable + le Sac à Dos sera un nouveau Produit Groupé. Remarque: LDM = Liste\nDes Matériaux", Parent Item,Article Parent, @@ -7564,7 +7564,7 @@ Default Values,Valeurs Par Défaut, Default Holiday List,Liste de Vacances par Défaut, Standard Working Hours,Heures de travail standard, Default Selling Terms,Conditions de vente par défaut, -Default Buying Terms,Conditions d'achat par défaut, +Default Buying Terms,Conditions d'achat par défaut, Default warehouse for Sales Return,Magasin par défaut pour retour de vente, Create Chart Of Accounts Based On,Créer un Plan Comptable Basé Sur, Standard Template,Modèle Standard, @@ -7579,8 +7579,8 @@ Total Monthly Sales,Total des Ventes Mensuelles, Default Cash Account,Compte de Caisse par Défaut, Default Receivable Account,Compte Client par Défaut, Round Off Cost Center,Centre de Coûts d’Arrondi, -Discount Allowed Account,Compte d'escompte autorisé, -Discount Received Account,Compte d'escompte reçu, +Discount Allowed Account,Compte d'escompte autorisé, +Discount Received Account,Compte d'escompte reçu, Exchange Gain / Loss Account,Compte de Profits / Pertes sur Change, Unrealized Exchange Gain/Loss Account,Compte de gains / pertes de change non réalisés, Allow Account Creation Against Child Company,Autoriser la création de compte contre une entreprise enfant, @@ -7608,7 +7608,7 @@ Company Logo,Logo de la société, Date of Incorporation,Date de constitution, Date of Commencement,Date de démarrage, Phone No,N° de Téléphone, -Company Description,Description de l'entreprise, +Company Description,Description de l'entreprise, Registration Details,Informations Légales, Company registration numbers for your reference. Tax numbers etc.,"Numéro d'immatriculation de la Société pour votre référence. Numéros de taxes, etc.", Delete Company Transactions,Supprimer les Transactions de la Société, @@ -7639,15 +7639,15 @@ Bank Credit Balance,Solde bancaire, Receivables,Créances, Payables,Dettes, Sales Orders to Bill,Commandes de vente à facture, -Purchase Orders to Bill,Commandes d'achat à facturer, +Purchase Orders to Bill,Commandes d'achat à facturer, New Sales Orders,Nouvelles Commandes Client, New Purchase Orders,Nouveaux Bons de Commande, Sales Orders to Deliver,Commandes de vente à livrer, -Purchase Orders to Receive,Commandes d'achat à recevoir, -New Purchase Invoice,Nouvelle facture d'achat, +Purchase Orders to Receive,Commandes d'achat à recevoir, +New Purchase Invoice,Nouvelle facture d'achat, New Quotations,Nouveaux Devis, Open Quotations,Citations ouvertes, -Purchase Orders Items Overdue,Articles de commandes d'achat en retard, +Purchase Orders Items Overdue,Articles de commandes d'achat en retard, Add Quote,Ajouter une Citation, Global Defaults,Valeurs par Défaut Globales, Default Company,Société par Défaut, @@ -7662,7 +7662,7 @@ Item Classification,Classification de l'Article, General Settings,Paramètres Généraux, Item Group Name,Nom du Groupe d'Article, Parent Item Group,Groupe d’Articles Parent, -Item Group Defaults,Groupe d'articles par défaut, +Item Group Defaults,Groupe d'articles par défaut, Item Tax,Taxe sur l'Article, Check this if you want to show in website,Cochez cette case si vous souhaitez afficher sur le site, Show this slideshow at the top of the page,Afficher ce diaporama en haut de la page, @@ -7723,7 +7723,7 @@ Website Item Group,Groupe d'Articles du Site Web, Cross Listing of Item in multiple groups,Liste Croisée d'Articles dans plusieurs groupes, Default settings for Shopping Cart,Paramètres par défaut pour le Panier d'Achat, Enable Shopping Cart,Activer Panier, -Display Settings,Paramètres d'affichage, +Display Settings,Paramètres d'affichage, Show Public Attachments,Afficher les Pièces Jointes Publiques, Show Price,Afficher le prix, Show Stock Availability,Afficher la disponibilité du stock, @@ -7756,7 +7756,7 @@ Tariff Number,Tarif, Delivery To,Livraison à, MAT-DN-.YYYY.-,MAT-DN-.YYYY.-, Is Return,Est un Retour, -Issue Credit Note,Note de crédit d'émission, +Issue Credit Note,Note de crédit d'émission, Return Against Delivery Note,Retour contre Bon de Livraison, Customer's Purchase Order No,Numéro bon de commande du client, Billing Address Name,Nom de l'Adresse de Facturation, @@ -7783,8 +7783,8 @@ Available Batch Qty at From Warehouse,Qté de Lot Disponible Depuis l'Entrepôt, Available Qty at From Warehouse,Qté Disponible Depuis l'Entrepôt, Delivery Settings,Paramètres de livraison, Dispatch Settings,Paramètres de répartition, -Dispatch Notification Template,Modèle de notification d'expédition, -Dispatch Notification Attachment,Pièce jointe de notification d'expédition, +Dispatch Notification Template,Modèle de notification d'expédition, +Dispatch Notification Attachment,Pièce jointe de notification d'expédition, Leave blank to use the standard Delivery Note format,Laissez vide pour utiliser le format de bon de livraison standard, Send with Attachment,Envoyer avec pièce jointe, Delay between Delivery Stops,Délai entre les arrêts de livraison, @@ -7793,7 +7793,7 @@ Visited,Visité, Order Information,Informations sur la commande, Contact Information,Informations de contact, Email sent to,Email Envoyé À, -Dispatch Information,Informations d'expédition, +Dispatch Information,Informations d'expédition, Estimated Arrival,Arrivée estimée, MAT-DT-.YYYY.-,MAT-DT-.YYYY.-, Initial Email Notification Sent,Notification initiale par e-mail envoyée, @@ -7804,10 +7804,10 @@ Total Estimated Distance,Distance totale estimée, Distance UOM,Distance UOM, Departure Time,Heure de départ, Delivery Stops,Étapes de Livraison, -Calculate Estimated Arrival Times,Calculer les heures d'arrivée estimées, -Use Google Maps Direction API to calculate estimated arrival times,Utiliser l'API Google Maps Direction pour calculer les heures d'arrivée estimées, -Optimize Route,Optimiser l'itinéraire, -Use Google Maps Direction API to optimize route,Utiliser l'API Google Maps Direction pour optimiser l'itinéraire, +Calculate Estimated Arrival Times,Calculer les heures d'arrivée estimées, +Use Google Maps Direction API to calculate estimated arrival times,Utiliser l'API Google Maps Direction pour calculer les heures d'arrivée estimées, +Optimize Route,Optimiser l'itinéraire, +Use Google Maps Direction API to optimize route,Utiliser l'API Google Maps Direction pour optimiser l'itinéraire, In Transit,En transit, Fulfillment User,Livreur, "A Product or a Service that is bought, sold or kept in stock.","Un Produit ou un Service qui est acheté, vendu ou conservé en stock.", @@ -7817,7 +7817,7 @@ Is Item from Hub,Est un article sur le Hub, Default Unit of Measure,Unité de Mesure par Défaut, Maintain Stock,Maintenir Stock, Standard Selling Rate,Prix de Vente Standard, -Auto Create Assets on Purchase,Création automatique d'actifs à l'achat, +Auto Create Assets on Purchase,Création automatique d'actifs à l'achat, Asset Naming Series,Nom de série de l'actif, Over Delivery/Receipt Allowance (%),Surlivrance / indemnité de réception (%), Barcodes,Codes-barres, @@ -7839,9 +7839,9 @@ Automatically Create New Batch,Créer un Nouveau Lot Automatiquement, Batch Number Series,Série de numéros de lots, "Example: ABCD.#####. If series is set and Batch No is not mentioned in transactions, then automatic batch number will be created based on this series. If you always want to explicitly mention Batch No for this item, leave this blank. Note: this setting will take priority over the Naming Series Prefix in Stock Settings.","Exemple: ABCD. #####. Si la série est définie et que le numéro de lot n'est pas mentionné dans les transactions, un numéro de lot sera automatiquement créé en avec cette série. Si vous préferez mentionner explicitement et systématiquement le numéro de lot pour cet article, laissez ce champ vide. Remarque: ce paramètre aura la priorité sur le préfixe de la série dans les paramètres de stock.", Has Expiry Date,A une date d'expiration, -Retain Sample,Conserver l'échantillon, -Max Sample Quantity,Quantité maximum d'échantillon, -Maximum sample quantity that can be retained,Quantité maximale d'échantillon pouvant être conservée, +Retain Sample,Conserver l'échantillon, +Max Sample Quantity,Quantité maximum d'échantillon, +Maximum sample quantity that can be retained,Quantité maximale d'échantillon pouvant être conservée, Has Serial No,A un N° de Série, Serial Number Series,Séries de Numéros de Série, "Example: ABCD.#####\nIf series is set and Serial No is not mentioned in transactions, then automatic serial number will be created based on this series. If you always want to explicitly mention Serial Nos for this item. leave this blank.","Exemple:. ABCD ##### Si la série est définie et que le N° de série n'est pas mentionné dans les transactions, alors un numéro de série automatique sera créé basé sur cette série. Si vous voulez toujours mentionner explicitement les numéros de série pour ce produit. laissez ce champ vide.", @@ -7852,13 +7852,13 @@ Variant Based On,Variante Basée Sur, Item Attribute,Attribut de l'Article, "Sales, Purchase, Accounting Defaults","Valeurs par défaut pour les ventes, les achats et la comptabilité", Item Defaults,Paramètres par défaut de l'article, -"Purchase, Replenishment Details","Détails d'achat, de réapprovisionnement", +"Purchase, Replenishment Details","Détails d'achat, de réapprovisionnement", Is Purchase Item,Est Article d'Achat, Default Purchase Unit of Measure,Unité de Mesure par défaut à l'Achat, Minimum Order Qty,Qté de Commande Minimum, -Minimum quantity should be as per Stock UOM,La quantité minimale doit être conforme à l'UdM du stock, +Minimum quantity should be as per Stock UOM,La quantité minimale doit être conforme à l'UdM du stock, Average time taken by the supplier to deliver,Délai moyen de livraison par le fournisseur, -Is Customer Provided Item,L'article est-il fourni par le client?, +Is Customer Provided Item,L'article est-il fourni par le client?, Delivered by Supplier (Drop Ship),Livré par le Fournisseur (Expédition Directe), Supplier Items,Articles Fournisseur, Foreign Trade Details,Détails du Commerce Extérieur, @@ -7886,7 +7886,7 @@ Website Item Groups,Groupes d'Articles du Site Web, List this Item in multiple groups on the website.,Liste cet article dans plusieurs groupes sur le site., Copy From Item Group,Copier Depuis un Groupe d'Articles, Website Content,Contenu du site Web, -You can use any valid Bootstrap 4 markup in this field. It will be shown on your Item Page.,Vous pouvez utiliser n'importe quelle balise Bootstrap 4 valide dans ce champ. Il sera affiché sur votre page d'article., +You can use any valid Bootstrap 4 markup in this field. It will be shown on your Item Page.,Vous pouvez utiliser n'importe quelle balise Bootstrap 4 valide dans ce champ. Il sera affiché sur votre page d'article., Total Projected Qty,Qté Totale Prévue, Hub Publishing Details,Détails Publiés sur le Hub, Publish in Hub,Publier dans le Hub, @@ -7898,7 +7898,7 @@ Synced With Hub,Synchronisé avec le Hub, Item Alternative,Alternative à l'Article, Alternative Item Code,Code de l'article alternatif, Two-way,A double-sens, -Alternative Item Name,Nom de l'article alternatif, +Alternative Item Name,Nom de l'article alternatif, Attribute Name,Nom de l'Attribut, Numeric Values,Valeurs Numériques, From Range,Plage Initiale, @@ -7925,7 +7925,7 @@ Sales Defaults,Valeurs par défaut pour la vente, Default Selling Cost Center,Centre de Coût Vendeur par Défaut, Item Manufacturer,Fabricant d'Article, Item Price,Prix de l'Article, -Packing Unit,Unité d'emballage, +Packing Unit,Unité d'emballage, Quantity that must be bought or sold per UOM,Quantité à acheter ou à vendre par unité de mesure, Valid From ,Valide à Partir de, Valid Upto ,Valide Jusqu'au, @@ -8001,7 +8001,7 @@ Pick List Item,Élément de la liste de choix, Picked Qty,Quantité choisie, Price List Master,Données de Base des Listes de Prix, Price List Name,Nom de la Liste de Prix, -Price Not UOM Dependent,Prix non dépendant de l'UOM, +Price Not UOM Dependent,Prix non dépendant de l'UOM, Applicable for Countries,Applicable pour les Pays, Price List Country,Pays de la Liste des Prix, MAT-PRE-.YYYY.-,MAT-PRE-YYYY.-, @@ -8018,7 +8018,7 @@ Vehicle Date,Date du Véhicule, Received and Accepted,Reçus et Acceptés, Accepted Quantity,Quantité Acceptée, Rejected Quantity,Quantité Rejetée, -Sample Quantity,Quantité d'échantillon, +Sample Quantity,Quantité d'échantillon, Rate and Amount,Prix et Montant, MAT-QA-.YYYY.-,MAT-QA-YYYY.-, Report Date,Date du Rapport, @@ -8038,7 +8038,7 @@ Reading 7,Lecture 7, Reading 8,Lecture 8, Reading 9,Lecture 9, Reading 10,Lecture 10, -Quality Inspection Template Name,Nom du modèle d'inspection de la qualité, +Quality Inspection Template Name,Nom du modèle d'inspection de la qualité, Quick Stock Balance,Solde rapide des stocks, Available Quantity,quantité disponible, Distinct unit of an Item,Unité distincte d'un Article, @@ -8048,8 +8048,8 @@ Creation Document Type,Type de Document de Création, Creation Document No,N° du Document de Création, Creation Date,Date de Création, Creation Time,Date de Création, -Asset Details,Détails de l'actif, -Asset Status,Statut de l'actif, +Asset Details,Détails de l'actif, +Asset Status,Statut de l'actif, Delivery Document Type,Type de Document de Livraison, Delivery Document No,Numéro de Document de Livraison, Delivery Time,Heure de la Livraison, @@ -8064,13 +8064,13 @@ Out of AMC,Sur AMC, Warranty Period (Days),Période de Garantie (Jours), Serial No Details,Détails du N° de Série, MAT-STE-.YYYY.-,MAT-STE-.YYYY.-, -Stock Entry Type,Type d'entrée de stock, +Stock Entry Type,Type d'entrée de stock, Stock Entry (Outward GIT),Entrée de stock (GIT sortant), Material Consumption for Manufacture,Consommation de matériaux pour la production, Repack,Ré-emballer, Send to Subcontractor,Envoyer au sous-traitant, -Send to Warehouse,Envoyer à l'entrepôt, -Receive at Warehouse,Recevez à l'entrepôt, +Send to Warehouse,Envoyer à l'entrepôt, +Receive at Warehouse,Recevez à l'entrepôt, Delivery Note No,Bon de Livraison N°, Sales Invoice No,N° de la Facture de Vente, Purchase Receipt No,N° du Reçu d'Achat, @@ -8080,9 +8080,9 @@ For Quantity,Pour la Quantité, As per Stock UOM,Selon UDM du Stock, Including items for sub assemblies,Incluant les articles pour des sous-ensembles, Default Source Warehouse,Entrepôt Source par Défaut, -Source Warehouse Address,Adresse de l'entrepôt source, +Source Warehouse Address,Adresse de l'entrepôt source, Default Target Warehouse,Entrepôt Cible par Défaut, -Target Warehouse Address,Adresse de l'entrepôt cible, +Target Warehouse Address,Adresse de l'entrepôt cible, Update Rate and Availability,Mettre à Jour le Prix et la Disponibilité, Total Incoming Value,Valeur Entrante Totale, Total Outgoing Value,Valeur Sortante Totale, @@ -8102,7 +8102,7 @@ Subcontracted Item,Article sous-traité, Against Stock Entry,Contre entrée de stock, Stock Entry Child,Entrée de stock enfant, PO Supplied Item,PO article fourni, -Reference Purchase Receipt,Reçu d'achat de référence, +Reference Purchase Receipt,Reçu d'achat de référence, Stock Ledger Entry,Écriture du Livre d'Inventaire, Outgoing Rate,Taux Sortant, Actual Qty After Transaction,Qté Réelle Après Transaction, @@ -8126,9 +8126,9 @@ Default Stock UOM,UDM par Défaut des Articles, Sample Retention Warehouse,Entrepôt de stockage des échantillons, Default Valuation Method,Méthode de Valorisation par Défaut, Percentage you are allowed to receive or deliver more against the quantity ordered. For example: If you have ordered 100 units. and your Allowance is 10% then you are allowed to receive 110 units.,Pourcentage que vous êtes autorisé à recevoir ou à livrer en plus de la quantité commandée. Par exemple : Si vous avez commandé 100 unités et que votre allocation est de 10% alors que vous êtes autorisé à recevoir 110 unités., -Action if Quality inspection is not submitted,Action si l'inspection qualité n'est pas soumise, +Action if Quality inspection is not submitted,Action si l'inspection qualité n'est pas soumise, Show Barcode Field,Afficher Champ Code Barre, -Convert Item Description to Clean HTML,Convertir la description de l'élément pour nettoyer le code HTML, +Convert Item Description to Clean HTML,Convertir la description de l'élément pour nettoyer le code HTML, Auto insert Price List rate if missing,Insertion automatique du taux de la Liste de Prix si manquante, Allow Negative Stock,Autoriser un Stock Négatif, Automatically Set Serial Nos based on FIFO,Régler Automatiquement les Nos de Série basés sur FIFO, @@ -8162,7 +8162,7 @@ Service Level Agreement Fulfilled,Contrat de niveau de service rempli, Ongoing,En cours, Resolution By,Résolution de, Resolution By Variance,Résolution par variance, -Service Level Agreement Creation,Création d'un contrat de niveau de service, +Service Level Agreement Creation,Création d'un contrat de niveau de service, Mins to First Response,Minutes avant la Première Réponse, First Responded On,Première Réponse Le, Resolution Details,Détails de la Résolution, @@ -8171,7 +8171,7 @@ Opening Time,Horaire d'Ouverture, Resolution Date,Date de Résolution, Via Customer Portal,Via le portail client, Support Team,Équipe de Support, -Issue Priority,Priorité d'émission, +Issue Priority,Priorité d'émission, Service Day,Jour de service, Workday,Journée de travail, Holiday List (ignored during SLA calculation),Liste de jours fériés (ignorée lors du calcul du contrat de niveau de service), @@ -8182,7 +8182,7 @@ Support Hours,Heures de Support, Support and Resolution,Support et résolution, Default Service Level Agreement,Contrat de niveau de service par défaut, Entity,Entité, -Agreement Details,Détails de l'accord, +Agreement Details,Détails de l'accord, Response and Resolution Time,Temps de réponse et de résolution, Service Level Priority,Priorité de niveau de service, Response Time,Temps de réponse, @@ -8202,11 +8202,11 @@ Post Description Key,Clé de description du message, Link Options,Options du lien, Source DocType,DocType source, Result Title Field,Champ du titre du résultat, -Result Preview Field,Champ d'aperçu du résultat, +Result Preview Field,Champ d'aperçu du résultat, Result Route Field,Champ du lien du résultat, Service Level Agreements,Accords de Niveau de Service, Track Service Level Agreement,Suivi du contrat de niveau de service, -Allow Resetting Service Level Agreement,Autoriser la réinitialisation de l'accord de niveau de service, +Allow Resetting Service Level Agreement,Autoriser la réinitialisation de l'accord de niveau de service, Close Issue After Days,Nbre de jours avant de fermer le ticket, Auto close Issue after 7 days,Fermer automatiquement le ticket après 7 jours, Support Portal,Portail du support, @@ -8263,12 +8263,12 @@ Course wise Assessment Report,Rapport d'Évaluation par Cours, Customer Acquisition and Loyalty,Acquisition et Fidélisation des Clients, Customer Credit Balance,Solde de Crédit des Clients, Customer Ledger Summary,Récapitulatif client, -Customer-wise Item Price,Prix de l'article par client, +Customer-wise Item Price,Prix de l'article par client, Customers Without Any Sales Transactions,Clients sans transactions de vente, Daily Timesheet Summary,Récapitulatif Quotidien des Feuilles de Présence, Daily Work Summary Replies,Réponses au récapitulatif de travail quotidien, DATEV,DATEV, -Delayed Item Report,Rapport d'élément retardé, +Delayed Item Report,Rapport d'élément retardé, Delayed Order Report,Rapport de commande retardé, Delivered Items To Be Billed,Articles Livrés à Facturer, Delivery Note Trends,Tendance des Bordereaux de Livraisons, @@ -8305,7 +8305,7 @@ Item Price Stock,Stock et prix de l'article, Item Prices,Prix des Articles, Item Shortage Report,Rapport de Rupture de Stock d'Article, Project Quantity,Quantité de Projet, -Item Variant Details,Détails de la variante de l'article, +Item Variant Details,Détails de la variante de l'article, Item-wise Price List Rate,Taux de la Liste des Prix par Article, Item-wise Purchase History,Historique d'Achats par Article, Item-wise Purchase Register,Registre des Achats par Article, @@ -8395,11 +8395,11 @@ Supplier-Wise Sales Analytics,Analyse des Ventes par Fournisseur, Support Hour Distribution,Répartition des Heures de Support, TDS Computation Summary,Résumé des calculs TDS, TDS Payable Monthly,TDS Payable Monthly, -Territory Target Variance Based On Item Group,Écart de cible de territoire basé sur un groupe d'articles, +Territory Target Variance Based On Item Group,Écart de cible de territoire basé sur un groupe d'articles, Territory-wise Sales,Ventes par territoire, Total Stock Summary,Récapitulatif de l'Inventaire Total, Trial Balance,Balance Générale, -Trial Balance (Simple),Balance d'essai (simple), +Trial Balance (Simple),Balance d'essai (simple), Trial Balance for Party,Balance Auxiliaire, Unpaid Expense Claim,Note de Frais Impayée, Warehouse wise Item Balance Age and Value,Balance des articles par entrepôt, From b86c0eb4043380a8b8c969a32493d82f790268bd Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 28 May 2020 12:07:07 +0530 Subject: [PATCH 230/608] fix: labels for date in manufacturing reports --- .../manufacturing/report/job_card_summary/job_card_summary.js | 2 +- .../report/work_order_summary/work_order_summary.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/manufacturing/report/job_card_summary/job_card_summary.js b/erpnext/manufacturing/report/job_card_summary/job_card_summary.js index 33953b1265e..bd68db190e7 100644 --- a/erpnext/manufacturing/report/job_card_summary/job_card_summary.js +++ b/erpnext/manufacturing/report/job_card_summary/job_card_summary.js @@ -41,7 +41,7 @@ frappe.query_reports["Job Card Summary"] = { reqd: 1 }, { - label: __("To Posting Datetime"), + label: __("To Posting Date"), fieldname:"to_date", fieldtype: "Date", default: frappe.defaults.get_user_default("year_end_date"), diff --git a/erpnext/manufacturing/report/work_order_summary/work_order_summary.js b/erpnext/manufacturing/report/work_order_summary/work_order_summary.js index 22928657b9b..eb23f17c477 100644 --- a/erpnext/manufacturing/report/work_order_summary/work_order_summary.js +++ b/erpnext/manufacturing/report/work_order_summary/work_order_summary.js @@ -41,7 +41,7 @@ frappe.query_reports["Work Order Summary"] = { reqd: 1 }, { - label: __("To Posting Datetime"), + label: __("To Posting Date"), fieldname:"to_date", fieldtype: "Date", default: frappe.defaults.get_user_default("year_end_date"), From 603cc3d05e8729f9133ef30b228db53eb8377e15 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Thu, 28 May 2020 12:49:33 +0530 Subject: [PATCH 231/608] fix: transaction date not found in sales invoice --- erpnext/accounts/doctype/pricing_rule/utils.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index cb05481df5a..53115f92d01 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -366,8 +366,7 @@ def get_qty_amount_data_for_cumulative(pr_doc, doc, items=[]): sum_qty, sum_amt = [0, 0] doctype = doc.get('parenttype') or doc.doctype - date_field = ('transaction_date' - if doc.get('transaction_date') else 'posting_date') + date_field = 'transaction_date' if frappe.get_meta(doctype).has_field('transaction_date') else 'posting_date' child_doctype = '{0} Item'.format(doctype) apply_on = frappe.scrub(pr_doc.get('apply_on')) From 8653419c4cd5241a09f9bb34bdd269ad8fcdc0d1 Mon Sep 17 00:00:00 2001 From: Michelle Alva <50285544+michellealva@users.noreply.github.com> Date: Thu, 28 May 2020 12:52:58 +0530 Subject: [PATCH 232/608] fix: change modified time changed modified time to sync the changes --- .../doctype/shopify_settings/shopify_settings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json index d1e5f40d6c1..5339c99155b 100644 --- a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json +++ b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json @@ -258,7 +258,7 @@ } ], "issingle": 1, - "modified": "2019-09-13 12:32:11.384757", + "modified": "2020-05-28 12:32:11.384757", "modified_by": "umair@erpnext.com", "module": "ERPNext Integrations", "name": "Shopify Settings", From dd39ba00140a348af397c899d93576a0f8d6ea6c Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Thu, 28 May 2020 13:11:04 +0530 Subject: [PATCH 233/608] chore: tests --- .../doctype/pricing_rule/test_pricing_rule.py | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py index 2da71dfd0e0..2bf0b725635 100644 --- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py @@ -385,6 +385,50 @@ class TestPricingRule(unittest.TestCase): so.load_from_db() self.assertEqual(so.items[1].is_free_item, 1) self.assertEqual(so.items[1].item_code, "_Test Item 2") + + def test_cumulative_pricing_rule(self): + frappe.delete_doc_if_exists('Pricing Rule', '_Test Cumulative Pricing Rule') + test_record = { + "doctype": "Pricing Rule", + "title": "_Test Cumulative Pricing Rule", + "apply_on": "Item Code", + "currency": "USD", + "items": [{ + "item_code": "_Test Item", + }], + "is_cumulative": 1, + "selling": 1, + "applicable_for": "Customer", + "customer": "_Test Customer", + "rate_or_discount": "Discount Percentage", + "rate": 0, + "min_amt": 0, + "max_amt": 10000, + "discount_percentage": 17.5, + "price_or_product_discount": "Price", + "company": "_Test Company", + "valid_from": frappe.utils.nowdate(), + "valid_upto": frappe.utils.nowdate() + } + frappe.get_doc(test_record.copy()).insert() + + args = frappe._dict({ + "item_code": "_Test Item", + "company": "_Test Company", + "price_list": "_Test Price List", + "currency": "_Test Currency", + "doctype": "Sales Invoice", + "conversion_rate": 1, + "price_list_currency": "_Test Currency", + "plc_conversion_rate": 1, + "order_type": "Sales", + "customer": "_Test Customer", + "name": None, + "transaction_date": frappe.utils.nowdate() + }) + details = get_item_details(args) + + self.assertTrue(details) def make_pricing_rule(**args): args = frappe._dict(args) From 706524f1ae4e89bd9f541c4b5e2db6b8a8bd7242 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Thu, 28 May 2020 13:33:58 +0530 Subject: [PATCH 234/608] fix: make transaction date of the oldest transaction as the last integration date (#22000) * fix: make transaction date of the oldest transaction as the last integration date * chore: remove blank line after docstring --- .../doctype/plaid_settings/plaid_settings.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py index a7062239c32..a45c6b13910 100644 --- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py +++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py @@ -124,10 +124,11 @@ def add_account_subtype(account_subtype): @frappe.whitelist() def sync_transactions(bank, bank_account): - - last_sync_date = frappe.db.get_value("Bank Account", bank_account, "last_integration_date") - if last_sync_date: - start_date = formatdate(last_sync_date, "YYYY-MM-dd") + '''Sync transactions based on the last integration date as the start date, after the sync is completed + add the transaction date of the oldest transaction as the last integration date''' + last_transaction_date = frappe.db.get_value("Bank Account", bank_account, "last_integration_date") + if last_transaction_date: + start_date = formatdate(last_transaction_date, "YYYY-MM-dd") else: start_date = formatdate(add_months(today(), -12), "YYYY-MM-dd") end_date = formatdate(today(), "YYYY-MM-dd") @@ -139,12 +140,14 @@ def sync_transactions(bank, bank_account): for transaction in reversed(transactions): result += new_bank_transaction(transaction) - frappe.logger().info("Plaid added {} new Bank Transactions from '{}' between {} and {}".format( - len(result), bank_account, start_date, end_date)) + if result: + end_date = frappe.db.get_value('Bank Transaction', result.pop(), 'date') - frappe.db.set_value("Bank Account", bank_account, "last_integration_date", getdate(end_date)) + frappe.logger().info("Plaid added {} new Bank Transactions from '{}' between {} and {}".format( + len(result), bank_account, start_date, end_date)) + + frappe.db.set_value("Bank Account", bank_account, "last_integration_date", end_date) - return result except Exception: frappe.log_error(frappe.get_traceback(), _("Plaid transactions sync error")) From f10771029a88f987795eb46e5081cb109b1a8d6f Mon Sep 17 00:00:00 2001 From: Anupam Kumar Date: Thu, 28 May 2020 13:34:55 +0530 Subject: [PATCH 235/608] CRM dashboard fixes (#21989) --- erpnext/crm/dashboard_fixtures.py | 24 ++++++++++++++++++------ erpnext/crm/desk_page/crm/crm.json | 4 ++-- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/erpnext/crm/dashboard_fixtures.py b/erpnext/crm/dashboard_fixtures.py index 16904b34295..0535cbbcc95 100644 --- a/erpnext/crm/dashboard_fixtures.py +++ b/erpnext/crm/dashboard_fixtures.py @@ -21,8 +21,8 @@ def get_dashboards(): { "chart": "Opportunity Trends", "width": "Full"}, { "chart": "Won Opportunities", "width": "Full" }, { "chart": "Territory Wise Opportunity Count", "width": "Half"}, - { "chart": "Territory Wise Sales", "width": "Half"}, { "chart": "Opportunities via Campaigns", "width": "Half" }, + { "chart": "Territory Wise Sales", "width": "Full"}, { "chart": "Lead Source", "width": "Half"} ], "cards": [ @@ -59,7 +59,7 @@ def get_charts(): 'is_public': 1, 'timeseries': 1, "owner": "Administrator", - "filters_json": json.dumps([["Opportunity", "company", "=", company, False]]), + "filters_json": json.dumps([]), "type": "Bar" }, { @@ -90,7 +90,11 @@ def get_charts(): 'timeseries': 1, "owner": "Administrator", "filters_json": json.dumps([["Opportunity", "company", "=", company, False]]), - "type": "Pie" + "type": "Pie", + "custom_options": json.dumps({ + "truncateLegends": 1, + "maxSlices": 8 + }) }, { "name": "Won Opportunities", @@ -123,7 +127,11 @@ def get_charts(): ["Opportunity", "company", "=", company, False] ]), "owner": "Administrator", - "type": "Donut" + "type": "Donut", + "custom_options": json.dumps({ + "truncateLegends": 1, + "maxSlices": 8 + }) }, { "name": "Territory Wise Sales", @@ -140,7 +148,7 @@ def get_charts(): ["Opportunity", "company", "=", company, False], ["Opportunity", "status", "=", "Converted", False] ]), - "type": "Donut" + "type": "Bar" }, { "name": "Lead Source", @@ -152,7 +160,11 @@ def get_charts(): "document_type": "Lead", 'is_public': 1, "owner": "Administrator", - "type": "Pie" + "type": "Pie", + "custom_options": json.dumps({ + "truncateLegends": 1, + "maxSlices": 8 + }) }] def get_number_cards(): diff --git a/erpnext/crm/desk_page/crm/crm.json b/erpnext/crm/desk_page/crm/crm.json index 2fc4582917a..013fabef89f 100644 --- a/erpnext/crm/desk_page/crm/crm.json +++ b/erpnext/crm/desk_page/crm/crm.json @@ -42,7 +42,7 @@ "idx": 0, "is_standard": 1, "label": "CRM", - "modified": "2020-05-20 12:11:36.250491", + "modified": "2020-05-28 13:29:28.253749", "modified_by": "Administrator", "module": "CRM", "name": "CRM", @@ -76,7 +76,7 @@ "type": "Report" }, { - "label": "CRM Dashboard", + "label": "Dashboard", "link_to": "CRM", "type": "Dashboard" } From cfcdd52bf4dd7dd17ae78673881a5afb3091fd22 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Thu, 28 May 2020 13:35:29 +0530 Subject: [PATCH 236/608] fix: Inter-company Invoice currency for multicurrency transactions (#21984) --- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 05b85dabd4f..57dc17936da 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1519,14 +1519,22 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None): def update_details(source_doc, target_doc, source_parent): target_doc.inter_company_invoice_reference = source_doc.name if target_doc.doctype in ["Purchase Invoice", "Purchase Order"]: + currency = frappe.db.get_value('Supplier', details.get('party'), 'default_currency') target_doc.company = details.get("company") target_doc.supplier = details.get("party") target_doc.buying_price_list = source_doc.selling_price_list + + if currency: + target_doc.currency = currency else: + currency = frappe.db.get_value('Customer', details.get('party'), 'default_currency') target_doc.company = details.get("company") target_doc.customer = details.get("party") target_doc.selling_price_list = source_doc.buying_price_list + if currency: + target_doc.currency = currency + doclist = get_mapped_doc(doctype, source_name, { doctype: { "doctype": target_doctype, From c8e8e29083aa5d75615cbf22dd1dbe60cd47f7aa Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 28 May 2020 13:48:09 +0530 Subject: [PATCH 237/608] test: Issue Metrics --- erpnext/support/doctype/issue/issue.py | 16 +++--- erpnext/support/doctype/issue/test_issue.py | 52 ++++++++++++++++++- .../test_service_level_agreement.py | 17 +++--- 3 files changed, 69 insertions(+), 16 deletions(-) diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index b7da29649d0..31797dff42a 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -76,9 +76,9 @@ class Issue(Document): self.agreement_fulfilled = "Ongoing" set_service_level_agreement_variance(issue=self.name) - self.handle_hold_time() + self.handle_hold_time(status) - def handle_hold_time(self): + def handle_hold_time(self, status): # set response and resolution variance as None as the issue is on Hold for status as Replied if self.status == "Replied" and status != "Replied": self.on_hold_since = frappe.flags.current_time or now_datetime() @@ -91,24 +91,26 @@ class Issue(Document): # calculate hold time when status is changed from Replied to any other status if self.status != "Replied" and status == "Replied": hold_time = self.total_hold_time if self.total_hold_time else 0 - self.total_hold_time = hold_time + time_diff_in_seconds(now_datetime(), self.on_hold_since) + now_time = frappe.flags.current_time or now_datetime() + self.total_hold_time = hold_time + time_diff_in_seconds(now_time, self.on_hold_since) # re-calculate SLA variables after issue changes from Replied to Open # add hold time to SLA variables if self.status == "Open" and status == "Replied": start_date_time = get_datetime(self.service_level_agreement_creation) priority = get_priority(self) + now_time = frappe.flags.current_time or now_datetime() hold_time = time_diff_in_seconds(now_datetime(), self.on_hold_since) if not self.first_responded_on: response_by = get_expected_time_for(parameter="response", service_level=priority, start_date_time=start_date_time) self.response_by = add_to_date(response_by, seconds=round(hold_time)) - response_by_variance = round(time_diff_in_hours(self.response_by, now_datetime())) + response_by_variance = round(time_diff_in_hours(self.response_by, now_time)) self.response_by_variance = response_by_variance + (hold_time // 3600) resolution_by = get_expected_time_for(parameter="resolution", service_level=priority, start_date_time=start_date_time) self.resolution_by = add_to_date(resolution_by, seconds=round(hold_time)) - resolution_by_variance = round(time_diff_in_hours(self.resolution_by, now_datetime())) + resolution_by_variance = round(time_diff_in_hours(self.resolution_by, now_time)) self.resolution_by_variance = resolution_by_variance + (hold_time // 3600) self.on_hold_since = None @@ -377,7 +379,7 @@ def set_average_response_time(issue): def set_resolution_time(issue): # total time taken from issue creation to closing - resolution_time = time_diff_in_seconds(now_datetime(), issue.creation) + resolution_time = time_diff_in_seconds(issue.resolution_date, issue.creation) issue.db_set("resolution_time", resolution_time) @@ -399,7 +401,7 @@ def set_user_resolution_time(issue): pending_time.append(wait_time) total_pending_time = sum(pending_time) - resolution_time_in_secs = time_diff_in_seconds(now_datetime(), issue.creation) + resolution_time_in_secs = time_diff_in_seconds(issue.resolution_date, issue.creation) user_resolution_time = resolution_time_in_secs - total_pending_time issue.db_set("user_resolution_time", user_resolution_time) diff --git a/erpnext/support/doctype/issue/test_issue.py b/erpnext/support/doctype/issue/test_issue.py index 7a5e3e300db..2818b1b8eb3 100644 --- a/erpnext/support/doctype/issue/test_issue.py +++ b/erpnext/support/doctype/issue/test_issue.py @@ -10,10 +10,13 @@ import datetime from datetime import timedelta class TestIssue(unittest.TestCase): - def test_response_time_and_resolution_time_based_on_different_sla(self): + def setUp(self): + frappe.db.sql("delete from `tabService Level Agreement`") + frappe.db.sql("delete from `tabEmployee`") frappe.db.set_value("Support Settings", None, "track_service_level_agreement", 1) create_service_level_agreements_for_issues() + def test_response_time_and_resolution_time_based_on_different_sla(self): creation = datetime.datetime(2019, 3, 4, 12, 0) # make issue with customer specific SLA @@ -72,6 +75,33 @@ class TestIssue(unittest.TestCase): self.assertEqual(issue.agreement_fulfilled, 'Fulfilled') + def test_issue_metrics(self): + creation = datetime.datetime(2020, 3, 4, 4, 0) + + # make issue with customer specific SLA + customer = create_customer("_Test Customer", "__Test SLA Customer Group", "__Test SLA Territory") + issue = make_issue(creation, "_Test Customer", 1) + create_communication(issue.name, "test@example.com", "Received", creation) + + creation = datetime.datetime(2020, 3, 4, 4, 15) + create_communication(issue.name, "test@admin.com", "Sent", creation) + + creation = datetime.datetime(2020, 3, 4, 5, 0) + create_communication(issue.name, "test@example.com", "Received", creation) + + creation = datetime.datetime(2020, 3, 4, 5, 5) + create_communication(issue.name, "test@admin.com", "Sent", creation) + issue = frappe.get_doc('Issue', issue.name) + + frappe.flags.current_time = datetime.datetime(2020, 3, 4, 5, 5) + issue.status = 'Closed' + issue.save() + + self.assertEqual(issue.avg_response_time, 600) + self.assertEqual(issue.resolution_time, 3900) + self.assertEqual(issue.user_resolution_time, 1200) + + def make_issue(creation=None, customer=None, index=0): issue = frappe.get_doc({ @@ -86,6 +116,7 @@ def make_issue(creation=None, customer=None, index=0): return issue + def create_customer(name, customer_group, territory): create_customer_group(customer_group) @@ -99,6 +130,7 @@ def create_customer(name, customer_group, territory): "territory": territory }).insert(ignore_permissions=True) + def create_customer_group(customer_group): if not frappe.db.exists("Customer Group", {"customer_group_name": customer_group}): @@ -107,6 +139,7 @@ def create_customer_group(customer_group): "customer_group_name": customer_group }).insert(ignore_permissions=True) + def create_territory(territory): if not frappe.db.exists("Territory", {"territory_name": territory}): @@ -114,3 +147,20 @@ def create_territory(territory): "doctype": "Territory", "territory_name": territory, }).insert(ignore_permissions=True) + + +def create_communication(reference_name, sender, sent_or_received, creation): + issue = frappe.get_doc({ + "doctype": "Communication", + "communication_type": "Communication", + "communication_medium": "Email", + "sent_or_received": sent_or_received, + "email_status": "Open", + "subject": "Test Issue", + "sender": sender, + "content": "Test", + "status": "Linked", + "reference_doctype": "Issue", + "creation": creation, + "reference_name": reference_name + }).insert(ignore_permissions=True) diff --git a/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py index 94e0b582f97..57d4747e5c9 100644 --- a/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py +++ b/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py @@ -16,7 +16,7 @@ class TestServiceLevelAgreement(unittest.TestCase): # Default Service Level Agreement create_default_service_level_agreement = create_service_level_agreement(default_service_level_agreement=1, holiday_list="__Test Holiday List", employee_group="_Test Employee Group", - entity_type=None, entity=None, response_time=4, resolution_time=6) + entity_type=None, entity=None, response_time=14400, resolution_time=21600) get_default_service_level_agreement = get_service_level_agreement(default_service_level_agreement=1) @@ -29,7 +29,7 @@ class TestServiceLevelAgreement(unittest.TestCase): customer = create_customer() create_customer_service_level_agreement = create_service_level_agreement(default_service_level_agreement=0, holiday_list="__Test Holiday List", employee_group="_Test Employee Group", - entity_type="Customer", entity=customer, response_time=2, resolution_time=3) + entity_type="Customer", entity=customer, response_time=7200, resolution_time=10800) get_customer_service_level_agreement = get_service_level_agreement(entity_type="Customer", entity=customer) self.assertEqual(create_customer_service_level_agreement.name, get_customer_service_level_agreement.name) @@ -41,7 +41,7 @@ class TestServiceLevelAgreement(unittest.TestCase): customer_group = create_customer_group() create_customer_group_service_level_agreement = create_service_level_agreement(default_service_level_agreement=0, holiday_list="__Test Holiday List", employee_group="_Test Employee Group", - entity_type="Customer Group", entity=customer_group, response_time=2, resolution_time=3) + entity_type="Customer Group", entity=customer_group, response_time=7200, resolution_time=10800) get_customer_group_service_level_agreement = get_service_level_agreement(entity_type="Customer Group", entity=customer_group) self.assertEqual(create_customer_group_service_level_agreement.name, get_customer_group_service_level_agreement.name) @@ -53,7 +53,7 @@ class TestServiceLevelAgreement(unittest.TestCase): territory = create_territory() create_territory_service_level_agreement = create_service_level_agreement(default_service_level_agreement=0, holiday_list="__Test Holiday List", employee_group="_Test Employee Group", - entity_type="Territory", entity=territory, response_time=2, resolution_time=3) + entity_type="Territory", entity=territory, response_time=7200, resolution_time=10800) get_territory_service_level_agreement = get_service_level_agreement(entity_type="Territory", entity=territory) self.assertEqual(create_territory_service_level_agreement.name, get_territory_service_level_agreement.name) @@ -83,6 +83,7 @@ def create_service_level_agreement(default_service_level_agreement, holiday_list "enable": 1, "service_level": "__Test Service Level", "default_service_level_agreement": default_service_level_agreement, + "default_priority": "Medium", "holiday_list": holiday_list, "employee_group": employee_group, "entity_type": entity_type, @@ -212,19 +213,19 @@ def create_territory(): def create_service_level_agreements_for_issues(): create_service_level_agreement(default_service_level_agreement=1, holiday_list="__Test Holiday List", - employee_group="_Test Employee Group", entity_type=None, entity=None, response_time=4, resolution_time=6) + employee_group="_Test Employee Group", entity_type=None, entity=None, response_time=14400, resolution_time=21600) create_customer() create_service_level_agreement(default_service_level_agreement=0, holiday_list="__Test Holiday List", - employee_group="_Test Employee Group", entity_type="Customer", entity="_Test Customer", response_time=2, resolution_time=3) + employee_group="_Test Employee Group", entity_type="Customer", entity="_Test Customer", response_time=7200, resolution_time=10800) create_customer_group() create_service_level_agreement(default_service_level_agreement=0, holiday_list="__Test Holiday List", - employee_group="_Test Employee Group", entity_type="Customer Group", entity="_Test SLA Customer Group", response_time=2, resolution_time=3) + employee_group="_Test Employee Group", entity_type="Customer Group", entity="_Test SLA Customer Group", response_time=7200, resolution_time=10800) create_territory() create_service_level_agreement(default_service_level_agreement=0, holiday_list="__Test Holiday List", - employee_group="_Test Employee Group", entity_type="Territory", entity="_Test SLA Territory", response_time=2, resolution_time=3) + employee_group="_Test Employee Group", entity_type="Territory", entity="_Test SLA Territory", response_time=7200, resolution_time=10800) def make_holiday_list(): holiday_list = frappe.db.exists("Holiday List", "__Test Holiday List") From 376a46f1e5d14d6fc0c2b1f9fa05af174cf2b629 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 28 May 2020 14:33:30 +0530 Subject: [PATCH 238/608] test: hold time for Replied status --- erpnext/support/doctype/issue/issue.py | 2 +- erpnext/support/doctype/issue/test_issue.py | 45 ++++++++++++++++++--- 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index 31797dff42a..c09c729c5c3 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -100,7 +100,7 @@ class Issue(Document): start_date_time = get_datetime(self.service_level_agreement_creation) priority = get_priority(self) now_time = frappe.flags.current_time or now_datetime() - hold_time = time_diff_in_seconds(now_datetime(), self.on_hold_since) + hold_time = time_diff_in_seconds(now_time, self.on_hold_since) if not self.first_responded_on: response_by = get_expected_time_for(parameter="response", service_level=priority, start_date_time=start_date_time) diff --git a/erpnext/support/doctype/issue/test_issue.py b/erpnext/support/doctype/issue/test_issue.py index 2818b1b8eb3..a0048432705 100644 --- a/erpnext/support/doctype/issue/test_issue.py +++ b/erpnext/support/doctype/issue/test_issue.py @@ -78,9 +78,7 @@ class TestIssue(unittest.TestCase): def test_issue_metrics(self): creation = datetime.datetime(2020, 3, 4, 4, 0) - # make issue with customer specific SLA - customer = create_customer("_Test Customer", "__Test SLA Customer Group", "__Test SLA Territory") - issue = make_issue(creation, "_Test Customer", 1) + issue = make_issue(creation, index=1) create_communication(issue.name, "test@example.com", "Received", creation) creation = datetime.datetime(2020, 3, 4, 4, 15) @@ -91,9 +89,9 @@ class TestIssue(unittest.TestCase): creation = datetime.datetime(2020, 3, 4, 5, 5) create_communication(issue.name, "test@admin.com", "Sent", creation) - issue = frappe.get_doc('Issue', issue.name) frappe.flags.current_time = datetime.datetime(2020, 3, 4, 5, 5) + issue.reload() issue.status = 'Closed' issue.save() @@ -101,9 +99,43 @@ class TestIssue(unittest.TestCase): self.assertEqual(issue.resolution_time, 3900) self.assertEqual(issue.user_resolution_time, 1200) + def test_hold_time_on_replied(self): + creation = datetime.datetime(2020, 3, 4, 4, 0) + + issue = make_issue(creation, index=1) + create_communication(issue.name, "test@example.com", "Received", creation) + + creation = datetime.datetime(2020, 3, 4, 4, 15) + create_communication(issue.name, "test@admin.com", "Sent", creation) + + frappe.flags.current_time = datetime.datetime(2020, 3, 4, 4, 15) + issue.reload() + issue.status = 'Replied' + issue.save() + + self.assertEqual(issue.on_hold_since, frappe.flags.current_time) + + creation = datetime.datetime(2020, 3, 4, 5, 0) + frappe.flags.current_time = datetime.datetime(2020, 3, 4, 5, 0) + create_communication(issue.name, "test@example.com", "Received", creation) + + issue.reload() + self.assertEqual(issue.total_hold_time, 2700) + self.assertEqual(issue.resolution_by, datetime.datetime(2020, 3, 4, 16, 45)) + + creation = datetime.datetime(2020, 3, 4, 5, 5) + create_communication(issue.name, "test@admin.com", "Sent", creation) + + frappe.flags.current_time = datetime.datetime(2020, 3, 4, 5, 5) + issue.reload() + issue.status = 'Closed' + issue.save() + + issue.reload() + self.assertEqual(issue.total_hold_time, 2700) + def make_issue(creation=None, customer=None, index=0): - issue = frappe.get_doc({ "doctype": "Issue", "subject": "Service Level Agreement Issue {0}".format(index), @@ -163,4 +195,5 @@ def create_communication(reference_name, sender, sent_or_received, creation): "reference_doctype": "Issue", "creation": creation, "reference_name": reference_name - }).insert(ignore_permissions=True) + }) + issue.save() From 6621e22c023319e17ee6b95d680825dc49b7723b Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 28 May 2020 09:18:50 +0000 Subject: [PATCH 239/608] refactor: clean up desk pages (#22004) * refactor: clean up desk pages * refactor: update label for CRM --- erpnext/buying/desk_page/buying/buying.json | 8 +- erpnext/crm/desk_page/crm/crm.json | 4 +- erpnext/crm/module_onboarding/crm/crm.json | 4 +- erpnext/hr/desk_page/hr/hr.json | 20 ++-- .../loan_management.json => loan/loan.json} | 6 +- .../manufacturing/manufacturing.json | 6 +- .../projects/desk_page/projects/projects.json | 6 +- .../selling/desk_page/selling/selling.json | 94 +++++++++++++++++++ .../support/desk_page/support/support.json | 25 ++--- 9 files changed, 142 insertions(+), 31 deletions(-) rename erpnext/loan_management/desk_page/{loan_management/loan_management.json => loan/loan.json} (96%) create mode 100644 erpnext/selling/desk_page/selling/selling.json diff --git a/erpnext/buying/desk_page/buying/buying.json b/erpnext/buying/desk_page/buying/buying.json index 31b26d4ff89..bddb9573ad5 100644 --- a/erpnext/buying/desk_page/buying/buying.json +++ b/erpnext/buying/desk_page/buying/buying.json @@ -41,7 +41,7 @@ "links": "[\n {\n \"description\": \"Import Italian Purchase Invoices\",\n \"label\": \"Import Supplier Invoice\",\n \"name\": \"Import Supplier Invoice\",\n \"type\": \"doctype\"\n } \n]" } ], - "cards_label": "Masters & Reports ", + "cards_label": "", "category": "Modules", "charts": [ { @@ -49,7 +49,7 @@ "label": "Purchase Order Trends" } ], - "charts_label": "Buying Dashboard", + "charts_label": "", "creation": "2020-01-28 11:50:26.195467", "developer_mode_only": 0, "disable_user_customization": 0, @@ -60,7 +60,7 @@ "idx": 0, "is_standard": 1, "label": "Buying", - "modified": "2020-05-27 22:23:38.529720", + "modified": "2020-05-28 13:32:49.960574", "modified_by": "Administrator", "module": "Buying", "name": "Buying", @@ -109,5 +109,5 @@ "type": "Dashboard" } ], - "shortcuts_label": "Quick Access" + "shortcuts_label": "" } \ No newline at end of file diff --git a/erpnext/crm/desk_page/crm/crm.json b/erpnext/crm/desk_page/crm/crm.json index 013fabef89f..eb69dc06b65 100644 --- a/erpnext/crm/desk_page/crm/crm.json +++ b/erpnext/crm/desk_page/crm/crm.json @@ -42,7 +42,7 @@ "idx": 0, "is_standard": 1, "label": "CRM", - "modified": "2020-05-28 13:29:28.253749", + "modified": "2020-05-28 13:33:52.906750", "modified_by": "Administrator", "module": "CRM", "name": "CRM", @@ -52,6 +52,7 @@ "pin_to_top": 0, "shortcuts": [ { + "color": "#ffe8cd", "format": "{} Open", "label": "Lead", "link_to": "Lead", @@ -59,6 +60,7 @@ "type": "DocType" }, { + "color": "#cef6d1", "format": "{} Assigned", "label": "Opportunity", "link_to": "Opportunity", diff --git a/erpnext/crm/module_onboarding/crm/crm.json b/erpnext/crm/module_onboarding/crm/crm.json index 9b3d91ecee8..c43fcaef8c4 100644 --- a/erpnext/crm/module_onboarding/crm/crm.json +++ b/erpnext/crm/module_onboarding/crm/crm.json @@ -16,7 +16,7 @@ "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/CRM", "idx": 0, "is_complete": 0, - "modified": "2020-05-27 11:33:09.941263", + "modified": "2020-05-28 13:59:33.693420", "modified_by": "Administrator", "module": "CRM", "name": "CRM", @@ -37,6 +37,6 @@ ], "subtitle": "Lead, Opportunity, Customer and more", "success_message": "CRM Module is all setup!", - "title": "Let's Setup Your CRM", + "title": "Let's Set Up Your CRM", "user_can_dismiss": 1 } \ No newline at end of file diff --git a/erpnext/hr/desk_page/hr/hr.json b/erpnext/hr/desk_page/hr/hr.json index 33132a6898a..7ac000b011a 100644 --- a/erpnext/hr/desk_page/hr/hr.json +++ b/erpnext/hr/desk_page/hr/hr.json @@ -89,10 +89,11 @@ "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, + "hide_custom": 0, "idx": 0, "is_standard": 1, "label": "HR", - "modified": "2020-05-23 12:41:52.543438", + "modified": "2020-05-28 13:36:07.710600", "modified_by": "Administrator", "module": "HR", "name": "HR", @@ -102,6 +103,7 @@ "pin_to_top": 0, "shortcuts": [ { + "color": "#cef6d1", "format": "{} Active", "label": "Employee", "link_to": "Employee", @@ -109,17 +111,19 @@ "type": "DocType" }, { - "label": "Attendance", - "link_to": "Attendance", - "stats_filter": "", - "type": "DocType" - }, - { + "color": "#ffe8cd", + "format": "{} Open", "label": "Leave Application", "link_to": "Leave Application", "stats_filter": "{\"status\":\"Open\"}", "type": "DocType" }, + { + "label": "Attendance", + "link_to": "Attendance", + "stats_filter": "", + "type": "DocType" + }, { "label": "Salary Structure", "link_to": "Payroll Entry", @@ -132,7 +136,7 @@ }, { "format": "{} Open", - "label": "HR Dashboard", + "label": "Dashboard", "link_to": "Human Resource", "stats_filter": "{\n \"status\": \"Open\"\n}", "type": "Dashboard" diff --git a/erpnext/loan_management/desk_page/loan_management/loan_management.json b/erpnext/loan_management/desk_page/loan/loan.json similarity index 96% rename from erpnext/loan_management/desk_page/loan_management/loan_management.json rename to erpnext/loan_management/desk_page/loan/loan.json index d2a17630c9e..d79860a3520 100644 --- a/erpnext/loan_management/desk_page/loan_management/loan_management.json +++ b/erpnext/loan_management/desk_page/loan/loan.json @@ -34,10 +34,11 @@ "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, + "hide_custom": 0, "idx": 0, "is_standard": 1, "label": "Loan", - "modified": "2020-05-22 11:28:51.380509", + "modified": "2020-05-28 13:37:42.017709", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan", @@ -46,8 +47,11 @@ "pin_to_top": 0, "shortcuts": [ { + "color": "#ffe8cd", + "format": "{} Open", "label": "Loan Application", "link_to": "Loan Application", + "stats_filter": "{ \"status\": \"Open\" }", "type": "DocType" }, { diff --git a/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json b/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json index f2e07bfe204..763f533a94b 100644 --- a/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json +++ b/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json @@ -47,7 +47,7 @@ "idx": 0, "is_standard": 1, "label": "Manufacturing", - "modified": "2020-05-20 11:50:20.029056", + "modified": "2020-05-28 13:54:02.048419", "modified_by": "Administrator", "module": "Manufacturing", "name": "Manufacturing", @@ -58,6 +58,7 @@ "restrict_to_domain": "Manufacturing", "shortcuts": [ { + "color": "#cef6d1", "format": "{} Active", "label": "Item", "link_to": "Item", @@ -66,6 +67,7 @@ "type": "DocType" }, { + "color": "#cef6d1", "format": "{} Active", "label": "BOM", "link_to": "BOM", @@ -74,6 +76,7 @@ "type": "DocType" }, { + "color": "#ffe8cd", "format": "{} Open", "label": "Work Order", "link_to": "Work Order", @@ -82,6 +85,7 @@ "type": "DocType" }, { + "color": "#ffe8cd", "format": "{} Open", "label": "Production Plan", "link_to": "Production Plan", diff --git a/erpnext/projects/desk_page/projects/projects.json b/erpnext/projects/desk_page/projects/projects.json index fdbe13b2fb7..d91fe5304a3 100644 --- a/erpnext/projects/desk_page/projects/projects.json +++ b/erpnext/projects/desk_page/projects/projects.json @@ -33,7 +33,7 @@ "idx": 0, "is_standard": 1, "label": "Projects", - "modified": "2020-05-19 21:09:52.031828", + "modified": "2020-05-28 13:38:19.934937", "modified_by": "Administrator", "module": "Projects", "name": "Projects", @@ -42,7 +42,7 @@ "pin_to_top": 0, "shortcuts": [ { - "color": "#4d4da8", + "color": "#cef6d1", "format": "{} Assigned", "label": "Task", "link_to": "Task", @@ -50,7 +50,7 @@ "type": "DocType" }, { - "color": "#4d4da8", + "color": "#ffe8cd", "format": "{} Open", "label": "Project", "link_to": "Project", diff --git a/erpnext/selling/desk_page/selling/selling.json b/erpnext/selling/desk_page/selling/selling.json new file mode 100644 index 00000000000..c32a7c8481d --- /dev/null +++ b/erpnext/selling/desk_page/selling/selling.json @@ -0,0 +1,94 @@ +{ + "cards": [ + { + "hidden": 0, + "label": "Items and Pricing", + "links": "[\n {\n \"description\": \"All Products or Services.\",\n \"label\": \"Item\",\n \"name\": \"Item\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Price List\"\n ],\n \"description\": \"Multiple Item prices.\",\n \"label\": \"Item Price\",\n \"name\": \"Item Price\",\n \"onboard\": 1,\n \"route\": \"#Report/Item Price\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Price List master.\",\n \"label\": \"Price List\",\n \"name\": \"Price List\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tree of Item Groups.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Item Group\",\n \"link\": \"Tree/Item Group\",\n \"name\": \"Item Group\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"description\": \"Bundle items at time of sale.\",\n \"label\": \"Product Bundle\",\n \"name\": \"Product Bundle\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Rules for applying different promotional schemes.\",\n \"label\": \"Promotional Scheme\",\n \"name\": \"Promotional Scheme\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"description\": \"Rules for applying pricing and discount.\",\n \"label\": \"Pricing Rule\",\n \"name\": \"Pricing Rule\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Rules for adding shipping costs.\",\n \"label\": \"Shipping Rule\",\n \"name\": \"Shipping Rule\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Define coupon codes.\",\n \"label\": \"Coupon Code\",\n \"name\": \"Coupon Code\",\n \"type\": \"doctype\"\n }\n]" + }, + { + "hidden": 0, + "label": "Settings", + "links": "[\n {\n \"description\": \"Default settings for selling transactions.\",\n \"label\": \"Selling Settings\",\n \"name\": \"Selling Settings\",\n \"settings\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Template of terms or contract.\",\n \"label\": \"Terms and Conditions Template\",\n \"name\": \"Terms and Conditions\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tax template for selling transactions.\",\n \"label\": \"Sales Taxes and Charges Template\",\n \"name\": \"Sales Taxes and Charges Template\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Track Leads by Lead Source.\",\n \"label\": \"Lead Source\",\n \"name\": \"Lead Source\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Manage Customer Group Tree.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Customer Group\",\n \"link\": \"Tree/Customer Group\",\n \"name\": \"Customer Group\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"All Contacts.\",\n \"label\": \"Contact\",\n \"name\": \"Contact\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"All Addresses.\",\n \"label\": \"Address\",\n \"name\": \"Address\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Manage Territory Tree.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Territory\",\n \"link\": \"Tree/Territory\",\n \"name\": \"Territory\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Sales campaigns.\",\n \"label\": \"Campaign\",\n \"name\": \"Campaign\",\n \"type\": \"doctype\"\n }\n]" + }, + { + "hidden": 0, + "label": "Other Reports", + "links": "[\n {\n \"dependencies\": [\n \"Lead\"\n ],\n \"doctype\": \"Lead\",\n \"is_query_report\": true,\n \"label\": \"Lead Details\",\n \"name\": \"Lead Details\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Address\"\n ],\n \"doctype\": \"Address\",\n \"is_query_report\": true,\n \"label\": \"Customer Addresses And Contacts\",\n \"name\": \"Address And Contacts\",\n \"route_options\": {\n \"party_type\": \"Customer\"\n },\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"BOM\"\n ],\n \"doctype\": \"BOM\",\n \"is_query_report\": true,\n \"label\": \"BOM Search\",\n \"name\": \"BOM Search\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"doctype\": \"Item\",\n \"is_query_report\": true,\n \"label\": \"Available Stock for Packing Items\",\n \"name\": \"Available Stock for Packing Items\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Pending SO Items For Purchase Request\",\n \"name\": \"Pending SO Items For Purchase Request\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Customer Credit Balance\",\n \"name\": \"Customer Credit Balance\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Customers Without Any Sales Transactions\",\n \"name\": \"Customers Without Any Sales Transactions\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Sales Partners Commission\",\n \"name\": \"Sales Partners Commission\",\n \"type\": \"report\"\n }\n]" + }, + { + "hidden": 0, + "label": "Sales", + "links": "[\n {\n \"description\": \"Customer Database.\",\n \"label\": \"Customer\",\n \"name\": \"Customer\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Quotes to Leads or Customers.\",\n \"label\": \"Quotation\",\n \"name\": \"Quotation\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Confirmed orders from Customers.\",\n \"label\": \"Sales Order\",\n \"name\": \"Sales Order\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Invoices for Costumers.\",\n \"label\": \"Sales Invoice\",\n \"name\": \"Sales Invoice\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Blanket Orders from Costumers.\",\n \"label\": \"Blanket Order\",\n \"name\": \"Blanket Order\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"description\": \"Manage Sales Partners.\",\n \"label\": \"Sales Partner\",\n \"name\": \"Sales Partner\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Manage Sales Person Tree.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Sales Person\",\n \"link\": \"Tree/Sales Person\",\n \"name\": \"Sales Person\",\n \"type\": \"doctype\"\n }\n]" + }, + { + "hidden": 0, + "label": "Key Reports", + "links": "[\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Analytics\",\n \"name\": \"Sales Analytics\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"icon\": \"fa fa-bar-chart\",\n \"label\": \"Sales Funnel\",\n \"name\": \"sales-funnel\",\n \"onboard\": 1,\n \"type\": \"page\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"icon\": \"fa fa-bar-chart\",\n \"is_query_report\": true,\n \"label\": \"Customer Acquisition and Loyalty\",\n \"name\": \"Customer Acquisition and Loyalty\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Inactive Customers\",\n \"name\": \"Inactive Customers\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Ordered Items To Be Delivered\",\n \"name\": \"Ordered Items To Be Delivered\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Person-wise Transaction Summary\",\n \"name\": \"Sales Person-wise Transaction Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"doctype\": \"Item\",\n \"is_query_report\": true,\n \"label\": \"Item-wise Sales History\",\n \"name\": \"Item-wise Sales History\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Quotation\"\n ],\n \"doctype\": \"Quotation\",\n \"is_query_report\": true,\n \"label\": \"Quotation Trends\",\n \"name\": \"Quotation Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Order Trends\",\n \"name\": \"Sales Order Trends\",\n \"type\": \"report\"\n }\n]" + } + ], + "category": "Modules", + "charts": [ + { + "chart_name": "Income", + "label": "Income" + } + ], + "creation": "2020-01-28 11:49:12.092882", + "developer_mode_only": 0, + "disable_user_customization": 0, + "docstatus": 0, + "doctype": "Desk Page", + "extends_another_page": 0, + "hide_custom": 1, + "idx": 0, + "is_standard": 1, + "label": "Selling", + "modified": "2020-05-28 13:46:08.314240", + "modified_by": "Administrator", + "module": "Selling", + "name": "Selling", + "owner": "Administrator", + "pin_to_bottom": 0, + "pin_to_top": 0, + "shortcuts": [ + { + "color": "#ffe8cd", + "format": "{} Draft", + "label": "Sales Invoice", + "link_to": "Sales Invoice", + "stats_filter": "{ \"status\": \"Draft\" }", + "type": "DocType" + }, + { + "color": "#ffe8cd", + "format": "{} To Deliver", + "label": "Sales Order", + "link_to": "Sales Order", + "stats_filter": "{\"Status\": \"To Deliver and Bill\"}", + "type": "DocType" + }, + { + "color": "#cef6d1", + "format": "{} Open", + "label": "Quotation", + "link_to": "Quotation", + "stats_filter": "{ \"Status\": \"Open\" }", + "type": "DocType" + }, + { + "label": "Delivery Note", + "link_to": "Delivery Note", + "type": "DocType" + }, + { + "label": "Accounts Receivable", + "link_to": "Accounts Receivable", + "type": "Report" + }, + { + "label": "Sales Register", + "link_to": "Sales Register", + "type": "Report" + } + ] +} \ No newline at end of file diff --git a/erpnext/support/desk_page/support/support.json b/erpnext/support/desk_page/support/support.json index 596987f46a5..a3fe72d0519 100644 --- a/erpnext/support/desk_page/support/support.json +++ b/erpnext/support/desk_page/support/support.json @@ -2,8 +2,8 @@ "cards": [ { "hidden": 0, - "label": "Service Level Agreement", - "links": "[\n {\n \"description\": \"Service Level.\",\n \"label\": \"Service Level\",\n \"name\": \"Service Level\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Service Level Agreement.\",\n \"label\": \"Service Level Agreement\",\n \"name\": \"Service Level Agreement\",\n \"type\": \"doctype\"\n }\n]" + "label": "Issues", + "links": "[\n {\n \"description\": \"Support queries from customers.\",\n \"label\": \"Issue\",\n \"name\": \"Issue\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Issue Type.\",\n \"label\": \"Issue Type\",\n \"name\": \"Issue Type\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Issue Priority.\",\n \"label\": \"Issue Priority\",\n \"name\": \"Issue Priority\",\n \"type\": \"doctype\"\n }\n]" }, { "hidden": 0, @@ -12,8 +12,8 @@ }, { "hidden": 0, - "label": "Issues", - "links": "[\n {\n \"description\": \"Support queries from customers.\",\n \"label\": \"Issue\",\n \"name\": \"Issue\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Issue Type.\",\n \"label\": \"Issue Type\",\n \"name\": \"Issue Type\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Issue Priority.\",\n \"label\": \"Issue Priority\",\n \"name\": \"Issue Priority\",\n \"type\": \"doctype\"\n }\n]" + "label": "Service Level Agreement", + "links": "[\n {\n \"description\": \"Service Level.\",\n \"label\": \"Service Level\",\n \"name\": \"Service Level\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Service Level Agreement.\",\n \"label\": \"Service Level Agreement\",\n \"name\": \"Service Level Agreement\",\n \"type\": \"doctype\"\n }\n]" }, { "hidden": 0, @@ -39,11 +39,11 @@ "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, - "icon": "", + "hide_custom": 0, "idx": 0, "is_standard": 1, "label": "Support", - "modified": "2020-04-01 11:28:51.120583", + "modified": "2020-05-28 13:51:23.869954", "modified_by": "Administrator", "module": "Support", "name": "Support", @@ -52,19 +52,22 @@ "pin_to_top": 0, "shortcuts": [ { + "color": "#ffc4c4", + "format": "{} Assigned", "label": "Issue", "link_to": "Issue", - "type": "DocType" - }, - { - "label": "Service Level", - "link_to": "Service Level", + "stats_filter": "{\n \"_assign\": [\"like\", '%' + frappe.session.user + '%'],\n \"status\": \"Open\"\n}", "type": "DocType" }, { "label": "Maintenance Visit", "link_to": "Maintenance Visit", "type": "DocType" + }, + { + "label": "Service Level", + "link_to": "Service Level", + "type": "DocType" } ] } \ No newline at end of file From fd351f826853913a7fd57afb143b914915d187b4 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 28 May 2020 15:18:47 +0530 Subject: [PATCH 240/608] fix: set fiscal year for Profit and Loss chart (#22006) (cherry picked from commit 4afda7601574a93e9232d06341556dbcf0f142ab) Co-authored-by: Shivam Mishra --- erpnext/accounts/dashboard_fixtures.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/dashboard_fixtures.py b/erpnext/accounts/dashboard_fixtures.py index 1eed5a0f9ca..421c86dba01 100644 --- a/erpnext/accounts/dashboard_fixtures.py +++ b/erpnext/accounts/dashboard_fixtures.py @@ -60,9 +60,9 @@ def get_charts(): "report_name": "Profit and Loss Statement", "filters_json": json.dumps({ "company": company.name, - "filter_based_on": "Date Range", - "period_start_date": get_date_str(fiscal_year[1]), - "period_end_date": get_date_str(fiscal_year[2]), + "filter_based_on": "Fiscal Year", + "from_fiscal_year": fiscal_year[0], + "to_fiscal_year": fiscal_year[0], "periodicity": "Monthly", "include_default_book_entries": 1 }), From 1a25aa83dec502e96467a9a0d6c9291308edc916 Mon Sep 17 00:00:00 2001 From: Marica Date: Thu, 28 May 2020 18:16:24 +0530 Subject: [PATCH 241/608] fix: Delete Auto Email Reports (#22009) --- erpnext/patches/v13_0/delete_old_purchase_reports.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/erpnext/patches/v13_0/delete_old_purchase_reports.py b/erpnext/patches/v13_0/delete_old_purchase_reports.py index 8271d2e6dce..8bdc07ee5b8 100644 --- a/erpnext/patches/v13_0/delete_old_purchase_reports.py +++ b/erpnext/patches/v13_0/delete_old_purchase_reports.py @@ -12,4 +12,12 @@ def execute(): for report in reports_to_delete: if frappe.db.exists("Report", report): - frappe.delete_doc("Report", report) \ No newline at end of file + delete_auto_email_reports(report) + + frappe.delete_doc("Report", report) + +def delete_auto_email_reports(report): + """ Check for one or multiple Auto Email Reports and delete """ + auto_email_reports = frappe.db.get_values("Auto Email Report", {"report": report}, ["name"]) + for auto_email_report in auto_email_reports: + frappe.delete_doc("Auto Email Report", auto_email_report[0]) \ No newline at end of file From 517ab955ac5a577abf0dd9d612510b0873eaa2eb Mon Sep 17 00:00:00 2001 From: sahil28297 <37302950+sahil28297@users.noreply.github.com> Date: Thu, 28 May 2020 18:32:16 +0530 Subject: [PATCH 242/608] fix(rename_bank_reconcilliation): do not delete doc after renaming (#22013) --- erpnext/patches/v12_0/rename_bank_reconciliation.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/erpnext/patches/v12_0/rename_bank_reconciliation.py b/erpnext/patches/v12_0/rename_bank_reconciliation.py index eda47a95e03..2efa854fba9 100644 --- a/erpnext/patches/v12_0/rename_bank_reconciliation.py +++ b/erpnext/patches/v12_0/rename_bank_reconciliation.py @@ -8,9 +8,6 @@ def execute(): if frappe.db.table_exists("Bank Reconciliation"): frappe.rename_doc('DocType', 'Bank Reconciliation', 'Bank Clearance', force=True) frappe.reload_doc('Accounts', 'doctype', 'Bank Clearance') - + frappe.rename_doc('DocType', 'Bank Reconciliation Detail', 'Bank Clearance Detail', force=True) frappe.reload_doc('Accounts', 'doctype', 'Bank Clearance Detail') - - frappe.delete_doc("DocType", "Bank Reconciliation") - frappe.delete_doc("DocType", "Bank Reconciliation Detail") From 1f3fe59e364d7ec16ed47f3a709df47bd75a9cac Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Thu, 28 May 2020 18:36:21 +0530 Subject: [PATCH 243/608] fix: Default period start date and period end date for financial statements (#22011) --- .../consolidated_financial_statement.js | 13 +++++++++++-- erpnext/public/js/financial_statements.js | 12 ++++++++++-- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js index 38fd5fa2787..09479221fbb 100644 --- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js +++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js @@ -33,7 +33,6 @@ frappe.query_reports["Consolidated Financial Statement"] = { "fieldname":"period_start_date", "label": __("Start Date"), "fieldtype": "Date", - "default": frappe.datetime.nowdate(), "hidden": 1, "reqd": 1 }, @@ -41,7 +40,6 @@ frappe.query_reports["Consolidated Financial Statement"] = { "fieldname":"period_end_date", "label": __("End Date"), "fieldtype": "Date", - "default": frappe.datetime.add_months(frappe.datetime.nowdate(), 12), "hidden": 1, "reqd": 1 }, @@ -106,5 +104,16 @@ frappe.query_reports["Consolidated Financial Statement"] = { value = $value.wrap("

").parent().html(); } return value; + }, + onload: function() { + let fiscal_year = frappe.defaults.get_user_default("fiscal_year") + + frappe.model.with_doc("Fiscal Year", fiscal_year, function(r) { + var fy = frappe.model.get_doc("Fiscal Year", fiscal_year); + frappe.query_report.set_filter_value({ + period_start_date: fy.year_start_date, + period_end_date: fy.year_end_date + }); + }); } } diff --git a/erpnext/public/js/financial_statements.js b/erpnext/public/js/financial_statements.js index cf98b7534e9..d89d4712e6d 100644 --- a/erpnext/public/js/financial_statements.js +++ b/erpnext/public/js/financial_statements.js @@ -47,6 +47,16 @@ erpnext.financial_statements = { // dropdown for links to other financial statements erpnext.financial_statements.filters = get_filters() + let fiscal_year = frappe.defaults.get_user_default("fiscal_year") + + frappe.model.with_doc("Fiscal Year", fiscal_year, function(r) { + var fy = frappe.model.get_doc("Fiscal Year", fiscal_year); + frappe.query_report.set_filter_value({ + period_start_date: fy.year_start_date, + period_end_date: fy.year_end_date + }); + }); + report.page.add_inner_button(__("Balance Sheet"), function() { var filters = report.get_values(); frappe.set_route('query-report', 'Balance Sheet', {company: filters.company}); @@ -99,7 +109,6 @@ function get_filters() { "fieldname":"period_start_date", "label": __("Start Date"), "fieldtype": "Date", - "default": frappe.datetime.nowdate(), "hidden": 1, "reqd": 1 }, @@ -107,7 +116,6 @@ function get_filters() { "fieldname":"period_end_date", "label": __("End Date"), "fieldtype": "Date", - "default": frappe.datetime.add_months(frappe.datetime.nowdate(), 12), "hidden": 1, "reqd": 1 }, From 9209b9a7dec0dbefdba918ba726580aa49e36b80 Mon Sep 17 00:00:00 2001 From: sahil28297 <37302950+sahil28297@users.noreply.github.com> Date: Thu, 28 May 2020 18:49:47 +0530 Subject: [PATCH 244/608] fix(set_purchase_receipt_delivery_note_detail): commit after every 100 sql updates (#22016) --- .../set_purchase_receipt_delivery_note_detail.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/erpnext/patches/v12_0/set_purchase_receipt_delivery_note_detail.py b/erpnext/patches/v12_0/set_purchase_receipt_delivery_note_detail.py index 6f843cdabd7..52c9a2d7b3c 100644 --- a/erpnext/patches/v12_0/set_purchase_receipt_delivery_note_detail.py +++ b/erpnext/patches/v12_0/set_purchase_receipt_delivery_note_detail.py @@ -64,11 +64,13 @@ def execute(): return_document_map = make_return_document_map(doctype, return_document_map) + count = 0 + #iterate through original documents and its return documents for docname in return_document_map: - doc_items = frappe.get_doc(doctype, docname).get("items") + doc_items = frappe.get_cached_doc(doctype, docname).get("items") for return_doc in return_document_map[docname]: - return_doc_items = frappe.get_doc(doctype, return_doc).get("items") + return_doc_items = frappe.get_cached_doc(doctype, return_doc).get("items") #iterate through return document items and original document items for mapping for return_item in return_doc_items: @@ -80,9 +82,11 @@ def execute(): else: continue + # commit after every 100 sql updates + count += 1 + if count%100 == 0: + frappe.db.commit() + set_document_detail_in_return_document("Purchase Receipt") set_document_detail_in_return_document("Delivery Note") frappe.db.commit() - - - From a3dd75e77f6139f47dc142212550768d78197fee Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 28 May 2020 19:03:37 +0530 Subject: [PATCH 245/608] fix: empty filters in Healthcare Charts --- erpnext/healthcare/dashboard_fixtures.py | 8 ++++---- erpnext/healthcare/desk_page/healthcare/healthcare.json | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/erpnext/healthcare/dashboard_fixtures.py b/erpnext/healthcare/dashboard_fixtures.py index 967117d22c2..94668a16d91 100644 --- a/erpnext/healthcare/dashboard_fixtures.py +++ b/erpnext/healthcare/dashboard_fixtures.py @@ -71,7 +71,7 @@ def get_charts(): "chart_name": _("Department wise Patient Appointments"), "chart_type": "Custom", "source": "Department wise Patient Appointments", - "filters_json": json.dumps({}), + "filters_json": json.dumps([]), 'is_public': 1, "owner": "Administrator", "type": "Bar", @@ -159,7 +159,7 @@ def get_charts(): "document_type": "Patient Encounter Symptom", "group_by_type": "Count", "group_by_based_on": "complaint", - "filters_json": json.dumps({}), + "filters_json": json.dumps([]), 'is_public': 1, "owner": "Administrator", "type": "Percentage", @@ -173,7 +173,7 @@ def get_charts(): "document_type": "Patient Encounter Diagnosis", "group_by_type": "Count", "group_by_based_on": "diagnosis", - "filters_json": json.dumps({}), + "filters_json": json.dumps([]), 'is_public': 1, "owner": "Administrator", "type": "Percentage", @@ -229,7 +229,7 @@ def get_number_cards(): }, { "name": "Appointments to Bill", - "label": _("Appointments to Bill"), + "label": _("Appointments To Bill"), "function": "Count", "doctype": "Number Card", "document_type": "Patient Appointment", diff --git a/erpnext/healthcare/desk_page/healthcare/healthcare.json b/erpnext/healthcare/desk_page/healthcare/healthcare.json index 60b53137cd7..334b65563bc 100644 --- a/erpnext/healthcare/desk_page/healthcare/healthcare.json +++ b/erpnext/healthcare/desk_page/healthcare/healthcare.json @@ -64,7 +64,7 @@ "idx": 0, "is_standard": 1, "label": "Healthcare", - "modified": "2020-05-19 20:57:22.797267", + "modified": "2020-05-28 19:02:28.824995", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare", @@ -109,7 +109,7 @@ "type": "Page" }, { - "label": "Healthcare Dashboard", + "label": "Dashboard", "link_to": "Healthcare", "type": "Dashboard" } From 8385fed04293e16cf2017af8435ff48899707f13 Mon Sep 17 00:00:00 2001 From: Anupam Kumar Date: Thu, 28 May 2020 22:14:59 +0530 Subject: [PATCH 246/608] crm onboarding typos (#22008) --- erpnext/crm/module_onboarding/crm/crm.json | 8 ++++---- .../create_and_send_quotation.json | 2 +- erpnext/crm/onboarding_step/create_lead/create_lead.json | 2 +- .../introduction_to_crm/introduction_to_crm.json | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/erpnext/crm/module_onboarding/crm/crm.json b/erpnext/crm/module_onboarding/crm/crm.json index c43fcaef8c4..44d672a7b59 100644 --- a/erpnext/crm/module_onboarding/crm/crm.json +++ b/erpnext/crm/module_onboarding/crm/crm.json @@ -16,7 +16,7 @@ "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/CRM", "idx": 0, "is_complete": 0, - "modified": "2020-05-28 13:59:33.693420", + "modified": "2020-05-28 21:07:41.278784", "modified_by": "Administrator", "module": "CRM", "name": "CRM", @@ -35,8 +35,8 @@ "step": "Create and Send Quotation" } ], - "subtitle": "Lead, Opportunity, Customer and more", - "success_message": "CRM Module is all setup!", - "title": "Let's Set Up Your CRM", + "subtitle": "Lead, Opportunity, Customer and more.", + "success_message": "CRM Module is all Set Up!", + "title": "Let's Set Up Your CRM.", "user_can_dismiss": 1 } \ No newline at end of file diff --git a/erpnext/crm/onboarding_step/create_and_send_quotation/create_and_send_quotation.json b/erpnext/crm/onboarding_step/create_and_send_quotation/create_and_send_quotation.json index 9201d77cf87..78f7e4de9c7 100644 --- a/erpnext/crm/onboarding_step/create_and_send_quotation/create_and_send_quotation.json +++ b/erpnext/crm/onboarding_step/create_and_send_quotation/create_and_send_quotation.json @@ -8,7 +8,7 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-27 11:30:28.237263", + "modified": "2020-05-28 21:07:11.461172", "modified_by": "Administrator", "name": "Create and Send Quotation", "owner": "Administrator", diff --git a/erpnext/crm/onboarding_step/create_lead/create_lead.json b/erpnext/crm/onboarding_step/create_lead/create_lead.json index 6ff0bd61f12..c45e8b036c5 100644 --- a/erpnext/crm/onboarding_step/create_lead/create_lead.json +++ b/erpnext/crm/onboarding_step/create_lead/create_lead.json @@ -8,7 +8,7 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-27 11:30:59.493720", + "modified": "2020-05-28 21:07:01.373403", "modified_by": "Administrator", "name": "Create Lead", "owner": "Administrator", diff --git a/erpnext/crm/onboarding_step/introduction_to_crm/introduction_to_crm.json b/erpnext/crm/onboarding_step/introduction_to_crm/introduction_to_crm.json index 545a756a596..fa26921ae2c 100644 --- a/erpnext/crm/onboarding_step/introduction_to_crm/introduction_to_crm.json +++ b/erpnext/crm/onboarding_step/introduction_to_crm/introduction_to_crm.json @@ -8,7 +8,7 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-27 11:28:07.452857", + "modified": "2020-05-14 17:28:16.448676", "modified_by": "Administrator", "name": "Introduction to CRM", "owner": "Administrator", From 3c460364b5345ae823e150d532ee625b64475971 Mon Sep 17 00:00:00 2001 From: Saqib Date: Fri, 29 May 2020 10:18:06 +0530 Subject: [PATCH 247/608] fix: disposed asset creates inconsistencies in asset depr report (#22021) --- .../asset_depreciations_and_balances.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py index 80bccafd28e..5001ad9f12f 100644 --- a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py +++ b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py @@ -93,7 +93,7 @@ def get_assets(filters): sum(results.depreciation_eliminated_during_the_period) as depreciation_eliminated_during_the_period, sum(results.depreciation_amount_during_the_period) as depreciation_amount_during_the_period from (SELECT a.asset_category, - ifnull(sum(case when ds.schedule_date < %(from_date)s then + ifnull(sum(case when ds.schedule_date < %(from_date)s and (ifnull(a.disposal_date, 0) = 0 or a.disposal_date >= %(from_date)s) then ds.depreciation_amount else 0 @@ -115,9 +115,7 @@ def get_assets(filters): group by a.asset_category union SELECT a.asset_category, - ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 - and (a.disposal_date < %(from_date)s or a.disposal_date > %(to_date)s) - then + ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 and (a.disposal_date < %(from_date)s or a.disposal_date > %(to_date)s) then 0 else a.opening_accumulated_depreciation From cf0168668f40dfbfe10b782beb64d875fa29c4f5 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Fri, 29 May 2020 10:19:33 +0530 Subject: [PATCH 248/608] fix: Correct filters (#22022) --- erpnext/hr/dashboard_fixtures.py | 2 +- .../create_holiday_list/create_holiday_list.json | 4 ++-- erpnext/hr/onboarding_step/hr_settings/hr_settings.json | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/hr/dashboard_fixtures.py b/erpnext/hr/dashboard_fixtures.py index 004477c26cc..6e042ac78d3 100644 --- a/erpnext/hr/dashboard_fixtures.py +++ b/erpnext/hr/dashboard_fixtures.py @@ -143,7 +143,7 @@ def get_number_cards(): number_cards.append( get_number_cards_doc("Employee", "Employees Left (Last year)", filters_json = json.dumps([ - ["Employee", "modified", "Previous", "1 year"], + ["Employee", "relieving_date", "Previous", "1 year"], ["Employee", "status", "=", "Left"] ]) ) diff --git a/erpnext/hr/onboarding_step/create_holiday_list/create_holiday_list.json b/erpnext/hr/onboarding_step/create_holiday_list/create_holiday_list.json index 208e394560e..32472b4b3fa 100644 --- a/erpnext/hr/onboarding_step/create_holiday_list/create_holiday_list.json +++ b/erpnext/hr/onboarding_step/create_holiday_list/create_holiday_list.json @@ -1,6 +1,6 @@ { "action": "Create Entry", - "creation": "2020-05-27 11:47:34.700174", + "creation": "2020-05-28 11:47:34.700174", "docstatus": 0, "doctype": "Onboarding Step", "idx": 0, @@ -14,6 +14,6 @@ "owner": "Administrator", "reference_document": "Holiday List", "show_full_form": 1, - "title": "Create Holiday list", + "title": "Create Holiday List", "validate_action": 0 } \ No newline at end of file diff --git a/erpnext/hr/onboarding_step/hr_settings/hr_settings.json b/erpnext/hr/onboarding_step/hr_settings/hr_settings.json index a8c96fb6827..0a1d0baf8aa 100644 --- a/erpnext/hr/onboarding_step/hr_settings/hr_settings.json +++ b/erpnext/hr/onboarding_step/hr_settings/hr_settings.json @@ -1,6 +1,6 @@ { "action": "Update Settings", - "creation": "2020-05-14 13:13:52.427711", + "creation": "2020-05-28 13:13:52.427711", "docstatus": 0, "doctype": "Onboarding Step", "idx": 0, @@ -14,6 +14,6 @@ "owner": "Administrator", "reference_document": "HR Settings", "show_full_form": 0, - "title": "HR settings", + "title": "HR Settings", "validate_action": 0 } \ No newline at end of file From c302d066a3528c48ada9dc62df4e67f1c10b8d53 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Fri, 29 May 2020 10:19:58 +0530 Subject: [PATCH 249/608] fix: only save end date when transactions are returned (#22023) --- .../doctype/plaid_settings/plaid_settings.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py index a45c6b13910..c3371ed5dfb 100644 --- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py +++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py @@ -141,12 +141,12 @@ def sync_transactions(bank, bank_account): result += new_bank_transaction(transaction) if result: - end_date = frappe.db.get_value('Bank Transaction', result.pop(), 'date') + last_transaction_date = frappe.db.get_value('Bank Transaction', result.pop(), 'date') frappe.logger().info("Plaid added {} new Bank Transactions from '{}' between {} and {}".format( len(result), bank_account, start_date, end_date)) - frappe.db.set_value("Bank Account", bank_account, "last_integration_date", end_date) + frappe.db.set_value("Bank Account", bank_account, "last_integration_date", last_transaction_date) except Exception: frappe.log_error(frappe.get_traceback(), _("Plaid transactions sync error")) From cd8b5d1e4ce8ae809b6ad36b0ccdb89ee75ca4ce Mon Sep 17 00:00:00 2001 From: Saqib Date: Fri, 29 May 2020 10:21:20 +0530 Subject: [PATCH 250/608] fix: cannot assign same task to other asset maintenance (#22024) --- .../asset_maintenance/asset_maintenance.json | 643 ++---------- .../asset_maintenance/asset_maintenance.py | 6 +- .../asset_maintenance_log.json | 967 ++++-------------- .../asset_maintenance_task.py | 3 +- 4 files changed, 295 insertions(+), 1324 deletions(-) diff --git a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.json b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.json index efb2de3f266..c0c2566fe23 100644 --- a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.json +++ b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.json @@ -1,559 +1,140 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "field:asset_name", - "beta": 0, - "creation": "2017-10-19 16:50:22.879545", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "autoname": "field:asset_name", + "creation": "2017-10-19 16:50:22.879545", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "asset_name", + "asset_category", + "company", + "column_break_3", + "item_code", + "item_name", + "section_break_6", + "maintenance_team", + "column_break_9", + "maintenance_manager", + "maintenance_manager_name", + "section_break_8", + "asset_maintenance_tasks" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "asset_name", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Asset Name", - "length": 0, - "no_copy": 0, - "options": "Asset", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "asset_name", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Asset Name", + "options": "Asset", + "reqd": 1, + "unique": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "asset_name.asset_category", - "fieldname": "asset_category", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Asset Category", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "asset_category", + "fieldtype": "Read Only", + "label": "Asset Category" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "asset_name.item_code", - "fieldname": "item_code", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Item Code", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "item_code", + "fieldtype": "Read Only", + "label": "Item Code" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "asset_name.item_name", - "fieldname": "item_name", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Item Name", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "item_name", + "fieldtype": "Read Only", + "label": "Item Name" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "company", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Company", - "length": 0, - "no_copy": 0, - "options": "Company", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "select_serial_no", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Select Serial No", - "length": 0, - "no_copy": 0, - "options": "Serial No", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_6", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "serial_no", - "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Serial No", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "maintenance_team", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Maintenance Team", + "options": "Asset Maintenance Team", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_6", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_9", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "maintenance_team", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Maintenance Team", - "length": 0, - "no_copy": 0, - "options": "Asset Maintenance Team", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_9", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "maintenance_team.maintenance_manager", - "fieldname": "maintenance_manager", - "fieldtype": "Data", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Maintenance Manager", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "maintenance_manager", + "fieldtype": "Data", + "hidden": 1, + "label": "Maintenance Manager", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "maintenance_team.maintenance_manager_name", - "fieldname": "maintenance_manager_name", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Maintenance Manager Name", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "maintenance_manager_name", + "fieldtype": "Read Only", + "label": "Maintenance Manager Name" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_8", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Tasks", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_8", + "fieldtype": "Section Break", + "label": "Tasks" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "asset_maintenance_tasks", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Maintenance Tasks", - "length": 0, - "no_copy": 0, - "options": "Asset Maintenance Task", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "asset_maintenance_tasks", + "fieldtype": "Table", + "label": "Maintenance Tasks", + "options": "Asset Maintenance Task", + "reqd": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-05-22 17:20:54.711885", - "modified_by": "Administrator", - "module": "Assets", - "name": "Asset Maintenance", - "name_case": "", - "owner": "Administrator", + ], + "links": [], + "modified": "2020-05-28 20:28:32.993823", + "modified_by": "Administrator", + "module": "Assets", + "name": "Asset Maintenance", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Quality Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Quality Manager", + "share": 1, "write": 1 - }, + }, { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Manufacturing User", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Manufacturing User", + "share": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py index 3f046113a0f..d6adde6a371 100644 --- a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py +++ b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py @@ -38,7 +38,7 @@ class AssetMaintenance(Document): @frappe.whitelist() def assign_tasks(asset_maintenance_name, assign_to_member, maintenance_task, next_due_date): - team_member = frappe.get_doc('User', assign_to_member).email + team_member = frappe.db.get_value('User', assign_to_member, "email") args = { 'doctype' : 'Asset Maintenance', 'assign_to' : team_member, @@ -77,7 +77,7 @@ def calculate_next_due_date(periodicity, start_date = None, end_date = None, las def update_maintenance_log(asset_maintenance, item_code, item_name, task): asset_maintenance_log = frappe.get_value("Asset Maintenance Log", {"asset_maintenance": asset_maintenance, - "task": task.maintenance_task, "maintenance_status": ('in',['Planned','Overdue'])}) + "task": task.name, "maintenance_status": ('in',['Planned','Overdue'])}) if not asset_maintenance_log: asset_maintenance_log = frappe.get_doc({ @@ -86,7 +86,7 @@ def update_maintenance_log(asset_maintenance, item_code, item_name, task): "asset_name": asset_maintenance, "item_code": item_code, "item_name": item_name, - "task": task.maintenance_task, + "task": task.name, "has_certificate": task.certificate_required, "description": task.description, "assign_to_name": task.assign_to_name, diff --git a/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.json b/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.json index 42aecdf44ac..7395bec1e62 100644 --- a/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.json +++ b/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.json @@ -1,819 +1,210 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "naming_series:", - "beta": 0, - "creation": "2017-10-23 16:58:44.424309", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Document", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "autoname": "naming_series:", + "creation": "2017-10-23 16:58:44.424309", + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "asset_maintenance", + "naming_series", + "asset_name", + "column_break_2", + "item_code", + "item_name", + "section_break_5", + "task", + "task_name", + "maintenance_type", + "periodicity", + "assign_to_name", + "column_break_6", + "due_date", + "completion_date", + "maintenance_status", + "section_break_12", + "has_certificate", + "certificate_attachement", + "section_break_6", + "description", + "column_break_9", + "actions_performed", + "amended_from" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "asset_maintenance", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Asset Maintenance", - "length": 0, - "no_copy": 0, - "options": "Asset Maintenance", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "asset_maintenance", + "fieldtype": "Link", + "label": "Asset Maintenance", + "options": "Asset Maintenance" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fieldname": "naming_series", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Series", - "length": 0, - "no_copy": 0, - "options": "ACC-AML-.YYYY.-", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Series", + "options": "ACC-AML-.YYYY.-", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "asset_maintenance.asset_name", - "fieldname": "asset_name", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Asset Name", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "asset_maintenance.asset_name", + "fieldname": "asset_name", + "fieldtype": "Read Only", + "label": "Asset Name" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_2", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "asset_maintenance.item_code", - "fieldname": "item_code", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Item Code", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "asset_maintenance.item_code", + "fieldname": "item_code", + "fieldtype": "Read Only", + "label": "Item Code" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "asset_maintenance.item_name", - "fieldname": "item_name", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Item Name", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "asset_maintenance.item_name", + "fieldname": "item_name", + "fieldtype": "Read Only", + "label": "Item Name" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_5", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_5", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "task", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Task", - "length": 0, - "no_copy": 0, - "options": "Asset Maintenance Task", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "task", + "fieldtype": "Link", + "label": "Task", + "options": "Asset Maintenance Task" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "task.maintenance_type", - "fieldname": "maintenance_type", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Maintenance Type", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "task.maintenance_type", + "fieldname": "maintenance_type", + "fieldtype": "Read Only", + "label": "Maintenance Type" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "task.periodicity", - "fieldname": "periodicity", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Periodicity", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "task.periodicity", + "fieldname": "periodicity", + "fieldtype": "Data", + "label": "Periodicity", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "task.assign_to_name", - "fieldname": "assign_to_name", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Assign To", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "task.assign_to_name", + "fieldname": "assign_to_name", + "fieldtype": "Read Only", + "label": "Assign To" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_6", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_6", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "task.next_due_date", - "fieldname": "due_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Due Date", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "task.next_due_date", + "fieldname": "due_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Due Date", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "completion_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Completion Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "completion_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Completion Date" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "maintenance_status", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 1, - "label": "Maintenance Status", - "length": 0, - "no_copy": 0, - "options": "Planned\nCompleted\nCancelled\nOverdue", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "maintenance_status", + "fieldtype": "Select", + "in_standard_filter": 1, + "label": "Maintenance Status", + "options": "Planned\nCompleted\nCancelled\nOverdue", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_12", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_12", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "task.certificate_required", - "fieldname": "has_certificate", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Has Certificate ", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fetch_from": "task.certificate_required", + "fieldname": "has_certificate", + "fieldtype": "Check", + "label": "Has Certificate " + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.has_certificate", - "fieldname": "certificate_attachement", - "fieldtype": "Attach", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Certificate", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:doc.has_certificate", + "fieldname": "certificate_attachement", + "fieldtype": "Attach", + "label": "Certificate" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_6", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_6", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "task.description", - "fieldname": "description", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Description", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "task.description", + "fieldname": "description", + "fieldtype": "Read Only", + "label": "Description", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_9", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_9", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "actions_performed", - "fieldtype": "Text Editor", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Actions performed", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "allow_on_submit": 1, + "fieldname": "actions_performed", + "fieldtype": "Text Editor", + "label": "Actions performed" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "amended_from", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Amended From", - "length": 0, - "no_copy": 1, - "options": "Asset Maintenance Log", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Asset Maintenance Log", + "print_hide": 1, + "read_only": 1 + }, + { + "fetch_from": "task.maintenance_task", + "fieldname": "task_name", + "fieldtype": "Data", + "in_preview": 1, + "label": "Task Name", + "read_only": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-08-21 14:44:51.457835", - "modified_by": "Administrator", - "module": "Assets", - "name": "Asset Maintenance Log", - "name_case": "", - "owner": "Administrator", + ], + "is_submittable": 1, + "links": [], + "modified": "2020-05-28 20:51:48.238397", + "modified_by": "Administrator", + "module": "Assets", + "name": "Asset Maintenance Log", + "owner": "Administrator", "permissions": [ { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Manufacturing User", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Manufacturing User", + "share": 1, + "submit": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "", - "track_changes": 1, - "track_seen": 1, - "track_views": 0 + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 1 } \ No newline at end of file diff --git a/erpnext/assets/doctype/asset_maintenance_task/asset_maintenance_task.py b/erpnext/assets/doctype/asset_maintenance_task/asset_maintenance_task.py index d98cc310b3b..2a5666d5064 100644 --- a/erpnext/assets/doctype/asset_maintenance_task/asset_maintenance_task.py +++ b/erpnext/assets/doctype/asset_maintenance_task/asset_maintenance_task.py @@ -7,5 +7,4 @@ import frappe from frappe.model.document import Document class AssetMaintenanceTask(Document): - def autoname(self): - self.name = self.maintenance_task + pass From bc38289a595749824530ed5f6399b9d50153f3db Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Fri, 29 May 2020 11:43:05 +0530 Subject: [PATCH 251/608] fix: added patch for sla enhancements --- erpnext/patches.txt | 3 +- .../patches/v13_0/update_sla_enhancements.py | 111 ++++++++++++++++++ 2 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 erpnext/patches/v13_0/update_sla_enhancements.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index b0421f43c64..3ea15375d2f 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -694,4 +694,5 @@ execute:frappe.delete_doc("Report", "Department Analytics") execute:frappe.rename_doc("Desk Page", "Loan Management", "Loan", force=True) erpnext.patches.v12_0.update_uom_conversion_factor erpnext.patches.v13_0.delete_old_purchase_reports -erpnext.patches.v12_0.set_italian_import_supplier_invoice_permissions \ No newline at end of file +erpnext.patches.v12_0.set_italian_import_supplier_invoice_permissions +erpnext.patches.v13_0.update_sla_enhancements \ No newline at end of file diff --git a/erpnext/patches/v13_0/update_sla_enhancements.py b/erpnext/patches/v13_0/update_sla_enhancements.py new file mode 100644 index 00000000000..1d5f372b9b8 --- /dev/null +++ b/erpnext/patches/v13_0/update_sla_enhancements.py @@ -0,0 +1,111 @@ +# Copyright (c) 2018, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals + +import frappe + +def execute(): + # add holiday list and employee group fields in SLA + # change response and resolution time in priorities child table + if frappe.db.exists('DocType', 'Service Level Agreement'): + sla_details = frappe.db.get_all('Service Level Agreement', fields=['name', 'service_level']) + priorities = frappe.db.get_all('Service Level Priority', fields=['*'], filters={ + 'parenttype': ('in', ['Service Level Agreement', 'Service Level']) + }) + + frappe.reload_doc('support', 'doctype', 'service_level_agreement') + frappe.reload_doc('support', 'doctype', 'service_level_priority') + + for entry in sla_details: + values = frappe.db.get_value('Service Level', entry.service_level, ['holiday_list', 'employee_group']) + if values: + holiday_list = values[0] + employee_group = values[1] + frappe.db.set_value('Service Level Agreement', entry.name, { + 'holiday_list': holiday_list, + 'employee_group': employee_group + }) + + priority_dict = {} + + for priority in priorities: + if priority.parenttype == 'Service Level Agreement': + response_time = convert_to_seconds(priority.response_time, priority.response_time_period) + resolution_time = convert_to_seconds(priority.resolution_time, priority.resolution_time_period) + frappe.db.set_value('Service Level Priority', priority.name, { + 'response_time': response_time, + 'resolution_time': resolution_time + }) + if priority.parenttype == 'Service Level': + if not priority.parent in priority_dict: + priority_dict[priority.parent] = [] + priority_dict[priority.parent].append(priority) + + + # copy Service Levels to Service Level Agreements + sl = [entry.service_level for entry in sla_details] + service_levels = frappe.db.get_all('Service Level', filters={'service_level': ('not in', sl)}, fields=['*']) + for entry in service_levels: + sla = frappe.new_doc('Service Level Agreement') + sla.service_level = entry.service_level + sla.holiday_list = entry.holiday_list + sla.employee_group = entry.employee_group + sla.flags.ignore_validate = True + sla = sla.insert(ignore_mandatory=True) + + frappe.db.sql(""" + UPDATE + `tabService Day` + SET + parent = %(new_parent)s , parentfield = 'support_and_resolution', parenttype = 'Service Level Agreement' + WHERE + parent = %(old_parent)s + """, {'new_parent': sla.name, 'old_parent': entry.name}, as_dict = 1) + + priority_list = priority_dict.get(entry.name) + if priority_list: + sla = frappe.get_doc('Service Level Agreement', sla.name) + for priority in priority_list: + row = sla.append('priorities', { + 'priority': priority.priority, + 'default_priority': priority.default_priority, + 'response_time': convert_to_seconds(priority.response_time, priority.response_time_period), + 'resolution_time': convert_to_seconds(priority.resolution_time, priority.resolution_time_period) + }) + row.db_update() + sla.db_update() + + # set issue status as Replied since Hold status is removed + if frappe.db.exists('DocType', 'Issue'): + issues_on_hold = frappe.db.sql(""" + SELECT + name + FROM + `tabIssue` + WHERE + status = 'Hold' + """, as_dict=1) + + issues = [entry.name for entry in issues_on_hold] + + frappe.reload_doc('support', 'doctype', 'issue') + frappe.db.sql(""" + UPDATE + `tabIssue` + SET + status='Replied' + WHERE + name in %(issues)s + """, {'issues': issues}, debug=1) + + +def convert_to_seconds(value, unit): + seconds = 0 + if unit == "Hour": + seconds = value * 3600 + if unit == "Day": + seconds = value * 3600 * 24 + if unit == "Week": + seconds = value * 3600 * 24 * 7 + return seconds From d6587fa1d5dc115cf2ea09946800aba9878f9200 Mon Sep 17 00:00:00 2001 From: "Chinmay D. Pai" Date: Fri, 29 May 2020 12:44:09 +0530 Subject: [PATCH 252/608] fix: check if swift_number exists in bank account Signed-off-by: Chinmay D. Pai --- .../patches/v12_0/move_bank_account_swift_number_to_bank.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/patches/v12_0/move_bank_account_swift_number_to_bank.py b/erpnext/patches/v12_0/move_bank_account_swift_number_to_bank.py index 3c9758eb84a..1ddbae6cd29 100644 --- a/erpnext/patches/v12_0/move_bank_account_swift_number_to_bank.py +++ b/erpnext/patches/v12_0/move_bank_account_swift_number_to_bank.py @@ -4,7 +4,7 @@ import frappe def execute(): frappe.reload_doc('accounts', 'doctype', 'bank', force=1) - if frappe.db.table_exists('Bank') and frappe.db.table_exists('Bank Account'): + if frappe.db.table_exists('Bank') and frappe.db.table_exists('Bank Account') and frappe.db.has_column('Bank Account', 'swift_number'): frappe.db.sql(""" UPDATE `tabBank` b, `tabBank Account` ba SET b.swift_number = ba.swift_number, b.branch_code = ba.branch_code @@ -12,4 +12,4 @@ def execute(): """) frappe.reload_doc('accounts', 'doctype', 'bank_account') - frappe.reload_doc('accounts', 'doctype', 'payment_request') \ No newline at end of file + frappe.reload_doc('accounts', 'doctype', 'payment_request') From 52baf8f863539b5ad5063c640374dd31ee7e6408 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 29 May 2020 17:20:03 +0530 Subject: [PATCH 253/608] fix: Test Cases --- .../purchase_invoice/purchase_invoice.json | 2 +- .../purchase_invoice/test_purchase_invoice.py | 13 +++++++------ .../doctype/sales_invoice/test_records.json | 16 ++++++++++++---- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index b8d173bcc89..a80637e7a45 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -1316,7 +1316,7 @@ "idx": 204, "is_submittable": 1, "links": [], - "modified": "2020-04-18 13:05:25.199832", + "modified": "2020-05-29 13:05:25.199832", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice", diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 5cbfad20517..f61d27aee35 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -15,6 +15,7 @@ from erpnext.controllers.accounts_controller import get_payment_terms from erpnext.exceptions import InvalidCurrency from erpnext.stock.doctype.stock_entry.test_stock_entry import get_qty_after_transaction from erpnext.accounts.doctype.account.test_account import get_inventory_account +from erpnext.projects.doctype.project.test_project import make_project test_dependencies = ["Item", "Cost Center", "Payment Term", "Payment Terms Template"] test_ignore = ["Serial No"] @@ -434,6 +435,8 @@ class TestPurchaseInvoice(unittest.TestCase): ) def test_total_purchase_cost_for_project(self): + make_project({'project_name':'_Test Project'}) + existing_purchase_cost = frappe.db.sql("""select sum(base_net_amount) from `tabPurchase Invoice Item` where project = '_Test Project' and docstatus=1""") existing_purchase_cost = existing_purchase_cost and existing_purchase_cost[0][0] or 0 @@ -819,7 +822,7 @@ class TestPurchaseInvoice(unittest.TestCase): "Creditors - _TC": { "cost_center": cost_center }, - "_Test Account Cost for Goods Sold - _TC": { + "Cost of Goods Sold - _TC": { "cost_center": cost_center } } @@ -856,10 +859,8 @@ class TestPurchaseInvoice(unittest.TestCase): for gle in gl_entries: self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center) - - def test_purchase_invoice_with_project_link(self): - from erpnext.projects.doctype.project.test_project import make_project + def test_purchase_invoice_with_project_link(self): project = make_project({ 'project_name': 'Purchase Invoice Project', 'project_template_name': 'Test Project Template', @@ -890,9 +891,9 @@ class TestPurchaseInvoice(unittest.TestCase): debit_in_account_currency, credit_in_account_currency from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s order by account asc""", pi.name, as_dict=1) - + self.assertTrue(gl_entries) - + for gle in gl_entries: self.assertEqual(expected_values[gle.account]["project"], gle.project) diff --git a/erpnext/accounts/doctype/sales_invoice/test_records.json b/erpnext/accounts/doctype/sales_invoice/test_records.json index ebe6e3da8df..11ebe6a573a 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_records.json +++ b/erpnext/accounts/doctype/sales_invoice/test_records.json @@ -3,6 +3,7 @@ "company": "_Test Company", "conversion_rate": 1.0, "currency": "INR", + "cost_center": "_Test Cost Center - _TC", "customer": "_Test Customer", "customer_name": "_Test Customer", "debit_to": "_Test Receivable - _TC", @@ -37,7 +38,8 @@ "charge_type": "On Net Total", "description": "VAT", "doctype": "Sales Taxes and Charges", - "parentfield": "taxes", + "parentfield": "taxes", + "cost_center": "_Test Cost Center - _TC", "rate": 6 }, { @@ -45,7 +47,8 @@ "charge_type": "On Net Total", "description": "Service Tax", "doctype": "Sales Taxes and Charges", - "parentfield": "taxes", + "parentfield": "taxes", + "cost_center": "_Test Cost Center - _TC", "rate": 6.36 } ], @@ -76,6 +79,7 @@ "customer_name": "_Test Customer", "debit_to": "_Test Receivable - _TC", "doctype": "Sales Invoice", + "cost_center": "_Test Cost Center - _TC", "items": [ { "amount": 500.0, @@ -107,7 +111,8 @@ "charge_type": "On Net Total", "description": "VAT", "doctype": "Sales Taxes and Charges", - "parentfield": "taxes", + "parentfield": "taxes", + "cost_center": "_Test Cost Center - _TC", "rate": 16 }, { @@ -115,7 +120,8 @@ "charge_type": "On Net Total", "description": "Service Tax", "doctype": "Sales Taxes and Charges", - "parentfield": "taxes", + "parentfield": "taxes", + "cost_center": "_Test Cost Center - _TC", "rate": 10 } ], @@ -132,6 +138,7 @@ "customer_name": "_Test Customer", "debit_to": "_Test Receivable - _TC", "doctype": "Sales Invoice", + "cost_center": "_Test Cost Center - _TC", "items": [ { "cost_center": "_Test Cost Center - _TC", @@ -259,6 +266,7 @@ "customer_name": "_Test Customer", "debit_to": "_Test Receivable - _TC", "doctype": "Sales Invoice", + "cost_center": "_Test Cost Center - _TC", "items": [ { "cost_center": "_Test Cost Center - _TC", From dfa32a77c1bdfc23ab2722682b3f0412986fe7e5 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 29 May 2020 17:20:33 +0530 Subject: [PATCH 254/608] fix: Patch to remove Property Setter --- erpnext/patches.txt | 3 ++- erpnext/patches/v12_0/unhide_cost_center_field.py | 13 +++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 erpnext/patches/v12_0/unhide_cost_center_field.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index b0421f43c64..7125abfb696 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -694,4 +694,5 @@ execute:frappe.delete_doc("Report", "Department Analytics") execute:frappe.rename_doc("Desk Page", "Loan Management", "Loan", force=True) erpnext.patches.v12_0.update_uom_conversion_factor erpnext.patches.v13_0.delete_old_purchase_reports -erpnext.patches.v12_0.set_italian_import_supplier_invoice_permissions \ No newline at end of file +erpnext.patches.v12_0.set_italian_import_supplier_invoice_permissions +erpnext.patches.v12_0.unhide_cost_center_field \ No newline at end of file diff --git a/erpnext/patches/v12_0/unhide_cost_center_field.py b/erpnext/patches/v12_0/unhide_cost_center_field.py new file mode 100644 index 00000000000..6005ab70726 --- /dev/null +++ b/erpnext/patches/v12_0/unhide_cost_center_field.py @@ -0,0 +1,13 @@ +# Copyright (c) 2017, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe + +def execute(): + frappe.db.sql(""" + DELETE FROM `tabProperty Setter` + WHERE doc_type in ('Sales Invoice', 'Purchase Invoice', 'Payment Entry') + AND field_name = 'cost_center' + AND property = 'hidden' + """) \ No newline at end of file From 2186c223b9513d72b8d935410aca305d732761d9 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 29 May 2020 21:26:49 +0530 Subject: [PATCH 255/608] fix: Migrate standard_tax_exemption_amount if field exists (#22036) --- ..._slabs_from_payroll_period_to_income_tax_slab.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/erpnext/patches/v13_0/move_tax_slabs_from_payroll_period_to_income_tax_slab.py b/erpnext/patches/v13_0/move_tax_slabs_from_payroll_period_to_income_tax_slab.py index ec94cd01d1e..5ade8ca0f4c 100644 --- a/erpnext/patches/v13_0/move_tax_slabs_from_payroll_period_to_income_tax_slab.py +++ b/erpnext/patches/v13_0/move_tax_slabs_from_payroll_period_to_income_tax_slab.py @@ -14,15 +14,21 @@ def execute(): frappe.reload_doc("hr", "doctype", doctype) + standard_tax_exemption_amount_exists = frappe.db.has_column("Payroll Period", "standard_tax_exemption_amount") + + select_fields = "name, start_date, end_date" + if standard_tax_exemption_amount_exists: + select_fields = "name, start_date, end_date, standard_tax_exemption_amount" + for company in frappe.get_all("Company"): payroll_periods = frappe.db.sql(""" SELECT - name, start_date, end_date, standard_tax_exemption_amount + {0} FROM `tabPayroll Period` WHERE company=%s ORDER BY start_date DESC - """, company.name, as_dict = 1) + """.format(select_fields), company.name, as_dict = 1) for i, period in enumerate(payroll_periods): income_tax_slab = frappe.new_doc("Income Tax Slab") @@ -36,7 +42,8 @@ def execute(): income_tax_slab.effective_from = period.start_date income_tax_slab.company = company.name income_tax_slab.allow_tax_exemption = 1 - income_tax_slab.standard_tax_exemption_amount = period.standard_tax_exemption_amount + if standard_tax_exemption_amount_exists: + income_tax_slab.standard_tax_exemption_amount = period.standard_tax_exemption_amount income_tax_slab.flags.ignore_mandatory = True income_tax_slab.submit() From 7544160374d1b954ea62d890ff5203756fa0feaa Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Fri, 29 May 2020 21:27:24 +0530 Subject: [PATCH 256/608] fix: routing operations table is blank on pull of operations in BOM (#22039) --- erpnext/manufacturing/doctype/bom/bom.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 1bdac5731ef..3253a496ed0 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -113,7 +113,13 @@ class BOM(WebsiteGenerator): self.set("operations", []) for d in frappe.get_all("BOM Operation", fields = ["*"], filters = {'parenttype': 'Routing', 'parent': self.routing}): - child = self.append('operations', d) + child = self.append('operations', { + "operation": d.operation, + "workstation": d.workstation, + "description": d.description, + "time_in_mins": d.time_in_mins, + "batch_size": d.batch_size + }) child.hour_rate = flt(d.hour_rate / self.conversion_rate, 2) def set_bom_material_details(self): From 2c3c8aaa2dc9599522beb2e3d4d53ede68ca70d8 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Fri, 29 May 2020 21:55:38 +0530 Subject: [PATCH 257/608] fix: submitted sales order can be updated with no permission --- erpnext/controllers/accounts_controller.py | 46 ++++++++++++++----- erpnext/public/js/utils.js | 7 +++ .../doctype/sales_order/test_sales_order.py | 16 +++++++ .../sales_order_item/sales_order_item.json | 28 +++++------ 4 files changed, 71 insertions(+), 26 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index eecb143d556..ca78070bf4e 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1137,8 +1137,8 @@ def set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, child_item.item_name = item.item_name child_item.description = item.description child_item.delivery_date = trans_item.get('delivery_date') or p_doc.delivery_date + child_item.conversion_factor = flt(d.get('conversion_factor')) or get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0 child_item.uom = item.stock_uom - child_item.conversion_factor = get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0 child_item.warehouse = get_item_warehouse(item, p_doc, overwrite_warehouse=True) if not child_item.warehouse: frappe.throw(_("Cannot find {} for item {}. Please set the same in Item Master or Stock Settings.") @@ -1157,8 +1157,8 @@ def set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docna child_item.item_name = item.item_name child_item.description = item.description child_item.schedule_date = trans_item.get('schedule_date') or p_doc.schedule_date + child_item.conversion_factor = flt(d.get('conversion_factor')) or get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0 child_item.uom = item.stock_uom - child_item.conversion_factor = get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0 child_item.base_rate = 1 # Initiallize value will update in parent validation child_item.base_amount = 1 # Initiallize value will update in parent validation return child_item @@ -1190,6 +1190,26 @@ def check_and_delete_children(parent, data): @frappe.whitelist() def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, child_docname="items"): + def check_permissions(doc, perm_type='create'): + try: + doc.check_permission(perm_type) + except: + action = "add" if perm_type == 'create' else "update" + frappe.throw(_("You do not have permissions to {} items in a Sales Order.").format(action), title=_("Insufficient Permissions")) + + def get_new_child_item(): + if parent_doctype == "Sales Order": + return set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, d) + if parent_doctype == "Purchase Order": + return set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, d) + + def validate_quantity(child_item, d): + if parent_doctype == "Sales Order" and flt(d.get("qty")) < flt(child_item.delivered_qty): + frappe.throw(_("Cannot set quantity less than delivered quantity")) + + if parent_doctype == "Purchase Order" and flt(d.get("qty")) < flt(child_item.received_qty): + frappe.throw(_("Cannot set quantity less than received quantity")) + data = json.loads(trans_items) sales_doctypes = ['Sales Order', 'Sales Invoice', 'Delivery Note', 'Quotation'] @@ -1201,20 +1221,19 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil new_child_flag = False if not d.get("docname"): new_child_flag = True - if parent_doctype == "Sales Order": - child_item = set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, d) - if parent_doctype == "Purchase Order": - child_item = set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, d) + check_permissions(parent, 'create') + child_item = get_new_child_item() else: + check_permissions(parent, 'write') child_item = frappe.get_doc(parent_doctype + ' Item', d.get("docname")) - if flt(child_item.get("rate")) == flt(d.get("rate")) and flt(child_item.get("qty")) == flt(d.get("qty")): + + rate_unchanged = flt(child_item.get("rate")) == flt(d.get("rate")) + qty_unchanged = flt(child_item.get("qty")) == flt(d.get("qty")) + conversion_factor_unchanged = flt(child_item.get("conversion_factor")) == flt(d.get("conversion_factor")) + if rate_unchanged and qty_unchanged and conversion_factor_changed: continue - if parent_doctype == "Sales Order" and flt(d.get("qty")) < flt(child_item.delivered_qty): - frappe.throw(_("Cannot set quantity less than delivered quantity")) - - if parent_doctype == "Purchase Order" and flt(d.get("qty")) < flt(child_item.received_qty): - frappe.throw(_("Cannot set quantity less than received quantity")) + validate_quantity(child_item, d) child_item.qty = flt(d.get("qty")) precision = child_item.precision("rate") or 2 @@ -1224,6 +1243,9 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil .format(child_item.idx, child_item.item_code)) else: child_item.rate = flt(d.get("rate")) + + if d.get("conversion_factor"): + child_item.conversion_factor = flt(d.get('conversion_factor')) if flt(child_item.price_list_rate): if flt(child_item.rate) > flt(child_item.price_list_rate): diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index 58969f2a9fd..a487f83fe64 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -472,6 +472,12 @@ erpnext.utils.update_child_items = function(opts) { in_list_view: 1, label: frm.doc.doctype == 'Sales Order' ? __("Delivery Date") : __("Reqd by date") }) + fields.splice(3, 0, { + fieldtype: 'Float', + fieldname: "conversion_factor", + in_list_view: 1, + label: __("UOM Conversion Factor") + }) } const dialog = new frappe.ui.Dialog({ @@ -519,6 +525,7 @@ erpnext.utils.update_child_items = function(opts) { "item_code": d.item_code, "delivery_date": d.delivery_date, "schedule_date": d.schedule_date, + "conversion_factor": d.conversion_factor, "qty": d.qty, "rate": d.rate, }); diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index b8b0d404e50..90f9b094d1e 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -400,6 +400,22 @@ class TestSalesOrder(unittest.TestCase): trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 200, 'qty' : 2, 'docname': so.items[0].name}]) self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Sales Order', trans_item, so.name) + def test_update_child_qty_rate_perm(self): + so = make_sales_order(item_code= "_Test Item", qty=4) + + user = 'test@example.com' + test_user = frappe.get_doc('User', user) + test_user.add_roles("Accounts User") + frappe.set_user(user) + + # update qty + trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 200, 'qty' : 7, 'docname': so.items[0].name}]) + self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Sales Order', trans_item, so.name) + + # add new item + trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 100, 'qty' : 2}]) + self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Sales Order', trans_item, so.name) + def test_warehouse_user(self): frappe.permissions.add_user_permission("Warehouse", "_Test Warehouse 1 - _TC", "test@example.com") frappe.permissions.add_user_permission("Warehouse", "_Test Warehouse 2 - _TC1", "test2@example.com") diff --git a/erpnext/selling/doctype/sales_order_item/sales_order_item.json b/erpnext/selling/doctype/sales_order_item/sales_order_item.json index e59349926e6..18691591cd5 100644 --- a/erpnext/selling/doctype/sales_order_item/sales_order_item.json +++ b/erpnext/selling/doctype/sales_order_item/sales_order_item.json @@ -214,7 +214,6 @@ "fieldtype": "Link", "label": "UOM", "options": "UOM", - "print_hide": 0, "reqd": 1 }, { @@ -769,22 +768,23 @@ "label": "Against Blanket Order" }, { - "fieldname": "bom_no", - "fieldtype": "Link", - "label": "BOM No", - "no_copy": 1, - "options": "BOM", - "print_hide": 1 - }, - { - "fieldname": "manufacturing_section_section", - "fieldtype": "Section Break", - "label": "Manufacturing Section" - } + "fieldname": "bom_no", + "fieldtype": "Link", + "label": "BOM No", + "no_copy": 1, + "options": "BOM", + "print_hide": 1 + }, + { + "fieldname": "manufacturing_section_section", + "fieldtype": "Section Break", + "label": "Manufacturing Section" + } ], "idx": 1, "istable": 1, - "modified": "2020-05-15 18:13:43.006493", + "links": [], + "modified": "2020-05-29 20:54:32.309460", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order Item", From 5da8b98a048bf16cd4bd396f5a5eaf4f2d07d150 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Fri, 29 May 2020 21:59:01 +0530 Subject: [PATCH 258/608] fix: label --- erpnext/public/js/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index a487f83fe64..1803fcdfd78 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -476,7 +476,7 @@ erpnext.utils.update_child_items = function(opts) { fieldtype: 'Float', fieldname: "conversion_factor", in_list_view: 1, - label: __("UOM Conversion Factor") + label: __("Conversion Factor") }) } From c579b0879ec170ea1a24cbaed8b7cc8d6c655da3 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Fri, 29 May 2020 22:21:50 +0530 Subject: [PATCH 259/608] fix: cannot update delivery date --- erpnext/controllers/accounts_controller.py | 24 ++++++++++++++++++---- erpnext/public/js/utils.js | 3 ++- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index ca78070bf4e..43a27333236 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1227,10 +1227,20 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil check_permissions(parent, 'write') child_item = frappe.get_doc(parent_doctype + ' Item', d.get("docname")) - rate_unchanged = flt(child_item.get("rate")) == flt(d.get("rate")) - qty_unchanged = flt(child_item.get("qty")) == flt(d.get("qty")) - conversion_factor_unchanged = flt(child_item.get("conversion_factor")) == flt(d.get("conversion_factor")) - if rate_unchanged and qty_unchanged and conversion_factor_changed: + prev_rate, new_rate = flt(child_item.get("rate")), flt(d.get("rate")) + prev_qty, new_qty = flt(child_item.get("qty")), flt(d.get("qty")) + prev_con_fac, new_con_fac = flt(child_item.get("conversion_factor")), flt(d.get("conversion_factor")) + + if parent_doctype == 'Sales Order': + prev_date, new_date = child_item.get("delivery_date"), d.get("delivery_date") + elif parent_doctype == 'Purchase Order': + prev_date, new_date = child_item.get("schedule_date") == d.get("schedule_date") + + rate_unchanged = prev_rate == new_rate + qty_unchanged = prev_qty == prev_qty + conversion_factor_unchanged = prev_con_fac == new_con_fac + date_unchanged = prev_date == new_date if prev_date and new_date else False # in case of delivery note etc + if rate_unchanged and qty_unchanged and conversion_factor_unchanged and date_unchanged: continue validate_quantity(child_item, d) @@ -1247,6 +1257,12 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil if d.get("conversion_factor"): child_item.conversion_factor = flt(d.get('conversion_factor')) + if d.get("delivery_date") and parent_doctype == 'Sales Order': + child_item.delivery_date = d.get('delivery_date') + + if d.get("schedule_date") and parent_doctype == 'Purchase Order': + child_item.schedule_date = d.get('schedule_date') + if flt(child_item.price_list_rate): if flt(child_item.rate) > flt(child_item.price_list_rate): # if rate is greater than price_list_rate, set margin diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index 1803fcdfd78..b10ec98d4cc 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -470,7 +470,8 @@ erpnext.utils.update_child_items = function(opts) { fieldtype: 'Date', fieldname: frm.doc.doctype == 'Sales Order' ? "delivery_date" : "schedule_date", in_list_view: 1, - label: frm.doc.doctype == 'Sales Order' ? __("Delivery Date") : __("Reqd by date") + label: frm.doc.doctype == 'Sales Order' ? __("Delivery Date") : __("Reqd by date"), + reqd: 1 }) fields.splice(3, 0, { fieldtype: 'Float', From a655db6a79e58f152229d84f0bb08d45ee69b392 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Fri, 29 May 2020 22:44:59 +0530 Subject: [PATCH 260/608] fix: unexpected removal of print hide field --- erpnext/selling/doctype/sales_order_item/sales_order_item.json | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/selling/doctype/sales_order_item/sales_order_item.json b/erpnext/selling/doctype/sales_order_item/sales_order_item.json index 18691591cd5..eff17f8bc78 100644 --- a/erpnext/selling/doctype/sales_order_item/sales_order_item.json +++ b/erpnext/selling/doctype/sales_order_item/sales_order_item.json @@ -214,6 +214,7 @@ "fieldtype": "Link", "label": "UOM", "options": "UOM", + "print_hide": 0, "reqd": 1 }, { From 40694c98aaedea789f9db4a997a46a63affeadde Mon Sep 17 00:00:00 2001 From: Abhishek Balam Date: Sat, 30 May 2020 01:02:06 +0530 Subject: [PATCH 261/608] fix(HR) : Filter Leave Type based on allocation for a particular employee (#22050) * table was showing empty with just headers when no leaves allocated, fixed template code * added filters on Leave Type based on leave allocation for a particular employee and to/from dates --- erpnext/hr/doctype/leave_application/leave_application.js | 7 +++++++ .../leave_application/leave_application_dashboard.html | 7 +++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/erpnext/hr/doctype/leave_application/leave_application.js b/erpnext/hr/doctype/leave_application/leave_application.js index 1f50e27098c..473aae6f42f 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.js +++ b/erpnext/hr/doctype/leave_application/leave_application.js @@ -67,6 +67,13 @@ frappe.ui.form.on("Leave Application", { }) ); frm.dashboard.show(); + frm.set_query('leave_type', function(){ + return { + filters : [ + ['leave_type_name', 'in', Object.keys(leave_details)] + ] + } + }); } }, diff --git a/erpnext/hr/doctype/leave_application/leave_application_dashboard.html b/erpnext/hr/doctype/leave_application/leave_application_dashboard.html index 295f3b43419..d30e3b9f9c6 100644 --- a/erpnext/hr/doctype/leave_application/leave_application_dashboard.html +++ b/erpnext/hr/doctype/leave_application/leave_application_dashboard.html @@ -1,5 +1,5 @@ -{% if data %} +{% if not jQuery.isEmptyObject(data) %}
{{ __("Allocated Leaves") }}
{{ __("Leave Type") }}{{ __("Total Allocated Leaves") }}{{ __("Used Leaves") }}{{ __("Pending Leaves") }}{{ __("Available Leaves") }}{{ __("Leave Type") }}{{ __("Total Allocated Leaves") }}{{ __("Expired Leaves") }}{{ __("Used Leaves") }}{{ __("Pending Leaves") }}{{ __("Available Leaves") }}
{%= key %} {%= value["total_leaves"] %} {%= value["expired_leaves"] %} {%= value["leaves_taken"] %} {%= value["pending_leaves"] %} {%= value["remaining_leaves"] %}
@@ -11,7 +11,6 @@ - {% for(const [key, value] of Object.entries(data)) { %} @@ -26,6 +25,6 @@ {% } %}
{{ __("Pending Leaves") }} {{ __("Available Leaves") }}
-{% } else { %} +{% else %}

No Leaves have been allocated.

-{% } %} \ No newline at end of file +{% endif %} \ No newline at end of file From f8550790fa8850c73ab1489379aa17e9d771091a Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 30 May 2020 11:37:36 +0530 Subject: [PATCH 262/608] This reverts commit aa196c0a667ee7c3f2400fc7bd65319ccbb25a5a --- .../purchase_invoice_item/purchase_invoice_item.json | 2 +- erpnext/assets/doctype/asset/depreciation.py | 3 ++- erpnext/crm/doctype/opportunity/test_records.json | 1 - erpnext/manufacturing/doctype/bom/test_records.json | 9 +++------ .../setup/setup_wizard/operations/install_fixtures.py | 1 + erpnext/www/book-appointment/__init__.py | 0 erpnext/www/book-appointment/verify/__init__.py | 0 7 files changed, 7 insertions(+), 9 deletions(-) delete mode 100644 erpnext/www/book-appointment/__init__.py delete mode 100644 erpnext/www/book-appointment/verify/__init__.py diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json index 52a5be09843..cfbe06ad029 100644 --- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json +++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json @@ -753,7 +753,7 @@ "depends_on": "is_fixed_asset", "fetch_from": "item_code.asset_category", "fieldname": "asset_category", - "fieldtype": "Link", + "fieldtype": "Data", "label": "Asset Category", "options": "Asset Category", "read_only": 1 diff --git a/erpnext/assets/doctype/asset/depreciation.py b/erpnext/assets/doctype/asset/depreciation.py index ad671ba0f2c..c50211e6ab1 100644 --- a/erpnext/assets/doctype/asset/depreciation.py +++ b/erpnext/assets/doctype/asset/depreciation.py @@ -58,7 +58,8 @@ def make_depreciation_entry(asset_name, date=None): "account": accumulated_depreciation_account, "credit_in_account_currency": d.depreciation_amount, "reference_type": "Asset", - "reference_name": asset.name + "reference_name": asset.name, + "cost_center": "" } debit_entry = { diff --git a/erpnext/crm/doctype/opportunity/test_records.json b/erpnext/crm/doctype/opportunity/test_records.json index 0a6c29b637c..a1e0ad921b4 100644 --- a/erpnext/crm/doctype/opportunity/test_records.json +++ b/erpnext/crm/doctype/opportunity/test_records.json @@ -2,7 +2,6 @@ { "doctype": "Opportunity", "name": "_Test Opportunity 1", - "company": "Wind Power LLC", "opportunity_from": "Lead", "enquiry_type": "Sales", "party_name": "_T-Lead-00001", diff --git a/erpnext/manufacturing/doctype/bom/test_records.json b/erpnext/manufacturing/doctype/bom/test_records.json index 3913268e1dc..25730f9b9f4 100644 --- a/erpnext/manufacturing/doctype/bom/test_records.json +++ b/erpnext/manufacturing/doctype/bom/test_records.json @@ -32,8 +32,7 @@ "is_active": 1, "is_default": 1, "item": "_Test Item Home Desktop Manufactured", - "quantity": 1.0, - "company": "_Test Company" + "quantity": 1.0 }, { "scrap_items":[ @@ -79,8 +78,7 @@ "is_default": 1, "currency": "USD", "item": "_Test FG Item", - "quantity": 1.0, - "company":"_Test Company" + "quantity": 1.0 }, { "operations": [ @@ -162,7 +160,6 @@ "currency": "USD", "item": "_Test Variant Item", "quantity": 1.0, - "with_operations": 1, - "company": "_Test Company" + "with_operations": 1 } ] diff --git a/erpnext/setup/setup_wizard/operations/install_fixtures.py b/erpnext/setup/setup_wizard/operations/install_fixtures.py index 56899d5fbb5..0d70d91f739 100644 --- a/erpnext/setup/setup_wizard/operations/install_fixtures.py +++ b/erpnext/setup/setup_wizard/operations/install_fixtures.py @@ -437,6 +437,7 @@ def install_defaults(args=None): global_defaults.update({ 'current_fiscal_year': current_fiscal_year.name, 'default_currency': args.get('currency'), + 'default_company':args.get('company_name') , "country": args.get("country"), }) diff --git a/erpnext/www/book-appointment/__init__.py b/erpnext/www/book-appointment/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/erpnext/www/book-appointment/verify/__init__.py b/erpnext/www/book-appointment/verify/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 From 36f7710356a2fa55db8c5401b4db48bb098b2a28 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 30 May 2020 11:38:04 +0530 Subject: [PATCH 263/608] Revert "fix: tests depending on global default company" This reverts commit 1f20c99ecfb51675d7182588001bb3c1d581f415. --- .../sales_invoice/test_sales_invoice.py | 1 - .../doctype/subscription/test_subscription.py | 43 ------------------- erpnext/assets/doctype/asset/test_asset.py | 3 +- .../test_procurement_tracker.py | 6 +-- erpnext/controllers/tests/test_mapper.py | 2 - .../doctype/opportunity/test_opportunity.py | 1 - .../plaid_settings/test_plaid_settings.py | 2 +- .../inpatient_record/test_inpatient_record.py | 3 +- .../test_compensatory_leave_request.py | 3 +- .../hr/doctype/department/test_department.py | 2 +- erpnext/hr/doctype/employee/test_employee.py | 2 +- ...test_employee_tax_exemption_declaration.py | 2 +- .../expense_claim/test_expense_claim.py | 1 - .../hr/doctype/job_offer/test_job_offer.py | 4 +- .../doctype/salary_slip/test_salary_slip.py | 2 +- .../salary_structure/test_salary_structure.py | 2 +- .../training_event/test_training_event.py | 3 +- .../delivery_note/test_delivery_note.py | 27 +++++++++++- .../delivery_trip/test_delivery_trip.py | 2 +- .../purchase_receipt/test_purchase_receipt.py | 24 +++++++++++ .../stock/doctype/serial_no/test_serial_no.py | 1 - 21 files changed, 64 insertions(+), 72 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index fc53f8818af..a2f9040670f 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -1065,7 +1065,6 @@ class TestSalesInvoice(unittest.TestCase): serial_no = frappe.get_doc({ "doctype": "Serial No", "item_code": "_Test Serialized Item With Series", - "company": "Wind Power LLC", "serial_no": make_autoname("SR", "Serial No") }) serial_no.save() diff --git a/erpnext/accounts/doctype/subscription/test_subscription.py b/erpnext/accounts/doctype/subscription/test_subscription.py index a0784e6719d..3d96f233b40 100644 --- a/erpnext/accounts/doctype/subscription/test_subscription.py +++ b/erpnext/accounts/doctype/subscription/test_subscription.py @@ -101,7 +101,6 @@ class TestSubscription(unittest.TestCase): subscription.delete() def test_invoice_is_generated_at_end_of_billing_period(self): - frappe.db.set_value("Global Defaults", None, "default_company", "_Test Company") subscription = frappe.new_doc('Subscription') subscription.customer = '_Test Customer' subscription.start = '2018-01-01' @@ -117,10 +116,8 @@ class TestSubscription(unittest.TestCase): self.assertEqual(subscription.current_invoice_start, '2018-01-01') self.assertEqual(subscription.status, 'Past Due Date') subscription.delete() - frappe.db.set_value("Global Defaults", None, "default_company", None) def test_status_goes_back_to_active_after_invoice_is_paid(self): - frappe.db.set_value("Global Defaults", None, "default_company", "_Test Company") subscription = frappe.new_doc('Subscription') subscription.customer = '_Test Customer' subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1}) @@ -144,10 +141,8 @@ class TestSubscription(unittest.TestCase): self.assertEqual(len(subscription.invoices), 1) subscription.delete() - frappe.db.set_value("Global Defaults", None, "default_company", None) def test_subscription_cancel_after_grace_period(self): - frappe.db.set_value("Global Defaults", None, "default_company", "_Test Company") settings = frappe.get_single('Subscription Settings') default_grace_period_action = settings.cancel_after_grace settings.cancel_after_grace = 1 @@ -169,11 +164,8 @@ class TestSubscription(unittest.TestCase): settings.cancel_after_grace = default_grace_period_action settings.save() subscription.delete() - frappe.db.set_value("Global Defaults", None, "default_company", None) def test_subscription_unpaid_after_grace_period(self): - frappe.db.set_value("Global Defaults", None, "default_company", "_Test Company") - settings = frappe.get_single('Subscription Settings') default_grace_period_action = settings.cancel_after_grace settings.cancel_after_grace = 0 @@ -195,11 +187,8 @@ class TestSubscription(unittest.TestCase): settings.cancel_after_grace = default_grace_period_action settings.save() subscription.delete() - frappe.db.set_value("Global Defaults", None, "default_company", None) def test_subscription_invoice_days_until_due(self): - frappe.db.set_value("Global Defaults", None, "default_company", "_Test Company") - subscription = frappe.new_doc('Subscription') subscription.customer = '_Test Customer' subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1}) @@ -211,11 +200,8 @@ class TestSubscription(unittest.TestCase): self.assertEqual(subscription.status, 'Active') subscription.delete() - frappe.db.set_value("Global Defaults", None, "default_company", None) def test_subscription_is_past_due_doesnt_change_within_grace_period(self): - frappe.db.set_value("Global Defaults", None, "default_company", "_Test Company") - settings = frappe.get_single('Subscription Settings') grace_period = settings.grace_period settings.grace_period = 1000 @@ -243,7 +229,6 @@ class TestSubscription(unittest.TestCase): settings.grace_period = grace_period settings.save() subscription.delete() - frappe.db.set_value("Global Defaults", None, "default_company", None) def test_subscription_remains_active_during_invoice_period(self): subscription = frappe.new_doc('Subscription') @@ -272,7 +257,6 @@ class TestSubscription(unittest.TestCase): subscription.delete() def test_subscription_cancelation(self): - frappe.db.set_value("Global Defaults", None, "default_company", "_Test Company") subscription = frappe.new_doc('Subscription') subscription.customer = '_Test Customer' subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1}) @@ -282,10 +266,8 @@ class TestSubscription(unittest.TestCase): self.assertEqual(subscription.status, 'Cancelled') subscription.delete() - frappe.db.set_value("Global Defaults", None, "default_company", None) def test_subscription_cancellation_invoices(self): - frappe.db.set_value("Global Defaults", None, "default_company", "_Test Company") settings = frappe.get_single('Subscription Settings') to_prorate = settings.prorate settings.prorate = 1 @@ -319,11 +301,8 @@ class TestSubscription(unittest.TestCase): subscription.delete() settings.prorate = to_prorate settings.save() - frappe.db.set_value("Global Defaults", None, "default_company", None) def test_subscription_cancellation_invoices_with_prorata_false(self): - frappe.db.set_value("Global Defaults", None, "default_company", "_Test Company") - settings = frappe.get_single('Subscription Settings') to_prorate = settings.prorate settings.prorate = 0 @@ -342,11 +321,8 @@ class TestSubscription(unittest.TestCase): settings.save() subscription.delete() - frappe.db.set_value("Global Defaults", None, "default_company", None) def test_subscription_cancellation_invoices_with_prorata_true(self): - frappe.db.set_value("Global Defaults", None, "default_company", "_Test Company") - settings = frappe.get_single('Subscription Settings') to_prorate = settings.prorate settings.prorate = 1 @@ -369,10 +345,8 @@ class TestSubscription(unittest.TestCase): settings.save() subscription.delete() - frappe.db.set_value("Global Defaults", None, "default_company", None) def test_subcription_cancellation_and_process(self): - frappe.db.set_value("Global Defaults", None, "default_company", "_Test Company") settings = frappe.get_single('Subscription Settings') default_grace_period_action = settings.cancel_after_grace settings.cancel_after_grace = 1 @@ -404,11 +378,8 @@ class TestSubscription(unittest.TestCase): settings.cancel_after_grace = default_grace_period_action settings.save() subscription.delete() - frappe.db.set_value("Global Defaults", None, "default_company", None) def test_subscription_restart_and_process(self): - frappe.db.set_value("Global Defaults", None, "default_company", "_Test Company") - settings = frappe.get_single('Subscription Settings') default_grace_period_action = settings.cancel_after_grace settings.grace_period = 0 @@ -445,11 +416,8 @@ class TestSubscription(unittest.TestCase): settings.cancel_after_grace = default_grace_period_action settings.save() subscription.delete() - frappe.db.set_value("Global Defaults", None, "default_company", None) def test_subscription_unpaid_back_to_active(self): - frappe.db.set_value("Global Defaults", None, "default_company", "_Test Company") - settings = frappe.get_single('Subscription Settings') default_grace_period_action = settings.cancel_after_grace settings.cancel_after_grace = 0 @@ -482,7 +450,6 @@ class TestSubscription(unittest.TestCase): settings.cancel_after_grace = default_grace_period_action settings.save() subscription.delete() - frappe.db.set_value("Global Defaults", None, "default_company", None) def test_restart_active_subscription(self): subscription = frappe.new_doc('Subscription') @@ -495,8 +462,6 @@ class TestSubscription(unittest.TestCase): subscription.delete() def test_subscription_invoice_discount_percentage(self): - frappe.db.set_value("Global Defaults", None, "default_company", "_Test Company") - subscription = frappe.new_doc('Subscription') subscription.customer = '_Test Customer' subscription.additional_discount_percentage = 10 @@ -510,11 +475,8 @@ class TestSubscription(unittest.TestCase): self.assertEqual(invoice.apply_discount_on, 'Grand Total') subscription.delete() - frappe.db.set_value("Global Defaults", None, "default_company", None) def test_subscription_invoice_discount_amount(self): - frappe.db.set_value("Global Defaults", None, "default_company", "_Test Company") - subscription = frappe.new_doc('Subscription') subscription.customer = '_Test Customer' subscription.additional_discount_amount = 11 @@ -528,12 +490,10 @@ class TestSubscription(unittest.TestCase): self.assertEqual(invoice.apply_discount_on, 'Grand Total') subscription.delete() - frappe.db.set_value("Global Defaults", None, "default_company", None) def test_prepaid_subscriptions(self): # Create a non pre-billed subscription, processing should not create # invoices. - frappe.db.set_value("Global Defaults", None, "default_company", "_Test Company") subscription = frappe.new_doc('Subscription') subscription.customer = '_Test Customer' subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1}) @@ -549,10 +509,8 @@ class TestSubscription(unittest.TestCase): subscription.process() self.assertEqual(len(subscription.invoices), 1) - frappe.db.set_value("Global Defaults", None, "default_company", None) def test_prepaid_subscriptions_with_prorate_true(self): - frappe.db.set_value("Global Defaults", None, "default_company", "_Test Company") settings = frappe.get_single('Subscription Settings') to_prorate = settings.prorate settings.prorate = 1 @@ -580,4 +538,3 @@ class TestSubscription(unittest.TestCase): settings.save() subscription.delete() - frappe.db.set_value("Global Defaults", None, "default_company", None) \ No newline at end of file diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index 9490c8cbc76..aed78e7746d 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -75,7 +75,6 @@ class TestAsset(unittest.TestCase): 'qty': 1, 'asset': asset.name }) - doc.company = 'Wind Power LLC' doc.set_missing_values() self.assertEquals(doc.items[0].is_fixed_asset, 1) @@ -667,7 +666,7 @@ def create_asset(**args): "asset_name": args.asset_name or "Macbook Pro 1", "asset_category": "Computers", "item_code": args.item_code or "Macbook Pro", - "company": args.company or "_Test Company", + "company": args.company or"_Test Company", "purchase_date": "2015-01-01", "calculate_depreciation": 0, "gross_purchase_amount": 100000, diff --git a/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py b/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py index 0c840ecbd83..bebf0ccec56 100644 --- a/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py +++ b/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py @@ -36,18 +36,16 @@ class TestProcurementTracker(unittest.TestCase): mr = make_material_request(company="_Test Procurement Company", warehouse=warehouse) po = make_purchase_order(mr.name) po.supplier = "_Test Supplier" - po.company = "_Test Procurement Company" - po.get("items")[0].cost_center = "_Test Cost Center - _TPC" + po.get("items")[0].cost_center = "_Test Cost Center - _TC" po.submit() pr = make_purchase_receipt(po.name) - pr.company = "_Test Procurement Company" pr.submit() frappe.db.commit() date_obj = datetime.date(datetime.now()) expected_data = { "material_request_date": date_obj, - "cost_center": "_Test Cost Center - _TPC", + "cost_center": "_Test Cost Center - _TC", "project": None, "requesting_site": "_Test Procurement Warehouse - _TPC", "requestor": "Administrator", diff --git a/erpnext/controllers/tests/test_mapper.py b/erpnext/controllers/tests/test_mapper.py index 08248be0e95..8839e002a4c 100644 --- a/erpnext/controllers/tests/test_mapper.py +++ b/erpnext/controllers/tests/test_mapper.py @@ -43,7 +43,6 @@ class TestMapper(unittest.TestCase): qtn = frappe.get_doc({ "doctype": "Quotation", "quotation_to": "Customer", - "company": "_Test Company", "party_name": customer, "order_type": "Sales", "transaction_date" : nowdate(), @@ -60,7 +59,6 @@ class TestMapper(unittest.TestCase): "base_amount": 1000.0, "base_rate": 100.0, "description": "CPU", - "company": "_Test Company", "doctype": "Sales Order Item", "item_code": "_Test Item Home Desktop 100", "item_name": "CPU", diff --git a/erpnext/crm/doctype/opportunity/test_opportunity.py b/erpnext/crm/doctype/opportunity/test_opportunity.py index fc852b19a1f..33d90076c4a 100644 --- a/erpnext/crm/doctype/opportunity/test_opportunity.py +++ b/erpnext/crm/doctype/opportunity/test_opportunity.py @@ -30,7 +30,6 @@ class TestOpportunity(unittest.TestCase): new_lead_email_id = "new{}@example.com".format(random_string(5)) args = { "doctype": "Opportunity", - "company": "_Test Company", "contact_email": new_lead_email_id, "opportunity_type": "Sales", "with_items": 0, diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/test_plaid_settings.py b/erpnext/erpnext_integrations/doctype/plaid_settings/test_plaid_settings.py index 8be2510b11a..1a063d6b6f8 100644 --- a/erpnext/erpnext_integrations/doctype/plaid_settings/test_plaid_settings.py +++ b/erpnext/erpnext_integrations/doctype/plaid_settings/test_plaid_settings.py @@ -108,7 +108,7 @@ class TestPlaidSettings(unittest.TestCase): } bank = json.dumps(frappe.get_doc("Bank", "Citi").as_dict(), default=json_handler) - company = frappe.db.get_single_value('Global Defaults', 'default_company') or '_Test Company' + company = frappe.db.get_single_value('Global Defaults', 'default_company') if frappe.db.get_value("Company", company, "default_bank_account") is None: frappe.db.set_value("Company", company, "default_bank_account", get_default_bank_cash_account(company, "Cash").get("account")) diff --git a/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py b/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py index c4826c94c3e..4c2d3f692a2 100644 --- a/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py +++ b/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py @@ -83,8 +83,7 @@ def get_healthcare_service_unit(): service_unit.service_unit_type = get_service_unit_type() service_unit.inpatient_occupancy = 1 service_unit.occupancy_status = "Vacant" - service_unit.is_group = 0, - service_unit.company = "_Test Company" + service_unit.is_group = 0 service_unit_parent_name = frappe.db.exists({ "doctype": "Healthcare Service Unit", "healthcare_service_unit_name": "All Healthcare Service Units", diff --git a/erpnext/hr/doctype/compensatory_leave_request/test_compensatory_leave_request.py b/erpnext/hr/doctype/compensatory_leave_request/test_compensatory_leave_request.py index 1181192cbe5..1615ab30f1d 100644 --- a/erpnext/hr/doctype/compensatory_leave_request/test_compensatory_leave_request.py +++ b/erpnext/hr/doctype/compensatory_leave_request/test_compensatory_leave_request.py @@ -104,8 +104,7 @@ def mark_attendance(employee, date=today(), status='Present'): "doctype": "Attendance", "employee": employee.name, "attendance_date": date, - "status": status, - "company": "_Test Company" + "status": status }) attendance.save() attendance.submit() diff --git a/erpnext/hr/doctype/department/test_department.py b/erpnext/hr/doctype/department/test_department.py index a6e8aae625e..2eeca26e303 100644 --- a/erpnext/hr/doctype/department/test_department.py +++ b/erpnext/hr/doctype/department/test_department.py @@ -16,7 +16,7 @@ def create_department(department_name, parent_department=None): 'is_group': 0, 'parent_department': parent_department, 'department_name': department_name, - 'company': frappe.defaults.get_defaults().company or 'Wind Power LLC' + 'company': frappe.defaults.get_defaults().company }).insert() return doc diff --git a/erpnext/hr/doctype/employee/test_employee.py b/erpnext/hr/doctype/employee/test_employee.py index b92b8e7e4e2..f4b214adc3c 100644 --- a/erpnext/hr/doctype/employee/test_employee.py +++ b/erpnext/hr/doctype/employee/test_employee.py @@ -60,7 +60,7 @@ def make_employee(user, company=None, **kwargs): "doctype": "Employee", "naming_series": "EMP-", "first_name": user, - "company": company or erpnext.get_default_company() or 'Wind Power LLC', + "company": company or erpnext.get_default_company(), "user_id": user, "date_of_birth": "1990-05-08", "date_of_joining": "2013-01-01", diff --git a/erpnext/hr/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py b/erpnext/hr/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py index e22948c655d..867c35b1b80 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py +++ b/erpnext/hr/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py @@ -88,7 +88,7 @@ def create_payroll_period(): payroll_period = frappe.get_doc(dict( doctype = 'Payroll Period', name = "_Test Payroll Period", - company = erpnext.get_default_company() or 'Wind Power LLC', + company = erpnext.get_default_company(), start_date = date(date.today().year, 1, 1), end_date = date(date.today().year, 12, 31) )).insert() diff --git a/erpnext/hr/doctype/expense_claim/test_expense_claim.py b/erpnext/hr/doctype/expense_claim/test_expense_claim.py index e7ee108142a..6e97f0513d6 100644 --- a/erpnext/hr/doctype/expense_claim/test_expense_claim.py +++ b/erpnext/hr/doctype/expense_claim/test_expense_claim.py @@ -94,7 +94,6 @@ class TestExpenseClaim(unittest.TestCase): payable_account = get_payable_account(company_name) expense_claim = frappe.get_doc({ "doctype": "Expense Claim", - "company": "_Test Company", "employee": "_T-Employee-00001", "payable_account": payable_account, "approval_status": "Rejected", diff --git a/erpnext/hr/doctype/job_offer/test_job_offer.py b/erpnext/hr/doctype/job_offer/test_job_offer.py index 1da107bfe72..88865964500 100644 --- a/erpnext/hr/doctype/job_offer/test_job_offer.py +++ b/erpnext/hr/doctype/job_offer/test_job_offer.py @@ -55,8 +55,7 @@ def create_job_offer(**args): "job_applicant": args.job_applicant or job_applicant.name, "offer_date": args.offer_date or nowdate(), "designation": args.designation or "Researcher", - "status": args.status or "Accepted", - "company": "_Test Company" + "status": args.status or "Accepted" }) return job_offer @@ -69,7 +68,6 @@ def create_staffing_plan(**args): staffing_plan = frappe.get_doc({ "doctype": "Staffing Plan", "name": args.name or "Test", - "company": "_Test Company", "from_date": args.from_date or nowdate(), "to_date": args.to_date or add_days(nowdate(), 10), "staffing_details": args.staffing_details or [{ diff --git a/erpnext/hr/doctype/salary_slip/test_salary_slip.py b/erpnext/hr/doctype/salary_slip/test_salary_slip.py index d002e5408ab..a61ce79d0f4 100644 --- a/erpnext/hr/doctype/salary_slip/test_salary_slip.py +++ b/erpnext/hr/doctype/salary_slip/test_salary_slip.py @@ -414,7 +414,7 @@ def make_salary_component(salary_components, test_tax, company_list=None): get_salary_component_account(salary_component["salary_component"], company_list) def get_salary_component_account(sal_comp, company_list=None): - company = erpnext.get_default_company() or 'Wind Power LLC' + company = erpnext.get_default_company() if company_list and company not in company_list: company_list.append(company) diff --git a/erpnext/hr/doctype/salary_structure/test_salary_structure.py b/erpnext/hr/doctype/salary_structure/test_salary_structure.py index ba3a968af09..d350031df78 100644 --- a/erpnext/hr/doctype/salary_structure/test_salary_structure.py +++ b/erpnext/hr/doctype/salary_structure/test_salary_structure.py @@ -116,7 +116,7 @@ def make_salary_structure(salary_structure, payroll_frequency, employee=None, do details = { "doctype": "Salary Structure", "name": salary_structure, - "company": company or erpnext.get_default_company() or "_Test Company", + "company": company or erpnext.get_default_company(), "earnings": make_earning_salary_component(test_tax=test_tax, company_list=["_Test Company"]), "deductions": make_deduction_salary_component(test_tax=test_tax, company_list=["_Test Company"]), "payroll_frequency": payroll_frequency, diff --git a/erpnext/hr/doctype/training_event/test_training_event.py b/erpnext/hr/doctype/training_event/test_training_event.py index 5ddb99b8ea8..57123e304f5 100644 --- a/erpnext/hr/doctype/training_event/test_training_event.py +++ b/erpnext/hr/doctype/training_event/test_training_event.py @@ -32,8 +32,7 @@ def create_training_program(training_program): frappe.get_doc({ "doctype": "Training Program", "training_program": training_program, - "description": training_program, - "company": "Wind Power LLC" + "description": training_program }).insert() def get_attendees(employee, employee2): diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py index 13bd02f4169..2e38e8ff9e7 100644 --- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py @@ -192,7 +192,6 @@ class TestDeliveryNote(unittest.TestCase): serial_no = frappe.get_doc({ "doctype": "Serial No", "item_code": "_Test Serialized Item With Series", - "company": "Wind Power LLC", "serial_no": make_autoname("SR", "Serial No") }) serial_no.save() @@ -566,6 +565,32 @@ class TestDeliveryNote(unittest.TestCase): for i, gle in enumerate(gl_entries): self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center) + def test_delivery_note_without_cost_center(self): + cost_center = "Main - TCP1" + + company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company') + + set_valuation_method("_Test Item", "FIFO") + + make_stock_entry(target="Stores - TCP1", qty=5, basic_rate=100) + + stock_in_hand_account = get_inventory_account('_Test Company with perpetual inventory') + dn = create_delivery_note(company='_Test Company with perpetual inventory', warehouse='Stores - TCP1', cost_center = 'Main - TCP1', expense_account = "Cost of Goods Sold - TCP1") + + gl_entries = get_gl_entries("Delivery Note", dn.name) + + self.assertTrue(gl_entries) + expected_values = { + "Cost of Goods Sold - TCP1": { + "cost_center": cost_center + }, + stock_in_hand_account: { + "cost_center": None + } + } + for i, gle in enumerate(gl_entries): + self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center) + def test_make_sales_invoice_from_dn_for_returned_qty(self): from erpnext.selling.doctype.sales_order.sales_order import make_delivery_note from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice diff --git a/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py b/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py index da2c97cb8c1..eeea6da7a42 100644 --- a/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py +++ b/erpnext/stock/doctype/delivery_trip/test_delivery_trip.py @@ -172,7 +172,7 @@ def create_delivery_trip(driver, address, contact=None): delivery_trip = frappe.get_doc({ "doctype": "Delivery Trip", - "company": erpnext.get_default_company() or 'Wind Power LLC', + "company": erpnext.get_default_company(), "departure_time": add_days(now_datetime(), 5), "driver": driver.name, "driver_address": address.name, diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index af25ba68ea8..53f8b47f68d 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -432,6 +432,30 @@ class TestPurchaseReceipt(unittest.TestCase): for i, gle in enumerate(gl_entries): self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center) + def test_purchase_receipt_without_cost_center(self): + if not frappe.db.exists('Location', 'Test Location'): + frappe.get_doc({ + 'doctype': 'Location', + 'location_name': 'Test Location' + }).insert() + pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1") + + stock_in_hand_account = get_inventory_account(pr.company, pr.get("items")[0].warehouse) + gl_entries = get_gl_entries("Purchase Receipt", pr.name) + + self.assertTrue(gl_entries) + + expected_values = { + "Stock Received But Not Billed - TCP1": { + "cost_center": None + }, + stock_in_hand_account: { + "cost_center": None + } + } + for i, gle in enumerate(gl_entries): + self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center) + def test_make_purchase_invoice_from_pr_for_returned_qty(self): from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order, create_pr_against_po diff --git a/erpnext/stock/doctype/serial_no/test_serial_no.py b/erpnext/stock/doctype/serial_no/test_serial_no.py index 7b06ef96ce8..ab061076e52 100644 --- a/erpnext/stock/doctype/serial_no/test_serial_no.py +++ b/erpnext/stock/doctype/serial_no/test_serial_no.py @@ -24,7 +24,6 @@ class TestSerialNo(unittest.TestCase): frappe.delete_doc_if_exists("Serial No", "_TCSER0001") sr = frappe.new_doc("Serial No") - sr.company = '_Test Company' sr.item_code = "_Test Serialized Item" sr.warehouse = "_Test Warehouse - _TC" sr.serial_no = "_TCSER0001" From 8a4dc521cdc721d75bb0078787c332d3599b3af4 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 30 May 2020 11:40:16 +0530 Subject: [PATCH 264/608] Revert 'fix: tests depending on global default company' --- erpnext/accounts/doctype/subscription/subscription.py | 4 ---- .../doctype/clinical_procedure/test_clinical_procedure.py | 2 -- erpnext/healthcare/doctype/patient/test_patient.py | 2 -- .../patient_medical_record/test_patient_medical_record.py | 2 -- .../test_employee_tax_exemption_declaration.py | 8 ++++---- erpnext/hr/doctype/leave_period/test_leave_period.py | 4 ++-- erpnext/hr/doctype/payroll_entry/test_payroll_entry.py | 6 +++--- erpnext/hr/doctype/salary_slip/test_salary_slip.py | 6 +++--- .../hr/doctype/salary_structure/test_salary_structure.py | 4 ++-- 9 files changed, 14 insertions(+), 24 deletions(-) diff --git a/erpnext/accounts/doctype/subscription/subscription.py b/erpnext/accounts/doctype/subscription/subscription.py index fbdaf1bc95a..0933c7e8b84 100644 --- a/erpnext/accounts/doctype/subscription/subscription.py +++ b/erpnext/accounts/doctype/subscription/subscription.py @@ -238,10 +238,6 @@ class Subscription(Document): Creates a `Sales Invoice`, submits it and returns it """ invoice = frappe.new_doc('Sales Invoice') - - if not invoice.company: - invoice.company = frappe.db.get_value('Global Defaults', None, 'default_company') - invoice.set_posting_time = 1 invoice.posting_date = self.current_invoice_start invoice.customer = self.customer diff --git a/erpnext/healthcare/doctype/clinical_procedure/test_clinical_procedure.py b/erpnext/healthcare/doctype/clinical_procedure/test_clinical_procedure.py index 100addc9ba2..207351ff209 100644 --- a/erpnext/healthcare/doctype/clinical_procedure/test_clinical_procedure.py +++ b/erpnext/healthcare/doctype/clinical_procedure/test_clinical_procedure.py @@ -18,7 +18,6 @@ class TestClinicalProcedure(unittest.TestCase): self.assertEquals(frappe.db.get_value('Item', procedure_template.item, 'disabled'), 1) def test_consumables(self): - frappe.db.set_value('Global Defaults', None, 'default_company', '_Test Company') patient, medical_department, practitioner = create_healthcare_docs() procedure_template = create_clinical_procedure_template() procedure_template.allow_stock_consumption = 1 @@ -39,7 +38,6 @@ class TestClinicalProcedure(unittest.TestCase): result = procedure.complete_procedure() # check consumption self.assertTrue(frappe.db.exists('Stock Entry', result)) - frappe.db.set_value('Global Defaults', None, 'default_company', None) def create_consumable(): diff --git a/erpnext/healthcare/doctype/patient/test_patient.py b/erpnext/healthcare/doctype/patient/test_patient.py index dfe61bd6c11..9274b6f5e85 100644 --- a/erpnext/healthcare/doctype/patient/test_patient.py +++ b/erpnext/healthcare/doctype/patient/test_patient.py @@ -15,7 +15,6 @@ class TestPatient(unittest.TestCase): self.assertTrue(frappe.db.get_value('Patient', patient, 'customer')) def test_patient_registration(self): - frappe.db.set_value('Global Defaults', None, 'default_company', '_Test Company') frappe.db.sql("""delete from `tabPatient`""") settings = frappe.get_single('Healthcare Settings') settings.collect_registration_fee = 1 @@ -33,4 +32,3 @@ class TestPatient(unittest.TestCase): settings.collect_registration_fee = 0 settings.save() - frappe.db.set_value('Global Defaults', None, 'default_company', None) diff --git a/erpnext/healthcare/doctype/patient_medical_record/test_patient_medical_record.py b/erpnext/healthcare/doctype/patient_medical_record/test_patient_medical_record.py index 4de90bc09f6..e5a5e4c010d 100644 --- a/erpnext/healthcare/doctype/patient_medical_record/test_patient_medical_record.py +++ b/erpnext/healthcare/doctype/patient_medical_record/test_patient_medical_record.py @@ -13,7 +13,6 @@ class TestPatientMedicalRecord(unittest.TestCase): frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 1) def test_medical_record(self): - frappe.db.set_value('Global Defaults', None, 'default_company', '_Test Company') patient, medical_department, practitioner = create_healthcare_docs() appointment = create_appointment(patient, practitioner, nowdate(), invoice=1) encounter = create_encounter(appointment) @@ -39,7 +38,6 @@ class TestPatientMedicalRecord(unittest.TestCase): # check for lab test medical_rec = frappe.db.exists('Patient Medical Record', {'status': 'Open', 'reference_name': lab_test.name}) self.assertTrue(medical_rec) - frappe.db.set_value('Global Defaults', None, 'default_company', None) def create_procedure(appointment): diff --git a/erpnext/hr/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py b/erpnext/hr/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py index 867c35b1b80..9549fd1b757 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py +++ b/erpnext/hr/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py @@ -20,7 +20,7 @@ class TestEmployeeTaxExemptionDeclaration(unittest.TestCase): declaration = frappe.get_doc({ "doctype": "Employee Tax Exemption Declaration", "employee": frappe.get_value("Employee", {"user_id":"employee@taxexepmtion.com"}, "name"), - "company": erpnext.get_default_company() or 'Wind Power LLC', + "company": erpnext.get_default_company(), "payroll_period": "_Test Payroll Period", "declarations": [ dict(exemption_sub_category = "_Test Sub Category", @@ -37,7 +37,7 @@ class TestEmployeeTaxExemptionDeclaration(unittest.TestCase): declaration = frappe.get_doc({ "doctype": "Employee Tax Exemption Declaration", "employee": frappe.get_value("Employee", {"user_id":"employee@taxexepmtion.com"}, "name"), - "company": erpnext.get_default_company() or 'Wind Power LLC', + "company": erpnext.get_default_company(), "payroll_period": "_Test Payroll Period", "declarations": [ dict(exemption_sub_category = "_Test Sub Category", @@ -52,7 +52,7 @@ class TestEmployeeTaxExemptionDeclaration(unittest.TestCase): duplicate_declaration = frappe.get_doc({ "doctype": "Employee Tax Exemption Declaration", "employee": frappe.get_value("Employee", {"user_id":"employee@taxexepmtion.com"}, "name"), - "company": erpnext.get_default_company() or 'Wind Power LLC', + "company": erpnext.get_default_company(), "payroll_period": "_Test Payroll Period", "declarations": [ dict(exemption_sub_category = "_Test Sub Category", @@ -68,7 +68,7 @@ class TestEmployeeTaxExemptionDeclaration(unittest.TestCase): declaration = frappe.get_doc({ "doctype": "Employee Tax Exemption Declaration", "employee": frappe.get_value("Employee", {"user_id":"employee@taxexepmtion.com"}, "name"), - "company": erpnext.get_default_company() or 'Wind Power LLC', + "company": erpnext.get_default_company(), "payroll_period": "_Test Payroll Period", "declarations": [ dict(exemption_sub_category = "_Test Sub Category", diff --git a/erpnext/hr/doctype/leave_period/test_leave_period.py b/erpnext/hr/doctype/leave_period/test_leave_period.py index 06e5836738b..1762cf917a2 100644 --- a/erpnext/hr/doctype/leave_period/test_leave_period.py +++ b/erpnext/hr/doctype/leave_period/test_leave_period.py @@ -45,7 +45,7 @@ class TestLeavePeriod(unittest.TestCase): def create_leave_period(from_date, to_date, company=None): leave_period = frappe.db.get_value('Leave Period', - dict(company=company or erpnext.get_default_company() or 'Wind Power LLC', + dict(company=company or erpnext.get_default_company(), from_date=from_date, to_date=to_date, is_active=1), 'name') @@ -54,7 +54,7 @@ def create_leave_period(from_date, to_date, company=None): leave_period = frappe.get_doc({ "doctype": "Leave Period", - "company": company or erpnext.get_default_company() or 'Wind Power LLC', + "company": company or erpnext.get_default_company(), "from_date": from_date, "to_date": to_date, "is_active": 1 diff --git a/erpnext/hr/doctype/payroll_entry/test_payroll_entry.py b/erpnext/hr/doctype/payroll_entry/test_payroll_entry.py index f1393b168da..3c318e78a24 100644 --- a/erpnext/hr/doctype/payroll_entry/test_payroll_entry.py +++ b/erpnext/hr/doctype/payroll_entry/test_payroll_entry.py @@ -27,7 +27,7 @@ class TestPayrollEntry(unittest.TestCase): frappe.db.set_value("HR Settings", None, "email_salary_slip_to_employee", 0) def test_payroll_entry(self): # pylint: disable=no-self-use - company = erpnext.get_default_company() or 'Wind Power LLC' + company = erpnext.get_default_company() for data in frappe.get_all('Salary Component', fields = ["name"]): if not frappe.db.get_value('Salary Component Account', {'parent': data.name, 'company': company}, 'name'): @@ -157,7 +157,7 @@ def make_payroll_entry(**args): args = frappe._dict(args) payroll_entry = frappe.new_doc("Payroll Entry") - payroll_entry.company = args.company or erpnext.get_default_company() or 'Wind Power LLC' + payroll_entry.company = args.company or erpnext.get_default_company() payroll_entry.start_date = args.start_date or "2016-11-01" payroll_entry.end_date = args.end_date or "2016-11-30" payroll_entry.payment_account = get_payment_account() @@ -183,7 +183,7 @@ def make_payroll_entry(**args): def get_payment_account(): return frappe.get_value('Account', - {'account_type': 'Cash', 'company': erpnext.get_default_company() or 'Wind Power LLC', 'is_group':0}, "name") + {'account_type': 'Cash', 'company': erpnext.get_default_company(),'is_group':0}, "name") def make_holiday(holiday_list_name): if not frappe.db.exists('Holiday List', holiday_list_name): diff --git a/erpnext/hr/doctype/salary_slip/test_salary_slip.py b/erpnext/hr/doctype/salary_slip/test_salary_slip.py index a61ce79d0f4..9584866f67c 100644 --- a/erpnext/hr/doctype/salary_slip/test_salary_slip.py +++ b/erpnext/hr/doctype/salary_slip/test_salary_slip.py @@ -242,7 +242,7 @@ class TestSalarySlip(unittest.TestCase): self.assertEqual(ss.net_pay, (flt(ss.gross_pay) - (flt(ss.total_deduction) + flt(ss.total_loan_repayment)))) def test_payroll_frequency(self): - fiscal_year = get_fiscal_year(nowdate(), company=erpnext.get_default_company() or 'Wind Power LLC')[0] + fiscal_year = get_fiscal_year(nowdate(), company=erpnext.get_default_company())[0] month = "%02d" % getdate(nowdate()).month m = get_month_details(fiscal_year, month) @@ -560,7 +560,7 @@ def create_exemption_declaration(employee, payroll_period): "doctype": "Employee Tax Exemption Declaration", "employee": employee, "payroll_period": payroll_period, - "company": erpnext.get_default_company() or 'Wind Power LLC' + "company": erpnext.get_default_company() }) declaration.append("declarations", { "exemption_sub_category": "_Test Sub Category", @@ -667,7 +667,7 @@ def create_additional_salary(employee, payroll_period, amount): frappe.get_doc({ "doctype": "Additional Salary", "employee": employee, - "company": erpnext.get_default_company() or 'Wind Power LLC', + "company": erpnext.get_default_company(), "salary_component": "Performance Bonus", "payroll_date": salary_date, "amount": amount, diff --git a/erpnext/hr/doctype/salary_structure/test_salary_structure.py b/erpnext/hr/doctype/salary_structure/test_salary_structure.py index d350031df78..eb5311e3b81 100644 --- a/erpnext/hr/doctype/salary_structure/test_salary_structure.py +++ b/erpnext/hr/doctype/salary_structure/test_salary_structure.py @@ -22,7 +22,7 @@ class TestSalaryStructure(unittest.TestCase): frappe.db.sql("delete from `tab%s`" % dt) self.make_holiday_list() - frappe.db.set_value("Company", erpnext.get_default_company() or 'Wind Power LLC', "default_holiday_list", "Salary Structure Test Holiday List") + frappe.db.set_value("Company", erpnext.get_default_company(), "default_holiday_list", "Salary Structure Test Holiday List") make_employee("test_employee@salary.com") make_employee("test_employee_2@salary.com") @@ -151,7 +151,7 @@ def create_salary_structure_assignment(employee, salary_structure, from_date=Non salary_structure_assignment.variable = 5000 salary_structure_assignment.from_date = from_date or add_days(nowdate(), -1) salary_structure_assignment.salary_structure = salary_structure - salary_structure_assignment.company = company or erpnext.get_default_company() or 'Wind Power LLC' + salary_structure_assignment.company = company or erpnext.get_default_company() salary_structure_assignment.save(ignore_permissions=True) salary_structure_assignment.income_tax_slab = "Tax Slab: _Test Payroll Period" salary_structure_assignment.submit() From c96bf870e4c3f08f258098639a71008d5b68078c Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 29 May 2020 23:50:04 +0530 Subject: [PATCH 265/608] fix: Test Cases --- .../invoice_discounting/invoice_discounting.py | 15 ++++++++++++--- .../purchase_invoice/test_purchase_invoice.py | 2 +- erpnext/assets/doctype/asset/depreciation.py | 8 +++++--- .../test_procurement_tracker.py | 1 + .../doctype/employee_advance/employee_advance.py | 2 ++ erpnext/hr/doctype/expense_claim/expense_claim.py | 4 +++- .../doctype/delivery_note/test_delivery_note.py | 10 +++++++--- .../purchase_receipt/test_purchase_receipt.py | 7 ++++--- 8 files changed, 35 insertions(+), 14 deletions(-) diff --git a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py index 594b4d4a223..8083b21f759 100644 --- a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py +++ b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py @@ -3,7 +3,7 @@ # For license information, please see license.txt from __future__ import unicode_literals -import frappe, json +import frappe, json, erpnext from frappe import _ from frappe.utils import flt, getdate, nowdate, add_days from erpnext.controllers.accounts_controller import AccountsController @@ -134,16 +134,19 @@ class InvoiceDiscounting(AccountsController): je.append("accounts", { "account": self.bank_account, "debit_in_account_currency": flt(self.total_amount) - flt(self.bank_charges), + "cost_center": erpnext.get_default_cost_center(self.company) }) je.append("accounts", { "account": self.bank_charges_account, - "debit_in_account_currency": flt(self.bank_charges) + "debit_in_account_currency": flt(self.bank_charges), + "cost_center": erpnext.get_default_cost_center(self.company) }) je.append("accounts", { "account": self.short_term_loan, "credit_in_account_currency": flt(self.total_amount), + "cost_center": erpnext.get_default_cost_center(self.company), "reference_type": "Invoice Discounting", "reference_name": self.name }) @@ -151,6 +154,7 @@ class InvoiceDiscounting(AccountsController): je.append("accounts", { "account": self.accounts_receivable_discounted, "debit_in_account_currency": flt(d.outstanding_amount), + "cost_center": erpnext.get_default_cost_center(self.company), "reference_type": "Invoice Discounting", "reference_name": self.name, "party_type": "Customer", @@ -160,6 +164,7 @@ class InvoiceDiscounting(AccountsController): je.append("accounts", { "account": self.accounts_receivable_credit, "credit_in_account_currency": flt(d.outstanding_amount), + "cost_center": erpnext.get_default_cost_center(self.company), "reference_type": "Invoice Discounting", "reference_name": self.name, "party_type": "Customer", @@ -177,13 +182,15 @@ class InvoiceDiscounting(AccountsController): je.append("accounts", { "account": self.short_term_loan, "debit_in_account_currency": flt(self.total_amount), + "cost_center": erpnext.get_default_cost_center(self.company), "reference_type": "Invoice Discounting", "reference_name": self.name, }) je.append("accounts", { "account": self.bank_account, - "credit_in_account_currency": flt(self.total_amount) + "credit_in_account_currency": flt(self.total_amount), + "cost_center": erpnext.get_default_cost_center(self.company) }) if getdate(self.loan_end_date) > getdate(nowdate()): @@ -193,6 +200,7 @@ class InvoiceDiscounting(AccountsController): je.append("accounts", { "account": self.accounts_receivable_discounted, "credit_in_account_currency": flt(outstanding_amount), + "cost_center": erpnext.get_default_cost_center(self.company), "reference_type": "Invoice Discounting", "reference_name": self.name, "party_type": "Customer", @@ -202,6 +210,7 @@ class InvoiceDiscounting(AccountsController): je.append("accounts", { "account": self.accounts_receivable_unpaid, "debit_in_account_currency": flt(outstanding_amount), + "cost_center": erpnext.get_default_cost_center(self.company), "reference_type": "Invoice Discounting", "reference_name": self.name, "party_type": "Customer", diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index f61d27aee35..4019815e19a 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -822,7 +822,7 @@ class TestPurchaseInvoice(unittest.TestCase): "Creditors - _TC": { "cost_center": cost_center }, - "Cost of Goods Sold - _TC": { + "_Test Account Cost for Goods Sold - _TC": { "cost_center": cost_center } } diff --git a/erpnext/assets/doctype/asset/depreciation.py b/erpnext/assets/doctype/asset/depreciation.py index c50211e6ab1..b7ebb4767e0 100644 --- a/erpnext/assets/doctype/asset/depreciation.py +++ b/erpnext/assets/doctype/asset/depreciation.py @@ -3,7 +3,7 @@ # For license information, please see license.txt from __future__ import unicode_literals -import frappe +import frappe, erpnext from frappe import _ from frappe.utils import flt, today, getdate, cint from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_checks_for_pl_and_bs_accounts @@ -197,12 +197,14 @@ def get_gl_entries_on_asset_disposal(asset, selling_amount=0, finance_book=None) { "account": fixed_asset_account, "credit_in_account_currency": asset.gross_purchase_amount, - "credit": asset.gross_purchase_amount + "credit": asset.gross_purchase_amount, + "cost_center": depreciation_cost_center }, { "account": accumulated_depr_account, "debit_in_account_currency": accumulated_depr_amount, - "debit": accumulated_depr_amount + "debit": accumulated_depr_amount, + "cost_center": depreciation_cost_center } ] diff --git a/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py b/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py index bebf0ccec56..acec4c85024 100644 --- a/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py +++ b/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py @@ -39,6 +39,7 @@ class TestProcurementTracker(unittest.TestCase): po.get("items")[0].cost_center = "_Test Cost Center - _TC" po.submit() pr = make_purchase_receipt(po.name) + pr.get("items")[0].cost_center = "_Test Cost Center - _TC" pr.submit() frappe.db.commit() date_obj = datetime.date(datetime.now()) diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.py b/erpnext/hr/doctype/employee_advance/employee_advance.py index db39eff0e4d..c5a9fcec1d4 100644 --- a/erpnext/hr/doctype/employee_advance/employee_advance.py +++ b/erpnext/hr/doctype/employee_advance/employee_advance.py @@ -119,12 +119,14 @@ def make_bank_entry(dt, dn): "reference_type": "Employee Advance", "reference_name": doc.name, "party_type": "Employee", + "cost_center": erpnext.get_default_cost_center(doc.company), "party": doc.employee, "is_advance": "Yes" }) je.append("accounts", { "account": payment_account.account, + "cost_center": erpnext.get_default_cost_center(doc.company), "credit_in_account_currency": flt(doc.advance_amount), "account_currency": payment_account.account_currency, "account_type": payment_account.account_type diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.py b/erpnext/hr/doctype/expense_claim/expense_claim.py index ea469b82c98..87bfbd18f40 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.py +++ b/erpnext/hr/doctype/expense_claim/expense_claim.py @@ -295,7 +295,7 @@ def make_bank_entry(dt, dn): je = frappe.new_doc("Journal Entry") je.voucher_type = 'Bank Entry' je.company = expense_claim.company - je.remark = 'Payment against Expense Claim: ' + dn; + je.remark = 'Payment against Expense Claim: ' + dn je.append("accounts", { "account": expense_claim.payable_account, @@ -303,6 +303,7 @@ def make_bank_entry(dt, dn): "reference_type": "Expense Claim", "party_type": "Employee", "party": expense_claim.employee, + "cost_center": erpnext.get_default_cost_center(expense_claim.company), "reference_name": expense_claim.name }) @@ -313,6 +314,7 @@ def make_bank_entry(dt, dn): "reference_name": expense_claim.name, "balance": default_bank_cash_account.balance, "account_currency": default_bank_cash_account.account_currency, + "cost_center": erpnext.get_default_cost_center(expense_claim.company), "account_type": default_bank_cash_account.account_type }) diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py index 2e38e8ff9e7..4b04a0a8c3d 100644 --- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py @@ -565,7 +565,7 @@ class TestDeliveryNote(unittest.TestCase): for i, gle in enumerate(gl_entries): self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center) - def test_delivery_note_without_cost_center(self): + def test_delivery_note_cost_center_with_balance_sheet_account(self): cost_center = "Main - TCP1" company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company') @@ -575,7 +575,11 @@ class TestDeliveryNote(unittest.TestCase): make_stock_entry(target="Stores - TCP1", qty=5, basic_rate=100) stock_in_hand_account = get_inventory_account('_Test Company with perpetual inventory') - dn = create_delivery_note(company='_Test Company with perpetual inventory', warehouse='Stores - TCP1', cost_center = 'Main - TCP1', expense_account = "Cost of Goods Sold - TCP1") + dn = create_delivery_note(company='_Test Company with perpetual inventory', warehouse='Stores - TCP1', cost_center = 'Main - TCP1', expense_account = "Cost of Goods Sold - TCP1", + do_not_submit=1) + + dn.get('items')[0].cost_center = None + dn.submit() gl_entries = get_gl_entries("Delivery Note", dn.name) @@ -585,7 +589,7 @@ class TestDeliveryNote(unittest.TestCase): "cost_center": cost_center }, stock_in_hand_account: { - "cost_center": None + "cost_center": cost_center } } for i, gle in enumerate(gl_entries): diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 53f8b47f68d..d97b9e82c3c 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -432,7 +432,7 @@ class TestPurchaseReceipt(unittest.TestCase): for i, gle in enumerate(gl_entries): self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center) - def test_purchase_receipt_without_cost_center(self): + def test_purchase_receipt_cost_center_with_balance_sheet_account(self): if not frappe.db.exists('Location', 'Test Location'): frappe.get_doc({ 'doctype': 'Location', @@ -444,13 +444,14 @@ class TestPurchaseReceipt(unittest.TestCase): gl_entries = get_gl_entries("Purchase Receipt", pr.name) self.assertTrue(gl_entries) + cost_center = pr.get('items')[0].cost_center expected_values = { "Stock Received But Not Billed - TCP1": { - "cost_center": None + "cost_center": cost_center }, stock_in_hand_account: { - "cost_center": None + "cost_center": cost_center } } for i, gle in enumerate(gl_entries): From 1aec30d849fd302726a694070c684d878830d152 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 30 May 2020 00:51:19 +0530 Subject: [PATCH 266/608] fix: Procurement Tracker test case --- .../report/procurement_tracker/test_procurement_tracker.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py b/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py index acec4c85024..ad355598a04 100644 --- a/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py +++ b/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py @@ -36,10 +36,10 @@ class TestProcurementTracker(unittest.TestCase): mr = make_material_request(company="_Test Procurement Company", warehouse=warehouse) po = make_purchase_order(mr.name) po.supplier = "_Test Supplier" - po.get("items")[0].cost_center = "_Test Cost Center - _TC" + po.get("items")[0].cost_center = "Main - _TPC" po.submit() pr = make_purchase_receipt(po.name) - pr.get("items")[0].cost_center = "_Test Cost Center - _TC" + pr.get("items")[0].cost_center = "Main - _TPC" pr.submit() frappe.db.commit() date_obj = datetime.date(datetime.now()) From 159d0734ef836c627b6acc142de750d05a732b4b Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 30 May 2020 10:08:32 +0530 Subject: [PATCH 267/608] fix: Proccurement tracker report test --- .../test_procurement_tracker.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py b/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py index ad355598a04..06da3368927 100644 --- a/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py +++ b/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py @@ -15,7 +15,7 @@ class TestProcurementTracker(unittest.TestCase): def test_result_for_procurement_tracker(self): filters = { 'company': '_Test Procurement Company', - 'cost_center': '_Test Cost Center - _TC' + 'cost_center': 'Main - _TPC' } expected_data = self.generate_expected_data() report = execute(filters) @@ -33,10 +33,12 @@ class TestProcurementTracker(unittest.TestCase): country="Pakistan" )).insert() warehouse = create_warehouse("_Test Procurement Warehouse", company="_Test Procurement Company") - mr = make_material_request(company="_Test Procurement Company", warehouse=warehouse) + mr = make_material_request(company="_Test Procurement Company", warehouse=warehouse, cost_center="Main - _TPC") po = make_purchase_order(mr.name) po.supplier = "_Test Supplier" po.get("items")[0].cost_center = "Main - _TPC" + po.items[0].rate = 500.0 + po.save() po.submit() pr = make_purchase_receipt(po.name) pr.get("items")[0].cost_center = "Main - _TPC" @@ -44,9 +46,11 @@ class TestProcurementTracker(unittest.TestCase): frappe.db.commit() date_obj = datetime.date(datetime.now()) + po.load_from_db() + expected_data = { "material_request_date": date_obj, - "cost_center": "_Test Cost Center - _TC", + "cost_center": "Main - _TPC", "project": None, "requesting_site": "_Test Procurement Warehouse - _TPC", "requestor": "Administrator", @@ -60,9 +64,10 @@ class TestProcurementTracker(unittest.TestCase): "supplier": "_Test Supplier", "estimated_cost": 0.0, "actual_cost": None, - "purchase_order_amt": 5000.0, - "purchase_order_amt_in_company_currency": 300000.0, + "purchase_order_amt": po.net_total, + "purchase_order_amt_in_company_currency": po.base_net_total, "expected_delivery_date": date_obj, "actual_delivery_date": date_obj } + return expected_data \ No newline at end of file From 3609ddedbb49e15700c83064edc8be2ff8a71eb5 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 30 May 2020 12:03:37 +0530 Subject: [PATCH 268/608] fix: Purchase receipt item json --- .../doctype/purchase_invoice_item/purchase_invoice_item.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json index cfbe06ad029..52a5be09843 100644 --- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json +++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json @@ -753,7 +753,7 @@ "depends_on": "is_fixed_asset", "fetch_from": "item_code.asset_category", "fieldname": "asset_category", - "fieldtype": "Data", + "fieldtype": "Link", "label": "Asset Category", "options": "Asset Category", "read_only": 1 From ca46bedfcb874ac5e03d44a00b996d31db336d85 Mon Sep 17 00:00:00 2001 From: karthikeyan5 Date: Sat, 30 May 2020 15:00:56 +0530 Subject: [PATCH 269/608] fix(ewb): remove checksum validation for TRANSIN --- erpnext/regional/india/utils.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index 732780a0a33..3085a310c41 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -615,8 +615,9 @@ def get_transport_details(data, doc): data.transDocDate = frappe.utils.formatdate(doc.lr_date, 'dd/mm/yyyy') if doc.gst_transporter_id: - validate_gstin_check_digit(doc.gst_transporter_id, label='GST Transporter ID') - data.transporterId = doc.gst_transporter_id + if doc.gst_transporter_id[0:2] != "88": + validate_gstin_check_digit(doc.gst_transporter_id, label='GST Transporter ID') + data.transporterId = doc.gst_transporter_id return data From c0c455c471a8952410cd15fd882037b5f6a4c9c1 Mon Sep 17 00:00:00 2001 From: marination Date: Sun, 31 May 2020 20:11:40 +0530 Subject: [PATCH 270/608] fix: Procurement Tracker Data Consistency --- .../procurement_tracker.py | 45 ++++++++++++------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/erpnext/buying/report/procurement_tracker/procurement_tracker.py b/erpnext/buying/report/procurement_tracker/procurement_tracker.py index 39668795cba..88a865f0f85 100644 --- a/erpnext/buying/report/procurement_tracker/procurement_tracker.py +++ b/erpnext/buying/report/procurement_tracker/procurement_tracker.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import frappe from frappe import _ +from frappe.utils import flt def execute(filters=None): columns = get_columns(filters) @@ -54,15 +55,16 @@ def get_columns(filters): "width": 140 }, { - "label": _("Description"), - "fieldname": "description", - "fieldtype": "Data", - "width": 200 + "label": _("Item"), + "fieldname": "item_code", + "fieldtype": "Link", + "options": "Item", + "width": 150 }, { "label": _("Quantity"), "fieldname": "quantity", - "fieldtype": "Int", + "fieldtype": "Float", "width": 140 }, { @@ -118,7 +120,7 @@ def get_columns(filters): }, { "label": _("Purchase Order Amount(Company Currency)"), - "fieldname": "purchase_order_amt_usd", + "fieldname": "purchase_order_amt_in_company_currency", "fieldtype": "Float", "width": 140 }, @@ -175,17 +177,17 @@ def get_data(filters): "requesting_site": po.warehouse, "requestor": po.owner, "material_request_no": po.material_request, - "description": po.description, - "quantity": po.qty, + "item_code": po.item_code, + "quantity": flt(po.qty), "unit_of_measurement": po.stock_uom, "status": po.status, "purchase_order_date": po.transaction_date, "purchase_order": po.parent, "supplier": po.supplier, - "estimated_cost": mr_record.get('amount'), - "actual_cost": pi_records.get(po.name), - "purchase_order_amt": po.amount, - "purchase_order_amt_in_company_currency": po.base_amount, + "estimated_cost": flt(mr_record.get('amount')), + "actual_cost": flt(pi_records.get(po.name)), + "purchase_order_amt": flt(po.amount), + "purchase_order_amt_in_company_currency": flt(po.base_amount), "expected_delivery_date": po.schedule_date, "actual_delivery_date": pr_records.get(po.name) } @@ -198,9 +200,14 @@ def get_mapped_mr_details(conditions): SELECT par.transaction_date, par.per_ordered, + par.owner, child.name, child.parent, - child.amount + child.amount, + child.qty, + child.item_code, + child.uom, + par.status FROM `tabMaterial Request` par, `tabMaterial Request Item` child WHERE par.per_ordered>=0 @@ -217,7 +224,15 @@ def get_mapped_mr_details(conditions): procurement_record_details = dict( material_request_date=record.transaction_date, material_request_no=record.parent, - estimated_cost=record.amount + requestor=record.owner, + item_code=record.item_code, + estimated_cost=flt(record.amount), + quantity=flt(record.qty), + unit_of_measurement=record.uom, + status=record.status, + actual_cost=0, + purchase_order_amt=0, + purchase_order_amt_in_company_currency=0 ) procurement_record_against_mr.append(procurement_record_details) return mr_records, procurement_record_against_mr @@ -259,7 +274,7 @@ def get_po_entries(conditions): child.warehouse, child.material_request, child.material_request_item, - child.description, + child.item_code, child.stock_uom, child.qty, child.amount, From de03d2cba44002c281235ea3226acd2fcc8779cb Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 1 Jun 2020 11:30:34 +0530 Subject: [PATCH 271/608] fix: Method in hooks for proccesing deferred revenue --- erpnext/hooks.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/hooks.py b/erpnext/hooks.py index ab161aa9f51..9d7cdc2a3b9 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -320,8 +320,7 @@ scheduler_events = { "erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual.process_loan_interest_accrual_for_term_loans" ], "monthly_long": [ - "erpnext.accounts.deferred_revenue.convert_deferred_revenue_to_income", - "erpnext.accounts.deferred_revenue.convert_deferred_expense_to_expense", + "erpnext.accounts.deferred_revenue.process_deferred_accounting", "erpnext.hr.utils.allocate_earned_leaves", "erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual.process_loan_interest_accrual_for_demand_loans" ] From 0df7f0fe9d6ed2889a94b8849964a7ebadba451a Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 1 Jun 2020 11:56:33 +0530 Subject: [PATCH 272/608] fix: Misleading Error message for Item Attribute. --- erpnext/controllers/item_variant.py | 15 +++++++++++---- .../doctype/item_attribute/item_attribute.py | 2 +- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/erpnext/controllers/item_variant.py b/erpnext/controllers/item_variant.py index 29f8dd57026..50b17abbe6d 100644 --- a/erpnext/controllers/item_variant.py +++ b/erpnext/controllers/item_variant.py @@ -70,7 +70,7 @@ def validate_item_variant_attributes(item, args=None): else: attributes_list = attribute_values.get(attribute.lower(), []) - validate_item_attribute_value(attributes_list, attribute, value, item.name) + validate_item_attribute_value(attributes_list, attribute, value, item.name, from_variant=True) def validate_is_incremental(numeric_attribute, attribute, value, item): from_range = numeric_attribute.from_range @@ -93,13 +93,20 @@ def validate_is_incremental(numeric_attribute, attribute, value, item): .format(attribute, from_range, to_range, increment, item), InvalidItemAttributeValueError, title=_('Invalid Attribute')) -def validate_item_attribute_value(attributes_list, attribute, attribute_value, item): +def validate_item_attribute_value(attributes_list, attribute, attribute_value, item, from_variant=True): allow_rename_attribute_value = frappe.db.get_single_value('Item Variant Settings', 'allow_rename_attribute_value') if allow_rename_attribute_value: pass elif attribute_value not in attributes_list: - frappe.throw(_("The value {0} is already assigned to an exisiting Item {2}.").format( - attribute_value, attribute, item), InvalidItemAttributeValueError, title=_('Rename Not Allowed')) + if from_variant: + frappe.throw(_("{0} is not a valid Value for Attribute {1} of Item {2}.").format( + frappe.bold(attribute_value), frappe.bold(attribute), frappe.bold(item)), InvalidItemAttributeValueError, title=_("Invalid Value")) + else: + msg = _("The value {0} is already assigned to an exisiting Item {1}.").format( + frappe.bold(attribute_value), frappe.bold(item)) + msg += "
" + _("To still proceed with editing this Attribute Value, enable {0} in Item Variant Settings.").format(frappe.bold("Allow Rename Attribute Value")) + + frappe.throw(msg, InvalidItemAttributeValueError, title=_('Edit Not Allowed')) def get_attribute_values(item): if not frappe.flags.attribute_values: diff --git a/erpnext/stock/doctype/item_attribute/item_attribute.py b/erpnext/stock/doctype/item_attribute/item_attribute.py index 71b998fb954..2f75bbd97c0 100644 --- a/erpnext/stock/doctype/item_attribute/item_attribute.py +++ b/erpnext/stock/doctype/item_attribute/item_attribute.py @@ -34,7 +34,7 @@ class ItemAttribute(Document): if self.numeric_values: validate_is_incremental(self, self.name, item.value, item.name) else: - validate_item_attribute_value(attributes_list, self.name, item.value, item.name) + validate_item_attribute_value(attributes_list, self.name, item.value, item.name, from_variant=False) def validate_numeric(self): if self.numeric_values: From 732f5c18674a9fde30b6e8fd5e0ae20b66a58802 Mon Sep 17 00:00:00 2001 From: Abhishek Balam Date: Mon, 1 Jun 2020 23:51:39 +0530 Subject: [PATCH 273/608] fixed customer count logic --- .../customer_acquisition_and_loyalty.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py b/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py index 88bd9c135d5..e1633ae5ed1 100644 --- a/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py +++ b/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py @@ -169,8 +169,7 @@ def get_customer_stats(filters, tree_view=False): customers_in = {} for si in frappe.db.sql('''select territory, posting_date, customer, base_grand_total from `tabSales Invoice` - where docstatus=1 and posting_date <= %(to_date)s and posting_date >= %(from_date)s - {company_condition} order by posting_date'''.format(company_condition=company_condition), + where docstatus=1 and posting_date <= %(to_date)s {company_condition} order by posting_date'''.format(company_condition=company_condition), filters, as_dict=1): key = si.territory if tree_view else si.posting_date.strftime('%Y-%m') From 192d4c8810a32e1ca98e4c6eae7550e6a553f61e Mon Sep 17 00:00:00 2001 From: Abhishek Balam Date: Mon, 1 Jun 2020 23:58:05 +0530 Subject: [PATCH 274/608] indent to tabs instead of spaces --- .../customer_acquisition_and_loyalty.py | 309 +++++++++--------- 1 file changed, 155 insertions(+), 154 deletions(-) diff --git a/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py b/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py index e1633ae5ed1..e78d0ff3a28 100644 --- a/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py +++ b/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py @@ -8,179 +8,180 @@ from frappe import _ from frappe.utils import cint, cstr def execute(filters=None): - common_columns = [ - { - 'label': _('New Customers'), - 'fieldname': 'new_customers', - 'fieldtype': 'Int', - 'default': 0, - 'width': 125 - }, - { - 'label': _('Repeat Customers'), - 'fieldname': 'repeat_customers', - 'fieldtype': 'Int', - 'default': 0, - 'width': 125 - }, - { - 'label': _('Total'), - 'fieldname': 'total', - 'fieldtype': 'Int', - 'default': 0, - 'width': 100 - }, - { - 'label': _('New Customer Revenue'), - 'fieldname': 'new_customer_revenue', - 'fieldtype': 'Currency', - 'default': 0.0, - 'width': 175 - }, - { - 'label': _('Repeat Customer Revenue'), - 'fieldname': 'repeat_customer_revenue', - 'fieldtype': 'Currency', - 'default': 0.0, - 'width': 175 - }, - { - 'label': _('Total Revenue'), - 'fieldname': 'total_revenue', - 'fieldtype': 'Currency', - 'default': 0.0, - 'width': 175 - } - ] - if filters.get('view_type') == 'Monthly': - return get_data_by_time(filters, common_columns) - else: - return get_data_by_territory(filters, common_columns) + common_columns = [ + { + 'label': _('New Customers'), + 'fieldname': 'new_customers', + 'fieldtype': 'Int', + 'default': 0, + 'width': 125 + }, + { + 'label': _('Repeat Customers'), + 'fieldname': 'repeat_customers', + 'fieldtype': 'Int', + 'default': 0, + 'width': 125 + }, + { + 'label': _('Total'), + 'fieldname': 'total', + 'fieldtype': 'Int', + 'default': 0, + 'width': 100 + }, + { + 'label': _('New Customer Revenue'), + 'fieldname': 'new_customer_revenue', + 'fieldtype': 'Currency', + 'default': 0.0, + 'width': 175 + }, + { + 'label': _('Repeat Customer Revenue'), + 'fieldname': 'repeat_customer_revenue', + 'fieldtype': 'Currency', + 'default': 0.0, + 'width': 175 + }, + { + 'label': _('Total Revenue'), + 'fieldname': 'total_revenue', + 'fieldtype': 'Currency', + 'default': 0.0, + 'width': 175 + } + ] + if filters.get('view_type') == 'Monthly': + return get_data_by_time(filters, common_columns) + else: + return get_data_by_territory(filters, common_columns) def get_data_by_time(filters, common_columns): - # key yyyy-mm - columns = [ - { - 'label': _('Year'), - 'fieldname': 'year', - 'fieldtype': 'Data', - 'width': 100 - }, - { - 'label': _('Month'), - 'fieldname': 'month', - 'fieldtype': 'Data', - 'width': 100 - }, - ] - columns += common_columns + # key yyyy-mm + columns = [ + { + 'label': _('Year'), + 'fieldname': 'year', + 'fieldtype': 'Data', + 'width': 100 + }, + { + 'label': _('Month'), + 'fieldname': 'month', + 'fieldtype': 'Data', + 'width': 100 + }, + ] + columns += common_columns - customers_in = get_customer_stats(filters) + customers_in = get_customer_stats(filters) - # time series - from_year, from_month, temp = filters.get('from_date').split('-') - to_year, to_month, temp = filters.get('to_date').split('-') + # time series + from_year, from_month, temp = filters.get('from_date').split('-') + to_year, to_month, temp = filters.get('to_date').split('-') - from_year, from_month, to_year, to_month = \ - cint(from_year), cint(from_month), cint(to_year), cint(to_month) + from_year, from_month, to_year, to_month = \ + cint(from_year), cint(from_month), cint(to_year), cint(to_month) - out = [] - for year in range(from_year, to_year+1): - for month in range(from_month if year==from_year else 1, (to_month+1) if year==to_year else 13): - key = '{year}-{month:02d}'.format(year=year, month=month) - data = customers_in.get(key) - new = data['new'] if data else [0, 0.0] - repeat = data['repeat'] if data else [0, 0.0] - out.append({ - 'year': cstr(year), - 'month': calendar.month_name[month], - 'new_customers': new[0], - 'repeat_customers': repeat[0], - 'total': new[0] + repeat[0], - 'new_customer_revenue': new[1], - 'repeat_customer_revenue': repeat[1], - 'total_revenue': new[1] + repeat[1] - }) - return columns, out + out = [] + for year in range(from_year, to_year+1): + for month in range(from_month if year==from_year else 1, (to_month+1) if year==to_year else 13): + key = '{year}-{month:02d}'.format(year=year, month=month) + data = customers_in.get(key) + new = data['new'] if data else [0, 0.0] + repeat = data['repeat'] if data else [0, 0.0] + out.append({ + 'year': cstr(year), + 'month': calendar.month_name[month], + 'new_customers': new[0], + 'repeat_customers': repeat[0], + 'total': new[0] + repeat[0], + 'new_customer_revenue': new[1], + 'repeat_customer_revenue': repeat[1], + 'total_revenue': new[1] + repeat[1] + }) + return columns, out def get_data_by_territory(filters, common_columns): - columns = [{ - 'label': 'Territory', - 'fieldname': 'territory', - 'fieldtype': 'Link', - 'options': 'Territory', - 'width': 150 - }] - columns += common_columns + columns = [{ + 'label': 'Territory', + 'fieldname': 'territory', + 'fieldtype': 'Link', + 'options': 'Territory', + 'width': 150 + }] + columns += common_columns - customers_in = get_customer_stats(filters, tree_view=True) + customers_in = get_customer_stats(filters, tree_view=True) - territory_dict = {} - for t in frappe.db.sql('''SELECT name, lft, parent_territory, is_group FROM `tabTerritory` ORDER BY lft''', as_dict=1): - territory_dict.update({ - t.name: { - 'parent': t.parent_territory, - 'is_group': t.is_group - } - }) + territory_dict = {} + for t in frappe.db.sql('''SELECT name, lft, parent_territory, is_group FROM `tabTerritory` ORDER BY lft''', as_dict=1): + territory_dict.update({ + t.name: { + 'parent': t.parent_territory, + 'is_group': t.is_group + } + }) - depth_map = frappe._dict() - for name, info in territory_dict.items(): - default = depth_map.get(info['parent']) + 1 if info['parent'] else 0 - depth_map.setdefault(name, default) + depth_map = frappe._dict() + for name, info in territory_dict.items(): + default = depth_map.get(info['parent']) + 1 if info['parent'] else 0 + depth_map.setdefault(name, default) - data = [] - for name, indent in depth_map.items(): - condition = customers_in.get(name) - new = customers_in[name]['new'] if condition else [0, 0.0] - repeat = customers_in[name]['repeat'] if condition else [0, 0.0] - temp = { - 'territory': name, - 'parent_territory': territory_dict[name]['parent'], - 'indent': indent, - 'new_customers': new[0], - 'repeat_customers': repeat[0], - 'total': new[0] + repeat[0], - 'new_customer_revenue': new[1], - 'repeat_customer_revenue': repeat[1], - 'total_revenue': new[1] + repeat[1], - 'bold': 0 if indent else 1 - } - data.append(temp) + data = [] + for name, indent in depth_map.items(): + condition = customers_in.get(name) + new = customers_in[name]['new'] if condition else [0, 0.0] + repeat = customers_in[name]['repeat'] if condition else [0, 0.0] + temp = { + 'territory': name, + 'parent_territory': territory_dict[name]['parent'], + 'indent': indent, + 'new_customers': new[0], + 'repeat_customers': repeat[0], + 'total': new[0] + repeat[0], + 'new_customer_revenue': new[1], + 'repeat_customer_revenue': repeat[1], + 'total_revenue': new[1] + repeat[1], + 'bold': 0 if indent else 1 + } + data.append(temp) - loop_data = sorted(data, key=lambda k: k['indent'], reverse=True) + loop_data = sorted(data, key=lambda k: k['indent'], reverse=True) - for ld in loop_data: - if ld['parent_territory']: - parent_data = [x for x in data if x['territory'] == ld['parent_territory']][0] - for key in parent_data.keys(): - if key not in ['indent', 'territory', 'parent_territory', 'bold']: - parent_data[key] += ld[key] + for ld in loop_data: + if ld['parent_territory']: + parent_data = [x for x in data if x['territory'] == ld['parent_territory']][0] + for key in parent_data.keys(): + if key not in ['indent', 'territory', 'parent_territory', 'bold']: + parent_data[key] += ld[key] - return columns, data, None, None, None, 1 + return columns, data, None, None, None, 1 def get_customer_stats(filters, tree_view=False): - """ Calculates number of new and repeated customers. """ - company_condition = '' - if filters.get('company'): - company_condition = ' and company=%(company)s' + """ Calculates number of new and repeated customers. """ + company_condition = '' + if filters.get('company'): + company_condition = ' and company=%(company)s' - customers = [] - customers_in = {} + customers = [] + customers_in = {} - for si in frappe.db.sql('''select territory, posting_date, customer, base_grand_total from `tabSales Invoice` - where docstatus=1 and posting_date <= %(to_date)s {company_condition} order by posting_date'''.format(company_condition=company_condition), - filters, as_dict=1): + for si in frappe.db.sql('''select territory, posting_date, customer, base_grand_total from `tabSales Invoice` + where docstatus=1 and posting_date <= %(to_date)s + {company_condition} order by posting_date'''.format(company_condition=company_condition), + filters, as_dict=1): - key = si.territory if tree_view else si.posting_date.strftime('%Y-%m') - customers_in.setdefault(key, {'new': [0, 0.0], 'repeat': [0, 0.0]}) + key = si.territory if tree_view else si.posting_date.strftime('%Y-%m') + customers_in.setdefault(key, {'new': [0, 0.0], 'repeat': [0, 0.0]}) - if not si.customer in customers: - customers_in[key]['new'][0] += 1 - customers_in[key]['new'][1] += si.base_grand_total - customers.append(si.customer) - else: - customers_in[key]['repeat'][0] += 1 - customers_in[key]['repeat'][1] += si.base_grand_total + if not si.customer in customers: + customers_in[key]['new'][0] += 1 + customers_in[key]['new'][1] += si.base_grand_total + customers.append(si.customer) + else: + customers_in[key]['repeat'][0] += 1 + customers_in[key]['repeat'][1] += si.base_grand_total - return customers_in + return customers_in From 60f3a2b8e7ce5a8b84f565342e1397b13b2bc095 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 2 Jun 2020 18:26:43 +0530 Subject: [PATCH 275/608] fix: Add total debit in Journal Entry list view --- erpnext/accounts/doctype/journal_entry/journal_entry.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.json b/erpnext/accounts/doctype/journal_entry/journal_entry.json index 9d5063929fd..af2aa65e6b2 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.json +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.json @@ -191,6 +191,7 @@ { "fieldname": "total_debit", "fieldtype": "Currency", + "in_list_view": 1, "label": "Total Debit", "no_copy": 1, "oldfieldname": "total_debit", @@ -252,7 +253,6 @@ "fieldname": "total_amount", "fieldtype": "Currency", "hidden": 1, - "in_list_view": 1, "label": "Total Amount", "no_copy": 1, "options": "total_amount_currency", @@ -503,7 +503,7 @@ "idx": 176, "is_submittable": 1, "links": [], - "modified": "2020-04-29 10:55:28.240916", + "modified": "2020-06-02 18:15:46.955697", "modified_by": "Administrator", "module": "Accounts", "name": "Journal Entry", From 789df326837b85210f769978dfdc6e7e5004eaf1 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 2 Jun 2020 13:48:22 +0000 Subject: [PATCH 276/608] feat: verify signature on webhook (#21872) --- .../doctype/membership/membership.py | 13 +++++++++- .../membership_settings.js | 26 +++++++++++++++++-- .../membership_settings.json | 11 ++++++-- .../membership_settings.py | 18 ++++++++++++- 4 files changed, 62 insertions(+), 6 deletions(-) diff --git a/erpnext/non_profit/doctype/membership/membership.py b/erpnext/non_profit/doctype/membership/membership.py index df19995a1c5..ac5078d45cc 100644 --- a/erpnext/non_profit/doctype/membership/membership.py +++ b/erpnext/non_profit/doctype/membership/membership.py @@ -64,9 +64,21 @@ def get_member_based_on_subscription(subscription_id, email): }, order_by="creation desc") return frappe.get_doc("Member", members[0]['name']) +def verify_signature(data): + signature = frappe.request.headers.get('X-Razorpay-Signature') + + settings = frappe.get_doc("Membership Settings") + key = settings.get_webhook_secret() + + controller = frappe.get_doc("Razorpay Settings") + + controller.verify_signature(data, signature, key) + + @frappe.whitelist(allow_guest=True) def trigger_razorpay_subscription(*args, **kwargs): data = frappe.request.get_data() + verify_signature(data): if isinstance(data, six.string_types): data = json.loads(data) @@ -113,7 +125,6 @@ def trigger_razorpay_subscription(*args, **kwargs): return True - def notify_failure(log): try: content = """Dear System Manager, diff --git a/erpnext/non_profit/doctype/membership_settings/membership_settings.js b/erpnext/non_profit/doctype/membership_settings/membership_settings.js index c01a0b23d5d..8c0e3a4fa76 100644 --- a/erpnext/non_profit/doctype/membership_settings/membership_settings.js +++ b/erpnext/non_profit/doctype/membership_settings/membership_settings.js @@ -1,8 +1,30 @@ // Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Membership Settings', { +frappe.ui.form.on("Membership Settings", { refresh: function(frm) { + if (frm.doc.webhook_secret) { + frm.add_custom_button(__("Revoke "), () => { + frm.call("revoke_key").then(() => { + frm.refresh(); + }) + }); + } + frm.trigger("add_generate_button"); + }, - } + add_generate_button: function(frm) { + let label; + + if (frm.doc.webhook_secret) { + label = __("Regenerate Webhook Secret"); + } else { + label = __("Generate Webhook Secret"); + } + frm.add_custom_button(label, () => { + frm.call("generate_webhook_key").then(() => { + frm.refresh(); + }); + }); + }, }); diff --git a/erpnext/non_profit/doctype/membership_settings/membership_settings.json b/erpnext/non_profit/doctype/membership_settings/membership_settings.json index 56b8eac4b12..52b9d01088b 100644 --- a/erpnext/non_profit/doctype/membership_settings/membership_settings.json +++ b/erpnext/non_profit/doctype/membership_settings/membership_settings.json @@ -8,7 +8,8 @@ "enable_razorpay", "razorpay_settings_section", "billing_cycle", - "billing_frequency" + "billing_frequency", + "webhook_secret" ], "fields": [ { @@ -34,11 +35,17 @@ "fieldname": "billing_frequency", "fieldtype": "Int", "label": "Billing Frequency" + }, + { + "fieldname": "webhook_secret", + "fieldtype": "Password", + "label": "Webhook Secret", + "read_only": 1 } ], "issingle": 1, "links": [], - "modified": "2020-04-07 18:42:51.496807", + "modified": "2020-05-22 12:38:27.103759", "modified_by": "Administrator", "module": "Non Profit", "name": "Membership Settings", diff --git a/erpnext/non_profit/doctype/membership_settings/membership_settings.py b/erpnext/non_profit/doctype/membership_settings/membership_settings.py index 2b8e37f2a65..f3b2eee6f97 100644 --- a/erpnext/non_profit/doctype/membership_settings/membership_settings.py +++ b/erpnext/non_profit/doctype/membership_settings/membership_settings.py @@ -4,11 +4,27 @@ from __future__ import unicode_literals import frappe +from frappe import _ from frappe.integrations.utils import get_payment_gateway_controller from frappe.model.document import Document class MembershipSettings(Document): - pass + def generate_webhook_key(self): + key = frappe.generate_hash(length=20) + self.webhook_secret = key + self.save() + + frappe.msgprint( + _("Here is your webhook secret, this will be shown to you only once.") + "

" + key, + _("Webhook Secret") + ); + + def revoke_key(self): + self.webhook_secret = None; + self.save() + + def get_webhook_secret(self): + return self.get_password(fieldname="webhook_secret", raise_exception=False) @frappe.whitelist() def get_plans_for_membership(*args, **kwargs): From f460c1796140b64cbdcdab121b64fd26b2d9b46a Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 2 Jun 2020 16:18:23 +0000 Subject: [PATCH 277/608] fix: syntax error (#22082) --- erpnext/non_profit/doctype/membership/membership.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/non_profit/doctype/membership/membership.py b/erpnext/non_profit/doctype/membership/membership.py index ac5078d45cc..4b932425b28 100644 --- a/erpnext/non_profit/doctype/membership/membership.py +++ b/erpnext/non_profit/doctype/membership/membership.py @@ -78,7 +78,7 @@ def verify_signature(data): @frappe.whitelist(allow_guest=True) def trigger_razorpay_subscription(*args, **kwargs): data = frappe.request.get_data() - verify_signature(data): + verify_signature(data) if isinstance(data, six.string_types): data = json.loads(data) From 24f9a80e7d6cfc2eddfa6e0831ee573e06051308 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 3 Jun 2020 10:59:37 +0530 Subject: [PATCH 278/608] fix(India): Reverse charge mechanism for GST --- erpnext/hooks.py | 3 ++ .../gstr_3b_report/gstr_3b_report.html | 2 +- .../doctype/gstr_3b_report/gstr_3b_report.py | 12 ++--- erpnext/regional/india/utils.py | 52 ++++++++++++++++++- 4 files changed, 61 insertions(+), 8 deletions(-) diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 9d7cdc2a3b9..742cc8efbd1 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -238,6 +238,9 @@ doc_events = { "on_cancel": "erpnext.regional.italy.utils.sales_invoice_on_cancel", "on_trash": "erpnext.regional.check_deletion_permission" }, + "Purchase Invoice": { + "on_submit": "erpnext.regional.india.utils.make_reverse_charge_entries" + }, "Payment Entry": { "on_submit": ["erpnext.regional.create_transaction_log", "erpnext.accounts.doctype.payment_request.payment_request.update_payment_req_status"], "on_trash": "erpnext.regional.check_deletion_permission" diff --git a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.html b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.html index 35f9cf674ce..888b2da48eb 100644 --- a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.html +++ b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.html @@ -52,7 +52,7 @@ - (d) {{__("Inward Supplies(liable to reverse charge")}} + (d) {{__("Inward Supplies(liable to reverse charge)")}} {{ flt(data.sup_details.isup_rev.txval, 2) }} {{ flt(data.sup_details.isup_rev.iamt, 2) }} {{ flt(data.sup_details.isup_rev.camt, 2) }} diff --git a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py index 9e7a023926d..2691552d802 100644 --- a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py +++ b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py @@ -158,7 +158,7 @@ class GSTR3BReport(Document): self.prepare_data("Sales Invoice", outward_supply_tax_amounts, "sup_details", "osup_det", ["Registered Regular"]) self.prepare_data("Sales Invoice", outward_supply_tax_amounts, "sup_details", "osup_zero", ["SEZ", "Deemed Export", "Overseas"]) - self.prepare_data("Purchase Invoice", inward_supply_tax_amounts, "sup_details", "isup_rev", ["Registered Regular"], reverse_charge="Y") + self.prepare_data("Purchase Invoice", inward_supply_tax_amounts, "sup_details", "isup_rev", ["Unregistered"], reverse_charge="Y") self.report_dict["sup_details"]["osup_nil_exmp"]["txval"] = flt(self.get_nil_rated_supply_value(), 2) self.set_itc_details(itc_details) @@ -196,11 +196,12 @@ class GSTR3BReport(Document): if d["ty"] == 'ISRC': reverse_charge = "Y" + itc_type = 'All Other ITC' + gst_category = 'Unregistered' else: reverse_charge = "N" for account_head in self.account_heads: - d["iamt"] += flt(itc_details.get((gst_category, itc_type, reverse_charge, account_head.get('igst_account')), {}).get("amount"), 2) d["camt"] += flt(itc_details.get((gst_category, itc_type, reverse_charge, account_head.get('cgst_account')), {}).get("amount"), 2) d["samt"] += flt(itc_details.get((gst_category, itc_type, reverse_charge, account_head.get('sgst_account')), {}).get("amount"), 2) @@ -274,17 +275,16 @@ class GSTR3BReport(Document): """ #nosec .format(doctype = doctype), (self.month_no, self.year, reverse_charge, self.company, self.gst_details.get("gstin")))) - def get_itc_details(self, reverse_charge='N'): - + def get_itc_details(self): itc_amount = frappe.db.sql(""" select s.gst_category, sum(t.tax_amount_after_discount_amount) as tax_amount, t.account_head, s.eligibility_for_itc, s.reverse_charge from `tabPurchase Invoice` s , `tabPurchase Taxes and Charges` t - where s.docstatus = 1 and t.parent = s.name and s.reverse_charge = %s + where s.docstatus = 1 and t.parent = s.name and month(s.posting_date) = %s and year(s.posting_date) = %s and s.company = %s and s.company_gstin = %s group by t.account_head, s.gst_category, s.eligibility_for_itc """, - (reverse_charge, self.month_no, self.year, self.company, self.gst_details.get("gstin")), as_dict=1) + (self.month_no, self.year, self.company, self.gst_details.get("gstin")), as_dict=1) itc_details = {} diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index 3085a310c41..9fe29eba1b8 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -9,6 +9,8 @@ from erpnext.hr.utils import get_salary_assignment from erpnext.hr.doctype.salary_structure.salary_structure import make_salary_slip from erpnext.regional.india import number_state_mapping from six import string_types +from erpnext.accounts.general_ledger import make_gl_entries +from erpnext.accounts.utils import get_account_currency def validate_gstin_for_india(doc, method): if hasattr(doc, 'gst_state') and doc.gst_state: @@ -658,5 +660,53 @@ def get_gst_accounts(company, account_wise=False): elif val: gst_accounts[val] = acc - return gst_accounts + +def make_reverse_charge_entries(doc, method): + country = frappe.get_cached_value('Company', doc.company, 'country') + + if country != 'India': + return + + if doc.reverse_charge == 'Y': + gl_entries = [] + gst_accounts = get_gst_accounts(doc.company) + gst_account_list = gst_accounts.get('cgst_account') + gst_accounts.get('sgst_account') \ + + gst_accounts.get('igst_account') + + for tax in doc.get('taxes'): + if tax.category not in ("Total", "Valuation and Total"): + continue + + if flt(tax.base_tax_amount_after_discount_amount) and tax.account_head in gst_account_list: + account_currency = get_account_currency(tax.account_head) + + gl_entries.append(doc.get_gl_dict( + { + "account": tax.account_head, + "cost_center": tax.cost_center, + "posting_date": doc.posting_date, + "against": doc.supplier, + "credit": tax.base_tax_amount_after_discount_amount, + "credits_in_account_currency": tax.base_tax_amount_after_discount_amount \ + if account_currency==doc.company_currency \ + else tax.tax_amount_after_discount_amount + }, account_currency, item=tax) + ) + + gl_entries.append(doc.get_gl_dict( + { + "account": doc.credit_to if doc.doctype == 'Purchase Invoice' else doc.debit_to, + "cost_center": doc.cost_center, + "posting_date": doc.posting_date, + "party_type": 'Supplier', + "party": doc.supplier, + "against": tax.account_head, + "debit": tax.base_tax_amount_after_discount_amount, + "debit_in_account_currency": tax.base_tax_amount_after_discount_amount \ + if account_currency==doc.company_currency \ + else tax.tax_amount_after_discount_amount + }, account_currency, item=doc) + ) + + make_gl_entries(gl_entries) \ No newline at end of file From 9276d164201a52ed7c699786a0181cb68d85cf5d Mon Sep 17 00:00:00 2001 From: Myuddin khatri Date: Wed, 3 Jun 2020 11:14:49 +0530 Subject: [PATCH 279/608] fix(stock): able to create purchase invoice from purchase receipt dashboard able to create purchase invoice from purchase receipt dashboard --- erpnext/stock/doctype/purchase_receipt/purchase_receipt.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js index e9568eeacc0..50c18f62824 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js @@ -25,7 +25,7 @@ frappe.ui.form.on("Purchase Receipt", { frm.custom_make_buttons = { 'Stock Entry': 'Return', - 'Purchase Invoice': 'Invoice' + 'Purchase Invoice': 'Purchase Invoice' }; frm.set_query("expense_account", "items", function() { From b84dd9b5f58746a8f9e4928ee2a6bb21c7c9ef28 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Wed, 3 Jun 2020 13:24:18 +0530 Subject: [PATCH 280/608] fix: dashboard chart does not exist on selling page --- erpnext/selling/desk_page/selling/selling.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/selling/desk_page/selling/selling.json b/erpnext/selling/desk_page/selling/selling.json index c32a7c8481d..9ec634354d3 100644 --- a/erpnext/selling/desk_page/selling/selling.json +++ b/erpnext/selling/desk_page/selling/selling.json @@ -29,7 +29,7 @@ "category": "Modules", "charts": [ { - "chart_name": "Income", + "chart_name": "Incoming Bills (Purchase Invoice)", "label": "Income" } ], @@ -43,7 +43,7 @@ "idx": 0, "is_standard": 1, "label": "Selling", - "modified": "2020-05-28 13:46:08.314240", + "modified": "2020-06-03 13:23:24.861706", "modified_by": "Administrator", "module": "Selling", "name": "Selling", From 0c7224940391ab3c512ce742048ab29a2a6271a9 Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 3 Jun 2020 14:32:07 +0530 Subject: [PATCH 281/608] fix: Apply shipping rule without address too --- erpnext/accounts/doctype/shipping_rule/shipping_rule.py | 4 +++- erpnext/public/js/controllers/transaction.js | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/shipping_rule/shipping_rule.py b/erpnext/accounts/doctype/shipping_rule/shipping_rule.py index b2638c78279..d32a348741e 100644 --- a/erpnext/accounts/doctype/shipping_rule/shipping_rule.py +++ b/erpnext/accounts/doctype/shipping_rule/shipping_rule.py @@ -45,7 +45,9 @@ class ShippingRule(Document): shipping_amount = 0.0 by_value = False - self.validate_countries(doc) + if doc.get_shipping_address(): + # validate country only if there is address + self.validate_countries(doc) if self.calculate_based_on == 'Net Total': value = doc.base_net_total diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 524a95804fd..94216680020 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -917,7 +917,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ shipping_rule: function() { var me = this; - if(this.frm.doc.shipping_rule && this.frm.doc.shipping_address) { + if(this.frm.doc.shipping_rule) { return this.frm.call({ doc: this.frm.doc, method: "apply_shipping_rule", From 6f7e9d2904bcc11ebf15e166232d4aa76b599b16 Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 3 Jun 2020 17:13:58 +0530 Subject: [PATCH 282/608] fix: '>=' not supported between instances of 'str' and 'int' --- erpnext/stock/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py index f21dc3f8b03..11e758fce32 100644 --- a/erpnext/stock/utils.py +++ b/erpnext/stock/utils.py @@ -230,12 +230,12 @@ def get_valuation_method(item_code): def get_fifo_rate(previous_stock_queue, qty): """get FIFO (average) Rate from Queue""" - if qty >= 0: + if flt(qty) >= 0: total = sum(f[0] for f in previous_stock_queue) return sum(flt(f[0]) * flt(f[1]) for f in previous_stock_queue) / flt(total) if total else 0.0 else: available_qty_for_outgoing, outgoing_cost = 0, 0 - qty_to_pop = abs(qty) + qty_to_pop = abs(flt(qty)) while qty_to_pop and previous_stock_queue: batch = previous_stock_queue[0] if 0 < batch[0] <= qty_to_pop: From 7135a75e5e28a5329b709174688761f78c307dff Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 3 Jun 2020 21:51:21 +0530 Subject: [PATCH 283/608] fix: Error when no data is present in AR/AP reeport --- .../report/accounts_receivable/accounts_receivable.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index d40e58b5287..66aa18058b6 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -169,9 +169,11 @@ class ReceivablePayableReport(object): def append_subtotal_row(self, party): sub_total_row = self.total_row_map.get(party) - self.data.append(sub_total_row) - self.data.append({}) - self.update_sub_total_row(sub_total_row, 'Total') + + if sub_total_row: + self.data.append(sub_total_row) + self.data.append({}) + self.update_sub_total_row(sub_total_row, 'Total') def get_voucher_balance(self, gle): if self.filters.get("sales_person"): @@ -232,7 +234,8 @@ class ReceivablePayableReport(object): if self.filters.get('group_by_party'): self.append_subtotal_row(self.previous_party) - self.data.append(self.total_row_map.get('Total')) + if self.data: + self.data.append(self.total_row_map.get('Total')) def append_row(self, row): self.allocate_future_payments(row) From b3338a149bbb14c982e75a3dfce1b25425d7087e Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 4 Jun 2020 12:35:00 +0530 Subject: [PATCH 284/608] fix: code clean-up --- .../patches/v13_0/update_sla_enhancements.py | 26 ++++++++----------- erpnext/support/doctype/issue/issue.js | 4 +-- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/erpnext/patches/v13_0/update_sla_enhancements.py b/erpnext/patches/v13_0/update_sla_enhancements.py index 1d5f372b9b8..91c613fa46d 100644 --- a/erpnext/patches/v13_0/update_sla_enhancements.py +++ b/erpnext/patches/v13_0/update_sla_enhancements.py @@ -22,10 +22,10 @@ def execute(): if values: holiday_list = values[0] employee_group = values[1] - frappe.db.set_value('Service Level Agreement', entry.name, { - 'holiday_list': holiday_list, - 'employee_group': employee_group - }) + frappe.db.set_value('Service Level Agreement', entry.name, { + 'holiday_list': holiday_list, + 'employee_group': employee_group + }) priority_dict = {} @@ -76,18 +76,14 @@ def execute(): row.db_update() sla.db_update() + frappe.delete_doc('DocType', 'Service Level') + # set issue status as Replied since Hold status is removed if frappe.db.exists('DocType', 'Issue'): - issues_on_hold = frappe.db.sql(""" - SELECT - name - FROM - `tabIssue` - WHERE - status = 'Hold' - """, as_dict=1) - + issues_on_hold = frappe.db.get_all('Issue', {'status': 'Hold'}) issues = [entry.name for entry in issues_on_hold] + if not issues: + return frappe.reload_doc('support', 'doctype', 'issue') frappe.db.sql(""" @@ -96,8 +92,8 @@ def execute(): SET status='Replied' WHERE - name in %(issues)s - """, {'issues': issues}, debug=1) + name IN %(issues)s + """, {'issues': issues}) def convert_to_seconds(value, unit): diff --git a/erpnext/support/doctype/issue/issue.js b/erpnext/support/doctype/issue/issue.js index 66c62d1ff2b..6632ab69baf 100644 --- a/erpnext/support/doctype/issue/issue.js +++ b/erpnext/support/doctype/issue/issue.js @@ -43,11 +43,11 @@ frappe.ui.form.on("Issue", { if (frm.doc.service_level_agreement) { if (frm.doc.status == "Replied") { frm.dashboard.clear_headline(); - let message = {"indicator": "orange", "msg": __("Replied {0}", [moment(frm.doc.on_hold_since).fromNow()])}; + let message = {"indicator": "orange", "msg": __("Replied {0}", [frappe.datetime.comment_when(frm.doc.on_hold_since)])}; frm.dashboard.set_headline_alert( '
' + '
' + - ' ' + + ''+ message.msg +' ' + '
' + '
' ); From 94d03c6100e5b6d0f14bca541a72602b3b492bbb Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 4 Jun 2020 12:35:36 +0530 Subject: [PATCH 285/608] fix: remove Service Level DocType from Support Desk Page --- erpnext/support/desk_page/support/support.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/support/desk_page/support/support.json b/erpnext/support/desk_page/support/support.json index a3fe72d0519..b1ad7c8aa0a 100644 --- a/erpnext/support/desk_page/support/support.json +++ b/erpnext/support/desk_page/support/support.json @@ -13,7 +13,7 @@ { "hidden": 0, "label": "Service Level Agreement", - "links": "[\n {\n \"description\": \"Service Level.\",\n \"label\": \"Service Level\",\n \"name\": \"Service Level\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Service Level Agreement.\",\n \"label\": \"Service Level Agreement\",\n \"name\": \"Service Level Agreement\",\n \"type\": \"doctype\"\n }\n]" + "links": "[\n {\n \"description\": \"Service Level Agreement.\",\n \"label\": \"Service Level Agreement\",\n \"name\": \"Service Level Agreement\",\n \"type\": \"doctype\"\n }\n]" }, { "hidden": 0, @@ -43,7 +43,7 @@ "idx": 0, "is_standard": 1, "label": "Support", - "modified": "2020-05-28 13:51:23.869954", + "modified": "2020-06-04 11:54:56.124219", "modified_by": "Administrator", "module": "Support", "name": "Support", @@ -65,8 +65,8 @@ "type": "DocType" }, { - "label": "Service Level", - "link_to": "Service Level", + "label": "Service Level Agreement", + "link_to": "Service Level Agreement", "type": "DocType" } ] From 225802e3a0b09fc074c9bc25950d108d2808d1da Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Thu, 4 Jun 2020 14:11:18 +0530 Subject: [PATCH 286/608] fix: problem during assigning --- erpnext/hr/utils.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/hr/utils.py b/erpnext/hr/utils.py index cd125108c61..8d95924681a 100644 --- a/erpnext/hr/utils.py +++ b/erpnext/hr/utils.py @@ -73,11 +73,11 @@ class EmployeeBoardingController(Document): def assign_task_to_users(self, task, users): for user in users: args = { - 'assign_to' : user, - 'doctype' : task.doctype, - 'name' : task.name, - 'description' : task.description or task.subject, - 'notify': self.notify_users_by_email + 'assign_to': [user], + 'doctype': task.doctype, + 'name': task.name, + 'description': task.description or task.subject, + 'notify': self.notify_users_by_email } assign_to.add(args) From 60c18550148ee7fe4972113eb5e1d3899fa839e1 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 4 Jun 2020 14:28:34 +0530 Subject: [PATCH 287/608] fix: import supplier invoice not working --- .../doctype/import_supplier_invoice/import_supplier_invoice.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py index 6b9567c0e57..31a7545a0df 100644 --- a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py +++ b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py @@ -64,7 +64,8 @@ class ImportSupplierInvoice(Document): "buying_price_list": self.default_buying_price_list } - if not invoices_args.get("invoice_no", ''): return + if not invoices_args.get("bill_no", ''): + frappe.throw(_("Numero has not set in the XML file")) supp_dict = get_supplier_details(file_content) invoices_args["destination_code"] = get_destination_code_from_file(file_content) From 404ed88c0ca34aae06cb6e7b91a8442ec494440b Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 4 Jun 2020 14:46:50 +0530 Subject: [PATCH 288/608] fix: routing operations not added sequentially in the BOM --- erpnext/manufacturing/doctype/bom/bom.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 3253a496ed0..2543eec53e4 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -112,13 +112,14 @@ class BOM(WebsiteGenerator): if self.routing: self.set("operations", []) for d in frappe.get_all("BOM Operation", fields = ["*"], - filters = {'parenttype': 'Routing', 'parent': self.routing}): + filters = {'parenttype': 'Routing', 'parent': self.routing}, order_by="idx"): child = self.append('operations', { "operation": d.operation, "workstation": d.workstation, "description": d.description, "time_in_mins": d.time_in_mins, - "batch_size": d.batch_size + "batch_size": d.batch_size, + "idx": d.idx }) child.hour_rate = flt(d.hour_rate / self.conversion_rate, 2) From 3f083501818d5522faf76a53307b3eb585478629 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 4 Jun 2020 15:56:59 +0530 Subject: [PATCH 289/608] fix: retain Hold status --- .../patches/v13_0/update_sla_enhancements.py | 17 ----------------- erpnext/support/doctype/issue/issue.json | 4 ++-- 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/erpnext/patches/v13_0/update_sla_enhancements.py b/erpnext/patches/v13_0/update_sla_enhancements.py index 91c613fa46d..2356fb2679b 100644 --- a/erpnext/patches/v13_0/update_sla_enhancements.py +++ b/erpnext/patches/v13_0/update_sla_enhancements.py @@ -78,23 +78,6 @@ def execute(): frappe.delete_doc('DocType', 'Service Level') - # set issue status as Replied since Hold status is removed - if frappe.db.exists('DocType', 'Issue'): - issues_on_hold = frappe.db.get_all('Issue', {'status': 'Hold'}) - issues = [entry.name for entry in issues_on_hold] - if not issues: - return - - frappe.reload_doc('support', 'doctype', 'issue') - frappe.db.sql(""" - UPDATE - `tabIssue` - SET - status='Replied' - WHERE - name IN %(issues)s - """, {'issues': issues}) - def convert_to_seconds(value, unit): seconds = 0 diff --git a/erpnext/support/doctype/issue/issue.json b/erpnext/support/doctype/issue/issue.json index 8fb94013af1..70595e483a2 100644 --- a/erpnext/support/doctype/issue/issue.json +++ b/erpnext/support/doctype/issue/issue.json @@ -120,7 +120,7 @@ "no_copy": 1, "oldfieldname": "status", "oldfieldtype": "Select", - "options": "Open\nReplied\nResolved\nClosed", + "options": "Open\nReplied\nHold\nResolved\nClosed", "search_index": 1 }, { @@ -420,7 +420,7 @@ "icon": "fa fa-ticket", "idx": 7, "links": [], - "modified": "2020-05-26 12:12:59.343559", + "modified": "2020-06-04 15:53:38.322514", "modified_by": "Administrator", "module": "Support", "name": "Issue", From 94582a89c7a25af17d65d9b03ad76519ba42eeba Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Thu, 4 Jun 2020 17:50:47 +0530 Subject: [PATCH 290/608] fix: Wrong filters --- erpnext/hr/doctype/leave_application/leave_application.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index f2968bcd889..4499fa6016c 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -131,8 +131,6 @@ class LeaveApplication(Document): for dt in daterange(getdate(self.from_date), getdate(self.to_date)): date = dt.strftime("%Y-%m-%d") status = "Half Day" if getdate(date) == getdate(self.half_day_date) else "On Leave" - print("-------->>>", status) - # frappe.throw("Hello") attendance_name = frappe.db.exists('Attendance', dict(employee = self.employee, attendance_date = date, docstatus = ('!=', 2))) @@ -442,6 +440,7 @@ def get_leave_details(employee, date): total_allocated_leaves = frappe.db.get_value('Leave Allocation', { 'from_date': ('<=', date), 'to_date': ('>=', date), + 'employee': employee, 'leave_type': allocation.leave_type, }, 'SUM(total_leaves_allocated)') or 0 From 6f87f97efe521526d3eac512305c7c6b62bb3b9e Mon Sep 17 00:00:00 2001 From: P-Froggy <60393001+P-Froggy@users.noreply.github.com> Date: Fri, 5 Jun 2020 07:27:07 +0200 Subject: [PATCH 291/608] fix: Wrong Ordered-Status Indicator for Material Request Items (#22118) The indicator displaying if a material request item has been ordered or not (green/orange) used the wrong quantity field for determining the status. The qty field used in the code is not in stock uom while the ordered_qty field is. Now, the stock_qty field is used for correct comparison with ordered_qty. --- erpnext/stock/doctype/material_request/material_request.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js index 3562181e25f..3a8deb6d254 100644 --- a/erpnext/stock/doctype/material_request/material_request.js +++ b/erpnext/stock/doctype/material_request/material_request.js @@ -18,7 +18,7 @@ frappe.ui.form.on('Material Request', { // formatter for material request item frm.set_indicator_formatter('item_code', - function(doc) { return (doc.qty<=doc.ordered_qty) ? "green" : "orange"; }); + function(doc) { return (doc.stock_qty<=doc.ordered_qty) ? "green" : "orange"; }); frm.set_query("item_code", "items", function() { return { From b29cb878683f79e0d7e2dd50ec5bc3f20fe50956 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Fri, 5 Jun 2020 13:35:38 +0530 Subject: [PATCH 292/608] refactor: setting avg_response_time moved to communication --- erpnext/support/doctype/issue/issue.py | 23 ------------------- .../service_level_priority.json | 22 +++++++++++++----- 2 files changed, 16 insertions(+), 29 deletions(-) diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index c09c729c5c3..ac700c91e70 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -64,7 +64,6 @@ class Issue(Document): if frappe.db.get_value("Issue", self.name, "agreement_fulfilled") == "Ongoing": set_service_level_agreement_variance(issue=self.name) self.update_agreement_status() - set_average_response_time(issue=self) set_resolution_time(issue=self) set_user_resolution_time(issue=self) @@ -265,7 +264,6 @@ class Issue(Document): def reset_issue_metrics(self): self.db_set("resolution_time", None) self.db_set("user_resolution_time", None) - self.db_set("avg_response_time", None) def get_priority(issue): @@ -355,27 +353,6 @@ def set_service_level_agreement_variance(issue=None): if variance < 0: frappe.db.set_value(dt="Issue", dn=doc.name, field="agreement_fulfilled", val="Failed", update_modified=False) -def set_average_response_time(issue): - # avg response time for all the responses - communications = frappe.get_list("Communication", filters={ - "reference_doctype": issue.doctype, - "reference_name": issue.name - }, - fields=["sent_or_received", "name", "creation"], - order_by="creation" - ) - - if len(communications): - response_times = [] - for i in range(len(communications)): - if communications[i].sent_or_received == "Sent" and communications[i-1].sent_or_received == "Received": - response_time = time_diff_in_seconds(communications[i].creation, communications[i-1].creation) - if response_time > 0: - response_times.append(response_time) - if response_times: - avg_response_time = sum(response_times) / len(response_times) - issue.db_set("avg_response_time", avg_response_time) - def set_resolution_time(issue): # total time taken from issue creation to closing diff --git a/erpnext/support/doctype/service_level_priority/service_level_priority.json b/erpnext/support/doctype/service_level_priority/service_level_priority.json index 6377d1a962a..3995d1e2488 100644 --- a/erpnext/support/doctype/service_level_priority/service_level_priority.json +++ b/erpnext/support/doctype/service_level_priority/service_level_priority.json @@ -20,11 +20,15 @@ "fieldtype": "Link", "in_list_view": 1, "label": "Priority", - "options": "Issue Priority" + "options": "Issue Priority", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "sb_00", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "columns": 2, @@ -35,11 +39,15 @@ }, { "fieldname": "cb_00", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "cb_01", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "columns": 1, @@ -47,7 +55,9 @@ "fieldname": "default_priority", "fieldtype": "Check", "in_list_view": 1, - "label": "Default Priority" + "label": "Default Priority", + "show_days": 1, + "show_seconds": 1 }, { "columns": 2, @@ -59,7 +69,7 @@ ], "istable": 1, "links": [], - "modified": "2020-05-04 22:08:04.503949", + "modified": "2020-06-05 13:08:26.428657", "modified_by": "Administrator", "module": "Support", "name": "Service Level Priority", From 5a82276389851d0ecf701d2219c8d10d0fe15433 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 5 Jun 2020 13:56:20 +0530 Subject: [PATCH 293/608] fix: 'ForecastingReport' object has no attribute 'total_demand' --- .../exponential_smoothing_forecasting.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py b/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py index cac80677297..2ca9f1694b3 100644 --- a/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py +++ b/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py @@ -217,6 +217,8 @@ class ForecastingReport(ExponentialSmoothingForecast): } def get_summary_data(self): + if not self.data: return + return [ { "value": sum(self.total_demand), From 2c9f7cf371ac0ee8d6751a87b6f44bd33edb17f7 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Fri, 5 Jun 2020 16:35:57 +0530 Subject: [PATCH 294/608] feat: pause SLA on statuses configuration --- erpnext/support/doctype/issue/issue.json | 3 +- erpnext/support/doctype/issue/issue.py | 64 ++++++++++--------- .../doctype/pause_sla_on_status/__init__.py | 0 .../pause_sla_on_status.json | 33 ++++++++++ .../pause_sla_on_status.py | 10 +++ .../support_settings/support_settings.js | 10 ++- .../support_settings/support_settings.json | 13 +++- 7 files changed, 100 insertions(+), 33 deletions(-) create mode 100644 erpnext/support/doctype/pause_sla_on_status/__init__.py create mode 100644 erpnext/support/doctype/pause_sla_on_status/pause_sla_on_status.json create mode 100644 erpnext/support/doctype/pause_sla_on_status/pause_sla_on_status.py diff --git a/erpnext/support/doctype/issue/issue.json b/erpnext/support/doctype/issue/issue.json index 70595e483a2..712b70c2dd7 100644 --- a/erpnext/support/doctype/issue/issue.json +++ b/erpnext/support/doctype/issue/issue.json @@ -403,6 +403,7 @@ { "fieldname": "on_hold_since", "fieldtype": "Datetime", + "hidden": 1, "label": "On Hold Since", "read_only": 1, "show_days": 1, @@ -420,7 +421,7 @@ "icon": "fa fa-ticket", "idx": 7, "links": [], - "modified": "2020-06-04 15:53:38.322514", + "modified": "2020-06-05 15:45:24.474425", "modified_by": "Administrator", "module": "Support", "name": "Issue", diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index ac700c91e70..146eb5a2499 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -78,40 +78,44 @@ class Issue(Document): self.handle_hold_time(status) def handle_hold_time(self, status): - # set response and resolution variance as None as the issue is on Hold for status as Replied - if self.status == "Replied" and status != "Replied": - self.on_hold_since = frappe.flags.current_time or now_datetime() - if not self.first_responded_on: - self.response_by = None - self.response_by_variance = None - self.resolution_by = None - self.resolution_by_variance = None + if frappe.db.get_single_value("Support Settings", "track_service_level_agreement"): + # set response and resolution variance as None as the issue is on Hold for status as Replied + pause_sla_on = frappe.db.get_all("Pause SLA On Status", fields=["status"]) + hold_statuses = [entry.status for entry in pause_sla_on] - # calculate hold time when status is changed from Replied to any other status - if self.status != "Replied" and status == "Replied": - hold_time = self.total_hold_time if self.total_hold_time else 0 - now_time = frappe.flags.current_time or now_datetime() - self.total_hold_time = hold_time + time_diff_in_seconds(now_time, self.on_hold_since) + if self.status in hold_statuses and status not in hold_statuses: + self.on_hold_since = frappe.flags.current_time or now_datetime() + if not self.first_responded_on: + self.response_by = None + self.response_by_variance = None + self.resolution_by = None + self.resolution_by_variance = None - # re-calculate SLA variables after issue changes from Replied to Open - # add hold time to SLA variables - if self.status == "Open" and status == "Replied": - start_date_time = get_datetime(self.service_level_agreement_creation) - priority = get_priority(self) - now_time = frappe.flags.current_time or now_datetime() - hold_time = time_diff_in_seconds(now_time, self.on_hold_since) + # calculate hold time when status is changed from Replied to any other status + if self.status not in hold_statuses and status in hold_statuses: + hold_time = self.total_hold_time if self.total_hold_time else 0 + now_time = frappe.flags.current_time or now_datetime() + self.total_hold_time = hold_time + time_diff_in_seconds(now_time, self.on_hold_since) - if not self.first_responded_on: - response_by = get_expected_time_for(parameter="response", service_level=priority, start_date_time=start_date_time) - self.response_by = add_to_date(response_by, seconds=round(hold_time)) - response_by_variance = round(time_diff_in_hours(self.response_by, now_time)) - self.response_by_variance = response_by_variance + (hold_time // 3600) + # re-calculate SLA variables after issue changes from Replied to Open + # add hold time to SLA variables + if self.status == "Open" and status in hold_statuses: + start_date_time = get_datetime(self.service_level_agreement_creation) + priority = get_priority(self) + now_time = frappe.flags.current_time or now_datetime() + hold_time = time_diff_in_seconds(now_time, self.on_hold_since) - resolution_by = get_expected_time_for(parameter="resolution", service_level=priority, start_date_time=start_date_time) - self.resolution_by = add_to_date(resolution_by, seconds=round(hold_time)) - resolution_by_variance = round(time_diff_in_hours(self.resolution_by, now_time)) - self.resolution_by_variance = resolution_by_variance + (hold_time // 3600) - self.on_hold_since = None + if not self.first_responded_on: + response_by = get_expected_time_for(parameter="response", service_level=priority, start_date_time=start_date_time) + self.response_by = add_to_date(response_by, seconds=round(hold_time)) + response_by_variance = round(time_diff_in_hours(self.response_by, now_time)) + self.response_by_variance = response_by_variance + (hold_time // 3600) + + resolution_by = get_expected_time_for(parameter="resolution", service_level=priority, start_date_time=start_date_time) + self.resolution_by = add_to_date(resolution_by, seconds=round(hold_time)) + resolution_by_variance = round(time_diff_in_hours(self.resolution_by, now_time)) + self.resolution_by_variance = resolution_by_variance + (hold_time // 3600) + self.on_hold_since = None def update_agreement_status(self): if self.service_level_agreement and self.agreement_fulfilled == "Ongoing": diff --git a/erpnext/support/doctype/pause_sla_on_status/__init__.py b/erpnext/support/doctype/pause_sla_on_status/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/support/doctype/pause_sla_on_status/pause_sla_on_status.json b/erpnext/support/doctype/pause_sla_on_status/pause_sla_on_status.json new file mode 100644 index 00000000000..5b03f25f489 --- /dev/null +++ b/erpnext/support/doctype/pause_sla_on_status/pause_sla_on_status.json @@ -0,0 +1,33 @@ +{ + "actions": [], + "creation": "2020-06-05 13:59:43.265588", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "status" + ], + "fields": [ + { + "fieldname": "status", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Status", + "reqd": 1, + "show_days": 1, + "show_seconds": 1 + } + ], + "istable": 1, + "links": [], + "modified": "2020-06-05 15:15:29.986608", + "modified_by": "Administrator", + "module": "Support", + "name": "Pause SLA On Status", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/support/doctype/pause_sla_on_status/pause_sla_on_status.py b/erpnext/support/doctype/pause_sla_on_status/pause_sla_on_status.py new file mode 100644 index 00000000000..a3b547e4801 --- /dev/null +++ b/erpnext/support/doctype/pause_sla_on_status/pause_sla_on_status.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +# import frappe +from frappe.model.document import Document + +class PauseSLAOnStatus(Document): + pass diff --git a/erpnext/support/doctype/support_settings/support_settings.js b/erpnext/support/doctype/support_settings/support_settings.js index 1d1069d58bb..33ddf0b0792 100644 --- a/erpnext/support/doctype/support_settings/support_settings.js +++ b/erpnext/support/doctype/support_settings/support_settings.js @@ -2,7 +2,15 @@ // For license information, please see license.txt frappe.ui.form.on('Support Settings', { - refresh: function(frm) { + setup: function(frm) { + let allow_statuses = []; + const exclude_statuses = ['Open', 'Closed', 'Resolved']; + frappe.model.with_doctype('Issue', () => { + let statuses = frappe.meta.get_docfield('Issue', 'status', frm.doc.name).options; + statuses = statuses.split('\n'); + allow_statuses = statuses.filter((status) => !exclude_statuses.includes(status)); + frappe.meta.get_docfield('Pause SLA On Status', 'status', frm.doc.name).options = [''].concat(allow_statuses); + }); } }); diff --git a/erpnext/support/doctype/support_settings/support_settings.json b/erpnext/support/doctype/support_settings/support_settings.json index 3f52181a465..da4b607e2cd 100644 --- a/erpnext/support/doctype/support_settings/support_settings.json +++ b/erpnext/support/doctype/support_settings/support_settings.json @@ -10,6 +10,7 @@ "allow_resetting_service_level_agreement", "issues_sb", "close_issue_after_days", + "pause_sla_on_status", "portal_sb", "get_started_sections", "show_latest_forum_posts", @@ -123,14 +124,24 @@ }, { "default": "0", + "depends_on": "eval:doc.track_service_level_agreement;", "fieldname": "allow_resetting_service_level_agreement", "fieldtype": "Check", "label": "Allow Resetting Service Level Agreement" + }, + { + "depends_on": "eval:doc.track_service_level_agreement;", + "fieldname": "pause_sla_on_status", + "fieldtype": "Table", + "label": "Pause SLA On", + "options": "Pause SLA On Status", + "show_days": 1, + "show_seconds": 1 } ], "issingle": 1, "links": [], - "modified": "2020-04-28 14:11:15.117019", + "modified": "2020-06-05 16:35:13.905096", "modified_by": "Administrator", "module": "Support", "name": "Support Settings", From 8d3992841847d09a118ae6d4efe6fa974c7fab63 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Fri, 5 Jun 2020 16:36:31 +0530 Subject: [PATCH 295/608] refactor: dashboard indicator when SLA is on hold --- erpnext/support/doctype/issue/issue.js | 42 +++++++++++++++++--------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/erpnext/support/doctype/issue/issue.js b/erpnext/support/doctype/issue/issue.js index 6632ab69baf..32a77739d0a 100644 --- a/erpnext/support/doctype/issue/issue.js +++ b/erpnext/support/doctype/issue/issue.js @@ -38,22 +38,35 @@ frappe.ui.form.on("Issue", { }, refresh: function (frm) { - if (frm.doc.status !== "Closed" && frm.doc.agreement_fulfilled === "Ongoing") { if (frm.doc.service_level_agreement) { - if (frm.doc.status == "Replied") { - frm.dashboard.clear_headline(); - let message = {"indicator": "orange", "msg": __("Replied {0}", [frappe.datetime.comment_when(frm.doc.on_hold_since)])}; - frm.dashboard.set_headline_alert( - '
' + - '
' + - ''+ message.msg +' ' + - '
' + - '
' - ); - } else { - set_time_to_resolve_and_response(frm); - } + frappe.call({ + 'method': 'frappe.client.get', + args: { + doctype: 'Support Settings', + name: 'Support Settings' + }, + callback: function(data) { + let statuses = data.message.pause_sla_on_status; + const hold_statuses = []; + $.each(statuses, (_i, entry) => { + hold_statuses.push(entry.status); + }); + if (hold_statuses.includes(frm.doc.status)) { + frm.dashboard.clear_headline(); + let message = {"indicator": "orange", "msg": __("SLA is on hold since {0}", [moment(frm.doc.on_hold_since).fromNow(true)])}; + frm.dashboard.set_headline_alert( + '
' + + '
' + + ''+ message.msg +' ' + + '
' + + '
' + ); + } else { + set_time_to_resolve_and_response(frm); + } + } + }); } frm.add_custom_button(__("Close"), function () { @@ -67,6 +80,7 @@ frappe.ui.form.on("Issue", { frm: frm }); }, __("Make")); + } else { if (frm.doc.service_level_agreement) { frm.dashboard.clear_headline(); From 25ef6a963811210c65d6ef3778bc0923c365c5ad Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Fri, 5 Jun 2020 16:37:20 +0530 Subject: [PATCH 296/608] fix: add default hold statuses in fixtures and patch --- erpnext/patches/v13_0/update_sla_enhancements.py | 4 ++++ erpnext/setup/install.py | 11 +++++++++++ 2 files changed, 15 insertions(+) diff --git a/erpnext/patches/v13_0/update_sla_enhancements.py b/erpnext/patches/v13_0/update_sla_enhancements.py index 2356fb2679b..884d01b10c1 100644 --- a/erpnext/patches/v13_0/update_sla_enhancements.py +++ b/erpnext/patches/v13_0/update_sla_enhancements.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import frappe +from erpnext.setup.install import add_sla_hold_statuses_to_support_settings def execute(): # add holiday list and employee group fields in SLA @@ -78,6 +79,9 @@ def execute(): frappe.delete_doc('DocType', 'Service Level') + # add SLA hold statuses to Support Settings + add_sla_hold_statuses_to_support_settings() + def convert_to_seconds(value, unit): seconds = 0 diff --git a/erpnext/setup/install.py b/erpnext/setup/install.py index e666a41f300..90e5f5a0ae0 100644 --- a/erpnext/setup/install.py +++ b/erpnext/setup/install.py @@ -25,6 +25,7 @@ def after_install(): create_default_success_action() create_default_energy_point_rules() add_company_to_session_defaults() + add_sla_hold_statuses_to_support_settings() frappe.db.commit() @@ -105,3 +106,13 @@ def add_company_to_session_defaults(): "ref_doctype": "Company" }) settings.save() + +def add_sla_hold_statuses_to_support_settings(): + settings = frappe.get_single("Support Settings") + settings.append("pause_sla_on_status", { + "status": "Replied" + }) + settings.append("pause_sla_on_status", { + "status": "Hold" + }) + settings.save() From 328a55749d8666028544e098cd280865d946f861 Mon Sep 17 00:00:00 2001 From: Abhishek Balam Date: Fri, 5 Jun 2020 16:46:57 +0530 Subject: [PATCH 297/608] fix(HR): Fix half day error when only 1 day is selected (#22122) * fix half day error when only 1 day is selected * adding validation fix for half day on serverside --- erpnext/hr/doctype/leave_application/leave_application.js | 3 +++ erpnext/hr/doctype/leave_application/leave_application.py | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/erpnext/hr/doctype/leave_application/leave_application.js b/erpnext/hr/doctype/leave_application/leave_application.js index 473aae6f42f..15ce468c138 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.js +++ b/erpnext/hr/doctype/leave_application/leave_application.js @@ -38,6 +38,9 @@ frappe.ui.form.on("Leave Application", { }, validate: function(frm) { + if (frm.doc.from_date == frm.doc.to_date && frm.doc.half_day == 1){ + frm.doc.half_day_date = frm.doc.from_date; + } frm.toggle_reqd("half_day_date", frm.doc.half_day == 1); }, diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index f2968bcd889..5eb84d770a1 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -33,6 +33,7 @@ class LeaveApplication(Document): self.validate_block_days() self.validate_salary_processed_days() self.validate_attendance() + self.set_half_day_date() if frappe.db.get_value("Leave Type", self.leave_type, 'is_optional_leave'): self.validate_optional_leave() self.validate_applicable_after() @@ -292,6 +293,10 @@ class LeaveApplication(Document): frappe.throw(_("{0} is not in Optional Holiday List").format(formatdate(day)), NotAnOptionalHoliday) day = add_days(day, 1) + def set_half_day_date(self): + if self.from_date == self.to_date and self.half_day == 1: + self.half_day_date = self.from_date + def notify_employee(self): employee = frappe.get_doc("Employee", self.employee) if not employee.user_id: From d7d3ca4214e2be005da27342bf2d0fd6e621baa5 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Fri, 5 Jun 2020 16:57:37 +0530 Subject: [PATCH 298/608] fix: tests --- erpnext/support/doctype/issue/test_issue.py | 2 ++ .../service_level_agreement/test_service_level_agreement.py | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/erpnext/support/doctype/issue/test_issue.py b/erpnext/support/doctype/issue/test_issue.py index a0048432705..93a6005cc24 100644 --- a/erpnext/support/doctype/issue/test_issue.py +++ b/erpnext/support/doctype/issue/test_issue.py @@ -5,6 +5,7 @@ from __future__ import unicode_literals import frappe import unittest from erpnext.support.doctype.service_level_agreement.test_service_level_agreement import create_service_level_agreements_for_issues +from erpnext.setup.install import add_sla_hold_statuses_to_support_settings from frappe.utils import now_datetime, get_datetime import datetime from datetime import timedelta @@ -100,6 +101,7 @@ class TestIssue(unittest.TestCase): self.assertEqual(issue.user_resolution_time, 1200) def test_hold_time_on_replied(self): + add_sla_hold_statuses_to_support_settings() creation = datetime.datetime(2020, 3, 4, 4, 0) issue = make_issue(creation, index=1) diff --git a/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py index 57d4747e5c9..26740ed899b 100644 --- a/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py +++ b/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py @@ -9,10 +9,11 @@ from erpnext.hr.doctype.employee_group.test_employee_group import make_employee_ from erpnext.support.doctype.issue_priority.test_issue_priority import make_priorities class TestServiceLevelAgreement(unittest.TestCase): - - def test_service_level_agreement(self): + def setUp(self): + frappe.db.sql("delete from `tabService Level Agreement`") frappe.db.set_value("Support Settings", None, "track_service_level_agreement", 1) + def test_service_level_agreement(self): # Default Service Level Agreement create_default_service_level_agreement = create_service_level_agreement(default_service_level_agreement=1, holiday_list="__Test Holiday List", employee_group="_Test Employee Group", From 73326d308b610458e28dc0516ff50aee9d1c7d35 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Fri, 5 Jun 2020 18:23:39 +0530 Subject: [PATCH 299/608] refactor: move pause SLA configuration to SLA DocType --- .../patches/v13_0/update_sla_enhancements.py | 4 ---- erpnext/setup/install.py | 10 ---------- erpnext/support/doctype/issue/issue.js | 6 +++--- erpnext/support/doctype/issue/issue.py | 5 +++-- erpnext/support/doctype/issue/test_issue.py | 2 -- .../service_level_agreement.js | 12 +++++++++++- .../service_level_agreement.json | 19 ++++++++++++++++++- .../test_service_level_agreement.py | 5 +++++ .../support_settings/support_settings.js | 12 ++---------- .../support_settings/support_settings.json | 12 +----------- 10 files changed, 43 insertions(+), 44 deletions(-) diff --git a/erpnext/patches/v13_0/update_sla_enhancements.py b/erpnext/patches/v13_0/update_sla_enhancements.py index 884d01b10c1..2356fb2679b 100644 --- a/erpnext/patches/v13_0/update_sla_enhancements.py +++ b/erpnext/patches/v13_0/update_sla_enhancements.py @@ -4,7 +4,6 @@ from __future__ import unicode_literals import frappe -from erpnext.setup.install import add_sla_hold_statuses_to_support_settings def execute(): # add holiday list and employee group fields in SLA @@ -79,9 +78,6 @@ def execute(): frappe.delete_doc('DocType', 'Service Level') - # add SLA hold statuses to Support Settings - add_sla_hold_statuses_to_support_settings() - def convert_to_seconds(value, unit): seconds = 0 diff --git a/erpnext/setup/install.py b/erpnext/setup/install.py index 90e5f5a0ae0..74ff0ecfd8f 100644 --- a/erpnext/setup/install.py +++ b/erpnext/setup/install.py @@ -25,7 +25,6 @@ def after_install(): create_default_success_action() create_default_energy_point_rules() add_company_to_session_defaults() - add_sla_hold_statuses_to_support_settings() frappe.db.commit() @@ -107,12 +106,3 @@ def add_company_to_session_defaults(): }) settings.save() -def add_sla_hold_statuses_to_support_settings(): - settings = frappe.get_single("Support Settings") - settings.append("pause_sla_on_status", { - "status": "Replied" - }) - settings.append("pause_sla_on_status", { - "status": "Hold" - }) - settings.save() diff --git a/erpnext/support/doctype/issue/issue.js b/erpnext/support/doctype/issue/issue.js index 32a77739d0a..e7e5bd312bc 100644 --- a/erpnext/support/doctype/issue/issue.js +++ b/erpnext/support/doctype/issue/issue.js @@ -43,11 +43,11 @@ frappe.ui.form.on("Issue", { frappe.call({ 'method': 'frappe.client.get', args: { - doctype: 'Support Settings', - name: 'Support Settings' + doctype: 'Service Level Agreement', + name: frm.doc.service_level_agreement }, callback: function(data) { - let statuses = data.message.pause_sla_on_status; + let statuses = data.message.pause_sla_on; const hold_statuses = []; $.each(statuses, (_i, entry) => { hold_statuses.push(entry.status); diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index 146eb5a2499..4003047e81c 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -78,9 +78,10 @@ class Issue(Document): self.handle_hold_time(status) def handle_hold_time(self, status): - if frappe.db.get_single_value("Support Settings", "track_service_level_agreement"): + if self.service_level_agreement: # set response and resolution variance as None as the issue is on Hold for status as Replied - pause_sla_on = frappe.db.get_all("Pause SLA On Status", fields=["status"]) + pause_sla_on = frappe.db.get_all("Pause SLA On Status", fields=["status"], + filters={"parent": self.service_level_agreement}) hold_statuses = [entry.status for entry in pause_sla_on] if self.status in hold_statuses and status not in hold_statuses: diff --git a/erpnext/support/doctype/issue/test_issue.py b/erpnext/support/doctype/issue/test_issue.py index 93a6005cc24..a0048432705 100644 --- a/erpnext/support/doctype/issue/test_issue.py +++ b/erpnext/support/doctype/issue/test_issue.py @@ -5,7 +5,6 @@ from __future__ import unicode_literals import frappe import unittest from erpnext.support.doctype.service_level_agreement.test_service_level_agreement import create_service_level_agreements_for_issues -from erpnext.setup.install import add_sla_hold_statuses_to_support_settings from frappe.utils import now_datetime, get_datetime import datetime from datetime import timedelta @@ -101,7 +100,6 @@ class TestIssue(unittest.TestCase): self.assertEqual(issue.user_resolution_time, 1200) def test_hold_time_on_replied(self): - add_sla_hold_statuses_to_support_settings() creation = datetime.datetime(2020, 3, 4, 4, 0) issue = make_issue(creation, index=1) diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.js b/erpnext/support/doctype/service_level_agreement/service_level_agreement.js index 7aeaae48bab..5346195a396 100644 --- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.js +++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.js @@ -2,5 +2,15 @@ // For license information, please see license.txt frappe.ui.form.on('Service Level Agreement', { + setup: function(frm) { + let allow_statuses = []; + const exclude_statuses = ['Open', 'Closed', 'Resolved']; -}); + frappe.model.with_doctype('Issue', () => { + let statuses = frappe.meta.get_docfield('Issue', 'status', frm.doc.name).options; + statuses = statuses.split('\n'); + allow_statuses = statuses.filter((status) => !exclude_statuses.includes(status)); + frappe.meta.get_docfield('Pause SLA On Status', 'status', frm.doc.name).options = [''].concat(allow_statuses); + }); + } +}); \ No newline at end of file diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.json b/erpnext/support/doctype/service_level_agreement/service_level_agreement.json index ede5f98ebae..e65169c0d43 100644 --- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.json +++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.json @@ -23,6 +23,8 @@ "active", "column_break_7", "end_date", + "section_break_18", + "pause_sla_on", "response_and_resolution_time_section", "priorities", "support_and_resolution_section_break", @@ -160,10 +162,25 @@ "read_only": 1, "show_days": 1, "show_seconds": 1 + }, + { + "fieldname": "section_break_18", + "fieldtype": "Section Break", + "hide_border": 1, + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "pause_sla_on", + "fieldtype": "Table", + "label": "Pause SLA On", + "options": "Pause SLA On Status", + "show_days": 1, + "show_seconds": 1 } ], "links": [], - "modified": "2020-05-26 16:02:59.859980", + "modified": "2020-06-05 17:51:15.050785", "modified_by": "Administrator", "module": "Support", "name": "Service Level Agreement", diff --git a/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py index 26740ed899b..0746a9c73ef 100644 --- a/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py +++ b/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py @@ -115,6 +115,11 @@ def create_service_level_agreement(default_service_level_agreement, holiday_list "resolution_time_period": "Hour", } ], + "pause_sla_on": [ + { + "status": "Replied" + } + ], "support_and_resolution": [ { "workday": "Monday", diff --git a/erpnext/support/doctype/support_settings/support_settings.js b/erpnext/support/doctype/support_settings/support_settings.js index 33ddf0b0792..78adca81ca5 100644 --- a/erpnext/support/doctype/support_settings/support_settings.js +++ b/erpnext/support/doctype/support_settings/support_settings.js @@ -2,15 +2,7 @@ // For license information, please see license.txt frappe.ui.form.on('Support Settings', { - setup: function(frm) { - let allow_statuses = []; - const exclude_statuses = ['Open', 'Closed', 'Resolved']; - - frappe.model.with_doctype('Issue', () => { - let statuses = frappe.meta.get_docfield('Issue', 'status', frm.doc.name).options; - statuses = statuses.split('\n'); - allow_statuses = statuses.filter((status) => !exclude_statuses.includes(status)); - frappe.meta.get_docfield('Pause SLA On Status', 'status', frm.doc.name).options = [''].concat(allow_statuses); - }); + refresh: function(frm) { + // } }); diff --git a/erpnext/support/doctype/support_settings/support_settings.json b/erpnext/support/doctype/support_settings/support_settings.json index da4b607e2cd..1c1b0c3517e 100644 --- a/erpnext/support/doctype/support_settings/support_settings.json +++ b/erpnext/support/doctype/support_settings/support_settings.json @@ -10,7 +10,6 @@ "allow_resetting_service_level_agreement", "issues_sb", "close_issue_after_days", - "pause_sla_on_status", "portal_sb", "get_started_sections", "show_latest_forum_posts", @@ -128,20 +127,11 @@ "fieldname": "allow_resetting_service_level_agreement", "fieldtype": "Check", "label": "Allow Resetting Service Level Agreement" - }, - { - "depends_on": "eval:doc.track_service_level_agreement;", - "fieldname": "pause_sla_on_status", - "fieldtype": "Table", - "label": "Pause SLA On", - "options": "Pause SLA On Status", - "show_days": 1, - "show_seconds": 1 } ], "issingle": 1, "links": [], - "modified": "2020-06-05 16:35:13.905096", + "modified": "2020-06-05 17:56:17.491684", "modified_by": "Administrator", "module": "Support", "name": "Support Settings", From 3375a2e85a5d50cdb9e2112e7a55168fdd2ef00a Mon Sep 17 00:00:00 2001 From: Revant Nandgaonkar Date: Sat, 6 Jun 2020 21:08:49 +0530 Subject: [PATCH 300/608] ci: trigger docker build on release (#22128) --- .github/workflows/docker-release.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .github/workflows/docker-release.yml diff --git a/.github/workflows/docker-release.yml b/.github/workflows/docker-release.yml new file mode 100644 index 00000000000..d36b11553ce --- /dev/null +++ b/.github/workflows/docker-release.yml @@ -0,0 +1,14 @@ +name: Trigger Docker build on release +on: + release: + types: [created] +jobs: + curl: + runs-on: ubuntu-latest + container: + image: alpine:latest + steps: + - name: curl + run: | + apk add curl bash + curl -s -X POST -H "Content-Type: application/json" -H "Accept: application/json" -H "Travis-API-Version: 3" -H "Authorization: token ${{ secrets.TRAVIS_CI_TOKEN }}" -d '{"request":{"branch":"master"}}' https://api.travis-ci.org/repo/frappe%2Ffrappe_docker/requests From 57f9d43c43f27444054ae8e32ecf52fc0d04a295 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 7 Jun 2020 00:01:20 +0530 Subject: [PATCH 301/608] fix: Cancelled entries in tds payable monthly report --- .../accounts/report/tds_payable_monthly/tds_payable_monthly.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py index 4ac0f656119..a9fb237a048 100644 --- a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py +++ b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py @@ -111,7 +111,7 @@ def get_gle_map(filters): # {"purchase_invoice": list of dict of all gle created for this invoice} gle_map = {} gle = frappe.db.get_all('GL Entry',\ - {"voucher_no": ["in", [d.get("name") for d in filters["invoices"]]]}, + {"voucher_no": ["in", [d.get("name") for d in filters["invoices"]]], 'is_cancelled': 0}, ["fiscal_year", "credit", "debit", "account", "voucher_no", "posting_date"]) for d in gle: From 303112816771639a7808663170dac497b4f8e2d1 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Sun, 7 Jun 2020 18:02:23 +0530 Subject: [PATCH 302/608] fix: fetch tax lines within the shipping lines (#22138) --- .../connectors/shopify_connection.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/erpnext/erpnext_integrations/connectors/shopify_connection.py b/erpnext/erpnext_integrations/connectors/shopify_connection.py index 7046038fb24..d59f9092983 100644 --- a/erpnext/erpnext_integrations/connectors/shopify_connection.py +++ b/erpnext/erpnext_integrations/connectors/shopify_connection.py @@ -241,14 +241,17 @@ def get_order_taxes(shopify_order, shopify_settings): return taxes def update_taxes_with_shipping_lines(taxes, shipping_lines, shopify_settings): + """Shipping lines represents the shipping details, + each such shipping detail consists of a list of tax_lines""" for shipping_charge in shipping_lines: - taxes.append({ - "charge_type": _("Actual"), - "account_head": get_tax_account_head(shipping_charge), - "description": shipping_charge["title"], - "tax_amount": shipping_charge["price"], - "cost_center": shopify_settings.cost_center - }) + for tax in shipping_charge.get("tax_lines"): + taxes.append({ + "charge_type": _("Actual"), + "account_head": get_tax_account_head(tax), + "description": tax["title"], + "tax_amount": tax["price"], + "cost_center": shopify_settings.cost_center + }) return taxes From e5ac8cc430e4e3165b62c550dcf5e3eef55cf8b4 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 7 Jun 2020 21:18:47 +0530 Subject: [PATCH 303/608] fix: Precision in loan amount calculation --- .../loan_management/desk_page/loan/loan.json | 5 +-- .../loan_interest_accrual.py | 30 +++++++------- .../doctype/loan_repayment/loan_repayment.py | 40 +++++++++++-------- .../doctype/loan_type/loan_type.json | 3 +- .../loan_security_status.py | 13 +++--- 5 files changed, 49 insertions(+), 42 deletions(-) diff --git a/erpnext/loan_management/desk_page/loan/loan.json b/erpnext/loan_management/desk_page/loan/loan.json index d79860a3520..48193b0a0d8 100644 --- a/erpnext/loan_management/desk_page/loan/loan.json +++ b/erpnext/loan_management/desk_page/loan/loan.json @@ -23,7 +23,7 @@ { "hidden": 0, "label": "Reports", - "links": "[\n {\n \"dependencies\": [\n \"Loan Repayment\"\n ],\n \"doctype\": \"Loan Repayment\",\n \"incomplete_dependencies\": [\n \"Loan Repayment\"\n ],\n \"is_query_report\": true,\n \"label\": \"Loan Repayment and Closure\",\n \"name\": \"Loan Repayment and Closure\",\n \"route\": \"#query-report/Loan Repayment and Closure\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Loan Security Pledge\"\n ],\n \"doctype\": \"Loan Security Pledge\",\n \"incomplete_dependencies\": [\n \"Loan Security Pledge\"\n ],\n \"is_query_report\": true,\n \"label\": \"Loan Security Status\",\n \"name\": \"Loan Security Status\",\n \"route\": \"#query-report/Loan Security Status\",\n \"type\": \"report\"\n }\n]" + "links": "[\n {\n \"doctype\": \"Loan Repayment\",\n \"is_query_report\": true,\n \"label\": \"Loan Repayment and Closure\",\n \"name\": \"Loan Repayment and Closure\",\n \"route\": \"#query-report/Loan Repayment and Closure\",\n \"type\": \"report\"\n },\n {\n \"doctype\": \"Loan Security Pledge\",\n \"is_query_report\": true,\n \"label\": \"Loan Security Status\",\n \"name\": \"Loan Security Status\",\n \"route\": \"#query-report/Loan Security Status\",\n \"type\": \"report\"\n }\n]" } ], "category": "Modules", @@ -34,11 +34,10 @@ "docstatus": 0, "doctype": "Desk Page", "extends_another_page": 0, - "hide_custom": 0, "idx": 0, "is_standard": 1, "label": "Loan", - "modified": "2020-05-28 13:37:42.017709", + "modified": "2020-06-07 19:42:14.947902", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan", diff --git a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py index 094b9c698c7..659173557be 100644 --- a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py +++ b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py @@ -176,21 +176,23 @@ def get_term_loans(date, term_loan=None, loan_type=None): return term_loans def make_loan_interest_accrual_entry(args): - loan_interest_accrual = frappe.new_doc("Loan Interest Accrual") - loan_interest_accrual.loan = args.loan - loan_interest_accrual.applicant_type = args.applicant_type - loan_interest_accrual.applicant = args.applicant - loan_interest_accrual.interest_income_account = args.interest_income_account - loan_interest_accrual.loan_account = args.loan_account - loan_interest_accrual.pending_principal_amount = flt(args.pending_principal_amount, 2) - loan_interest_accrual.interest_amount = flt(args.interest_amount, 2) - loan_interest_accrual.posting_date = args.posting_date or nowdate() - loan_interest_accrual.process_loan_interest_accrual = args.process_loan_interest - loan_interest_accrual.repayment_schedule_name = args.repayment_schedule_name - loan_interest_accrual.payable_principal_amount = args.payable_principal + precision = cint(frappe.db.get_default("currency_precision")) or 2 - loan_interest_accrual.save() - loan_interest_accrual.submit() + loan_interest_accrual = frappe.new_doc("Loan Interest Accrual") + loan_interest_accrual.loan = args.loan + loan_interest_accrual.applicant_type = args.applicant_type + loan_interest_accrual.applicant = args.applicant + loan_interest_accrual.interest_income_account = args.interest_income_account + loan_interest_accrual.loan_account = args.loan_account + loan_interest_accrual.pending_principal_amount = flt(args.pending_principal_amount, precision) + loan_interest_accrual.interest_amount = flt(args.interest_amount, precision) + loan_interest_accrual.posting_date = args.posting_date or nowdate() + loan_interest_accrual.process_loan_interest_accrual = args.process_loan_interest + loan_interest_accrual.repayment_schedule_name = args.repayment_schedule_name + loan_interest_accrual.payable_principal_amount = args.payable_principal + + loan_interest_accrual.save() + loan_interest_accrual.submit() def get_no_of_days_for_interest_accural(loan, posting_date): diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py index 2ab668a0e11..c28994e280b 100644 --- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py +++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py @@ -6,7 +6,7 @@ from __future__ import unicode_literals import frappe, erpnext import json from frappe import _ -from frappe.utils import flt, getdate +from frappe.utils import flt, getdate, cint from six import iteritems from frappe.model.document import Document from frappe.utils import date_diff, add_days, getdate, add_months, get_first_day, get_datetime @@ -29,8 +29,11 @@ class LoanRepayment(AccountsController): def on_cancel(self): self.mark_as_unpaid() self.make_gl_entries(cancel=1) + self.ignore_linked_doctypes = ['GL Entry'] def set_missing_values(self, amounts): + precision = cint(frappe.db.get_default("currency_precision")) or 2 + if not self.posting_date: self.posting_date = get_datetime() @@ -38,24 +41,26 @@ class LoanRepayment(AccountsController): self.cost_center = erpnext.get_default_cost_center(self.company) if not self.interest_payable: - self.interest_payable = flt(amounts['interest_amount'], 2) + self.interest_payable = flt(amounts['interest_amount'], precision) if not self.penalty_amount: - self.penalty_amount = flt(amounts['penalty_amount'], 2) + self.penalty_amount = flt(amounts['penalty_amount'], precision) if not self.pending_principal_amount: - self.pending_principal_amount = flt(amounts['pending_principal_amount'], 2) + self.pending_principal_amount = flt(amounts['pending_principal_amount'], precision) if not self.payable_principal_amount and self.is_term_loan: - self.payable_principal_amount = flt(amounts['payable_principal_amount'], 2) + self.payable_principal_amount = flt(amounts['payable_principal_amount'], precision) if not self.payable_amount: - self.payable_amount = flt(amounts['payable_amount'], 2) + self.payable_amount = flt(amounts['payable_amount'], precision) if amounts.get('due_date'): self.due_date = amounts.get('due_date') def validate_amount(self): + precision = cint(frappe.db.get_default("currency_precision")) or 2 + if not self.amount_paid: frappe.throw(_("Amount paid cannot be zero")) @@ -63,11 +68,13 @@ class LoanRepayment(AccountsController): msg = _("Paid amount cannot be less than {0}").format(self.penalty_amount) frappe.throw(msg) - if self.payment_type == "Loan Closure" and flt(self.amount_paid, 2) < flt(self.payable_amount, 2): + if self.payment_type == "Loan Closure" and flt(self.amount_paid, precision) < flt(self.payable_amount, precision): msg = _("Amount of {0} is required for Loan closure").format(self.payable_amount) frappe.throw(msg) def update_paid_amount(self): + precision = cint(frappe.db.get_default("currency_precision")) or 2 + loan = frappe.get_doc("Loan", self.against_loan) for payment in self.repayment_details: @@ -75,9 +82,9 @@ class LoanRepayment(AccountsController): SET paid_principal_amount = `paid_principal_amount` + %s, paid_interest_amount = `paid_interest_amount` + %s WHERE name = %s""", - (flt(payment.paid_principal_amount), flt(payment.paid_interest_amount), payment.loan_interest_accrual)) + (flt(payment.paid_principal_amount, precision), flt(payment.paid_interest_amount, precision), payment.loan_interest_accrual)) - if flt(loan.total_principal_paid + self.principal_amount_paid, 2) >= flt(loan.total_payment, 2): + if flt(loan.total_principal_paid + self.principal_amount_paid, precision) >= flt(loan.total_payment, precision): if loan.is_secured_loan: frappe.db.set_value("Loan", self.against_loan, "status", "Loan Closure Requested") else: @@ -253,6 +260,7 @@ def get_accrued_interest_entries(against_loan): # So it pulls all the unpaid Loan Interest Accrual Entries and calculates the penalty if applicable def get_amounts(amounts, against_loan, posting_date, payment_type): + precision = cint(frappe.db.get_default("currency_precision")) or 2 against_loan_doc = frappe.get_doc("Loan", against_loan) loan_type_details = frappe.get_doc("Loan Type", against_loan_doc.loan_type) @@ -282,8 +290,8 @@ def get_amounts(amounts, against_loan, posting_date, payment_type): payable_principal_amount += entry.payable_principal_amount pending_accrual_entries.setdefault(entry.name, { - 'interest_amount': flt(entry.interest_amount), - 'payable_principal_amount': flt(entry.payable_principal_amount) + 'interest_amount': flt(entry.interest_amount, precision), + 'payable_principal_amount': flt(entry.payable_principal_amount, precision) }) if not final_due_date: @@ -301,11 +309,11 @@ def get_amounts(amounts, against_loan, posting_date, payment_type): per_day_interest = (payable_principal_amount * (loan_type_details.rate_of_interest / 100))/365 total_pending_interest += (pending_days * per_day_interest) - amounts["pending_principal_amount"] = pending_principal_amount - amounts["payable_principal_amount"] = payable_principal_amount - amounts["interest_amount"] = total_pending_interest - amounts["penalty_amount"] = penalty_amount - amounts["payable_amount"] = payable_principal_amount + total_pending_interest + penalty_amount + amounts["pending_principal_amount"] = flt(pending_principal_amount, precision) + amounts["payable_principal_amount"] = flt(payable_principal_amount, precision) + amounts["interest_amount"] = flt(total_pending_interest, precision) + amounts["penalty_amount"] = flt(penalty_amount, precision) + amounts["payable_amount"] = flt(payable_principal_amount + total_pending_interest + penalty_amount, precision) amounts["pending_accrual_entries"] = pending_accrual_entries if final_due_date: diff --git a/erpnext/loan_management/doctype/loan_type/loan_type.json b/erpnext/loan_management/doctype/loan_type/loan_type.json index 51c5cb98a63..1dd3710cd2a 100644 --- a/erpnext/loan_management/doctype/loan_type/loan_type.json +++ b/erpnext/loan_management/doctype/loan_type/loan_type.json @@ -41,6 +41,7 @@ "options": "Company:company:default_currency" }, { + "default": "0", "fieldname": "rate_of_interest", "fieldtype": "Percent", "label": "Rate of Interest (%) Yearly", @@ -143,7 +144,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-04-15 00:24:43.259963", + "modified": "2020-06-07 18:55:59.346292", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan Type", diff --git a/erpnext/loan_management/report/loan_security_status/loan_security_status.py b/erpnext/loan_management/report/loan_security_status/loan_security_status.py index ea6a2ee6452..19518554759 100644 --- a/erpnext/loan_management/report/loan_security_status/loan_security_status.py +++ b/erpnext/loan_management/report/loan_security_status/loan_security_status.py @@ -76,7 +76,8 @@ def get_columns(filters): "fieldtype": "Link", "fieldname": "currency", "options": "Currency", - "width": 50 + "width": 50, + "hidden": 1 } ] @@ -84,17 +85,13 @@ def get_columns(filters): def get_data(filters): - loan_security_price_map = frappe._dict(frappe.get_all("Loan Security", - fields=["name", "loan_security_price"], as_list=1 - )) - data = [] conditions = get_conditions(filters) loan_security_pledges = frappe.db.sql(""" SELECT p.name, p.applicant, p.loan, p.status, p.pledge_time, - c.loan_security, c.qty + c.loan_security, c.qty, c.loan_security_price, c.amount FROM `tabLoan Security Pledge` p, `tabPledge` c WHERE @@ -115,8 +112,8 @@ def get_data(filters): row["pledge_time"] = pledge.pledge_time row["loan_security"] = pledge.loan_security row["qty"] = pledge.qty - row["loan_security_price"] = loan_security_price_map.get(pledge.loan_security) - row["loan_security_value"] = row["loan_security_price"] * pledge.qty + row["loan_security_price"] = pledge.loan_security_price + row["loan_security_value"] = pledge.amount row["currency"] = default_currency data.append(row) From 0bc163c230c8b5095bddf200300a3b18a3d421d7 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 7 Jun 2020 21:23:55 +0530 Subject: [PATCH 304/608] fix: Ignore GL Entry on cancel --- .../doctype/loan_disbursement/loan_disbursement.py | 1 + .../doctype/loan_interest_accrual/loan_interest_accrual.py | 1 + 2 files changed, 2 insertions(+) diff --git a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py index c9e36a84ddb..d44088bee74 100644 --- a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py +++ b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py @@ -27,6 +27,7 @@ class LoanDisbursement(AccountsController): def on_cancel(self): self.make_gl_entries(cancel=1) + self.ignore_linked_doctypes = ['GL Entry'] def set_missing_values(self): if not self.disbursement_date: diff --git a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py index 659173557be..e6ceb551850 100644 --- a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py +++ b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py @@ -31,6 +31,7 @@ class LoanInterestAccrual(AccountsController): self.update_is_accrued() self.make_gl_entries(cancel=1) + self.ignore_linked_doctypes = ['GL Entry'] def update_is_accrued(self): frappe.db.set_value('Repayment Schedule', self.repayment_schedule_name, 'is_accrued', 0) From e9ff6d3d52a0a1320bd2d3b0934e6280393b3a5c Mon Sep 17 00:00:00 2001 From: Karthikeyan S Date: Mon, 8 Jun 2020 12:20:21 +0530 Subject: [PATCH 305/608] feat(Cost Center): Distributed Cost Center (#21531) * feat: Distributed Cost Center Squashed commit of the following: commit 274236eb92dfddfd015583ee1e6b5e1937366d28 Author: Vignesh Date: Mon Mar 23 18:32:26 2020 +0530 Fix: Indentation commit beae0b9849f333dd1d9f29c27192ea7de2e4e809 Author: Vignesh Date: Mon Mar 23 18:26:50 2020 +0530 The validations are added to distributed cost center. commit 3f66943adacb5797338ef3510251bf6633be3dba Author: Vignesh Date: Thu Mar 19 22:17:25 2020 +0530 The distributed cost centers are shown with the percentage allocation in the reports commit 1ac11df522096fa187d2f5863b4c970e18b37fac Author: Vignesh Date: Thu Mar 19 13:36:04 2020 +0530 The child table distributed cost center are added to cost center doctype and then validate percentage allocation. * fix(Distributed Cost Center): validation and filters * fix(Distributed Cost Center): financial statement query * fix(Distributed Cost Center): add test cases Co-authored-by: Vignesh S * fix(Distributed Cost Center): budget variance, general ledger, profitability analysis reports. * Merge branch 'develop' into develop-distributed-cost-center * fix(Distributed Cost Center): Added DCC reflection in actual amount column of Budget Variance Report. * Update creation field in general ledger report Co-authored-by: Nabin Hait * creation field updated in gl_entries query Co-authored-by: Nabin Hait Co-authored-by: KaviyaPeriyasamy Co-authored-by: Vignesh S Co-authored-by: KaviyaPeriyasamy Co-authored-by: Kaviya Periyasamy <36359901+KaviyaPeriyasamy@users.noreply.github.com> Co-authored-by: Nabin Hait --- .../doctype/cost_center/cost_center.js | 13 +++- .../doctype/cost_center/cost_center.json | 21 +++++ .../doctype/cost_center/cost_center.py | 36 ++++++++- .../doctype/cost_center/test_cost_center.py | 27 +++++++ .../distributed_cost_center/__init__.py | 0 .../distributed_cost_center.json | 40 ++++++++++ .../distributed_cost_center.py | 10 +++ .../budget_variance_report.py | 77 ++++++++++++------- .../accounts/report/financial_statements.py | 34 +++++++- .../report/general_ledger/general_ledger.py | 39 +++++++++- .../profitability_analysis.py | 14 ++++ 11 files changed, 275 insertions(+), 36 deletions(-) create mode 100644 erpnext/accounts/doctype/distributed_cost_center/__init__.py create mode 100644 erpnext/accounts/doctype/distributed_cost_center/distributed_cost_center.json create mode 100644 erpnext/accounts/doctype/distributed_cost_center/distributed_cost_center.py diff --git a/erpnext/accounts/doctype/cost_center/cost_center.js b/erpnext/accounts/doctype/cost_center/cost_center.js index 9e2f6eed3b6..f341f782078 100644 --- a/erpnext/accounts/doctype/cost_center/cost_center.js +++ b/erpnext/accounts/doctype/cost_center/cost_center.js @@ -14,7 +14,18 @@ frappe.ui.form.on('Cost Center', { is_group: 1 } } - }) + }); + + frm.set_query("cost_center", "distributed_cost_center", function() { + return { + filters: { + company: frm.doc.company, + is_group: 0, + enable_distributed_cost_center: 0, + name: ['!=', frm.doc.name] + } + }; + }); }, refresh: function(frm) { if (!frm.is_new()) { diff --git a/erpnext/accounts/doctype/cost_center/cost_center.json b/erpnext/accounts/doctype/cost_center/cost_center.json index 5013c92a327..c9bbbabe798 100644 --- a/erpnext/accounts/doctype/cost_center/cost_center.json +++ b/erpnext/accounts/doctype/cost_center/cost_center.json @@ -16,6 +16,9 @@ "cb0", "is_group", "disabled", + "section_break_9", + "enable_distributed_cost_center", + "distributed_cost_center", "lft", "rgt", "old_parent" @@ -119,6 +122,24 @@ "fieldname": "disabled", "fieldtype": "Check", "label": "Disabled" + }, + { + "default": "0", + "fieldname": "enable_distributed_cost_center", + "fieldtype": "Check", + "label": "Enable Distributed Cost Center" + }, + { + "depends_on": "eval:doc.is_group==0", + "fieldname": "section_break_9", + "fieldtype": "Section Break" + }, + { + "depends_on": "enable_distributed_cost_center", + "fieldname": "distributed_cost_center", + "fieldtype": "Table", + "label": "Distributed Cost Center", + "options": "Distributed Cost Center" } ], "icon": "fa fa-money", diff --git a/erpnext/accounts/doctype/cost_center/cost_center.py b/erpnext/accounts/doctype/cost_center/cost_center.py index 0294e78111c..12094d4f989 100644 --- a/erpnext/accounts/doctype/cost_center/cost_center.py +++ b/erpnext/accounts/doctype/cost_center/cost_center.py @@ -19,6 +19,24 @@ class CostCenter(NestedSet): def validate(self): self.validate_mandatory() self.validate_parent_cost_center() + self.validate_distributed_cost_center() + + def validate_distributed_cost_center(self): + if cint(self.enable_distributed_cost_center): + if not self.distributed_cost_center: + frappe.throw(_("Please enter distributed cost center")) + if sum(x.percentage_allocation for x in self.distributed_cost_center) != 100: + frappe.throw(_("Total percentage allocation for distributed cost center should be equal to 100")) + if not self.get('__islocal'): + if not cint(frappe.get_cached_value("Cost Center", {"name": self.name}, "enable_distributed_cost_center")) \ + and self.check_if_part_of_distributed_cost_center(): + frappe.throw(_("Cannot enable Distributed Cost Center for a Cost Center already allocated in another Distributed Cost Center")) + if next((True for x in self.distributed_cost_center if x.cost_center == x.parent), False): + frappe.throw(_("Parent Cost Center cannot be added in Distributed Cost Center")) + if check_if_distributed_cost_center_enabled(list(x.cost_center for x in self.distributed_cost_center)): + frappe.throw(_("A Distributed Cost Center cannot be added in the Distributed Cost Center allocation table.")) + else: + self.distributed_cost_center = [] def validate_mandatory(self): if self.cost_center_name != self.company and not self.parent_cost_center: @@ -43,12 +61,15 @@ class CostCenter(NestedSet): return 1 def convert_ledger_to_group(self): + if cint(self.enable_distributed_cost_center): + frappe.throw(_("Cost Center with enabled distributed cost center can not be converted to group")) + if self.check_if_part_of_distributed_cost_center(): + frappe.throw(_("Cost Center Already Allocated in a Distributed Cost Center cannot be converted to group")) if self.check_gle_exists(): frappe.throw(_("Cost Center with existing transactions can not be converted to group")) - else: - self.is_group = 1 - self.save() - return 1 + self.is_group = 1 + self.save() + return 1 def check_gle_exists(self): return frappe.db.get_value("GL Entry", {"cost_center": self.name}) @@ -57,6 +78,9 @@ class CostCenter(NestedSet): return frappe.db.sql("select name from `tabCost Center` where \ parent_cost_center = %s and docstatus != 2", self.name) + def check_if_part_of_distributed_cost_center(self): + return frappe.db.get_value("Distributed Cost Center", {"cost_center": self.name}) + def before_rename(self, olddn, newdn, merge=False): # Add company abbr if not provided from erpnext.setup.doctype.company.company import get_name_with_abbr @@ -100,3 +124,7 @@ def get_name_with_number(new_account, account_number): if account_number and not new_account[0].isdigit(): new_account = account_number + " - " + new_account return new_account + +def check_if_distributed_cost_center_enabled(cost_center_list): + value_list = frappe.get_list("Cost Center", {"name": ["in", cost_center_list]}, "enable_distributed_cost_center", as_list=1) + return next((True for x in value_list if x[0]), False) \ No newline at end of file diff --git a/erpnext/accounts/doctype/cost_center/test_cost_center.py b/erpnext/accounts/doctype/cost_center/test_cost_center.py index 8f23d906760..b5fc7e3b497 100644 --- a/erpnext/accounts/doctype/cost_center/test_cost_center.py +++ b/erpnext/accounts/doctype/cost_center/test_cost_center.py @@ -22,6 +22,33 @@ class TestCostCenter(unittest.TestCase): self.assertRaises(frappe.ValidationError, cost_center.save) + def test_validate_distributed_cost_center(self): + + if not frappe.db.get_value('Cost Center', {'name': '_Test Cost Center - _TC'}): + frappe.get_doc(test_records[0]).insert() + + if not frappe.db.get_value('Cost Center', {'name': '_Test Cost Center 2 - _TC'}): + frappe.get_doc(test_records[1]).insert() + + invalid_distributed_cost_center = frappe.get_doc({ + "company": "_Test Company", + "cost_center_name": "_Test Distributed Cost Center", + "doctype": "Cost Center", + "is_group": 0, + "parent_cost_center": "_Test Company - _TC", + "enable_distributed_cost_center": 1, + "distributed_cost_center": [{ + "cost_center": "_Test Cost Center - _TC", + "percentage_allocation": 40 + }, { + "cost_center": "_Test Cost Center 2 - _TC", + "percentage_allocation": 50 + } + ] + }) + + self.assertRaises(frappe.ValidationError, invalid_distributed_cost_center.save) + def create_cost_center(**args): args = frappe._dict(args) if args.cost_center_name: diff --git a/erpnext/accounts/doctype/distributed_cost_center/__init__.py b/erpnext/accounts/doctype/distributed_cost_center/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/accounts/doctype/distributed_cost_center/distributed_cost_center.json b/erpnext/accounts/doctype/distributed_cost_center/distributed_cost_center.json new file mode 100644 index 00000000000..45b0e2df9ba --- /dev/null +++ b/erpnext/accounts/doctype/distributed_cost_center/distributed_cost_center.json @@ -0,0 +1,40 @@ +{ + "actions": [], + "creation": "2020-03-19 12:34:01.500390", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "cost_center", + "percentage_allocation" + ], + "fields": [ + { + "fieldname": "cost_center", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Cost Center", + "options": "Cost Center", + "reqd": 1 + }, + { + "fieldname": "percentage_allocation", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Percentage Allocation", + "reqd": 1 + } + ], + "istable": 1, + "links": [], + "modified": "2020-03-19 12:54:43.674655", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Distributed Cost Center", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/distributed_cost_center/distributed_cost_center.py b/erpnext/accounts/doctype/distributed_cost_center/distributed_cost_center.py new file mode 100644 index 00000000000..48c589f0c0f --- /dev/null +++ b/erpnext/accounts/doctype/distributed_cost_center/distributed_cost_center.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +# import frappe +from frappe.model.document import Document + +class DistributedCostCenter(Document): + pass diff --git a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py index 05dc2826611..9c9ada871c8 100644 --- a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py +++ b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py @@ -29,37 +29,60 @@ def execute(filters=None): for dimension in dimensions: dimension_items = cam_map.get(dimension) if dimension_items: - for account, monthwise_data in iteritems(dimension_items): - row = [dimension, account] - totals = [0, 0, 0] - for year in get_fiscal_years(filters): - last_total = 0 - for relevant_months in period_month_ranges: - period_data = [0, 0, 0] - for month in relevant_months: - if monthwise_data.get(year[0]): - month_data = monthwise_data.get(year[0]).get(month, {}) - for i, fieldname in enumerate(["target", "actual", "variance"]): - value = flt(month_data.get(fieldname)) - period_data[i] += value - totals[i] += value - - period_data[0] += last_total - - if filters.get("show_cumulative"): - last_total = period_data[0] - period_data[1] - - period_data[2] = period_data[0] - period_data[1] - row += period_data - totals[2] = totals[0] - totals[1] - if filters["period"] != "Yearly": - row += totals - data.append(row) + data = get_final_data(dimension, dimension_items, filters, period_month_ranges, data, 0) + else: + DCC_allocation = frappe.db.sql('''SELECT parent, sum(percentage_allocation) as percentage_allocation + FROM `tabDistributed Cost Center` + WHERE cost_center IN %(dimension)s + AND parent NOT IN %(dimension)s + GROUP BY parent''',{'dimension':[dimension]}) + if DCC_allocation: + filters['budget_against_filter'] = [DCC_allocation[0][0]] + cam_map = get_dimension_account_month_map(filters) + dimension_items = cam_map.get(DCC_allocation[0][0]) + if dimension_items: + data = get_final_data(dimension, dimension_items, filters, period_month_ranges, data, DCC_allocation[0][1]) chart = get_chart_data(filters, columns, data) return columns, data, None, chart +def get_final_data(dimension, dimension_items, filters, period_month_ranges, data, DCC_allocation): + + for account, monthwise_data in iteritems(dimension_items): + row = [dimension, account] + totals = [0, 0, 0] + for year in get_fiscal_years(filters): + last_total = 0 + for relevant_months in period_month_ranges: + period_data = [0, 0, 0] + for month in relevant_months: + if monthwise_data.get(year[0]): + month_data = monthwise_data.get(year[0]).get(month, {}) + for i, fieldname in enumerate(["target", "actual", "variance"]): + value = flt(month_data.get(fieldname)) + period_data[i] += value + totals[i] += value + + period_data[0] += last_total + + if DCC_allocation: + period_data[0] = period_data[0]*(DCC_allocation/100) + period_data[1] = period_data[1]*(DCC_allocation/100) + + if(filters.get("show_cumulative")): + last_total = period_data[0] - period_data[1] + + period_data[2] = period_data[0] - period_data[1] + row += period_data + totals[2] = totals[0] - totals[1] + if filters["period"] != "Yearly" : + row += totals + data.append(row) + + return data + + def get_columns(filters): columns = [ { @@ -366,7 +389,7 @@ def get_chart_data(filters, columns, data): budget_values[i] += values[index] actual_values[i] += values[index+1] index += 3 - + return { 'data': { 'labels': labels, diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index 4a35a668658..0339e4920a8 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -387,11 +387,41 @@ def set_gl_entries_by_account( key: value }) + distributed_cost_center_query = "" + if filters and filters.get('cost_center'): + distributed_cost_center_query = """ + UNION ALL + SELECT posting_date, + account, + debit*(DCC_allocation.percentage_allocation/100) as debit, + credit*(DCC_allocation.percentage_allocation/100) as credit, + is_opening, + fiscal_year, + debit_in_account_currency*(DCC_allocation.percentage_allocation/100) as debit_in_account_currency, + credit_in_account_currency*(DCC_allocation.percentage_allocation/100) as credit_in_account_currency, + account_currency + FROM `tabGL Entry`, + ( + SELECT parent, sum(percentage_allocation) as percentage_allocation + FROM `tabDistributed Cost Center` + WHERE cost_center IN %(cost_center)s + AND parent NOT IN %(cost_center)s + GROUP BY parent + ) as DCC_allocation + WHERE company=%(company)s + {additional_conditions} + AND posting_date <= %(to_date)s + AND cost_center = DCC_allocation.parent + """.format(additional_conditions=additional_conditions.replace("and cost_center in %(cost_center)s ", '')) + gl_entries = frappe.db.sql("""select posting_date, account, debit, credit, is_opening, fiscal_year, debit_in_account_currency, credit_in_account_currency, account_currency from `tabGL Entry` where company=%(company)s {additional_conditions} and posting_date <= %(to_date)s - order by account, posting_date""".format(additional_conditions=additional_conditions), gl_filters, as_dict=True) #nosec + {distributed_cost_center_query} + order by account, posting_date""".format( + additional_conditions=additional_conditions, + distributed_cost_center_query=distributed_cost_center_query), gl_filters, as_dict=True) #nosec if filters and filters.get('presentation_currency'): convert_to_presentation_currency(gl_entries, get_currency(filters)) @@ -489,4 +519,4 @@ def get_columns(periodicity, period_list, accumulated_values=1, company=None): "width": 150 }) - return columns + return columns \ No newline at end of file diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index f83a2595f6a..fcd36e4e6e2 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -128,18 +128,53 @@ def get_gl_entries(filters): filters['company_fb'] = frappe.db.get_value("Company", filters.get("company"), 'default_finance_book') + distributed_cost_center_query = "" + if filters and filters.get('cost_center'): + select_fields_with_percentage = """, debit*(DCC_allocation.percentage_allocation/100) as debit, credit*(DCC_allocation.percentage_allocation/100) as credit, debit_in_account_currency*(DCC_allocation.percentage_allocation/100) as debit_in_account_currency, + credit_in_account_currency*(DCC_allocation.percentage_allocation/100) as credit_in_account_currency """ + + distributed_cost_center_query = """ + UNION ALL + SELECT name as gl_entry, + posting_date, + account, + party_type, + party, + voucher_type, + voucher_no, + cost_center, project, + against_voucher_type, + against_voucher, + account_currency, + remarks, against, + is_opening, `tabGL Entry`.creation {select_fields_with_percentage} + FROM `tabGL Entry`, + ( + SELECT parent, sum(percentage_allocation) as percentage_allocation + FROM `tabDistributed Cost Center` + WHERE cost_center IN %(cost_center)s + AND parent NOT IN %(cost_center)s + GROUP BY parent + ) as DCC_allocation + WHERE company=%(company)s + {conditions} + AND posting_date <= %(to_date)s + AND cost_center = DCC_allocation.parent + """.format(select_fields_with_percentage=select_fields_with_percentage, conditions=get_conditions(filters).replace("and cost_center in %(cost_center)s ", '')) + gl_entries = frappe.db.sql( """ select name as gl_entry, posting_date, account, party_type, party, voucher_type, voucher_no, cost_center, project, against_voucher_type, against_voucher, account_currency, - remarks, against, is_opening {select_fields} + remarks, against, is_opening, creation {select_fields} from `tabGL Entry` where company=%(company)s {conditions} + {distributed_cost_center_query} {order_by_statement} """.format( - select_fields=select_fields, conditions=get_conditions(filters), + select_fields=select_fields, conditions=get_conditions(filters), distributed_cost_center_query=distributed_cost_center_query, order_by_statement=order_by_statement ), filters, as_dict=1) diff --git a/erpnext/accounts/report/profitability_analysis/profitability_analysis.py b/erpnext/accounts/report/profitability_analysis/profitability_analysis.py index 6e9b31f2f6d..60e675f2f1f 100644 --- a/erpnext/accounts/report/profitability_analysis/profitability_analysis.py +++ b/erpnext/accounts/report/profitability_analysis/profitability_analysis.py @@ -105,6 +105,7 @@ def accumulate_values_into_parents(accounts, accounts_by_name): def prepare_data(accounts, filters, total_row, parent_children_map, based_on): data = [] + new_accounts = accounts company_currency = frappe.get_cached_value('Company', filters.get("company"), "default_currency") for d in accounts: @@ -118,6 +119,19 @@ def prepare_data(accounts, filters, total_row, parent_children_map, based_on): "currency": company_currency, "based_on": based_on } + if based_on == 'cost_center': + cost_center_doc = frappe.get_doc("Cost Center",d.name) + if not cost_center_doc.enable_distributed_cost_center: + DCC_allocation = frappe.db.sql("""SELECT parent, sum(percentage_allocation) as percentage_allocation + FROM `tabDistributed Cost Center` + WHERE cost_center IN %(cost_center)s + AND parent NOT IN %(cost_center)s + GROUP BY parent""",{'cost_center': [d.name]}) + if DCC_allocation: + for account in new_accounts: + if account['name'] == DCC_allocation[0][0]: + for value in value_fields: + d[value] += account[value]*(DCC_allocation[0][1]/100) for key in value_fields: row[key] = flt(d.get(key, 0.0), 3) From f5b9deea5b718b9b87b77e5681dfc98a226c2d6c Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 8 Jun 2020 13:02:00 +0530 Subject: [PATCH 306/608] fix: Finished Product Valuation at Repack --- erpnext/stock/doctype/stock_entry/stock_entry.py | 2 +- .../stock_entry_detail/stock_entry_detail.json | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 18d68539daa..5fbd512bf4f 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -500,7 +500,7 @@ class StockEntry(StockController): if raw_material_cost and self.purpose == "Manufacture": d.basic_rate = flt((raw_material_cost - scrap_material_cost) / flt(d.transfer_qty), d.precision("basic_rate")) d.basic_amount = flt((raw_material_cost - scrap_material_cost), d.precision("basic_amount")) - elif self.purpose == "Repack" and total_fg_qty: + elif self.purpose == "Repack" and total_fg_qty and not d.set_basic_rate_manually: d.basic_rate = flt(raw_material_cost) / flt(total_fg_qty) d.basic_amount = d.basic_rate * d.qty diff --git a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json index c16a41c24fa..7b9c129804e 100644 --- a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json +++ b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json @@ -23,6 +23,7 @@ "image", "image_view", "quantity_and_rate", + "set_basic_rate_manually", "qty", "basic_rate", "basic_amount", @@ -491,12 +492,21 @@ "no_copy": 1, "print_hide": 1, "read_only": 1 + }, + { + "default": "0", + "depends_on": "eval:parent.purpose===\"Repack\" && doc.t_warehouse", + "fieldname": "set_basic_rate_manually", + "fieldtype": "Check", + "label": "Set Basic Rate Manually", + "show_days": 1, + "show_seconds": 1 } ], "idx": 1, "istable": 1, "links": [], - "modified": "2020-04-23 19:19:28.539769", + "modified": "2020-06-08 12:57:03.172887", "modified_by": "Administrator", "module": "Stock", "name": "Stock Entry Detail", From d5b97d69965efbfbabde144728852f20d9c6d58c Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 8 Jun 2020 19:51:55 +0530 Subject: [PATCH 307/608] Update CODEOWNERS --- CODEOWNERS | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index 5e1113d34f9..7cf65a7a732 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -3,17 +3,16 @@ # These owners will be the default owners for everything in # the repo. Unless a later match takes precedence, -* @nabinhait -manufacturing/ @rohitwaghchaure +manufacturing/ @rohitwaghchaure @marination accounts/ @deepeshgarg007 @nextchamp-saqib -loan_management/ @deepeshgarg007 -pos* @nextchamp-saqib -assets/ @nextchamp-saqib +loan_management/ @deepeshgarg007 @rohitwaghchaure +pos* @nextchamp-saqib @rohitwaghchaure +assets/ @nextchamp-saqib @deepeshgarg007 stock/ @marination @rohitwaghchaure -buying/ @marination @rohitwaghchaure -hr/ @Anurag810 -projects/ @hrwX -support/ @hrwX -healthcare/ @ruchamahabal -erpnext_integrations/ @Mangesh-Khairnar +buying/ @marination @deepeshgarg007 +hr/ @Anurag810 @rohitwaghchaure +projects/ @hrwX @nextchamp-saqib +support/ @hrwX @marination +healthcare/ @ruchamahabal @marination +erpnext_integrations/ @Mangesh-Khairnar @nextchamp-saqib requirements.txt @gavindsouza From d27d88c3e45360ebb5f7ed18f4c2db8910bb93f5 Mon Sep 17 00:00:00 2001 From: Raffael Meyer <14891507+barredterra@users.noreply.github.com> Date: Mon, 8 Jun 2020 22:52:44 +0200 Subject: [PATCH 308/608] feat: bill all hours by default --- erpnext/projects/doctype/timesheet/timesheet.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/projects/doctype/timesheet/timesheet.js b/erpnext/projects/doctype/timesheet/timesheet.js index 3eea390ff31..bd48e550070 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.js +++ b/erpnext/projects/doctype/timesheet/timesheet.js @@ -206,6 +206,9 @@ frappe.ui.form.on("Timesheet Detail", { update_billing_hours(frm, cdt, cdn); update_time_rates(frm, cdt, cdn); calculate_billing_costing_amount(frm, cdt, cdn); + + // bill all `hours` by default + frappe.model.set_value(cdt, cdn, "billing_hours", locals[cdt][cdn].hours); }, activity_type: function(frm, cdt, cdn) { From e5fe00cf58bf1aa0edbb07bfbc70dc66c60023f2 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Tue, 9 Jun 2020 17:41:27 +0530 Subject: [PATCH 309/608] Revert "fix: docfield of sales_order is not fetching route options for new doc (#21123)" This reverts commit 5c54adec28c7fda2e290a1bd2bfd1ab7f3c751d0. --- erpnext/projects/doctype/project/project.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/projects/doctype/project/project.js b/erpnext/projects/doctype/project/project.js index 58629634968..3570a0f2be4 100644 --- a/erpnext/projects/doctype/project/project.js +++ b/erpnext/projects/doctype/project/project.js @@ -18,7 +18,7 @@ frappe.ui.form.on("Project", { }; }, onload: function (frm) { - var so = frm.get_docfield("Project", "sales_order"); + var so = frappe.meta.get_docfield("Project", "sales_order"); so.get_route_options_for_new_doc = function (field) { if (frm.is_new()) return; return { @@ -135,4 +135,4 @@ function open_form(frm, doctype, child_doctype, parentfield) { frappe.ui.form.make_quick_entry(doctype, null, null, new_doc); }); -} +} \ No newline at end of file From 088ab75083efe44fa9b4a1e6b2a67a79c8ed4c83 Mon Sep 17 00:00:00 2001 From: Raffael Meyer <14891507+barredterra@users.noreply.github.com> Date: Tue, 9 Jun 2020 16:09:40 +0200 Subject: [PATCH 310/608] fix: move feature into update_billing_hours --- erpnext/projects/doctype/timesheet/timesheet.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/erpnext/projects/doctype/timesheet/timesheet.js b/erpnext/projects/doctype/timesheet/timesheet.js index bd48e550070..defc18bf4e9 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.js +++ b/erpnext/projects/doctype/timesheet/timesheet.js @@ -206,9 +206,6 @@ frappe.ui.form.on("Timesheet Detail", { update_billing_hours(frm, cdt, cdn); update_time_rates(frm, cdt, cdn); calculate_billing_costing_amount(frm, cdt, cdn); - - // bill all `hours` by default - frappe.model.set_value(cdt, cdn, "billing_hours", locals[cdt][cdn].hours); }, activity_type: function(frm, cdt, cdn) { @@ -261,7 +258,12 @@ var calculate_end_time = function(frm, cdt, cdn) { var update_billing_hours = function(frm, cdt, cdn){ var child = locals[cdt][cdn]; - if(!child.billable) frappe.model.set_value(cdt, cdn, 'billing_hours', 0.0); + if(!child.billable) { + frappe.model.set_value(cdt, cdn, 'billing_hours', 0.0); + } else { + // bill all hours by default + frappe.model.set_value(cdt, cdn, "billing_hours", child.hours); + } }; var update_time_rates = function(frm, cdt, cdn){ From bb3b616a046b07996a47e1154e59d878aff45cd1 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 9 Jun 2020 23:17:51 +0530 Subject: [PATCH 311/608] fix: set valuesin db for handle hold time --- erpnext/support/doctype/issue/issue.py | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index 4003047e81c..a23fe0564f5 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -83,20 +83,21 @@ class Issue(Document): pause_sla_on = frappe.db.get_all("Pause SLA On Status", fields=["status"], filters={"parent": self.service_level_agreement}) hold_statuses = [entry.status for entry in pause_sla_on] + update_values = {} if self.status in hold_statuses and status not in hold_statuses: - self.on_hold_since = frappe.flags.current_time or now_datetime() + update_values['on_hold_since'] = frappe.flags.current_time or now_datetime() if not self.first_responded_on: - self.response_by = None - self.response_by_variance = None - self.resolution_by = None - self.resolution_by_variance = None + update_values['response_by'] = None + update_values['response_by_variance'] = 0 + update_values['resolution_by'] = None + update_values['resolution_by_variance'] = 0 # calculate hold time when status is changed from Replied to any other status if self.status not in hold_statuses and status in hold_statuses: hold_time = self.total_hold_time if self.total_hold_time else 0 now_time = frappe.flags.current_time or now_datetime() - self.total_hold_time = hold_time + time_diff_in_seconds(now_time, self.on_hold_since) + update_values['total_hold_time'] = hold_time + time_diff_in_seconds(now_time, self.on_hold_since) # re-calculate SLA variables after issue changes from Replied to Open # add hold time to SLA variables @@ -108,15 +109,17 @@ class Issue(Document): if not self.first_responded_on: response_by = get_expected_time_for(parameter="response", service_level=priority, start_date_time=start_date_time) - self.response_by = add_to_date(response_by, seconds=round(hold_time)) + update_values['response_by'] = add_to_date(response_by, seconds=round(hold_time)) response_by_variance = round(time_diff_in_hours(self.response_by, now_time)) - self.response_by_variance = response_by_variance + (hold_time // 3600) + update_values['response_by_variance'] = response_by_variance + (hold_time // 3600) resolution_by = get_expected_time_for(parameter="resolution", service_level=priority, start_date_time=start_date_time) - self.resolution_by = add_to_date(resolution_by, seconds=round(hold_time)) + update_values['resolution_by'] = add_to_date(resolution_by, seconds=round(hold_time)) resolution_by_variance = round(time_diff_in_hours(self.resolution_by, now_time)) - self.resolution_by_variance = resolution_by_variance + (hold_time // 3600) - self.on_hold_since = None + update_values['resolution_by_variance'] = resolution_by_variance + (hold_time // 3600) + update_values['on_hold_since'] = None + + self.db_set(update_values) def update_agreement_status(self): if self.service_level_agreement and self.agreement_fulfilled == "Ongoing": From 934e30ecac59dcd6aa5dcb06700c37a51209689c Mon Sep 17 00:00:00 2001 From: Anupam Kumar Date: Wed, 10 Jun 2020 11:07:43 +0530 Subject: [PATCH 312/608] update program course (#22166) --- .../program_course/program_course.json | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/erpnext/education/doctype/program_course/program_course.json b/erpnext/education/doctype/program_course/program_course.json index a24e88a8611..940358e4e9b 100644 --- a/erpnext/education/doctype/program_course/program_course.json +++ b/erpnext/education/doctype/program_course/program_course.json @@ -1,4 +1,5 @@ { + "actions": [], "creation": "2015-09-07 14:37:01.886859", "doctype": "DocType", "editable_grid": 1, @@ -16,26 +17,33 @@ "in_list_view": 1, "label": "Course", "options": "Course", - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, - { + { + "fetch_from": "course.course_name", "fieldname": "course_name", "fieldtype": "Data", "in_list_view": 1, "label": "Course Name", - "fetch_from": "course.course_name", - "read_only":1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "required", "fieldtype": "Check", "in_list_view": 1, - "label": "Mandatory" + "label": "Mandatory", + "show_days": 1, + "show_seconds": 1 } ], "istable": 1, - "modified": "2019-06-12 12:42:12.845972", + "links": [], + "modified": "2020-06-09 18:56:10.213241", "modified_by": "Administrator", "module": "Education", "name": "Program Course", @@ -45,4 +53,4 @@ "sort_field": "modified", "sort_order": "DESC", "track_changes": 1 -} +} \ No newline at end of file From 402c347f83827090a4ce23ae73cdf50437333de8 Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Wed, 10 Jun 2020 12:22:12 +0530 Subject: [PATCH 313/608] fix: set cost center in child table --- erpnext/hr/doctype/expense_claim/expense_claim.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.js b/erpnext/hr/doctype/expense_claim/expense_claim.js index fb2310396b7..6bb9af98263 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.js +++ b/erpnext/hr/doctype/expense_claim/expense_claim.js @@ -243,7 +243,6 @@ frappe.ui.form.on("Expense Claim", { }, update_employee_advance_claimed_amount: function(frm) { - console.log("update_employee_advance_claimed_amount") let amount_to_be_allocated = frm.doc.grand_total; $.each(frm.doc.advances || [], function(i, advance){ if (amount_to_be_allocated >= advance.unclaimed_amount){ @@ -295,6 +294,16 @@ frappe.ui.form.on("Expense Claim", { frm.events.get_advances(frm); }, + cost_center: function(frm) { + frm.events.set_child_cost_center(frm); + }, + set_child_cost_center: function(frm){ + (frm.doc.expenses || []).forEach(function(d) { + if (!d.cost_center){ + d.cost_center = frm.doc.cost_center; + } + }); + }, get_taxes: function(frm) { if(frm.doc.taxes) { frappe.call({ @@ -338,8 +347,7 @@ frappe.ui.form.on("Expense Claim", { frappe.ui.form.on("Expense Claim Detail", { expenses_add: function(frm, cdt, cdn) { - var row = frappe.get_doc(cdt, cdn); - frm.script_manager.copy_from_first_row("expenses", row, ["cost_center"]); + frm.events.set_child_cost_center(frm); }, amount: function(frm, cdt, cdn) { var child = locals[cdt][cdn]; From 452b7760df4d96712aba27f03c2dfd9a828f2b04 Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Wed, 10 Jun 2020 12:48:21 +0530 Subject: [PATCH 314/608] fix: dependency for leave Period in Desk page --- erpnext/hr/desk_page/hr/hr.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/hr/desk_page/hr/hr.json b/erpnext/hr/desk_page/hr/hr.json index 7ac000b011a..1c24444fdd2 100644 --- a/erpnext/hr/desk_page/hr/hr.json +++ b/erpnext/hr/desk_page/hr/hr.json @@ -18,7 +18,7 @@ { "hidden": 0, "label": "Leaves", - "links": "[\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Leave Application\",\n \"name\": \"Leave Application\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Leave Allocation\",\n \"name\": \"Leave Allocation\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Leave Type\"\n ],\n \"label\": \"Leave Policy\",\n \"name\": \"Leave Policy\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Leave Period\",\n \"name\": \"Leave Period\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Leave Type\",\n \"name\": \"Leave Type\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Holiday List\",\n \"name\": \"Holiday List\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Compensatory Leave Request\",\n \"name\": \"Compensatory Leave Request\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Leave Encashment\",\n \"name\": \"Leave Encashment\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Leave Block List\",\n \"name\": \"Leave Block List\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Leave Application\"\n ],\n \"doctype\": \"Leave Application\",\n \"is_query_report\": true,\n \"label\": \"Employee Leave Balance\",\n \"name\": \"Employee Leave Balance\",\n \"type\": \"report\"\n }\n]" + "links": "[\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Leave Application\",\n \"name\": \"Leave Application\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Leave Allocation\",\n \"name\": \"Leave Allocation\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Leave Type\"\n ],\n \"label\": \"Leave Policy\",\n \"name\": \"Leave Policy\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Leave Period\",\n \"name\": \"Leave Period\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Leave Type\",\n \"name\": \"Leave Type\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Holiday List\",\n \"name\": \"Holiday List\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Compensatory Leave Request\",\n \"name\": \"Compensatory Leave Request\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Leave Encashment\",\n \"name\": \"Leave Encashment\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Leave Block List\",\n \"name\": \"Leave Block List\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Leave Application\"\n ],\n \"doctype\": \"Leave Application\",\n \"is_query_report\": true,\n \"label\": \"Employee Leave Balance\",\n \"name\": \"Employee Leave Balance\",\n \"type\": \"report\"\n }\n]" }, { "hidden": 0, @@ -93,7 +93,7 @@ "idx": 0, "is_standard": 1, "label": "HR", - "modified": "2020-05-28 13:36:07.710600", + "modified": "2020-06-10 12:41:41.695669", "modified_by": "Administrator", "module": "HR", "name": "HR", From 1e49c6b68b6794794c4113319aadf211d848d7fe Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 10 Jun 2020 12:58:16 +0530 Subject: [PATCH 315/608] fix: update duration options --- erpnext/support/doctype/issue/issue.json | 22 +++++----------- .../service_level_agreement.json | 14 +++------- .../service_level_priority.json | 26 +++++++------------ 3 files changed, 20 insertions(+), 42 deletions(-) diff --git a/erpnext/support/doctype/issue/issue.json b/erpnext/support/doctype/issue/issue.json index 712b70c2dd7..6525ab27d30 100644 --- a/erpnext/support/doctype/issue/issue.json +++ b/erpnext/support/doctype/issue/issue.json @@ -380,48 +380,38 @@ "fieldname": "avg_response_time", "fieldtype": "Duration", "label": "Average Response Time", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "resolution_time", "fieldtype": "Duration", "label": "Resolution Time", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "user_resolution_time", "fieldtype": "Duration", "label": "User Resolution Time", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "on_hold_since", "fieldtype": "Datetime", "hidden": 1, "label": "On Hold Since", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "total_hold_time", "fieldtype": "Duration", "label": "Total Hold Time", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 } ], "icon": "fa fa-ticket", "idx": 7, "links": [], - "modified": "2020-06-05 15:45:24.474425", + "modified": "2020-06-10 12:47:37.146914", "modified_by": "Administrator", "module": "Support", "name": "Issue", diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.json b/erpnext/support/doctype/service_level_agreement/service_level_agreement.json index e65169c0d43..939c1999828 100644 --- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.json +++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.json @@ -159,28 +159,22 @@ "fieldtype": "Link", "label": "Default Priority", "options": "Issue Priority", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "section_break_18", "fieldtype": "Section Break", - "hide_border": 1, - "show_days": 1, - "show_seconds": 1 + "hide_border": 1 }, { "fieldname": "pause_sla_on", "fieldtype": "Table", "label": "Pause SLA On", - "options": "Pause SLA On Status", - "show_days": 1, - "show_seconds": 1 + "options": "Pause SLA On Status" } ], "links": [], - "modified": "2020-06-05 17:51:15.050785", + "modified": "2020-06-10 12:30:15.050785", "modified_by": "Administrator", "module": "Support", "name": "Service Level Agreement", diff --git a/erpnext/support/doctype/service_level_priority/service_level_priority.json b/erpnext/support/doctype/service_level_priority/service_level_priority.json index 3995d1e2488..0a88c53bf00 100644 --- a/erpnext/support/doctype/service_level_priority/service_level_priority.json +++ b/erpnext/support/doctype/service_level_priority/service_level_priority.json @@ -20,34 +20,28 @@ "fieldtype": "Link", "in_list_view": 1, "label": "Priority", - "options": "Issue Priority", - "show_days": 1, - "show_seconds": 1 + "options": "Issue Priority" }, { "fieldname": "sb_00", - "fieldtype": "Section Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Section Break" }, { "columns": 2, "fieldname": "resolution_time", "fieldtype": "Duration", + "hide_days": 1, + "hide_seconds": 1, "in_list_view": 1, "label": "Resolution Time" }, { "fieldname": "cb_00", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "fieldname": "cb_01", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "columns": 1, @@ -55,21 +49,21 @@ "fieldname": "default_priority", "fieldtype": "Check", "in_list_view": 1, - "label": "Default Priority", - "show_days": 1, - "show_seconds": 1 + "label": "Default Priority" }, { "columns": 2, "fieldname": "response_time", "fieldtype": "Duration", + "hide_days": 1, + "hide_seconds": 1, "in_list_view": 1, "label": "First Response Time" } ], "istable": 1, "links": [], - "modified": "2020-06-05 13:08:26.428657", + "modified": "2020-06-10 12:45:47.545915", "modified_by": "Administrator", "module": "Support", "name": "Service Level Priority", From afe8e88cb8fa6e75a4016d3a76988882d81378b4 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 10 Jun 2020 17:55:24 +0530 Subject: [PATCH 316/608] fix: Cannot read property 'has_batch_no' of undefined --- erpnext/public/js/controllers/transaction.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 524a95804fd..2ffc728df0d 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -552,7 +552,8 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ if (show_batch_dialog) return frappe.db.get_value("Item", item.item_code, ["has_batch_no", "has_serial_no"]) .then((r) => { - if(r.message.has_batch_no || r.message.has_serial_no) { + if (r.message && + (r.message.has_batch_no || r.message.has_serial_no)) { frappe.flags.hide_serial_batch_dialog = false; } }); From fdddb679eda5015e8ab0bbefb5853a14d2608b6e Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 10 Jun 2020 18:22:59 +0530 Subject: [PATCH 317/608] fix: Prioritize Default Customer Price List in Portal --- erpnext/shopping_cart/cart.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/erpnext/shopping_cart/cart.py b/erpnext/shopping_cart/cart.py index d04c8c25a34..7096c17fb18 100644 --- a/erpnext/shopping_cart/cart.py +++ b/erpnext/shopping_cart/cart.py @@ -337,21 +337,17 @@ def set_price_list_and_rate(quotation, cart_settings): def _set_price_list(cart_settings, quotation=None): """Set price list based on customer or shopping cart default""" from erpnext.accounts.party import get_default_price_list - - # check if customer price list exists + party_name = quotation.get("party_name") if quotation else get_party().get("name") selling_price_list = None - if quotation and quotation.get("party_name"): - selling_price_list = frappe.db.get_value('Customer', quotation.get("party_name"), 'default_price_list') - # else check for territory based price list + # check if default customer price list exists + if party_name: + selling_price_list = get_default_price_list(frappe.get_doc("Customer", party_name)) + + # check default price list in shopping cart if not selling_price_list: selling_price_list = cart_settings.price_list - party_name = quotation.get("party_name") if quotation else get_party().get("name") - - if not selling_price_list and party_name: - selling_price_list = get_default_price_list(frappe.get_doc("Customer", party_name)) - if quotation: quotation.selling_price_list = selling_price_list From 08fee1226606fa19a0d86eb28cbb135bf0b18e96 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 10 Jun 2020 18:33:24 +0530 Subject: [PATCH 318/608] fix: Item-wise sales and purchase register export --- .../item_wise_purchase_register.py | 7 ------- .../item_wise_sales_register.py | 16 ++++++++-------- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py index 9777ed1dfde..3445df7206f 100644 --- a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py +++ b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py @@ -265,13 +265,6 @@ def get_columns(additional_table_columns, filters): 'fieldtype': 'Currency', 'options': 'currency', 'width': 100 - }, - { - 'fieldname': 'currency', - 'label': _('Currency'), - 'fieldtype': 'Currency', - 'width': 80, - 'hidden': 1 } ] diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py index bb78ee2d675..a05dcd75ce5 100644 --- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py +++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py @@ -223,7 +223,7 @@ def get_columns(additional_table_columns, filters): } ] - if filters.get('group_by') != 'Terriotory': + if filters.get('group_by') != 'Territory': columns.extend([ { 'label': _("Territory"), @@ -304,13 +304,6 @@ def get_columns(additional_table_columns, filters): 'fieldtype': 'Currency', 'options': 'currency', 'width': 100 - }, - { - 'fieldname': 'currency', - 'label': _('Currency'), - 'fieldtype': 'Currency', - 'width': 80, - 'hidden': 1 } ] @@ -536,6 +529,13 @@ def get_tax_accounts(item_list, columns, company_currency, 'fieldtype': 'Currency', 'options': 'currency', 'width': 100 + }, + { + 'fieldname': 'currency', + 'label': _('Currency'), + 'fieldtype': 'Currency', + 'width': 80, + 'hidden': 1 } ] From a6acf18f6c66ae2c55f35a76d9e8dfd35df8600d Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 10 Jun 2020 18:33:24 +0530 Subject: [PATCH 319/608] fix: Item-wise sales and purchase register export --- .../item_wise_purchase_register.py | 7 ------- .../item_wise_sales_register.py | 16 ++++++++-------- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py index 127f3133f5b..6c5dec957b7 100644 --- a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py +++ b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py @@ -266,13 +266,6 @@ def get_columns(additional_table_columns, filters): 'fieldtype': 'Currency', 'options': 'currency', 'width': 100 - }, - { - 'fieldname': 'currency', - 'label': _('Currency'), - 'fieldtype': 'Currency', - 'width': 80, - 'hidden': 1 } ] diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py index 0c8957ae441..0c71deb7500 100644 --- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py +++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py @@ -224,7 +224,7 @@ def get_columns(additional_table_columns, filters): } ] - if filters.get('group_by') != 'Terriotory': + if filters.get('group_by') != 'Territory': columns.extend([ { 'label': _("Territory"), @@ -305,13 +305,6 @@ def get_columns(additional_table_columns, filters): 'fieldtype': 'Currency', 'options': 'currency', 'width': 100 - }, - { - 'fieldname': 'currency', - 'label': _('Currency'), - 'fieldtype': 'Currency', - 'width': 80, - 'hidden': 1 } ] @@ -537,6 +530,13 @@ def get_tax_accounts(item_list, columns, company_currency, 'fieldtype': 'Currency', 'options': 'currency', 'width': 100 + }, + { + 'fieldname': 'currency', + 'label': _('Currency'), + 'fieldtype': 'Currency', + 'width': 80, + 'hidden': 1 } ] From d60d2e18263f1645a6122818b37146190b4706f2 Mon Sep 17 00:00:00 2001 From: Rohan Date: Wed, 10 Jun 2020 19:10:56 +0530 Subject: [PATCH 320/608] fix: only auto-set serial nos and batches if allowed in Stock Settings (develop) (#21781) * fix: only auto-set serial nos and batches if allowed in Stock Settings * fix: bug with setting disabled batch no in Pick List * fix: remove auto-set batch variable Co-authored-by: Marica --- erpnext/stock/doctype/pick_list/pick_list.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index 93b29c8daff..4b8b594ed9d 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -119,11 +119,13 @@ def get_items_with_location_and_quantity(item_doc, item_location_map): if item_location.serial_no: serial_nos = '\n'.join(item_location.serial_no[0: cint(stock_qty)]) + auto_set_serial_no = frappe.db.get_single_value("Stock Settings", "automatically_set_serial_nos_based_on_fifo") + locations.append(frappe._dict({ 'qty': qty, 'stock_qty': stock_qty, 'warehouse': item_location.warehouse, - 'serial_no': serial_nos, + 'serial_no': serial_nos if auto_set_serial_no else item_doc.serial_no, 'batch_no': item_location.batch_no })) @@ -206,6 +208,7 @@ def get_available_item_locations_for_batched_item(item_code, from_warehouses, re sle.batch_no = batch.name and sle.`item_code`=%(item_code)s and sle.`company` = %(company)s + and batch.disabled = 0 and IFNULL(batch.`expiry_date`, '2200-01-01') > %(today)s {warehouse_condition} GROUP BY @@ -471,4 +474,4 @@ def update_common_item_properties(item, location): item.material_request = location.material_request item.serial_no = location.serial_no item.batch_no = location.batch_no - item.material_request_item = location.material_request_item \ No newline at end of file + item.material_request_item = location.material_request_item From 7963e2b708db5c0240b228846f17d999373f59b3 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 10 Jun 2020 19:57:49 +0530 Subject: [PATCH 321/608] fix: Party validation for inter-warehouse transaction --- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 57dc17936da..8b5d4d110cc 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1450,11 +1450,17 @@ def get_inter_company_details(doc, doctype): parties = frappe.db.get_all("Supplier", fields=["name"], filters={"disabled": 0, "is_internal_supplier": 1, "represents_company": doc.company}) company = frappe.get_cached_value("Customer", doc.customer, "represents_company") + if not parties: + frappe.throw(_('No Supplier found for Inter Company Transactions which represents company {0}').format(frappe.bold(doc.company))) + party = get_internal_party(parties, "Supplier", doc) else: parties = frappe.db.get_all("Customer", fields=["name"], filters={"disabled": 0, "is_internal_customer": 1, "represents_company": doc.company}) company = frappe.get_cached_value("Supplier", doc.supplier, "represents_company") + if not parties: + frappe.throw(_('No Customer found for Inter Company Transactions which represents company {0}').format(frappe.bold(doc.company))) + party = get_internal_party(parties, "Customer", doc) return { From a0fd97f2ac1598ce7e3457aa4b30b19720906537 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 10 Jun 2020 22:15:27 +0530 Subject: [PATCH 322/608] fix: Update payment schedule based on payment terms --- .../doctype/payment_entry/payment_entry.py | 33 +++++++++++-------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index d2245d6a6d6..15e51bbd995 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -319,7 +319,7 @@ class PaymentEntry(AccountsController): invoice_payment_amount_map.setdefault(key, 0.0) invoice_payment_amount_map[key] += reference.allocated_amount - if not invoice_paid_amount_map.get(reference.reference_name): + if not invoice_paid_amount_map.get(key): payment_schedule = frappe.get_all('Payment Schedule', filters={'parent': reference.reference_name}, fields=['paid_amount', 'payment_amount', 'payment_term']) for term in payment_schedule: @@ -332,12 +332,14 @@ class PaymentEntry(AccountsController): frappe.db.sql(""" UPDATE `tabPayment Schedule` SET paid_amount = `paid_amount` - %s WHERE parent = %s and payment_term = %s""", (amount, key[1], key[0])) else: - outstanding = invoice_paid_amount_map.get(key)['outstanding'] + outstanding = flt(invoice_paid_amount_map.get(key, {}).get('outstanding')) + if amount > outstanding: frappe.throw(_('Cannot allocate more than {0} against payment term {1}').format(outstanding, key[0])) - frappe.db.sql(""" UPDATE `tabPayment Schedule` SET paid_amount = `paid_amount` + %s - WHERE parent = %s and payment_term = %s""", (amount, key[1], key[0])) + if amount and outstanding: + frappe.db.sql(""" UPDATE `tabPayment Schedule` SET paid_amount = `paid_amount` + %s + WHERE parent = %s and payment_term = %s""", (amount, key[1], key[0])) def set_status(self): if self.docstatus == 2: @@ -1091,17 +1093,20 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount= def get_reference_as_per_payment_terms(payment_schedule, dt, dn, doc, grand_total, outstanding_amount): references = [] for payment_term in payment_schedule: - references.append({ - 'reference_doctype': dt, - 'reference_name': dn, - 'bill_no': doc.get('bill_no'), - 'due_date': doc.get('due_date'), - 'total_amount': grand_total, - 'outstanding_amount': outstanding_amount, - 'payment_term': payment_term.payment_term, - 'allocated_amount': flt(payment_term.payment_amount - payment_term.paid_amount, + payment_term_outstanding = flt(payment_term.payment_amount - payment_term.paid_amount, payment_term.precision('payment_amount')) - }) + + if payment_term_outstanding: + references.append({ + 'reference_doctype': dt, + 'reference_name': dn, + 'bill_no': doc.get('bill_no'), + 'due_date': doc.get('due_date'), + 'total_amount': grand_total, + 'outstanding_amount': outstanding_amount, + 'payment_term': payment_term.payment_term, + 'allocated_amount': payment_term_outstanding + }) return references From 8d61a0abefc5604ae0424dd03cfc1c62f1b8abf3 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 11 Jun 2020 13:34:25 +0530 Subject: [PATCH 323/608] fix: use string to verify webhook --- erpnext/non_profit/doctype/membership/membership.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/non_profit/doctype/membership/membership.py b/erpnext/non_profit/doctype/membership/membership.py index 4b932425b28..c4f43185f78 100644 --- a/erpnext/non_profit/doctype/membership/membership.py +++ b/erpnext/non_profit/doctype/membership/membership.py @@ -77,7 +77,7 @@ def verify_signature(data): @frappe.whitelist(allow_guest=True) def trigger_razorpay_subscription(*args, **kwargs): - data = frappe.request.get_data() + data = frappe.request.get_data(as_text=True) verify_signature(data) if isinstance(data, six.string_types): From 84fcc55a36fef6caa1ca45056bdf7d0d8711a0de Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 11 Jun 2020 13:34:40 +0530 Subject: [PATCH 324/608] refactor: do nothing if member is not found --- erpnext/non_profit/doctype/membership/membership.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/erpnext/non_profit/doctype/membership/membership.py b/erpnext/non_profit/doctype/membership/membership.py index c4f43185f78..7a0caed621e 100644 --- a/erpnext/non_profit/doctype/membership/membership.py +++ b/erpnext/non_profit/doctype/membership/membership.py @@ -62,7 +62,10 @@ def get_member_based_on_subscription(subscription_id, email): 'subscription_id': subscription_id, 'email_id': email }, order_by="creation desc") - return frappe.get_doc("Member", members[0]['name']) + try: + return frappe.get_doc("Member", members[0]['name']) + except: + return None def verify_signature(data): signature = frappe.request.headers.get('X-Razorpay-Signature') @@ -96,7 +99,10 @@ def trigger_razorpay_subscription(*args, **kwargs): except Exception as e: error_log = frappe.log_error(frappe.get_traceback() + '\n' + data_json , _("Membership Webhook Failed")) notify_failure(error_log) - raise e + return False + + if not member: + return False if data.event == "subscription.activated": member.customer_id = payment.customer_id From 49d51b8ce72f7bcdb8931a3c23fde76e43c7a7b9 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 11 Jun 2020 14:24:07 +0530 Subject: [PATCH 325/608] feat: check duplicate contact explicitly --- erpnext/non_profit/doctype/member/member.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/non_profit/doctype/member/member.py b/erpnext/non_profit/doctype/member/member.py index 571f87af874..aaa56ba118d 100644 --- a/erpnext/non_profit/doctype/member/member.py +++ b/erpnext/non_profit/doctype/member/member.py @@ -92,6 +92,10 @@ def create_customer(user_details): }) contact.insert() + + except frappe.DuplicateEntryError: + return customer.name + except Exception as e: frappe.log_error(frappe.get_traceback(), _("Contact Creation Failed")) pass From 8c24810f309f525d5ae334862af87b15cccb3073 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 11 Jun 2020 14:37:50 +0530 Subject: [PATCH 326/608] refactor: ignore mandatory for customer --- erpnext/non_profit/doctype/member/member.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/non_profit/doctype/member/member.py b/erpnext/non_profit/doctype/member/member.py index aaa56ba118d..d1294ccc08b 100644 --- a/erpnext/non_profit/doctype/member/member.py +++ b/erpnext/non_profit/doctype/member/member.py @@ -77,6 +77,7 @@ def create_customer(user_details): customer = frappe.new_doc("Customer") customer.customer_name = user_details.fullname customer.customer_type = "Individual" + customer.flags.ignore_mandatory = True customer.insert(ignore_permissions=True) try: @@ -91,7 +92,7 @@ def create_customer(user_details): "link_name": customer.name }) - contact.insert() + contact.save() except frappe.DuplicateEntryError: return customer.name From 596560c3c219d218e23eef575e18e7d0627bcf6f Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 11 Jun 2020 16:39:03 +0530 Subject: [PATCH 327/608] fix: Don't prompt for Quality Inspection on Return Documents. --- erpnext/controllers/stock_controller.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 90d293088b0..2ce41590cff 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -19,7 +19,8 @@ class QualityInspectionNotSubmittedError(frappe.ValidationError): pass class StockController(AccountsController): def validate(self): super(StockController, self).validate() - self.validate_inspection() + if not self.is_return: + self.validate_inspection() self.validate_serialized_batch() self.validate_customer_provided_item() From 9c27d683ba542574c07ff45d9f72a9d158c4cb67 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 11 Jun 2020 17:03:53 +0530 Subject: [PATCH 328/608] fix: priority column width Co-authored-by: Himanshu --- .../service_level_priority/service_level_priority.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/support/doctype/service_level_priority/service_level_priority.json b/erpnext/support/doctype/service_level_priority/service_level_priority.json index 0a88c53bf00..65d51694cc3 100644 --- a/erpnext/support/doctype/service_level_priority/service_level_priority.json +++ b/erpnext/support/doctype/service_level_priority/service_level_priority.json @@ -15,7 +15,7 @@ ], "fields": [ { - "columns": 1, + "columns": 2, "fieldname": "priority", "fieldtype": "Link", "in_list_view": 1, @@ -73,4 +73,4 @@ "sort_field": "modified", "sort_order": "DESC", "track_changes": 1 -} \ No newline at end of file +} From d7481f59ba6dec000b0fd5b970cf970e96211369 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 11 Jun 2020 17:41:05 +0530 Subject: [PATCH 329/608] fix: patch --- .../patches/v13_0/update_sla_enhancements.py | 59 ++++++++++--------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/erpnext/patches/v13_0/update_sla_enhancements.py b/erpnext/patches/v13_0/update_sla_enhancements.py index 2356fb2679b..3eb0411f827 100644 --- a/erpnext/patches/v13_0/update_sla_enhancements.py +++ b/erpnext/patches/v13_0/update_sla_enhancements.py @@ -45,38 +45,39 @@ def execute(): # copy Service Levels to Service Level Agreements sl = [entry.service_level for entry in sla_details] - service_levels = frappe.db.get_all('Service Level', filters={'service_level': ('not in', sl)}, fields=['*']) - for entry in service_levels: - sla = frappe.new_doc('Service Level Agreement') - sla.service_level = entry.service_level - sla.holiday_list = entry.holiday_list - sla.employee_group = entry.employee_group - sla.flags.ignore_validate = True - sla = sla.insert(ignore_mandatory=True) + if frappe.db.exists('DocType', 'Service Level'): + service_levels = frappe.db.get_all('Service Level', filters={'service_level': ('not in', sl)}, fields=['*']) + for entry in service_levels: + sla = frappe.new_doc('Service Level Agreement') + sla.service_level = entry.service_level + sla.holiday_list = entry.holiday_list + sla.employee_group = entry.employee_group + sla.flags.ignore_validate = True + sla = sla.insert(ignore_mandatory=True) - frappe.db.sql(""" - UPDATE - `tabService Day` - SET - parent = %(new_parent)s , parentfield = 'support_and_resolution', parenttype = 'Service Level Agreement' - WHERE - parent = %(old_parent)s - """, {'new_parent': sla.name, 'old_parent': entry.name}, as_dict = 1) + frappe.db.sql(""" + UPDATE + `tabService Day` + SET + parent = %(new_parent)s , parentfield = 'support_and_resolution', parenttype = 'Service Level Agreement' + WHERE + parent = %(old_parent)s + """, {'new_parent': sla.name, 'old_parent': entry.name}, as_dict = 1) - priority_list = priority_dict.get(entry.name) - if priority_list: - sla = frappe.get_doc('Service Level Agreement', sla.name) - for priority in priority_list: - row = sla.append('priorities', { - 'priority': priority.priority, - 'default_priority': priority.default_priority, - 'response_time': convert_to_seconds(priority.response_time, priority.response_time_period), - 'resolution_time': convert_to_seconds(priority.resolution_time, priority.resolution_time_period) - }) - row.db_update() - sla.db_update() + priority_list = priority_dict.get(entry.name) + if priority_list: + sla = frappe.get_doc('Service Level Agreement', sla.name) + for priority in priority_list: + row = sla.append('priorities', { + 'priority': priority.priority, + 'default_priority': priority.default_priority, + 'response_time': convert_to_seconds(priority.response_time, priority.response_time_period), + 'resolution_time': convert_to_seconds(priority.resolution_time, priority.resolution_time_period) + }) + row.db_update() + sla.db_update() - frappe.delete_doc('DocType', 'Service Level') + frappe.delete_doc_if_exists('DocType', 'Service Level') def convert_to_seconds(value, unit): From 44b8cf4fefb3d9228a6b09220cf7e4b7532ad2df Mon Sep 17 00:00:00 2001 From: Abhishek Balam Date: Thu, 11 Jun 2020 19:31:41 +0530 Subject: [PATCH 330/608] enable 'user cannot create' for payment request (#22044) Co-authored-by: Nabin Hait --- erpnext/accounts/doctype/payment_request/payment_request.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.json b/erpnext/accounts/doctype/payment_request/payment_request.json index 7508683c080..eef6be1a7a1 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.json +++ b/erpnext/accounts/doctype/payment_request/payment_request.json @@ -349,9 +349,10 @@ "read_only": 1 } ], + "in_create": 1, "is_submittable": 1, "links": [], - "modified": "2020-05-08 10:23:02.815237", + "modified": "2020-05-29 17:38:49.392713", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Request", From c9a6e4f151faf4823e1d508c34eada28ab4544ff Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 11 Jun 2020 19:54:44 +0530 Subject: [PATCH 331/608] fix: Check for Company before rendering tree in Account Tree --- erpnext/accounts/doctype/account/account_tree.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/accounts/doctype/account/account_tree.js b/erpnext/accounts/doctype/account/account_tree.js index f62d07668de..28b090bdadb 100644 --- a/erpnext/accounts/doctype/account/account_tree.js +++ b/erpnext/accounts/doctype/account/account_tree.js @@ -14,6 +14,9 @@ frappe.treeview_settings["Account"] = { on_change: function() { var me = frappe.treeview_settings['Account'].treeview; var company = me.page.fields_dict.company.get_value(); + if (!company) { + frappe.throw(__("Please set a Company")); + } frappe.call({ method: "erpnext.accounts.doctype.account.account.get_root_company", args: { From 9df4532d14ea1355c2d6538e0e5f2f5ee5a16b4b Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 11 Jun 2020 21:33:43 +0530 Subject: [PATCH 332/608] fix: Travis(develop) --- erpnext/accounts/dashboard_fixtures.py | 30 +++++++++--- .../sales_invoice/test_sales_invoice.py | 47 ------------------- erpnext/assets/dashboard_fixtures.py | 38 +++++++-------- erpnext/buying/dashboard_fixtures.py | 27 ++++++----- erpnext/manufacturing/dashboard_fixtures.py | 1 - erpnext/regional/united_states/setup.py | 4 +- erpnext/stock/dashboard_fixtures.py | 37 +++++++-------- 7 files changed, 73 insertions(+), 111 deletions(-) diff --git a/erpnext/accounts/dashboard_fixtures.py b/erpnext/accounts/dashboard_fixtures.py index 421c86dba01..f90ac26f356 100644 --- a/erpnext/accounts/dashboard_fixtures.py +++ b/erpnext/accounts/dashboard_fixtures.py @@ -5,7 +5,18 @@ import frappe import json from frappe.utils import nowdate, add_months, get_date_str from frappe import _ -from erpnext.accounts.utils import get_fiscal_year, get_account_name +from erpnext.accounts.utils import get_fiscal_year, get_account_name, FiscalYearError + +def _get_fiscal_year(date=None): + try: + fiscal_year = get_fiscal_year(date=nowdate()) + except FiscalYearError: + #if no fiscal year for current date then get default fiscal year + try: + fiscal_year = get_fiscal_year() + except FiscalYearError: + #if still no fiscal year found then no accounting data created, return + return None def get_company_for_dashboards(): company = frappe.defaults.get_defaults().company @@ -18,10 +29,16 @@ def get_company_for_dashboards(): return None def get_data(): + + fiscal_year = _get_fiscal_year(nowdate()) + + if not fiscal_year: + return frappe._dict() + return frappe._dict({ "dashboards": get_dashboards(), - "charts": get_charts(), - "number_cards": get_number_cards() + "charts": get_charts(fiscal_year), + "number_cards": get_number_cards(fiscal_year) }) def get_dashboards(): @@ -46,10 +63,9 @@ def get_dashboards(): ] }] -def get_charts(): +def get_charts(fiscal_year): company = frappe.get_doc("Company", get_company_for_dashboards()) bank_account = company.default_bank_account or get_account_name("Bank", company=company.name) - fiscal_year = get_fiscal_year(date=nowdate()) default_cost_center = company.cost_center return [ @@ -190,8 +206,8 @@ def get_charts(): }, ] -def get_number_cards(): - fiscal_year = get_fiscal_year(date=nowdate()) +def get_number_cards(fiscal_year): + year_start_date = get_date_str(fiscal_year[1]) year_end_date = get_date_str(fiscal_year[2]) return [ diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index c82a2498432..6cdf9b57d1d 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -1745,53 +1745,6 @@ class TestSalesInvoice(unittest.TestCase): check_gl_entries(self, si.name, expected_gle, "2019-01-30") - def test_deferred_error_email(self): - deferred_account = create_account(account_name="Deferred Revenue", - parent_account="Current Liabilities - _TC", company="_Test Company") - - item = create_item("_Test Item for Deferred Accounting") - item.enable_deferred_revenue = 1 - item.deferred_revenue_account = deferred_account - item.no_of_months = 12 - item.save() - - si = create_sales_invoice(item=item.name, posting_date="2019-01-10", do_not_submit=True) - si.items[0].enable_deferred_revenue = 1 - si.items[0].service_start_date = "2019-01-10" - si.items[0].service_end_date = "2019-03-15" - si.items[0].deferred_revenue_account = deferred_account - si.save() - si.submit() - - from erpnext.accounts.deferred_revenue import convert_deferred_revenue_to_income - - acc_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') - acc_settings.acc_frozen_upto = '2019-01-31' - acc_settings.save() - - pda = frappe.get_doc(dict( - doctype='Process Deferred Accounting', - posting_date=nowdate(), - start_date="2019-01-01", - end_date="2019-03-31", - type="Income", - company="_Test Company" - )) - - pda.insert() - pda.submit() - - email = frappe.db.sql(""" select name from `tabEmail Queue` - where message like %(txt)s """, { - 'txt': "%%%s%%" % "Error while processing deferred accounting for {0}".format(pda.name) - }) - - self.assertTrue(email) - - acc_settings.load_from_db() - acc_settings.acc_frozen_upto = None - acc_settings.save() - def test_inter_company_transaction(self): if not frappe.db.exists("Customer", "_Test Internal Customer"): diff --git a/erpnext/assets/dashboard_fixtures.py b/erpnext/assets/dashboard_fixtures.py index 9af45d16b62..d1e65e11d19 100644 --- a/erpnext/assets/dashboard_fixtures.py +++ b/erpnext/assets/dashboard_fixtures.py @@ -5,14 +5,23 @@ import frappe import json from frappe.utils import nowdate, add_months, get_date_str from frappe import _ -from erpnext.accounts.utils import get_fiscal_year - +from erpnext.accounts.dashboard_fixtures import _get_fiscal_year +from erpnext.buying.dashboard_fixtures import get_company_for_dashboards def get_data(): + + fiscal_year = _get_fiscal_year(nowdate()) + + if not fiscal_year: + return frappe._dict() + + year_start_date = get_date_str(fiscal_year[1]) + year_end_date = get_date_str(fiscal_year[2]) + return frappe._dict({ "dashboards": get_dashboards(), - "charts": get_charts(), - "number_cards": get_number_cards(), + "charts": get_charts(fiscal_year, year_start_date, year_end_date), + "number_cards": get_number_cards(fiscal_year, year_start_date, year_end_date), }) def get_dashboards(): @@ -31,12 +40,7 @@ def get_dashboards(): ] }] -fiscal_year = get_fiscal_year(date=nowdate()) -year_start_date = get_date_str(fiscal_year[1]) -year_end_date = get_date_str(fiscal_year[2]) - - -def get_charts(): +def get_charts(fiscal_year, year_start_date, year_end_date): company = get_company_for_dashboards() return [ { @@ -134,7 +138,7 @@ def get_charts(): } ] -def get_number_cards(): +def get_number_cards(fiscal_year, year_start_date, year_end_date): return [ { "name": "Total Assets", @@ -172,14 +176,4 @@ def get_number_cards(): "filters_json": "[]", "doctype": "Number Card" } - ] - -def get_company_for_dashboards(): - company = frappe.defaults.get_defaults().company - if company: - return company - else: - company_list = frappe.get_list("Company") - if company_list: - return company_list[0].name - return None \ No newline at end of file + ] \ No newline at end of file diff --git a/erpnext/buying/dashboard_fixtures.py b/erpnext/buying/dashboard_fixtures.py index 186bfb23af9..172c936bd2b 100644 --- a/erpnext/buying/dashboard_fixtures.py +++ b/erpnext/buying/dashboard_fixtures.py @@ -5,13 +5,24 @@ import frappe import json from frappe import _ from frappe.utils import nowdate -from erpnext.accounts.utils import get_fiscal_year +from erpnext.accounts.dashboard_fixtures import _get_fiscal_year def get_data(): + + fiscal_year = _get_fiscal_year(nowdate()) + + if not fiscal_year: + return frappe._dict() + + company = frappe.get_doc("Company", get_company_for_dashboards()) + fiscal_year_name = fiscal_year.get("name") + start_date = str(fiscal_year.get("year_start_date")) + end_date = str(fiscal_year.get("year_end_date")) + return frappe._dict({ "dashboards": get_dashboards(), - "charts": get_charts(), - "number_cards": get_number_cards(), + "charts": get_charts(company, fiscal_year_name, start_date, end_date), + "number_cards": get_number_cards(company, fiscal_year_name, start_date, end_date), }) def get_company_for_dashboards(): @@ -24,12 +35,6 @@ def get_company_for_dashboards(): return company_list[0].name return None -company = frappe.get_doc("Company", get_company_for_dashboards()) -fiscal_year = get_fiscal_year(nowdate(), as_dict=1) -fiscal_year_name = fiscal_year.get("name") -start_date = str(fiscal_year.get("year_start_date")) -end_date = str(fiscal_year.get("year_end_date")) - def get_dashboards(): return [{ "name": "Buying", @@ -48,7 +53,7 @@ def get_dashboards(): ] }] -def get_charts(): +def get_charts(company, fiscal_year_name, start_date, end_date): return [ { "name": "Purchase Order Analysis", @@ -139,7 +144,7 @@ def get_charts(): } ] -def get_number_cards(): +def get_number_cards(company, fiscal_year_name, start_date, end_date): return [ { "name": "Annual Purchase", diff --git a/erpnext/manufacturing/dashboard_fixtures.py b/erpnext/manufacturing/dashboard_fixtures.py index 4a17fd07fbb..64e4bc6ed06 100644 --- a/erpnext/manufacturing/dashboard_fixtures.py +++ b/erpnext/manufacturing/dashboard_fixtures.py @@ -4,7 +4,6 @@ import frappe, erpnext, json from frappe import _ from frappe.utils import nowdate, get_first_day, get_last_day, add_months -from erpnext.accounts.utils import get_fiscal_year def get_data(): return frappe._dict({ diff --git a/erpnext/regional/united_states/setup.py b/erpnext/regional/united_states/setup.py index 6d344025d26..cae28bee8bc 100644 --- a/erpnext/regional/united_states/setup.py +++ b/erpnext/regional/united_states/setup.py @@ -9,14 +9,14 @@ def setup(company=None, patch=True): make_custom_fields() add_print_formats() -def make_custom_fields(): +def make_custom_fields(update=True): custom_fields = { 'Supplier': [ dict(fieldname='irs_1099', fieldtype='Check', insert_after='tax_id', label='Is IRS 1099 reporting required for supplier?') ] } - create_custom_fields(custom_fields) + create_custom_fields(custom_fields, update=update) def add_print_formats(): frappe.reload_doc("regional", "print_format", "irs_1099_form") diff --git a/erpnext/stock/dashboard_fixtures.py b/erpnext/stock/dashboard_fixtures.py index 0f1fd128f0b..7625b1ad283 100644 --- a/erpnext/stock/dashboard_fixtures.py +++ b/erpnext/stock/dashboard_fixtures.py @@ -5,31 +5,26 @@ import frappe import json from frappe import _ from frappe.utils import nowdate -from erpnext.accounts.utils import get_fiscal_year +from erpnext.accounts.dashboard_fixtures import _get_fiscal_year +from erpnext.buying.dashboard_fixtures import get_company_for_dashboards def get_data(): + fiscal_year = _get_fiscal_year(nowdate()) + + if not fiscal_year: + return frappe._dict() + + company = frappe.get_doc("Company", get_company_for_dashboards()) + fiscal_year_name = fiscal_year.get("name") + start_date = str(fiscal_year.get("year_start_date")) + end_date = str(fiscal_year.get("year_end_date")) + return frappe._dict({ "dashboards": get_dashboards(), - "charts": get_charts(), - "number_cards": get_number_cards(), + "charts": get_charts(company, fiscal_year_name, start_date, end_date), + "number_cards": get_number_cards(company, fiscal_year_name, start_date, end_date), }) -def get_company_for_dashboards(): - company = frappe.defaults.get_defaults().company - if company: - return company - else: - company_list = frappe.get_list("Company") - if company_list: - return company_list[0].name - return None - -company = frappe.get_doc("Company", get_company_for_dashboards()) -fiscal_year = get_fiscal_year(nowdate(), as_dict=1) -fiscal_year_name = fiscal_year.get("name") -start_date = str(fiscal_year.get("year_start_date")) -end_date = str(fiscal_year.get("year_end_date")) - def get_dashboards(): return [{ "name": "Stock", @@ -48,7 +43,7 @@ def get_dashboards(): ] }] -def get_charts(): +def get_charts(company, fiscal_year_name, start_date, end_date): return [ { "doctype": "Dashboard Chart", @@ -133,7 +128,7 @@ def get_charts(): } ] -def get_number_cards(): +def get_number_cards(company, fiscal_year_name, start_date, end_date): return [ { "name": "Total Active Items", From 6b55f66f02b8ee2bfc9f1a11bc176c776f03366e Mon Sep 17 00:00:00 2001 From: John Clarke Date: Thu, 11 Jun 2020 10:24:48 -0600 Subject: [PATCH 333/608] =?UTF-8?q?Stock=20Ageing=20TypeError:=20=E2=80=98?= =?UTF-8?q?<=E2=80=99=20not=20supported=20between=20instances=20of=20?= =?UTF-8?q?=E2=80=98int=E2=80=99=20and=20=E2=80=98str=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Proposed fix re this error report https://discuss.erpnext.com/t/stock-ageing-report-typeerror-not-supported-between-instances-of-int-and-str/62605 --- erpnext/stock/report/stock_ageing/stock_ageing.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.py b/erpnext/stock/report/stock_ageing/stock_ageing.py index af997801551..53bdfb523d2 100644 --- a/erpnext/stock/report/stock_ageing/stock_ageing.py +++ b/erpnext/stock/report/stock_ageing/stock_ageing.py @@ -180,14 +180,14 @@ def get_fifo_queue(filters, sle=None): qty_to_pop = abs(d.actual_qty) while qty_to_pop: batch = fifo_queue[0] if fifo_queue else [0, None] - if 0 < batch[0] <= qty_to_pop: + if 0 < cint(batch[0]) <= qty_to_pop: # if batch qty > 0 # not enough or exactly same qty in current batch, clear batch - qty_to_pop -= batch[0] + qty_to_pop -= cint(batch[0]) transferred_item_details[(d.voucher_no, d.name)].append(fifo_queue.pop(0)) else: # all from current batch - batch[0] -= qty_to_pop + cint(batch[0]) -= qty_to_pop transferred_item_details[(d.voucher_no, d.name)].append([qty_to_pop, batch[1]]) qty_to_pop = 0 @@ -262,4 +262,4 @@ def get_chart_data(data, filters): ] }, "type" : "bar" - } \ No newline at end of file + } From faf33072c8aa1b4ebbe83c42253198131b1d8cc9 Mon Sep 17 00:00:00 2001 From: Alirio Castro Date: Thu, 11 Jun 2020 23:32:53 -0400 Subject: [PATCH 334/608] fix: key was mispelled --- .../itemwise_recommended_reorder_level.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.py b/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.py index 9a972104a27..5df3fa8067b 100644 --- a/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.py +++ b/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.py @@ -8,7 +8,7 @@ from frappe.utils import getdate, flt def execute(filters=None): if not filters: filters = {} - float_preceision = frappe.db.get_default("float_preceision") + float_precision = frappe.db.get_default("float_precision") condition = get_condition(filters) @@ -25,7 +25,7 @@ def execute(filters=None): data = [] for item in items: total_outgoing = flt(consumed_item_map.get(item.name, 0)) + flt(delivered_item_map.get(item.name,0)) - avg_daily_outgoing = flt(total_outgoing / diff, float_preceision) + avg_daily_outgoing = flt(total_outgoing / diff, float_precision) reorder_level = (avg_daily_outgoing * flt(item.lead_time_days)) + flt(item.safety_stock) data.append([item.name, item.item_name, item.item_group, item.brand, item.description, From f596b5950ebd137abfdfb9b32cbd944743d5f3cb Mon Sep 17 00:00:00 2001 From: Abhishek Balam Date: Fri, 12 Jun 2020 11:07:28 +0530 Subject: [PATCH 335/608] indent to tabs --- .../customer_acquisition_and_loyalty.py | 310 +++++++++--------- 1 file changed, 155 insertions(+), 155 deletions(-) diff --git a/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py b/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py index 88bd9c135d5..7cdad4a5141 100644 --- a/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py +++ b/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py @@ -8,180 +8,180 @@ from frappe import _ from frappe.utils import cint, cstr def execute(filters=None): - common_columns = [ - { - 'label': _('New Customers'), - 'fieldname': 'new_customers', - 'fieldtype': 'Int', - 'default': 0, - 'width': 125 - }, - { - 'label': _('Repeat Customers'), - 'fieldname': 'repeat_customers', - 'fieldtype': 'Int', - 'default': 0, - 'width': 125 - }, - { - 'label': _('Total'), - 'fieldname': 'total', - 'fieldtype': 'Int', - 'default': 0, - 'width': 100 - }, - { - 'label': _('New Customer Revenue'), - 'fieldname': 'new_customer_revenue', - 'fieldtype': 'Currency', - 'default': 0.0, - 'width': 175 - }, - { - 'label': _('Repeat Customer Revenue'), - 'fieldname': 'repeat_customer_revenue', - 'fieldtype': 'Currency', - 'default': 0.0, - 'width': 175 - }, - { - 'label': _('Total Revenue'), - 'fieldname': 'total_revenue', - 'fieldtype': 'Currency', - 'default': 0.0, - 'width': 175 - } - ] - if filters.get('view_type') == 'Monthly': - return get_data_by_time(filters, common_columns) - else: - return get_data_by_territory(filters, common_columns) + common_columns = [ + { + 'label': _('New Customers'), + 'fieldname': 'new_customers', + 'fieldtype': 'Int', + 'default': 0, + 'width': 125 + }, + { + 'label': _('Repeat Customers'), + 'fieldname': 'repeat_customers', + 'fieldtype': 'Int', + 'default': 0, + 'width': 125 + }, + { + 'label': _('Total'), + 'fieldname': 'total', + 'fieldtype': 'Int', + 'default': 0, + 'width': 100 + }, + { + 'label': _('New Customer Revenue'), + 'fieldname': 'new_customer_revenue', + 'fieldtype': 'Currency', + 'default': 0.0, + 'width': 175 + }, + { + 'label': _('Repeat Customer Revenue'), + 'fieldname': 'repeat_customer_revenue', + 'fieldtype': 'Currency', + 'default': 0.0, + 'width': 175 + }, + { + 'label': _('Total Revenue'), + 'fieldname': 'total_revenue', + 'fieldtype': 'Currency', + 'default': 0.0, + 'width': 175 + } + ] + if filters.get('view_type') == 'Monthly': + return get_data_by_time(filters, common_columns) + else: + return get_data_by_territory(filters, common_columns) def get_data_by_time(filters, common_columns): - # key yyyy-mm - columns = [ - { - 'label': _('Year'), - 'fieldname': 'year', - 'fieldtype': 'Data', - 'width': 100 - }, - { - 'label': _('Month'), - 'fieldname': 'month', - 'fieldtype': 'Data', - 'width': 100 - }, - ] - columns += common_columns + # key yyyy-mm + columns = [ + { + 'label': _('Year'), + 'fieldname': 'year', + 'fieldtype': 'Data', + 'width': 100 + }, + { + 'label': _('Month'), + 'fieldname': 'month', + 'fieldtype': 'Data', + 'width': 100 + }, + ] + columns += common_columns - customers_in = get_customer_stats(filters) + customers_in = get_customer_stats(filters) - # time series - from_year, from_month, temp = filters.get('from_date').split('-') - to_year, to_month, temp = filters.get('to_date').split('-') + # time series + from_year, from_month, temp = filters.get('from_date').split('-') + to_year, to_month, temp = filters.get('to_date').split('-') - from_year, from_month, to_year, to_month = \ - cint(from_year), cint(from_month), cint(to_year), cint(to_month) + from_year, from_month, to_year, to_month = \ + cint(from_year), cint(from_month), cint(to_year), cint(to_month) - out = [] - for year in range(from_year, to_year+1): - for month in range(from_month if year==from_year else 1, (to_month+1) if year==to_year else 13): - key = '{year}-{month:02d}'.format(year=year, month=month) - data = customers_in.get(key) - new = data['new'] if data else [0, 0.0] - repeat = data['repeat'] if data else [0, 0.0] - out.append({ - 'year': cstr(year), - 'month': calendar.month_name[month], - 'new_customers': new[0], - 'repeat_customers': repeat[0], - 'total': new[0] + repeat[0], - 'new_customer_revenue': new[1], - 'repeat_customer_revenue': repeat[1], - 'total_revenue': new[1] + repeat[1] - }) - return columns, out + out = [] + for year in range(from_year, to_year+1): + for month in range(from_month if year==from_year else 1, (to_month+1) if year==to_year else 13): + key = '{year}-{month:02d}'.format(year=year, month=month) + data = customers_in.get(key) + new = data['new'] if data else [0, 0.0] + repeat = data['repeat'] if data else [0, 0.0] + out.append({ + 'year': cstr(year), + 'month': calendar.month_name[month], + 'new_customers': new[0], + 'repeat_customers': repeat[0], + 'total': new[0] + repeat[0], + 'new_customer_revenue': new[1], + 'repeat_customer_revenue': repeat[1], + 'total_revenue': new[1] + repeat[1] + }) + return columns, out def get_data_by_territory(filters, common_columns): - columns = [{ - 'label': 'Territory', - 'fieldname': 'territory', - 'fieldtype': 'Link', - 'options': 'Territory', - 'width': 150 - }] - columns += common_columns + columns = [{ + 'label': 'Territory', + 'fieldname': 'territory', + 'fieldtype': 'Link', + 'options': 'Territory', + 'width': 150 + }] + columns += common_columns - customers_in = get_customer_stats(filters, tree_view=True) + customers_in = get_customer_stats(filters, tree_view=True) - territory_dict = {} - for t in frappe.db.sql('''SELECT name, lft, parent_territory, is_group FROM `tabTerritory` ORDER BY lft''', as_dict=1): - territory_dict.update({ - t.name: { - 'parent': t.parent_territory, - 'is_group': t.is_group - } - }) + territory_dict = {} + for t in frappe.db.sql('''SELECT name, lft, parent_territory, is_group FROM `tabTerritory` ORDER BY lft''', as_dict=1): + territory_dict.update({ + t.name: { + 'parent': t.parent_territory, + 'is_group': t.is_group + } + }) - depth_map = frappe._dict() - for name, info in territory_dict.items(): - default = depth_map.get(info['parent']) + 1 if info['parent'] else 0 - depth_map.setdefault(name, default) + depth_map = frappe._dict() + for name, info in territory_dict.items(): + default = depth_map.get(info['parent']) + 1 if info['parent'] else 0 + depth_map.setdefault(name, default) - data = [] - for name, indent in depth_map.items(): - condition = customers_in.get(name) - new = customers_in[name]['new'] if condition else [0, 0.0] - repeat = customers_in[name]['repeat'] if condition else [0, 0.0] - temp = { - 'territory': name, - 'parent_territory': territory_dict[name]['parent'], - 'indent': indent, - 'new_customers': new[0], - 'repeat_customers': repeat[0], - 'total': new[0] + repeat[0], - 'new_customer_revenue': new[1], - 'repeat_customer_revenue': repeat[1], - 'total_revenue': new[1] + repeat[1], - 'bold': 0 if indent else 1 - } - data.append(temp) + data = [] + for name, indent in depth_map.items(): + condition = customers_in.get(name) + new = customers_in[name]['new'] if condition else [0, 0.0] + repeat = customers_in[name]['repeat'] if condition else [0, 0.0] + temp = { + 'territory': name, + 'parent_territory': territory_dict[name]['parent'], + 'indent': indent, + 'new_customers': new[0], + 'repeat_customers': repeat[0], + 'total': new[0] + repeat[0], + 'new_customer_revenue': new[1], + 'repeat_customer_revenue': repeat[1], + 'total_revenue': new[1] + repeat[1], + 'bold': 0 if indent else 1 + } + data.append(temp) - loop_data = sorted(data, key=lambda k: k['indent'], reverse=True) + loop_data = sorted(data, key=lambda k: k['indent'], reverse=True) - for ld in loop_data: - if ld['parent_territory']: - parent_data = [x for x in data if x['territory'] == ld['parent_territory']][0] - for key in parent_data.keys(): - if key not in ['indent', 'territory', 'parent_territory', 'bold']: - parent_data[key] += ld[key] + for ld in loop_data: + if ld['parent_territory']: + parent_data = [x for x in data if x['territory'] == ld['parent_territory']][0] + for key in parent_data.keys(): + if key not in ['indent', 'territory', 'parent_territory', 'bold']: + parent_data[key] += ld[key] - return columns, data, None, None, None, 1 + return columns, data, None, None, None, 1 def get_customer_stats(filters, tree_view=False): - """ Calculates number of new and repeated customers. """ - company_condition = '' - if filters.get('company'): - company_condition = ' and company=%(company)s' + """ Calculates number of new and repeated customers and revenue. """ + company_condition = '' + if filters.get('company'): + company_condition = ' and company=%(company)s' - customers = [] - customers_in = {} + customers = [] + customers_in = {} - for si in frappe.db.sql('''select territory, posting_date, customer, base_grand_total from `tabSales Invoice` - where docstatus=1 and posting_date <= %(to_date)s and posting_date >= %(from_date)s - {company_condition} order by posting_date'''.format(company_condition=company_condition), - filters, as_dict=1): + for si in frappe.db.sql('''select territory, posting_date, customer, base_grand_total from `tabSales Invoice` + where docstatus=1 and posting_date <= %(to_date)s + {company_condition} order by posting_date'''.format(company_condition=company_condition), + filters, as_dict=1): - key = si.territory if tree_view else si.posting_date.strftime('%Y-%m') - customers_in.setdefault(key, {'new': [0, 0.0], 'repeat': [0, 0.0]}) + key = si.territory if tree_view else si.posting_date.strftime('%Y-%m') + customers_in.setdefault(key, {'new': [0, 0.0], 'repeat': [0, 0.0]}) - if not si.customer in customers: - customers_in[key]['new'][0] += 1 - customers_in[key]['new'][1] += si.base_grand_total - customers.append(si.customer) - else: - customers_in[key]['repeat'][0] += 1 - customers_in[key]['repeat'][1] += si.base_grand_total + if not si.customer in customers: + customers_in[key]['new'][0] += 1 + customers_in[key]['new'][1] += si.base_grand_total + customers.append(si.customer) + else: + customers_in[key]['repeat'][0] += 1 + customers_in[key]['repeat'][1] += si.base_grand_total - return customers_in + return customers_in \ No newline at end of file From 72450d2af30ded6d2a66b8a47dc2a4b92fb14414 Mon Sep 17 00:00:00 2001 From: Abhishek Balam Date: Fri, 12 Jun 2020 11:07:57 +0530 Subject: [PATCH 336/608] add revenue if within date range --- .../customer_acquisition_and_loyalty.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py b/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py index 7cdad4a5141..0ab6eda6478 100644 --- a/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py +++ b/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py @@ -175,13 +175,16 @@ def get_customer_stats(filters, tree_view=False): key = si.territory if tree_view else si.posting_date.strftime('%Y-%m') customers_in.setdefault(key, {'new': [0, 0.0], 'repeat': [0, 0.0]}) + revenue_condition = (filters.from_date <= si.posting_date.strftime('%Y-%m-%d')) if not si.customer in customers: customers_in[key]['new'][0] += 1 - customers_in[key]['new'][1] += si.base_grand_total + if revenue_condition: + customers_in[key]['new'][1] += si.base_grand_total customers.append(si.customer) else: customers_in[key]['repeat'][0] += 1 - customers_in[key]['repeat'][1] += si.base_grand_total + if revenue_condition: + customers_in[key]['repeat'][1] += si.base_grand_total return customers_in \ No newline at end of file From 996b306cdae6417492e9866ab3dfca27943c9d72 Mon Sep 17 00:00:00 2001 From: Abhishek Balam Date: Fri, 12 Jun 2020 11:38:07 +0530 Subject: [PATCH 337/608] fix territory count --- .../customer_acquisition_and_loyalty.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py b/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py index 0ab6eda6478..4288b52acac 100644 --- a/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py +++ b/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py @@ -174,17 +174,15 @@ def get_customer_stats(filters, tree_view=False): filters, as_dict=1): key = si.territory if tree_view else si.posting_date.strftime('%Y-%m') + new_or_repeat = 'new' if si.customer not in customers else 'repeat' + customers_in.setdefault(key, {'new': [0, 0.0], 'repeat': [0, 0.0]}) revenue_condition = (filters.from_date <= si.posting_date.strftime('%Y-%m-%d')) - if not si.customer in customers: - customers_in[key]['new'][0] += 1 - if revenue_condition: - customers_in[key]['new'][1] += si.base_grand_total + if revenue_condition: + customers_in[key][new_or_repeat][0] += 1 + customers_in[key][new_or_repeat][1] += si.base_grand_total + if new_or_repeat == 'new': customers.append(si.customer) - else: - customers_in[key]['repeat'][0] += 1 - if revenue_condition: - customers_in[key]['repeat'][1] += si.base_grand_total return customers_in \ No newline at end of file From fd3d976e067464cb5f4ec347fb79538b68c13cac Mon Sep 17 00:00:00 2001 From: Marica Date: Fri, 12 Jun 2020 12:30:59 +0530 Subject: [PATCH 338/608] Update erpnext/controllers/stock_controller.py Co-authored-by: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> --- erpnext/controllers/stock_controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 2ce41590cff..2888c764ef7 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -19,7 +19,7 @@ class QualityInspectionNotSubmittedError(frappe.ValidationError): pass class StockController(AccountsController): def validate(self): super(StockController, self).validate() - if not self.is_return: + if not self.get('is_return'): self.validate_inspection() self.validate_serialized_batch() self.validate_customer_provided_item() From 90957b7f747b89e85037aeb713043a4e4437e96d Mon Sep 17 00:00:00 2001 From: Vignesh S Date: Fri, 12 Jun 2020 12:32:55 +0530 Subject: [PATCH 339/608] feat(Attendance): Add In and Out time to attendance (#21547) * feat(Attendance): Add In and Out time to attendance Co-authored-by: Karthikeyan S * fix:add depends in attendance IN time and OUT time Co-authored-by: Karthikeyan S --- erpnext/hr/doctype/attendance/attendance.json | 35 ++++++++++++++++--- .../employee_checkin/employee_checkin.py | 6 ++-- erpnext/hr/doctype/shift_type/shift_type.py | 13 +++---- 3 files changed, 42 insertions(+), 12 deletions(-) diff --git a/erpnext/hr/doctype/attendance/attendance.json b/erpnext/hr/doctype/attendance/attendance.json index 906f6f77f21..a656a7ea5f7 100644 --- a/erpnext/hr/doctype/attendance/attendance.json +++ b/erpnext/hr/doctype/attendance/attendance.json @@ -19,11 +19,15 @@ "attendance_date", "company", "department", - "shift", "attendance_request", - "amended_from", + "details_section", + "shift", + "in_time", + "out_time", + "column_break_18", "late_entry", - "early_exit" + "early_exit", + "amended_from" ], "fields": [ { @@ -172,13 +176,36 @@ "fieldname": "early_exit", "fieldtype": "Check", "label": "Early Exit" + }, + { + "fieldname": "details_section", + "fieldtype": "Section Break", + "label": "Details" + }, + { + "depends_on": "shift", + "fieldname": "in_time", + "fieldtype": "Datetime", + "label": "In Time", + "read_only": 1 + }, + { + "depends_on": "shift", + "fieldname": "out_time", + "fieldtype": "Datetime", + "label": "Out Time", + "read_only": 1 + }, + { + "fieldname": "column_break_18", + "fieldtype": "Column Break" } ], "icon": "fa fa-ok", "idx": 1, "is_submittable": 1, "links": [], - "modified": "2020-04-11 11:40:14.319496", + "modified": "2020-05-29 13:51:37.177231", "modified_by": "Administrator", "module": "HR", "name": "Attendance", diff --git a/erpnext/hr/doctype/employee_checkin/employee_checkin.py b/erpnext/hr/doctype/employee_checkin/employee_checkin.py index 86705121ac5..15fbd4e0153 100644 --- a/erpnext/hr/doctype/employee_checkin/employee_checkin.py +++ b/erpnext/hr/doctype/employee_checkin/employee_checkin.py @@ -72,7 +72,7 @@ def add_log_based_on_employee_field(employee_field_value, timestamp, device_id=N return doc -def mark_attendance_and_link_log(logs, attendance_status, attendance_date, working_hours=None, late_entry=False, early_exit=False, shift=None): +def mark_attendance_and_link_log(logs, attendance_status, attendance_date, working_hours=None, late_entry=False, early_exit=False, in_time=None, out_time=None, shift=None): """Creates an attendance and links the attendance to the Employee Checkin. Note: If attendance is already present for the given date, the logs are marked as skipped and no exception is thrown. @@ -100,7 +100,9 @@ def mark_attendance_and_link_log(logs, attendance_status, attendance_date, worki 'company': employee_doc.company, 'shift': shift, 'late_entry': late_entry, - 'early_exit': early_exit + 'early_exit': early_exit, + 'in_time': in_time, + 'out_time': out_time } attendance = frappe.get_doc(doc_dict).insert() attendance.submit() diff --git a/erpnext/hr/doctype/shift_type/shift_type.py b/erpnext/hr/doctype/shift_type/shift_type.py index d56080eecd4..19735648aa9 100644 --- a/erpnext/hr/doctype/shift_type/shift_type.py +++ b/erpnext/hr/doctype/shift_type/shift_type.py @@ -28,13 +28,14 @@ class ShiftType(Document): logs = frappe.db.get_list('Employee Checkin', fields="*", filters=filters, order_by="employee,time") for key, group in itertools.groupby(logs, key=lambda x: (x['employee'], x['shift_actual_start'])): single_shift_logs = list(group) - attendance_status, working_hours, late_entry, early_exit = self.get_attendance(single_shift_logs) - mark_attendance_and_link_log(single_shift_logs, attendance_status, key[1].date(), working_hours, late_entry, early_exit, self.name) + attendance_status, working_hours, late_entry, early_exit, in_time, out_time = self.get_attendance(single_shift_logs) + mark_attendance_and_link_log(single_shift_logs, attendance_status, key[1].date(), working_hours, late_entry, early_exit, in_time, out_time, self.name) for employee in self.get_assigned_employee(self.process_attendance_after, True): self.mark_absent_for_dates_with_no_attendance(employee) def get_attendance(self, logs): - """Return attendance_status, working_hours for a set of logs belonging to a single shift. + """Return attendance_status, working_hours, late_entry, early_exit, in_time, out_time + for a set of logs belonging to a single shift. Assumtion: 1. These logs belongs to an single shift, single employee and is not in a holiday date. 2. Logs are in chronological order @@ -48,10 +49,10 @@ class ShiftType(Document): early_exit = True if self.working_hours_threshold_for_absent and total_working_hours < self.working_hours_threshold_for_absent: - return 'Absent', total_working_hours, late_entry, early_exit + return 'Absent', total_working_hours, late_entry, early_exit, in_time, out_time if self.working_hours_threshold_for_half_day and total_working_hours < self.working_hours_threshold_for_half_day: - return 'Half Day', total_working_hours, late_entry, early_exit - return 'Present', total_working_hours, late_entry, early_exit + return 'Half Day', total_working_hours, late_entry, early_exit, in_time, out_time + return 'Present', total_working_hours, late_entry, early_exit, in_time, out_time def mark_absent_for_dates_with_no_attendance(self, employee): """Marks Absents for the given employee on working days in this shift which have no attendance marked. From ccd03bc1206ebfd72d1e7c14d53c764e92d0c425 Mon Sep 17 00:00:00 2001 From: Rohan Bansal Date: Tue, 25 Feb 2020 17:56:15 +0530 Subject: [PATCH 340/608] feat: notify credit controller role users with credit limit extension requests --- erpnext/selling/doctype/customer/customer.py | 50 +++++++++++++++++--- 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index a6889e080d9..ac3bc201e96 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -3,16 +3,19 @@ from __future__ import unicode_literals import frappe +import json from frappe.model.naming import set_name_by_naming_series -from frappe import _, msgprint, throw +from frappe import _, msgprint import frappe.defaults -from frappe.utils import flt, cint, cstr, today +from frappe.utils import flt, cint, cstr, today, get_formatted_email from frappe.desk.reportview import build_match_conditions, get_filters_cond from erpnext.utilities.transaction_base import TransactionBase from erpnext.accounts.party import validate_party_accounts, get_dashboard_info, get_timeline_data # keep this from frappe.contacts.address_and_contact import load_address_and_contact, delete_contact_and_address from frappe.model.rename_doc import update_linked_doctypes from frappe.model.mapper import get_mapped_doc +from frappe.utils.user import get_users_with_role + class Customer(TransactionBase): def get_feed(self): @@ -378,10 +381,45 @@ def check_credit_limit(customer, company, ignore_outstanding_sales_order=False, .format(customer, customer_outstanding, credit_limit)) # If not authorized person raise exception - credit_controller = frappe.db.get_value('Accounts Settings', None, 'credit_controller') - if not credit_controller or credit_controller not in frappe.get_roles(): - throw(_("Please contact to the user who have Sales Master Manager {0} role") - .format(" / " + credit_controller if credit_controller else "")) + credit_controller_role = frappe.db.get_single_value('Accounts Settings', 'credit_controller') + if not credit_controller_role or credit_controller_role not in frappe.get_roles(): + # form a list of emails for the credit controller users + credit_controller_users = get_users_with_role(credit_controller_role or "Sales Master Manager") + + # form a list of emails and names to show to the user + credit_controller_users_list = [user for user in credit_controller_users if frappe.db.exists("Employee", {"prefered_email": user})] + credit_controller_users = [get_formatted_email(user).replace("<", "(").replace(">", ")") for user in credit_controller_users_list] + + if not credit_controller_users: + frappe.throw(_("Please contact your administrator to extend the credit limits for {0}.".format(customer))) + + message = """Please contact any of the following users to extend the credit limits for {0}: +

  • {1}
""".format(customer, '
  • '.join(credit_controller_users)) + + # if the current user does not have permissions to override credit limit, + # prompt them to send out an email to the controller users + frappe.msgprint(message, + title="Notify", + raise_exception=1, + primary_action={ + 'label': 'Send Email', + 'server_action': 'erpnext.selling.doctype.customer.customer.send_emails', + 'args': { + 'customer': customer, + 'customer_outstanding': customer_outstanding, + 'credit_limit': credit_limit, + 'credit_controller_users_list': credit_controller_users_list + } + } + ) + +@frappe.whitelist() +def send_emails(args): + args = json.loads(args) + subject = (_("Credit limit reached for customer {0}").format(args.get('customer'))) + message = (_("Credit limit has been crossed for customer {0} ({1}/{2})") + .format(args.get('customer'), args.get('customer_outstanding'), args.get('credit_limit'))) + frappe.sendmail(recipients=[args.get('credit_controller_users_list')], subject=subject, message=message) def get_customer_outstanding(customer, company, ignore_outstanding_sales_order=False, cost_center=None): # Outstanding based on GL Entries From 86ea75e49e0e952d35aadc9579a533bb37e96d3e Mon Sep 17 00:00:00 2001 From: Abhishek Balam Date: Fri, 12 Jun 2020 13:13:14 +0530 Subject: [PATCH 341/608] fix(HR): Change Due Advance Amount to Pending Amount (#22123) * rename 'Due Advance Amount' field to 'Pending Amount' * changed fieldname and references for easier debugging * added patch for moving data * added newline added Co-authored-by: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> --- .../employee_advance/employee_advance.js | 4 ++-- .../employee_advance/employee_advance.json | 20 +++++++++---------- .../employee_advance/employee_advance.py | 2 +- erpnext/patches.txt | 3 ++- ...ve_due_advance_amount_to_pending_amount.py | 9 +++++++++ 5 files changed, 24 insertions(+), 14 deletions(-) create mode 100644 erpnext/patches/v12_0/move_due_advance_amount_to_pending_amount.py diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.js b/erpnext/hr/doctype/employee_advance/employee_advance.js index 6cc49cfff23..cba8ee9a404 100644 --- a/erpnext/hr/doctype/employee_advance/employee_advance.js +++ b/erpnext/hr/doctype/employee_advance/employee_advance.js @@ -139,13 +139,13 @@ frappe.ui.form.on('Employee Advance', { employee: function (frm) { if (frm.doc.employee) { return frappe.call({ - method: "erpnext.hr.doctype.employee_advance.employee_advance.get_due_advance_amount", + method: "erpnext.hr.doctype.employee_advance.employee_advance.get_pending_amount", args: { "employee": frm.doc.employee, "posting_date": frm.doc.posting_date }, callback: function(r) { - frm.set_value("due_advance_amount",r.message); + frm.set_value("pending_amount",r.message); } }); } diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.json b/erpnext/hr/doctype/employee_advance/employee_advance.json index 8c5ce42d870..0d909138719 100644 --- a/erpnext/hr/doctype/employee_advance/employee_advance.json +++ b/erpnext/hr/doctype/employee_advance/employee_advance.json @@ -19,7 +19,7 @@ "column_break_11", "advance_amount", "paid_amount", - "due_advance_amount", + "pending_amount", "claimed_amount", "return_amount", "section_break_7", @@ -102,14 +102,6 @@ "options": "Company:company:default_currency", "read_only": 1 }, - { - "depends_on": "eval:cur_frm.doc.employee", - "fieldname": "due_advance_amount", - "fieldtype": "Currency", - "label": "Due Advance Amount", - "options": "Company:company:default_currency", - "read_only": 1 - }, { "fieldname": "claimed_amount", "fieldtype": "Currency", @@ -177,11 +169,19 @@ "fieldname": "repay_unclaimed_amount_from_salary", "fieldtype": "Check", "label": "Repay unclaimed amount from salary" + }, + { + "depends_on": "eval:cur_frm.doc.employee", + "fieldname": "pending_amount", + "fieldtype": "Currency", + "label": "Pending Amount", + "options": "Company:company:default_currency", + "read_only": 1 } ], "is_submittable": 1, "links": [], - "modified": "2020-03-06 15:11:33.747535", + "modified": "2020-06-12 12:42:39.833818", "modified_by": "Administrator", "module": "HR", "name": "Employee Advance", diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.py b/erpnext/hr/doctype/employee_advance/employee_advance.py index db39eff0e4d..a49dfcfc2a2 100644 --- a/erpnext/hr/doctype/employee_advance/employee_advance.py +++ b/erpnext/hr/doctype/employee_advance/employee_advance.py @@ -95,7 +95,7 @@ class EmployeeAdvance(Document): frappe.db.set_value("Employee Advance", self.name, "status", self.status) @frappe.whitelist() -def get_due_advance_amount(employee, posting_date): +def get_pending_amount(employee, posting_date): employee_due_amount = frappe.get_all("Employee Advance", \ filters = {"employee":employee, "docstatus":1, "posting_date":("<=", posting_date)}, \ fields = ["advance_amount", "paid_amount"]) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index b0421f43c64..1f5d4d563ae 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -680,6 +680,7 @@ erpnext.patches.v12_0.update_appointment_reminder_scheduler_entry erpnext.patches.v12_0.retain_permission_rules_for_video_doctype erpnext.patches.v12_0.remove_duplicate_leave_ledger_entries #2020-05-22 erpnext.patches.v13_0.patch_to_fix_reverse_linking_in_additional_salary_encashment_and_incentive +erpnext.patches.v12_0.move_due_advance_amount_to_pending_amount execute:frappe.delete_doc_if_exists("Page", "appointment-analytic") execute:frappe.rename_doc("Desk Page", "Getting Started", "Home", force=True) erpnext.patches.v12_0.unset_customer_supplier_based_on_type_of_item_price @@ -694,4 +695,4 @@ execute:frappe.delete_doc("Report", "Department Analytics") execute:frappe.rename_doc("Desk Page", "Loan Management", "Loan", force=True) erpnext.patches.v12_0.update_uom_conversion_factor erpnext.patches.v13_0.delete_old_purchase_reports -erpnext.patches.v12_0.set_italian_import_supplier_invoice_permissions \ No newline at end of file +erpnext.patches.v12_0.set_italian_import_supplier_invoice_permissions diff --git a/erpnext/patches/v12_0/move_due_advance_amount_to_pending_amount.py b/erpnext/patches/v12_0/move_due_advance_amount_to_pending_amount.py new file mode 100644 index 00000000000..f1ffaf9d2d4 --- /dev/null +++ b/erpnext/patches/v12_0/move_due_advance_amount_to_pending_amount.py @@ -0,0 +1,9 @@ +# Copyright (c) 2019, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe + +def execute(): + ''' Move from due_advance_amount to pending_amount ''' + frappe.db.sql(''' UPDATE `tabEmployee Advance` SET pending_amount=due_advance_amount ''') From 182c1f860b2c0c53f509559daa7c334fabfbcd0d Mon Sep 17 00:00:00 2001 From: Sahil Khan Date: Fri, 12 Jun 2020 15:16:20 +0530 Subject: [PATCH 342/608] fix(patch): escape special characters in company field --- .../patches/v13_0/set_company_field_in_healthcare_doctypes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/patches/v13_0/set_company_field_in_healthcare_doctypes.py b/erpnext/patches/v13_0/set_company_field_in_healthcare_doctypes.py index a7d4c665a1e..be5e30f3074 100644 --- a/erpnext/patches/v13_0/set_company_field_in_healthcare_doctypes.py +++ b/erpnext/patches/v13_0/set_company_field_in_healthcare_doctypes.py @@ -7,4 +7,4 @@ def execute(): for entry in doctypes: if frappe.db.exists('DocType', entry): frappe.reload_doc('Healthcare', 'doctype', entry) - frappe.db.sql("update `tab{dt}` set company = '{company}' where ifnull(company, '') = ''".format(dt=entry, company=company)) + frappe.db.sql("update `tab{dt}` set company = {company} where ifnull(company, '') = ''".format(dt=entry, company=frappe.db.escape(company))) From 7e974c9e2c77eda1aebe48bd8356c9b36937a5c0 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 12 Jun 2020 15:29:40 +0530 Subject: [PATCH 343/608] fix: Test Cases --- erpnext/accounts/dashboard_fixtures.py | 20 +++++++++++-------- .../accounting_dimension.py | 6 +++++- erpnext/assets/dashboard_fixtures.py | 8 ++++---- .../test_procurement_tracker.py | 19 ++++++++++-------- .../inpatient_record/test_inpatient_record.py | 5 ++++- erpnext/projects/doctype/task/test_task.py | 2 +- 6 files changed, 37 insertions(+), 23 deletions(-) diff --git a/erpnext/accounts/dashboard_fixtures.py b/erpnext/accounts/dashboard_fixtures.py index f90ac26f356..b2abffc79d7 100644 --- a/erpnext/accounts/dashboard_fixtures.py +++ b/erpnext/accounts/dashboard_fixtures.py @@ -9,11 +9,15 @@ from erpnext.accounts.utils import get_fiscal_year, get_account_name, FiscalYear def _get_fiscal_year(date=None): try: - fiscal_year = get_fiscal_year(date=nowdate()) + fiscal_year = get_fiscal_year(date=nowdate(), as_dict=True) + return fiscal_year + except FiscalYearError: #if no fiscal year for current date then get default fiscal year try: - fiscal_year = get_fiscal_year() + fiscal_year = get_fiscal_year(as_dict=True) + return fiscal_year + except FiscalYearError: #if still no fiscal year found then no accounting data created, return return None @@ -77,8 +81,8 @@ def get_charts(fiscal_year): "filters_json": json.dumps({ "company": company.name, "filter_based_on": "Fiscal Year", - "from_fiscal_year": fiscal_year[0], - "to_fiscal_year": fiscal_year[0], + "from_fiscal_year": fiscal_year.get('name'), + "to_fiscal_year": fiscal_year.get('name'), "periodicity": "Monthly", "include_default_book_entries": 1 }), @@ -174,8 +178,8 @@ def get_charts(fiscal_year): "report_name": "Budget Variance Report", "filters_json": json.dumps({ "company": company.name, - "from_fiscal_year": fiscal_year[0], - "to_fiscal_year": fiscal_year[0], + "from_fiscal_year": fiscal_year.get('name'), + "to_fiscal_year": fiscal_year.get('name'), "period": "Monthly", "budget_against": "Cost Center" }), @@ -208,8 +212,8 @@ def get_charts(fiscal_year): def get_number_cards(fiscal_year): - year_start_date = get_date_str(fiscal_year[1]) - year_end_date = get_date_str(fiscal_year[2]) + year_start_date = get_date_str(fiscal_year.get("year_start_date")) + year_end_date = get_date_str(fiscal_year.get("year_end_date")) return [ { "doctype": "Number Card", diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py index 894ec5bdec5..8834385135f 100644 --- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py +++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py @@ -72,7 +72,11 @@ def make_dimension_in_accounting_doctypes(doc): if doctype == "Budget": add_dimension_to_budget_doctype(df, doc) else: - create_custom_field(doctype, df) + meta = frappe.get_meta(doctype, cached=False) + fieldnames = [d.fieldname for d in meta.get("fields")] + + if df['fieldname'] not in fieldnames: + create_custom_field(doctype, df) count += 1 diff --git a/erpnext/assets/dashboard_fixtures.py b/erpnext/assets/dashboard_fixtures.py index d1e65e11d19..7f3c1de406a 100644 --- a/erpnext/assets/dashboard_fixtures.py +++ b/erpnext/assets/dashboard_fixtures.py @@ -15,8 +15,8 @@ def get_data(): if not fiscal_year: return frappe._dict() - year_start_date = get_date_str(fiscal_year[1]) - year_end_date = get_date_str(fiscal_year[2]) + year_start_date = get_date_str(fiscal_year.get('year_start_date')) + year_end_date = get_date_str(fiscal_year.get('year_end_date')) return frappe._dict({ "dashboards": get_dashboards(), @@ -59,8 +59,8 @@ def get_charts(fiscal_year, year_start_date, year_end_date): "company": company, "status": "In Location", "filter_based_on": "Fiscal Year", - "from_fiscal_year": fiscal_year[0], - "to_fiscal_year": fiscal_year[0], + "from_fiscal_year": fiscal_year.get('name'), + "to_fiscal_year": fiscal_year.get('name'), "period_start_date": year_start_date, "period_end_date": year_end_date, "date_based_on": "Purchase Date", diff --git a/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py b/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py index bebf0ccec56..c7204a1f341 100644 --- a/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py +++ b/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py @@ -15,7 +15,7 @@ class TestProcurementTracker(unittest.TestCase): def test_result_for_procurement_tracker(self): filters = { 'company': '_Test Procurement Company', - 'cost_center': '_Test Cost Center - _TC' + 'cost_center': 'Main - _TPC' } expected_data = self.generate_expected_data() report = execute(filters) @@ -33,24 +33,27 @@ class TestProcurementTracker(unittest.TestCase): country="Pakistan" )).insert() warehouse = create_warehouse("_Test Procurement Warehouse", company="_Test Procurement Company") - mr = make_material_request(company="_Test Procurement Company", warehouse=warehouse) + mr = make_material_request(company="_Test Procurement Company", warehouse=warehouse, cost_center="Main - _TPC") po = make_purchase_order(mr.name) po.supplier = "_Test Supplier" - po.get("items")[0].cost_center = "_Test Cost Center - _TC" + po.get("items")[0].cost_center = "Main - _TPC" po.submit() pr = make_purchase_receipt(po.name) + pr.get("items")[0].cost_center = "Main - _TPC" pr.submit() frappe.db.commit() date_obj = datetime.date(datetime.now()) + po.load_from_db() + expected_data = { "material_request_date": date_obj, - "cost_center": "_Test Cost Center - _TC", + "cost_center": "Main - _TPC", "project": None, "requesting_site": "_Test Procurement Warehouse - _TPC", "requestor": "Administrator", "material_request_no": mr.name, - "description": '_Test Item 1', + "item_code": '_Test Item', "quantity": 10.0, "unit_of_measurement": "_Test UOM", "status": "To Bill", @@ -58,9 +61,9 @@ class TestProcurementTracker(unittest.TestCase): "purchase_order": po.name, "supplier": "_Test Supplier", "estimated_cost": 0.0, - "actual_cost": None, - "purchase_order_amt": 5000.0, - "purchase_order_amt_in_company_currency": 300000.0, + "actual_cost": 0.0, + "purchase_order_amt": po.net_total, + "purchase_order_amt_in_company_currency": po.base_net_total, "expected_delivery_date": date_obj, "actual_delivery_date": date_obj } diff --git a/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py b/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py index 4c2d3f692a2..2bef5fb5bdd 100644 --- a/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py +++ b/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py @@ -15,6 +15,7 @@ class TestInpatientRecord(unittest.TestCase): patient = create_patient() # Schedule Admission ip_record = create_inpatient(patient) + ip_record.expected_length_of_stay = 0 ip_record.save(ignore_permissions = True) self.assertEqual(ip_record.name, frappe.db.get_value("Patient", patient, "inpatient_record")) self.assertEqual(ip_record.status, frappe.db.get_value("Patient", patient, "inpatient_status")) @@ -26,7 +27,7 @@ class TestInpatientRecord(unittest.TestCase): self.assertEqual("Occupied", frappe.db.get_value("Healthcare Service Unit", service_unit, "occupancy_status")) # Discharge - schedule_discharge(patient=patient) + schedule_discharge(frappe.as_json({'patient': patient})) self.assertEqual("Vacant", frappe.db.get_value("Healthcare Service Unit", service_unit, "occupancy_status")) ip_record1 = frappe.get_doc("Inpatient Record", ip_record.name) @@ -44,8 +45,10 @@ class TestInpatientRecord(unittest.TestCase): patient = create_patient() ip_record = create_inpatient(patient) + ip_record.expected_length_of_stay = 0 ip_record.save(ignore_permissions = True) ip_record_new = create_inpatient(patient) + ip_record_new.expected_length_of_stay = 0 self.assertRaises(frappe.ValidationError, ip_record_new.save) service_unit = get_healthcare_service_unit() diff --git a/erpnext/projects/doctype/task/test_task.py b/erpnext/projects/doctype/task/test_task.py index bd3369447ba..47a28fd1114 100644 --- a/erpnext/projects/doctype/task/test_task.py +++ b/erpnext/projects/doctype/task/test_task.py @@ -64,7 +64,7 @@ class TestTask(unittest.TestCase): def assign(): from frappe.desk.form import assign_to assign_to.add({ - "assign_to": "test@example.com", + "assign_to": ["test@example.com"], "doctype": task.doctype, "name": task.name, "description": "Close this task" From f9c4b209853569df89db57d1456b3d8128f7aa3e Mon Sep 17 00:00:00 2001 From: marination Date: Fri, 12 Jun 2020 15:49:53 +0530 Subject: [PATCH 344/608] fix: Added PO test and conversion factor fix - Don't change conversion factor if stock uom and uom is the same - Added PO test - Added Accounts User basic role in PO - Minor fixes, wrong variables --- .../purchase_order/purchase_order.json | 8 ++++++- .../purchase_order/test_purchase_order.py | 22 ++++++++++++++++--- erpnext/controllers/accounts_controller.py | 15 ++++++++----- 3 files changed, 35 insertions(+), 10 deletions(-) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json index a4f60fbba5c..64dc3ed324b 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.json +++ b/erpnext/buying/doctype/purchase_order/purchase_order.json @@ -1068,7 +1068,7 @@ "idx": 105, "is_submittable": 1, "links": [], - "modified": "2020-04-24 12:13:14.186280", + "modified": "2020-06-12 14:08:11.777120", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order", @@ -1112,6 +1112,12 @@ "read": 1, "role": "Purchase Manager", "write": 1 + }, + { + "email": 1, + "print": 1, + "read": 1, + "role": "Accounts User" } ], "search_fields": "status, transaction_date, supplier,grand_total", diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py index 1712369e60b..3d6cba891a6 100644 --- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py @@ -118,7 +118,7 @@ class TestPurchaseOrder(unittest.TestCase): self.assertEqual(po.get("items")[0].amount, 1400) self.assertEqual(get_ordered_qty(), existing_ordered_qty + 3) - + def test_add_new_item_in_update_child_qty_rate(self): po = create_purchase_order(do_not_save=1) po.items[0].qty = 4 @@ -144,7 +144,7 @@ class TestPurchaseOrder(unittest.TestCase): self.assertEquals(len(po.get('items')), 2) self.assertEqual(po.status, 'To Receive and Bill') - + def test_remove_item_in_update_child_qty_rate(self): po = create_purchase_order(do_not_save=1) po.items[0].qty = 4 @@ -185,6 +185,22 @@ class TestPurchaseOrder(unittest.TestCase): self.assertEquals(len(po.get('items')), 1) self.assertEqual(po.status, 'To Receive and Bill') + def test_update_child_qty_rate_perm(self): + po = create_purchase_order(item_code= "_Test Item", qty=4) + + user = 'test@example.com' + test_user = frappe.get_doc('User', user) + test_user.add_roles("Accounts User") + frappe.set_user(user) + + # update qty + trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 200, 'qty' : 7, 'docname': po.items[0].name}]) + self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Purchase Order', trans_item, po.name) + + # add new item + trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 100, 'qty' : 2}]) + self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Purchase Order', trans_item, po.name) + def test_update_qty(self): po = create_purchase_order() @@ -689,7 +705,7 @@ class TestPurchaseOrder(unittest.TestCase): po.save() self.assertEqual(po.schedule_date, add_days(nowdate(), 2)) - + def test_po_optional_blanket_order(self): """ Expected result: Blanket order Ordered Quantity should only be affected on Purchase Order with against_blanket_order = 1. diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 43a27333236..c75aff9264e 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1137,7 +1137,7 @@ def set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, child_item.item_name = item.item_name child_item.description = item.description child_item.delivery_date = trans_item.get('delivery_date') or p_doc.delivery_date - child_item.conversion_factor = flt(d.get('conversion_factor')) or get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0 + child_item.conversion_factor = flt(trans_item.get('conversion_factor')) or get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0 child_item.uom = item.stock_uom child_item.warehouse = get_item_warehouse(item, p_doc, overwrite_warehouse=True) if not child_item.warehouse: @@ -1157,7 +1157,7 @@ def set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docna child_item.item_name = item.item_name child_item.description = item.description child_item.schedule_date = trans_item.get('schedule_date') or p_doc.schedule_date - child_item.conversion_factor = flt(d.get('conversion_factor')) or get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0 + child_item.conversion_factor = flt(trans_item.get('conversion_factor')) or get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0 child_item.uom = item.stock_uom child_item.base_rate = 1 # Initiallize value will update in parent validation child_item.base_amount = 1 # Initiallize value will update in parent validation @@ -1237,7 +1237,7 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil prev_date, new_date = child_item.get("schedule_date") == d.get("schedule_date") rate_unchanged = prev_rate == new_rate - qty_unchanged = prev_qty == prev_qty + qty_unchanged = prev_qty == new_qty conversion_factor_unchanged = prev_con_fac == new_con_fac date_unchanged = prev_date == new_date if prev_date and new_date else False # in case of delivery note etc if rate_unchanged and qty_unchanged and conversion_factor_unchanged and date_unchanged: @@ -1253,13 +1253,16 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil .format(child_item.idx, child_item.item_code)) else: child_item.rate = flt(d.get("rate")) - + if d.get("conversion_factor"): - child_item.conversion_factor = flt(d.get('conversion_factor')) + if child_item.stock_uom == child_item.uom: + child_item.conversion_factor = 1 + else: + child_item.conversion_factor = flt(d.get('conversion_factor')) if d.get("delivery_date") and parent_doctype == 'Sales Order': child_item.delivery_date = d.get('delivery_date') - + if d.get("schedule_date") and parent_doctype == 'Purchase Order': child_item.schedule_date = d.get('schedule_date') From ec54653852a954381838c4616cb52283d0e9b82e Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Fri, 12 Jun 2020 17:02:31 +0530 Subject: [PATCH 345/608] fix: Child is shown in Parent process if added from tree view --- .../doctype/quality_procedure/quality_procedure.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py index d29710dd8e3..44405c15077 100644 --- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py +++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py @@ -10,7 +10,7 @@ from frappe import _ class QualityProcedure(NestedSet): nsm_parent_field = 'parent_quality_procedure' - def before_save(self): + def on_save(self): for process in self.processes: if process.procedure: doc = frappe.get_doc("Quality Procedure", process.procedure) @@ -23,6 +23,11 @@ class QualityProcedure(NestedSet): def after_insert(self): self.set_parent() + #if Child is Added through Tree View. + if self.parent_quality_procedure: + parent_quality_procedure = frappe.get_doc("Quality Procedure", self.parent_quality_procedure) + parent_quality_procedure.append("processes", {"procedure": self.name}) + parent_quality_procedure.save() def on_trash(self): if self.parent_quality_procedure: From 677978dd00e92533d7897dec041a901c0e3cbba9 Mon Sep 17 00:00:00 2001 From: John Clarke Date: Fri, 12 Jun 2020 05:53:24 -0600 Subject: [PATCH 346/608] Update stock_ageing.py As reviewer Deepesh Garg advised thanks! batch[0] can be int of float - see line 49 - so change proposed cint to flt --- erpnext/stock/report/stock_ageing/stock_ageing.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.py b/erpnext/stock/report/stock_ageing/stock_ageing.py index 53bdfb523d2..eea2312a245 100644 --- a/erpnext/stock/report/stock_ageing/stock_ageing.py +++ b/erpnext/stock/report/stock_ageing/stock_ageing.py @@ -180,14 +180,14 @@ def get_fifo_queue(filters, sle=None): qty_to_pop = abs(d.actual_qty) while qty_to_pop: batch = fifo_queue[0] if fifo_queue else [0, None] - if 0 < cint(batch[0]) <= qty_to_pop: + if 0 < flt(batch[0]) <= qty_to_pop: # if batch qty > 0 # not enough or exactly same qty in current batch, clear batch - qty_to_pop -= cint(batch[0]) + qty_to_pop -= flt(batch[0]) transferred_item_details[(d.voucher_no, d.name)].append(fifo_queue.pop(0)) else: # all from current batch - cint(batch[0]) -= qty_to_pop + flt(batch[0]) -= qty_to_pop transferred_item_details[(d.voucher_no, d.name)].append([qty_to_pop, batch[1]]) qty_to_pop = 0 From 7558e64d6dd2b1e34f8ff9c976e479f39c0f69ee Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Fri, 12 Jun 2020 17:37:59 +0530 Subject: [PATCH 347/608] fix: set_query in leave application (#22197) --- .../hr/doctype/leave_application/leave_application.js | 11 +++++++++-- .../hr/doctype/leave_application/leave_application.py | 8 ++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/erpnext/hr/doctype/leave_application/leave_application.js b/erpnext/hr/doctype/leave_application/leave_application.js index 15ce468c138..fb1f2c00b13 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.js +++ b/erpnext/hr/doctype/leave_application/leave_application.js @@ -46,6 +46,7 @@ frappe.ui.form.on("Leave Application", { make_dashboard: function(frm) { var leave_details; + let lwps; if (frm.doc.employee) { frappe.call({ method: "erpnext.hr.doctype.leave_application.leave_application.get_leave_details", @@ -61,6 +62,7 @@ frappe.ui.form.on("Leave Application", { if (!r.exc && r.message['leave_approver']) { frm.set_value('leave_approver', r.message['leave_approver']); } + lwps = r.message["lwps"]; } }); $("div").remove(".form-dashboard-section.custom"); @@ -70,12 +72,17 @@ frappe.ui.form.on("Leave Application", { }) ); frm.dashboard.show(); + let allowed_leave_types = Object.keys(leave_details); + + // lwps should be allowed, lwps don't have any allocation + allowed_leave_types = allowed_leave_types.concat(lwps); + frm.set_query('leave_type', function(){ return { filters : [ - ['leave_type_name', 'in', Object.keys(leave_details)] + ['leave_type_name', 'in', allowed_leave_types] ] - } + }; }); } }, diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index c2b4cdf1058..0423824c0e9 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -19,7 +19,6 @@ class NotAnOptionalHoliday(frappe.ValidationError): pass from frappe.model.document import Document class LeaveApplication(Document): - def get_feed(self): return _("{0}: From {0} of type {1}").format(self.employee_name, self.leave_type) @@ -463,9 +462,14 @@ def get_leave_details(employee, date): "pending_leaves": leaves_pending, "remaining_leaves": remaining_leaves} + #is used in set query + lwps = frappe.get_list("Leave Type", filters = {"is_lwp": 1}) + lwps = [lwp.name for lwp in lwps] + ret = { 'leave_allocation': leave_allocation, - 'leave_approver': get_leave_approver(employee) + 'leave_approver': get_leave_approver(employee), + 'lwps': lwps } return ret From c258ac82c58f5937a0f5fcd564ef624f5ec6227a Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 12 Jun 2020 18:33:42 +0530 Subject: [PATCH 348/608] feat: Accounting entries for service item in Purchase receipt --- erpnext/accounts/doctype/account/account.json | 82 +++- .../purchase_invoice/purchase_invoice.py | 14 +- erpnext/setup/doctype/company/company.js | 5 +- erpnext/setup/doctype/company/company.json | 373 +++++++++++++----- .../purchase_receipt/purchase_receipt.py | 27 ++ .../stock_settings/stock_settings.json | 131 ++++-- 6 files changed, 490 insertions(+), 142 deletions(-) diff --git a/erpnext/accounts/doctype/account/account.json b/erpnext/accounts/doctype/account/account.json index af252e61911..d2659d429b0 100644 --- a/erpnext/accounts/doctype/account/account.json +++ b/erpnext/accounts/doctype/account/account.json @@ -34,11 +34,15 @@ { "fieldname": "properties", "fieldtype": "Section Break", - "oldfieldtype": "Section Break" + "oldfieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break0", "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -49,7 +53,9 @@ "no_copy": 1, "oldfieldname": "account_name", "oldfieldtype": "Data", - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "account_number", @@ -57,13 +63,17 @@ "in_list_view": 1, "in_standard_filter": 1, "label": "Account Number", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "is_group", "fieldtype": "Check", - "label": "Is Group" + "label": "Is Group", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "company", @@ -75,7 +85,9 @@ "options": "Company", "read_only": 1, "remember_last_selected_value": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "root_type", @@ -83,7 +95,9 @@ "in_standard_filter": 1, "label": "Root Type", "options": "\nAsset\nLiability\nIncome\nExpense\nEquity", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "report_type", @@ -91,24 +105,32 @@ "in_standard_filter": 1, "label": "Report Type", "options": "\nBalance Sheet\nProfit and Loss", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.is_group==0", "fieldname": "account_currency", "fieldtype": "Link", "label": "Currency", - "options": "Currency" + "options": "Currency", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "inter_company_account", "fieldtype": "Check", - "label": "Inter Company Account" + "label": "Inter Company Account", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break1", "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -120,7 +142,9 @@ "oldfieldtype": "Link", "options": "Account", "reqd": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "description": "Setting Account Type helps in selecting this Account in transactions.", @@ -130,7 +154,9 @@ "label": "Account Type", "oldfieldname": "account_type", "oldfieldtype": "Select", - "options": "\nAccumulated Depreciation\nAsset Received But Not Billed\nBank\nCash\nChargeable\nCapital Work in Progress\nCost of Goods Sold\nDepreciation\nEquity\nExpense Account\nExpenses Included In Asset Valuation\nExpenses Included In Valuation\nFixed Asset\nIncome Account\nPayable\nReceivable\nRound Off\nStock\nStock Adjustment\nStock Received But Not Billed\nTax\nTemporary" + "options": "\nAccumulated Depreciation\nAsset Received But Not Billed\nBank\nCash\nChargeable\nCapital Work in Progress\nCost of Goods Sold\nDepreciation\nEquity\nExpense Account\nExpenses Included In Asset Valuation\nExpenses Included In Valuation\nFixed Asset\nIncome Account\nPayable\nReceivable\nRound Off\nStock\nStock Adjustment\nStock Received But Not Billed\nService Received But Not Billed\nTax\nTemporary", + "show_days": 1, + "show_seconds": 1 }, { "description": "Rate at which this tax is applied", @@ -138,7 +164,9 @@ "fieldtype": "Float", "label": "Rate", "oldfieldname": "tax_rate", - "oldfieldtype": "Currency" + "oldfieldtype": "Currency", + "show_days": 1, + "show_seconds": 1 }, { "description": "If the account is frozen, entries are allowed to restricted users.", @@ -147,13 +175,17 @@ "label": "Frozen", "oldfieldname": "freeze_account", "oldfieldtype": "Select", - "options": "No\nYes" + "options": "No\nYes", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "balance_must_be", "fieldtype": "Select", "label": "Balance must be", - "options": "\nDebit\nCredit" + "options": "\nDebit\nCredit", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "lft", @@ -162,7 +194,9 @@ "label": "Lft", "print_hide": 1, "read_only": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "rgt", @@ -171,7 +205,9 @@ "label": "Rgt", "print_hide": 1, "read_only": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "old_parent", @@ -179,27 +215,33 @@ "hidden": 1, "label": "Old Parent", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "depends_on": "eval:(doc.report_type == 'Profit and Loss' && !doc.is_group)", "fieldname": "include_in_gross", "fieldtype": "Check", - "label": "Include in gross" + "label": "Include in gross", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "disabled", "fieldtype": "Check", - "label": "Disable" + "label": "Disable", + "show_days": 1, + "show_seconds": 1 } ], "icon": "fa fa-money", "idx": 1, "is_tree": 1, "links": [], - "modified": "2020-03-18 17:57:52.063233", + "modified": "2020-06-11 15:15:54.338622", "modified_by": "Administrator", "module": "Accounts", "name": "Account", diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index aa1d5b526c8..30c661a9721 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -570,6 +570,18 @@ class PurchaseInvoice(BuyingController): else: amount = flt(item.base_net_amount + item.item_tax_amount, item.precision("base_net_amount")) + auto_accounting_for_non_stock_items = cint(frappe.db.get_single_value('Stock Settings', 'enable_perpetual_inventory_for_non_stock_items')) + service_received_but_not_billed_account = self.get_company_default("service_received_but_not_billed") + + if item.purchase_receipt and auto_accounting_for_non_stock_items: + # Post reverse entry for Stock-Received-But-Not-Billed if it is booked in Purchase Receipt + expense_booked_in_pr = frappe.db.get_value('GL Entry', {'is_cancelled': 0, + 'voucher_type': 'Purchase Receipt', 'voucher_no': item.purchase_receipt, 'voucher_detail_no': item.pr_detail, + 'account':service_received_but_not_billed_account}, ['name']) + + if expense_booked_in_pr: + expense_account = service_received_but_not_billed_account + gl_entries.append(self.get_gl_dict({ "account": expense_account, "against": self.supplier, @@ -1020,7 +1032,7 @@ class PurchaseInvoice(BuyingController): # calculate totals again after applying TDS self.calculate_taxes_and_totals() - + def set_status(self, update=False, status=None, update_modified=True): if self.is_new(): if self.get('amended_from'): diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js index 875904fe6fe..7ae5385a23c 100644 --- a/erpnext/setup/doctype/company/company.js +++ b/erpnext/setup/doctype/company/company.js @@ -264,7 +264,10 @@ erpnext.company.setup_queries = function(frm) { ["expenses_included_in_valuation", {"root_type": "Expense", "account_type": "Expenses Included in Valuation"}], ["stock_received_but_not_billed", - {"root_type": "Liability", "account_type": "Stock Received But Not Billed"}] + {"root_type": "Liability", "account_type": "Stock Received But Not Billed"}], + ["service_received_but_not_billed", + {"root_type": "Liability", "account_type": "Service Received But Not Billed"}], + ], function(i, v) { erpnext.company.set_custom_query(frm, v); }); diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json index 020a93ff6ac..0b91b700347 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -71,6 +71,7 @@ "stock_adjustment_account", "column_break_32", "stock_received_but_not_billed", + "service_received_but_not_billed", "expenses_included_in_valuation", "fixed_asset_depreciation_settings", "accumulated_depreciation_account", @@ -106,7 +107,9 @@ { "fieldname": "details", "fieldtype": "Section Break", - "oldfieldtype": "Section Break" + "oldfieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "company_name", @@ -115,6 +118,8 @@ "oldfieldname": "company_name", "oldfieldtype": "Data", "reqd": 1, + "show_days": 1, + "show_seconds": 1, "unique": 1 }, { @@ -123,36 +128,48 @@ "label": "Abbr", "oldfieldname": "abbr", "oldfieldtype": "Data", - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:!doc.__islocal && in_list(frappe.user_roles, \"System Manager\")", "fieldname": "change_abbr", "fieldtype": "Button", - "label": "Change Abbreviation" + "label": "Change Abbreviation", + "show_days": 1, + "show_seconds": 1 }, { "bold": 1, "default": "0", "fieldname": "is_group", "fieldtype": "Check", - "label": "Is Group" + "label": "Is Group", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "default_finance_book", "fieldtype": "Link", "label": "Default Finance Book", - "options": "Finance Book" + "options": "Finance Book", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "cb0", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "domain", "fieldtype": "Link", "label": "Domain", - "options": "Domain" + "options": "Domain", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "parent_company", @@ -160,24 +177,32 @@ "ignore_user_permissions": 1, "in_list_view": 1, "label": "Parent Company", - "options": "Company" + "options": "Company", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "company_logo", "fieldtype": "Attach Image", "hidden": 1, - "label": "Company Logo" + "label": "Company Logo", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "company_description", "fieldtype": "Text Editor", - "label": "Company Description" + "label": "Company Description", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "sales_settings", "fieldtype": "Section Break", - "label": "Sales Settings" + "label": "Sales Settings", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "sales_monthly_history", @@ -185,7 +210,9 @@ "hidden": 1, "label": "Sales Monthly History", "no_copy": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "transactions_annual_history", @@ -193,17 +220,23 @@ "hidden": 1, "label": "Transactions Annual History", "no_copy": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "monthly_sales_target", "fieldtype": "Currency", "label": "Monthly Sales Target", - "options": "default_currency" + "options": "default_currency", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_goals", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_monthly_sales", @@ -211,12 +244,16 @@ "label": "Total Monthly Sales", "no_copy": 1, "options": "default_currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "charts_section", "fieldtype": "Section Break", - "label": "Default Values" + "label": "Default Values", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "default_currency", @@ -224,34 +261,46 @@ "ignore_user_permissions": 1, "label": "Default Currency", "options": "Currency", - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "default_letter_head", "fieldtype": "Link", "label": "Default Letter Head", - "options": "Letter Head" + "options": "Letter Head", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "default_holiday_list", "fieldtype": "Link", "label": "Default Holiday List", - "options": "Holiday List" + "options": "Holiday List", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "standard_working_hours", "fieldtype": "Float", - "label": "Standard Working Hours" + "label": "Standard Working Hours", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "default_warehouse_for_sales_return", "fieldtype": "Link", "label": "Default warehouse for Sales Return", - "options": "Warehouse" + "options": "Warehouse", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_10", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "country", @@ -259,20 +308,26 @@ "in_list_view": 1, "label": "Country", "options": "Country", - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "create_chart_of_accounts_based_on", "fieldtype": "Select", "label": "Create Chart Of Accounts Based On", - "options": "\nStandard Template\nExisting Company" + "options": "\nStandard Template\nExisting Company", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.create_chart_of_accounts_based_on===\"Standard Template\"", "fieldname": "chart_of_accounts", "fieldtype": "Select", "label": "Chart Of Accounts Template", - "no_copy": 1 + "no_copy": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.create_chart_of_accounts_based_on===\"Existing Company\"", @@ -281,23 +336,31 @@ "ignore_user_permissions": 1, "label": "Existing Company ", "no_copy": 1, - "options": "Company" + "options": "Company", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "tax_id", "fieldtype": "Data", - "label": "Tax ID" + "label": "Tax ID", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "date_of_establishment", "fieldtype": "Date", - "label": "Date of Establishment" + "label": "Date of Establishment", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "default_settings", "fieldtype": "Section Break", "label": "Accounts Settings", - "oldfieldtype": "Section Break" + "oldfieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:!doc.__islocal", @@ -308,7 +371,9 @@ "no_copy": 1, "oldfieldname": "default_bank_account", "oldfieldtype": "Link", - "options": "Account" + "options": "Account", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:!doc.__islocal", @@ -317,7 +382,9 @@ "ignore_user_permissions": 1, "label": "Default Cash Account", "no_copy": 1, - "options": "Account" + "options": "Account", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:!doc.__islocal", @@ -328,54 +395,72 @@ "no_copy": 1, "oldfieldname": "receivables_group", "oldfieldtype": "Link", - "options": "Account" + "options": "Account", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "round_off_account", "fieldtype": "Link", "label": "Round Off Account", - "options": "Account" + "options": "Account", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "round_off_cost_center", "fieldtype": "Link", "label": "Round Off Cost Center", - "options": "Cost Center" + "options": "Cost Center", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "write_off_account", "fieldtype": "Link", "label": "Write Off Account", - "options": "Account" + "options": "Account", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "discount_allowed_account", "fieldtype": "Link", "label": "Discount Allowed Account", - "options": "Account" + "options": "Account", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "discount_received_account", "fieldtype": "Link", "label": "Discount Received Account", - "options": "Account" + "options": "Account", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "exchange_gain_loss_account", "fieldtype": "Link", "label": "Exchange Gain / Loss Account", - "options": "Account" + "options": "Account", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "unrealized_exchange_gain_loss_account", "fieldtype": "Link", "label": "Unrealized Exchange Gain/Loss Account", - "options": "Account" + "options": "Account", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break0", "fieldtype": "Column Break", "oldfieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -383,7 +468,9 @@ "depends_on": "eval:doc.parent_company", "fieldname": "allow_account_creation_against_child_company", "fieldtype": "Check", - "label": "Allow Account Creation Against Child Company" + "label": "Allow Account Creation Against Child Company", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:!doc.__islocal", @@ -394,14 +481,18 @@ "no_copy": 1, "oldfieldname": "payables_group", "oldfieldtype": "Link", - "options": "Account" + "options": "Account", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "default_employee_advance_account", "fieldtype": "Link", "label": "Default Employee Advance Account", "no_copy": 1, - "options": "Account" + "options": "Account", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:!doc.__islocal", @@ -410,7 +501,9 @@ "ignore_user_permissions": 1, "label": "Default Cost of Goods Sold Account", "no_copy": 1, - "options": "Account" + "options": "Account", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:!doc.__islocal", @@ -419,7 +512,9 @@ "ignore_user_permissions": 1, "label": "Default Income Account", "no_copy": 1, - "options": "Account" + "options": "Account", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:!doc.__islocal", @@ -428,7 +523,9 @@ "ignore_user_permissions": 1, "label": "Default Deferred Revenue Account", "no_copy": 1, - "options": "Account" + "options": "Account", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:!doc.__islocal", @@ -437,7 +534,9 @@ "ignore_user_permissions": 1, "label": "Default Deferred Expense Account", "no_copy": 1, - "options": "Account" + "options": "Account", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:!doc.__islocal", @@ -446,7 +545,9 @@ "ignore_user_permissions": 1, "label": "Default Payroll Payable Account", "no_copy": 1, - "options": "Account" + "options": "Account", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:!doc.__islocal", @@ -455,11 +556,15 @@ "ignore_user_permissions": 1, "label": "Default Expense Claim Payable Account", "no_copy": 1, - "options": "Account" + "options": "Account", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "section_break_22", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:!doc.__islocal", @@ -468,11 +573,15 @@ "ignore_user_permissions": 1, "label": "Default Cost Center", "no_copy": 1, - "options": "Cost Center" + "options": "Cost Center", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_26", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:!doc.__islocal", @@ -481,31 +590,41 @@ "label": "Credit Limit", "oldfieldname": "credit_limit", "oldfieldtype": "Currency", - "options": "default_currency" + "options": "default_currency", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "payment_terms", "fieldtype": "Link", "label": "Default Payment Terms Template", - "options": "Payment Terms Template" + "options": "Payment Terms Template", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:!doc.__islocal", "fieldname": "auto_accounting_for_stock_settings", "fieldtype": "Section Break", - "label": "Stock Settings" + "label": "Stock Settings", + "show_days": 1, + "show_seconds": 1 }, { "default": "1", "fieldname": "enable_perpetual_inventory", "fieldtype": "Check", - "label": "Enable Perpetual Inventory" + "label": "Enable Perpetual Inventory", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "default_inventory_account", "fieldtype": "Link", "label": "Default Inventory Account", - "options": "Account" + "options": "Account", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "stock_adjustment_account", @@ -513,11 +632,15 @@ "ignore_user_permissions": 1, "label": "Stock Adjustment Account", "no_copy": 1, - "options": "Account" + "options": "Account", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_32", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "stock_received_but_not_billed", @@ -525,7 +648,9 @@ "ignore_user_permissions": 1, "label": "Stock Received But Not Billed", "no_copy": 1, - "options": "Account" + "options": "Account", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "expenses_included_in_valuation", @@ -533,108 +658,144 @@ "ignore_user_permissions": 1, "label": "Expenses Included In Valuation", "no_copy": 1, - "options": "Account" + "options": "Account", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "fixed_asset_depreciation_settings", "fieldtype": "Section Break", - "label": "Fixed Asset Depreciation Settings" + "label": "Fixed Asset Depreciation Settings", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "accumulated_depreciation_account", "fieldtype": "Link", "label": "Accumulated Depreciation Account", "no_copy": 1, - "options": "Account" + "options": "Account", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "depreciation_expense_account", "fieldtype": "Link", "label": "Depreciation Expense Account", "no_copy": 1, - "options": "Account" + "options": "Account", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "series_for_depreciation_entry", "fieldtype": "Data", - "label": "Series for Asset Depreciation Entry (Journal Entry)" + "label": "Series for Asset Depreciation Entry (Journal Entry)", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "expenses_included_in_asset_valuation", "fieldtype": "Link", "label": "Expenses Included In Asset Valuation", - "options": "Account" + "options": "Account", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_40", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "disposal_account", "fieldtype": "Link", "label": "Gain/Loss Account on Asset Disposal", "no_copy": 1, - "options": "Account" + "options": "Account", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "depreciation_cost_center", "fieldtype": "Link", "label": "Asset Depreciation Cost Center", "no_copy": 1, - "options": "Cost Center" + "options": "Cost Center", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "capital_work_in_progress_account", "fieldtype": "Link", "label": "Capital Work In Progress Account", - "options": "Account" + "options": "Account", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "asset_received_but_not_billed", "fieldtype": "Link", "label": "Asset Received But Not Billed", - "options": "Account" + "options": "Account", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "budget_detail", "fieldtype": "Section Break", - "label": "Budget Detail" + "label": "Budget Detail", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "exception_budget_approver_role", "fieldtype": "Link", "label": "Exception Budget Approver Role", - "options": "Role" + "options": "Role", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "description": "For reference only.", "fieldname": "company_info", "fieldtype": "Section Break", - "label": "Company Info" + "label": "Company Info", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "date_of_incorporation", "fieldtype": "Date", - "label": "Date of Incorporation" + "label": "Date of Incorporation", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "address_html", - "fieldtype": "HTML" + "fieldtype": "HTML", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break1", "fieldtype": "Column Break", "oldfieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { "depends_on": "eval:doc.date_of_incorporation", "fieldname": "date_of_commencement", "fieldtype": "Date", - "label": "Date of Commencement" + "label": "Date of Commencement", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "phone_no", @@ -642,7 +803,9 @@ "label": "Phone No", "oldfieldname": "phone_no", "oldfieldtype": "Data", - "options": "Phone" + "options": "Phone", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "fax", @@ -650,7 +813,9 @@ "label": "Fax", "oldfieldname": "fax", "oldfieldtype": "Data", - "options": "Phone" + "options": "Phone", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "email", @@ -658,19 +823,25 @@ "label": "Email", "oldfieldname": "email", "oldfieldtype": "Data", - "options": "Email" + "options": "Email", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "website", "fieldtype": "Data", "label": "Website", "oldfieldname": "website", - "oldfieldtype": "Data" + "oldfieldtype": "Data", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "registration_info", "fieldtype": "Section Break", "oldfieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -679,12 +850,16 @@ "fieldtype": "Code", "label": "Registration Details", "oldfieldname": "registration_details", - "oldfieldtype": "Code" + "oldfieldtype": "Code", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "delete_company_transactions", "fieldtype": "Button", - "label": "Delete Company Transactions" + "label": "Delete Company Transactions", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "lft", @@ -693,7 +868,9 @@ "label": "Lft", "print_hide": 1, "read_only": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "rgt", @@ -702,7 +879,9 @@ "label": "Rgt", "print_hide": 1, "read_only": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "old_parent", @@ -710,19 +889,35 @@ "hidden": 1, "label": "old_parent", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "default_selling_terms", "fieldtype": "Link", "label": "Default Selling Terms", - "options": "Terms and Conditions" + "options": "Terms and Conditions", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "default_buying_terms", "fieldtype": "Link", "label": "Default Buying Terms", - "options": "Terms and Conditions" + "options": "Terms and Conditions", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "service_received_but_not_billed", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Service Received But Not Billed", + "no_copy": 1, + "options": "Account", + "show_days": 1, + "show_seconds": 1 } ], "icon": "fa fa-building", @@ -730,7 +925,7 @@ "image_field": "company_logo", "is_tree": 1, "links": [], - "modified": "2020-03-21 18:09:53.534211", + "modified": "2020-06-11 15:32:59.147735", "modified_by": "Administrator", "module": "Setup", "name": "Company", diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index e6ab8d634d8..94a6edd2c1c 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -211,6 +211,8 @@ class PurchaseReceipt(BuyingController): stock_rbnb = self.get_company_default("stock_received_but_not_billed") landed_cost_entries = get_item_account_wise_additional_cost(self.name) expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation") + service_received_but_not_billed_account = self.get_company_default("service_received_but_not_billed") + auto_accounting_for_non_stock_items = cint(frappe.db.get_single_value('Stock Settings', 'enable_perpetual_inventory_for_non_stock_items')) gl_entries = [] warehouse_with_no_account = [] @@ -301,6 +303,31 @@ class PurchaseReceipt(BuyingController): elif d.warehouse not in warehouse_with_no_account or \ d.rejected_warehouse not in warehouse_with_no_account: warehouse_with_no_account.append(d.warehouse) + elif d.item_code not in stock_items and flt(d.qty) and auto_accounting_for_non_stock_items: + + credit_currency = get_account_currency(service_received_but_not_billed_account) + + gl_entries.append(self.get_gl_dict({ + "account": service_received_but_not_billed_account, + "against": d.expense_account, + "cost_center": d.cost_center, + "remarks": self.get("remarks") or _("Accounting Entry for Service"), + "project": d.project, + "credit": d.amount, + "voucher_detail_no": d.name + }, credit_currency, item=d)) + + debit_currency = get_account_currency(d.expense_account) + + gl_entries.append(self.get_gl_dict({ + "account": d.expense_account, + "against": service_received_but_not_billed_account, + "cost_center": d.cost_center, + "remarks": self.get("remarks") or _("Accounting Entry for Service"), + "project": d.project, + "debit": d.amount, + "voucher_detail_no": d.name + }, debit_currency, item=d)) self.get_asset_gl_entry(gl_entries) # Cost center-wise amount breakup for other charges included for valuation diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.json b/erpnext/stock/doctype/stock_settings/stock_settings.json index c9a3527e919..a7ed620dcaf 100644 --- a/erpnext/stock/doctype/stock_settings/stock_settings.json +++ b/erpnext/stock/doctype/stock_settings/stock_settings.json @@ -16,6 +16,7 @@ "action_if_quality_inspection_is_not_submitted", "show_barcode_field", "clean_description_html", + "enable_perpetual_inventory_for_non_stock_items", "section_break_7", "auto_insert_price_list_rate_if_missing", "allow_negative_stock", @@ -43,180 +44,248 @@ "fieldtype": "Select", "in_list_view": 1, "label": "Item Naming By", - "options": "Item Code\nNaming Series" + "options": "Item Code\nNaming Series", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "item_group", "fieldtype": "Link", "in_list_view": 1, "label": "Default Item Group", - "options": "Item Group" + "options": "Item Group", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "stock_uom", "fieldtype": "Link", "in_list_view": 1, "label": "Default Stock UOM", - "options": "UOM" + "options": "UOM", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "default_warehouse", "fieldtype": "Link", "label": "Default Warehouse", - "options": "Warehouse" + "options": "Warehouse", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "sample_retention_warehouse", "fieldtype": "Link", "label": "Sample Retention Warehouse", - "options": "Warehouse" + "options": "Warehouse", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_4", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "valuation_method", "fieldtype": "Select", "label": "Default Valuation Method", - "options": "FIFO\nMoving Average" + "options": "FIFO\nMoving Average", + "show_days": 1, + "show_seconds": 1 }, { "description": "Percentage you are allowed to receive or deliver more against the quantity ordered. For example: If you have ordered 100 units. and your Allowance is 10% then you are allowed to receive 110 units.", "fieldname": "over_delivery_receipt_allowance", "fieldtype": "Float", - "label": "Over Delivery/Receipt Allowance (%)" + "label": "Over Delivery/Receipt Allowance (%)", + "show_days": 1, + "show_seconds": 1 }, { "default": "Stop", "fieldname": "action_if_quality_inspection_is_not_submitted", "fieldtype": "Select", "label": "Action if Quality inspection is not submitted", - "options": "Stop\nWarn" + "options": "Stop\nWarn", + "show_days": 1, + "show_seconds": 1 }, { "default": "1", "fieldname": "show_barcode_field", "fieldtype": "Check", - "label": "Show Barcode Field" + "label": "Show Barcode Field", + "show_days": 1, + "show_seconds": 1 }, { "default": "1", "fieldname": "clean_description_html", "fieldtype": "Check", - "label": "Convert Item Description to Clean HTML" + "label": "Convert Item Description to Clean HTML", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "section_break_7", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "auto_insert_price_list_rate_if_missing", "fieldtype": "Check", - "label": "Auto insert Price List rate if missing" + "label": "Auto insert Price List rate if missing", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "allow_negative_stock", "fieldtype": "Check", - "label": "Allow Negative Stock" + "label": "Allow Negative Stock", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_10", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "default": "1", "fieldname": "automatically_set_serial_nos_based_on_fifo", "fieldtype": "Check", - "label": "Automatically Set Serial Nos based on FIFO" + "label": "Automatically Set Serial Nos based on FIFO", + "show_days": 1, + "show_seconds": 1 }, { "default": "1", "fieldname": "set_qty_in_transactions_based_on_serial_no_input", "fieldtype": "Check", - "label": "Set Qty in Transactions based on Serial No Input" + "label": "Set Qty in Transactions based on Serial No Input", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "auto_material_request", "fieldtype": "Section Break", - "label": "Auto Material Request" + "label": "Auto Material Request", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "auto_indent", "fieldtype": "Check", - "label": "Raise Material Request when stock reaches re-order level" + "label": "Raise Material Request when stock reaches re-order level", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "reorder_email_notify", "fieldtype": "Check", - "label": "Notify by Email on creation of automatic Material Request" + "label": "Notify by Email on creation of automatic Material Request", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "freeze_stock_entries", "fieldtype": "Section Break", - "label": "Freeze Stock Entries" + "label": "Freeze Stock Entries", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "stock_frozen_upto", "fieldtype": "Date", - "label": "Stock Frozen Upto" + "label": "Stock Frozen Upto", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "stock_frozen_upto_days", "fieldtype": "Int", - "label": "Freeze Stocks Older Than [Days]" + "label": "Freeze Stocks Older Than [Days]", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "stock_auth_role", "fieldtype": "Link", "label": "Role Allowed to edit frozen stock", - "options": "Role" + "options": "Role", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "batch_id_sb", "fieldtype": "Section Break", - "label": "Batch Identification" + "label": "Batch Identification", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "use_naming_series", "fieldtype": "Check", - "label": "Use Naming Series" + "label": "Use Naming Series", + "show_days": 1, + "show_seconds": 1 }, { "default": "BATCH-", "depends_on": "eval:doc.use_naming_series==1", "fieldname": "naming_series_prefix", "fieldtype": "Data", - "label": "Naming Series Prefix" + "label": "Naming Series Prefix", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "inter_warehouse_transfer_settings_section", "fieldtype": "Section Break", - "label": "Inter Warehouse Transfer Settings" + "label": "Inter Warehouse Transfer Settings", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "allow_from_dn", "fieldtype": "Check", - "label": "Allow Material Transfer From Delivery Note and Sales Invoice" + "label": "Allow Material Transfer From Delivery Note and Sales Invoice", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "allow_from_pr", "fieldtype": "Check", - "label": "Allow Material Transfer From Purchase Receipt and Purchase Invoice" + "label": "Allow Material Transfer From Purchase Receipt and Purchase Invoice", + "show_days": 1, + "show_seconds": 1 + }, + { + "default": "0", + "fieldname": "enable_perpetual_inventory_for_non_stock_items", + "fieldtype": "Check", + "label": "Enable Perpetual Inventory For Non Stock Items", + "show_days": 1, + "show_seconds": 1 } ], "icon": "icon-cog", "idx": 1, "issingle": 1, "links": [], - "modified": "2020-04-01 18:11:25.417678", + "modified": "2020-06-11 15:10:41.211638", "modified_by": "Administrator", "module": "Stock", "name": "Stock Settings", From 85f237257a57cc6e92d04c57515e69bab52661e8 Mon Sep 17 00:00:00 2001 From: John Clarke Date: Fri, 12 Jun 2020 07:22:14 -0600 Subject: [PATCH 349/608] Thanks so much for your pointer Marica - that violation is clear and obvious to me in retrospect, but a good relearning experience in any case --- erpnext/stock/report/stock_ageing/stock_ageing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.py b/erpnext/stock/report/stock_ageing/stock_ageing.py index eea2312a245..723ed5c1c46 100644 --- a/erpnext/stock/report/stock_ageing/stock_ageing.py +++ b/erpnext/stock/report/stock_ageing/stock_ageing.py @@ -187,7 +187,7 @@ def get_fifo_queue(filters, sle=None): transferred_item_details[(d.voucher_no, d.name)].append(fifo_queue.pop(0)) else: # all from current batch - flt(batch[0]) -= qty_to_pop + batch[0] -= qty_to_pop transferred_item_details[(d.voucher_no, d.name)].append([qty_to_pop, batch[1]]) qty_to_pop = 0 From 5b8a47d0729243cceb52fba807d33b730e431715 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Sat, 13 Jun 2020 01:04:32 +0530 Subject: [PATCH 350/608] fix: patch for expense claim (#22226) --- erpnext/patches.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 1f5d4d563ae..a0707b77ca7 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -680,6 +680,7 @@ erpnext.patches.v12_0.update_appointment_reminder_scheduler_entry erpnext.patches.v12_0.retain_permission_rules_for_video_doctype erpnext.patches.v12_0.remove_duplicate_leave_ledger_entries #2020-05-22 erpnext.patches.v13_0.patch_to_fix_reverse_linking_in_additional_salary_encashment_and_incentive +execute:frappe.reload_doc("HR", "doctype", "Employee Advance") erpnext.patches.v12_0.move_due_advance_amount_to_pending_amount execute:frappe.delete_doc_if_exists("Page", "appointment-analytic") execute:frappe.rename_doc("Desk Page", "Getting Started", "Home", force=True) From 8bc414620b7a819aab05a92ca25bf8c3e5ddac01 Mon Sep 17 00:00:00 2001 From: Anupam K Date: Sat, 13 Jun 2020 13:11:43 +0530 Subject: [PATCH 351/608] opportunity-dashboard-fix --- erpnext/crm/doctype/opportunity/opportunity_dashboard.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/erpnext/crm/doctype/opportunity/opportunity_dashboard.py b/erpnext/crm/doctype/opportunity/opportunity_dashboard.py index 9ed616afd23..68f0104fd6c 100644 --- a/erpnext/crm/doctype/opportunity/opportunity_dashboard.py +++ b/erpnext/crm/doctype/opportunity/opportunity_dashboard.py @@ -3,11 +3,7 @@ from frappe import _ def get_data(): return { - 'fieldname': 'prevdoc_docname', - 'non_standard_fieldnames': { - 'Supplier Quotation': 'opportunity', - 'Quotation': 'opportunity' - }, + 'fieldname': 'opportunity', 'transactions': [ { 'items': ['Quotation', 'Supplier Quotation'] From 15231aa60d948828acaab798732049383414b007 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 13 Jun 2020 19:16:37 +0530 Subject: [PATCH 352/608] fix: Data not appearing properly for some fiscal_year in financial statemets --- erpnext/accounts/report/financial_statements.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index 0339e4920a8..393c5d3aa60 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -56,9 +56,8 @@ def get_period_list(from_fiscal_year, to_fiscal_year, period_start_date, period_ to_date = add_months(start_date, months_to_add) start_date = to_date - if to_date == get_first_day(to_date): - # if to_date is the first day, get the last day of previous month - to_date = add_days(to_date, -1) + # Subtract one day from to_date, as it may be first day in next fiscal year or month + to_date = add_days(to_date, -1) if to_date <= year_end_date: # the normal case From d80d442aa1f82a32a9286320270243cba73675f7 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 13 Jun 2020 21:12:19 +0530 Subject: [PATCH 353/608] fix: Do not select cancelled entries in financial statements --- erpnext/accounts/report/financial_statements.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index 0339e4920a8..bb33a0025a3 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -418,6 +418,7 @@ def set_gl_entries_by_account( where company=%(company)s {additional_conditions} and posting_date <= %(to_date)s + and is_cancelled = 0 {distributed_cost_center_query} order by account, posting_date""".format( additional_conditions=additional_conditions, From cc4943d54d81270ef90fe35687a3f45066d5c7cc Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 13 Jun 2020 21:13:52 +0530 Subject: [PATCH 354/608] fix: Distributed cost center query updation --- erpnext/accounts/report/financial_statements.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index bb33a0025a3..769eb0a57f0 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -406,6 +406,7 @@ def set_gl_entries_by_account( FROM `tabDistributed Cost Center` WHERE cost_center IN %(cost_center)s AND parent NOT IN %(cost_center)s + AND is_cancelled = 0 GROUP BY parent ) as DCC_allocation WHERE company=%(company)s From 67600776747c0a0079e3fa33e7f66cc75790a8d8 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 13 Jun 2020 22:40:23 +0530 Subject: [PATCH 355/608] fix: Billing address in for Purchase documents --- .../purchase_invoice/purchase_invoice.json | 616 +++++++++++++----- .../purchase_order/purchase_order.json | 508 +++++++++++---- erpnext/public/js/controllers/buying.js | 7 + .../purchase_receipt/purchase_receipt.json | 456 ++++++++++--- 4 files changed, 1215 insertions(+), 372 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index 98ba5c72ae2..829c34da67c 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -42,6 +42,8 @@ "col_break_address", "shipping_address", "shipping_address_display", + "billing_address", + "billing_address_display", "currency_and_price_list", "currency", "conversion_rate", @@ -168,7 +170,9 @@ "hidden": 1, "label": "Title", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "naming_series", @@ -180,7 +184,9 @@ "options": "ACC-PINV-.YYYY.-", "print_hide": 1, "reqd": 1, - "set_only_once": 1 + "set_only_once": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "supplier", @@ -192,7 +198,9 @@ "options": "Supplier", "print_hide": 1, "reqd": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "bold": 1, @@ -204,7 +212,9 @@ "label": "Supplier Name", "oldfieldname": "supplier_name", "oldfieldtype": "Data", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fetch_from": "supplier.tax_id", @@ -212,21 +222,27 @@ "fieldtype": "Read Only", "label": "Tax Id", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "due_date", "fieldtype": "Date", "label": "Due Date", "oldfieldname": "due_date", - "oldfieldtype": "Date" + "oldfieldtype": "Date", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "is_paid", "fieldtype": "Check", "label": "Is Paid", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", @@ -234,19 +250,25 @@ "fieldtype": "Check", "label": "Is Return (Debit Note)", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "apply_tds", "fieldtype": "Check", "label": "Apply Tax Withholding Amount", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break1", "fieldtype": "Column Break", "oldfieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -256,13 +278,17 @@ "label": "Company", "options": "Company", "print_hide": 1, - "remember_last_selected_value": 1 + "remember_last_selected_value": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "cost_center", "fieldtype": "Link", "label": "Cost Center", - "options": "Cost Center" + "options": "Cost Center", + "show_days": 1, + "show_seconds": 1 }, { "default": "Today", @@ -274,7 +300,9 @@ "oldfieldtype": "Date", "print_hide": 1, "reqd": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "posting_time", @@ -283,6 +311,8 @@ "no_copy": 1, "print_hide": 1, "print_width": "100px", + "show_days": 1, + "show_seconds": 1, "width": "100px" }, { @@ -291,7 +321,9 @@ "fieldname": "set_posting_time", "fieldtype": "Check", "label": "Edit Posting Date and Time", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "amended_from", @@ -303,44 +335,58 @@ "oldfieldtype": "Link", "options": "Purchase Invoice", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "collapsible_depends_on": "eval:doc.on_hold", "fieldname": "sb_14", "fieldtype": "Section Break", - "label": "Hold Invoice" + "label": "Hold Invoice", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "on_hold", "fieldtype": "Check", - "label": "Hold Invoice" + "label": "Hold Invoice", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.on_hold", "description": "Once set, this invoice will be on hold till the set date", "fieldname": "release_date", "fieldtype": "Date", - "label": "Release Date" + "label": "Release Date", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "cb_17", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.on_hold", "fieldname": "hold_comment", "fieldtype": "Small Text", - "label": "Reason For Putting On Hold" + "label": "Reason For Putting On Hold", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "collapsible_depends_on": "bill_no", "fieldname": "supplier_invoice_details", "fieldtype": "Section Break", - "label": "Supplier Invoice Details" + "label": "Supplier Invoice Details", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "bill_no", @@ -348,11 +394,15 @@ "label": "Supplier Invoice No", "oldfieldname": "bill_no", "oldfieldtype": "Data", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_15", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "bill_date", @@ -360,13 +410,17 @@ "label": "Supplier Invoice Date", "oldfieldname": "bill_date", "oldfieldtype": "Date", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "return_against", "fieldname": "returns", "fieldtype": "Section Break", - "label": "Returns" + "label": "Returns", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "return_against", @@ -376,26 +430,34 @@ "no_copy": 1, "options": "Purchase Invoice", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "section_addresses", "fieldtype": "Section Break", - "label": "Address and Contact" + "label": "Address and Contact", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "supplier_address", "fieldtype": "Link", "label": "Select Supplier Address", "options": "Address", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "address_display", "fieldtype": "Small Text", "label": "Address", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_person", @@ -403,51 +465,67 @@ "in_global_search": 1, "label": "Contact Person", "options": "Contact", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_display", "fieldtype": "Small Text", "label": "Contact", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_mobile", "fieldtype": "Small Text", "label": "Mobile No", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_email", "fieldtype": "Small Text", "label": "Contact Email", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "col_break_address", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "shipping_address", "fieldtype": "Link", "label": "Select Shipping Address", "options": "Address", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "shipping_address_display", "fieldtype": "Small Text", "label": "Shipping Address", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "currency_and_price_list", "fieldtype": "Section Break", "label": "Currency and Price List", - "options": "fa fa-tag" + "options": "fa fa-tag", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "currency", @@ -456,7 +534,9 @@ "oldfieldname": "currency", "oldfieldtype": "Select", "options": "Currency", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "conversion_rate", @@ -465,18 +545,24 @@ "oldfieldname": "conversion_rate", "oldfieldtype": "Currency", "precision": "9", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break2", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "buying_price_list", "fieldtype": "Link", "label": "Price List", "options": "Price List", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "price_list_currency", @@ -484,14 +570,18 @@ "label": "Price List Currency", "options": "Currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "plc_conversion_rate", "fieldtype": "Float", "label": "Price List Exchange Rate", "precision": "9", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", @@ -500,11 +590,15 @@ "label": "Ignore Pricing Rule", "no_copy": 1, "permlevel": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "sec_warehouse", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "update_stock", @@ -512,7 +606,9 @@ "fieldtype": "Link", "label": "Set Accepted Warehouse", "options": "Warehouse", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "update_stock", @@ -522,11 +618,15 @@ "label": "Rejected Warehouse", "no_copy": 1, "options": "Warehouse", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "col_break_warehouse", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "default": "No", @@ -534,7 +634,9 @@ "fieldtype": "Select", "label": "Raw Materials Supplied", "options": "No\nYes", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.is_subcontracted==\"Yes\"", @@ -545,25 +647,33 @@ "options": "Warehouse", "print_hide": 1, "print_width": "50px", + "show_days": 1, + "show_seconds": 1, "width": "50px" }, { "fieldname": "items_section", "fieldtype": "Section Break", "oldfieldtype": "Section Break", - "options": "fa fa-shopping-cart" + "options": "fa fa-shopping-cart", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "update_stock", "fieldtype": "Check", "label": "Update Stock", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "scan_barcode", "fieldtype": "Data", - "label": "Scan Barcode" + "label": "Scan Barcode", + "show_days": 1, + "show_seconds": 1 }, { "allow_bulk_edit": 1, @@ -573,42 +683,56 @@ "oldfieldname": "entries", "oldfieldtype": "Table", "options": "Purchase Invoice Item", - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "pricing_rule_details", "fieldtype": "Section Break", - "label": "Pricing Rules" + "label": "Pricing Rules", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "pricing_rules", "fieldtype": "Table", "label": "Pricing Rule Detail", "options": "Pricing Rule Detail", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible_depends_on": "supplied_items", "fieldname": "raw_materials_supplied", "fieldtype": "Section Break", - "label": "Raw Materials Supplied" + "label": "Raw Materials Supplied", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "supplied_items", "fieldtype": "Table", "label": "Supplied Items", "options": "Purchase Receipt Item Supplied", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "section_break_26", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_qty", "fieldtype": "Float", "label": "Total Quantity", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_total", @@ -616,7 +740,9 @@ "label": "Total (Company Currency)", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_net_total", @@ -626,18 +752,24 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_28", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total", "fieldtype": "Currency", "label": "Total", "options": "currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "net_total", @@ -647,42 +779,56 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_net_weight", "fieldtype": "Float", "label": "Total Net Weight", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_section", "fieldtype": "Section Break", "oldfieldtype": "Section Break", - "options": "fa fa-money" + "options": "fa fa-money", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "tax_category", "fieldtype": "Link", "label": "Tax Category", "options": "Tax Category", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_49", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "shipping_rule", "fieldtype": "Link", "label": "Shipping Rule", "options": "Shipping Rule", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "section_break_51", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_and_charges", @@ -691,7 +837,9 @@ "oldfieldname": "purchase_other_charges", "oldfieldtype": "Link", "options": "Purchase Taxes and Charges Template", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes", @@ -699,13 +847,17 @@ "label": "Purchase Taxes and Charges", "oldfieldname": "purchase_tax_details", "oldfieldtype": "Table", - "options": "Purchase Taxes and Charges" + "options": "Purchase Taxes and Charges", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "sec_tax_breakup", "fieldtype": "Section Break", - "label": "Tax Breakup" + "label": "Tax Breakup", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "other_charges_calculation", @@ -714,13 +866,17 @@ "no_copy": 1, "oldfieldtype": "HTML", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "totals", "fieldtype": "Section Break", "oldfieldtype": "Section Break", - "options": "fa fa-money" + "options": "fa fa-money", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_taxes_and_charges_added", @@ -730,7 +886,9 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_taxes_and_charges_deducted", @@ -740,7 +898,9 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_total_taxes_and_charges", @@ -750,11 +910,15 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_40", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_and_charges_added", @@ -764,7 +928,9 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_and_charges_deducted", @@ -774,7 +940,9 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_taxes_and_charges", @@ -782,14 +950,18 @@ "label": "Total Taxes and Charges", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "collapsible_depends_on": "discount_amount", "fieldname": "section_break_44", "fieldtype": "Section Break", - "label": "Additional Discount" + "label": "Additional Discount", + "show_days": 1, + "show_seconds": 1 }, { "default": "Grand Total", @@ -797,7 +969,9 @@ "fieldtype": "Select", "label": "Apply Additional Discount On", "options": "\nGrand Total\nNet Total", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_discount_amount", @@ -805,28 +979,38 @@ "label": "Additional Discount Amount (Company Currency)", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_46", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "additional_discount_percentage", "fieldtype": "Float", "label": "Additional Discount Percentage", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "discount_amount", "fieldtype": "Currency", "label": "Additional Discount Amount", "options": "currency", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "section_break_49", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_grand_total", @@ -836,7 +1020,9 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_rounding_adjustment", @@ -845,7 +1031,9 @@ "no_copy": 1, "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:!doc.disable_rounded_total", @@ -855,7 +1043,9 @@ "no_copy": 1, "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_in_words", @@ -864,13 +1054,17 @@ "oldfieldname": "in_words", "oldfieldtype": "Data", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break8", "fieldtype": "Column Break", "oldfieldtype": "Column Break", "print_hide": 1, + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -881,7 +1075,9 @@ "oldfieldname": "grand_total_import", "oldfieldtype": "Currency", "options": "currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "rounding_adjustment", @@ -890,7 +1086,9 @@ "no_copy": 1, "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:!doc.disable_rounded_total", @@ -900,7 +1098,9 @@ "no_copy": 1, "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "in_words", @@ -909,7 +1109,9 @@ "oldfieldname": "in_words_import", "oldfieldtype": "Data", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_advance", @@ -920,7 +1122,9 @@ "oldfieldtype": "Currency", "options": "party_account_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "outstanding_amount", @@ -931,14 +1135,18 @@ "oldfieldtype": "Currency", "options": "party_account_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "depends_on": "grand_total", "fieldname": "disable_rounded_total", "fieldtype": "Check", - "label": "Disable Rounded Total" + "label": "Disable Rounded Total", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -946,30 +1154,40 @@ "depends_on": "eval:doc.is_paid===1||(doc.advances && doc.advances.length>0)", "fieldname": "payments_section", "fieldtype": "Section Break", - "label": "Payments" + "label": "Payments", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "mode_of_payment", "fieldtype": "Link", "label": "Mode of Payment", "options": "Mode of Payment", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "cash_bank_account", "fieldtype": "Link", "label": "Cash/Bank Account", - "options": "Account" + "options": "Account", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "clearance_date", "fieldtype": "Date", "hidden": 1, - "label": "Clearance Date" + "label": "Clearance Date", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "col_br_payments", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "is_paid", @@ -978,7 +1196,9 @@ "label": "Paid Amount", "no_copy": 1, "options": "currency", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_paid_amount", @@ -987,7 +1207,9 @@ "no_copy": 1, "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -995,7 +1217,9 @@ "depends_on": "grand_total", "fieldname": "write_off", "fieldtype": "Section Break", - "label": "Write Off" + "label": "Write Off", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "write_off_amount", @@ -1003,7 +1227,9 @@ "label": "Write Off Amount", "no_copy": 1, "options": "currency", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_write_off_amount", @@ -1012,11 +1238,15 @@ "no_copy": 1, "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_61", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:flt(doc.write_off_amount)!=0", @@ -1024,7 +1254,9 @@ "fieldtype": "Link", "label": "Write Off Account", "options": "Account", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:flt(doc.write_off_amount)!=0", @@ -1032,7 +1264,9 @@ "fieldtype": "Link", "label": "Write Off Cost Center", "options": "Cost Center", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -1042,13 +1276,17 @@ "label": "Advance Payments", "oldfieldtype": "Section Break", "options": "fa fa-money", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "allocate_advances_automatically", "fieldtype": "Check", - "label": "Set Advances and Allocate (FIFO)" + "label": "Set Advances and Allocate (FIFO)", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:!doc.allocate_advances_automatically", @@ -1056,7 +1294,9 @@ "fieldtype": "Button", "label": "Get Advances Paid", "oldfieldtype": "Button", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "advances", @@ -1066,20 +1306,26 @@ "oldfieldname": "advance_allocation_details", "oldfieldtype": "Table", "options": "Purchase Invoice Advance", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "collapsible_depends_on": "eval:(!doc.is_return)", "fieldname": "payment_schedule_section", "fieldtype": "Section Break", - "label": "Payment Terms" + "label": "Payment Terms", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "payment_terms_template", "fieldtype": "Link", "label": "Payment Terms Template", - "options": "Payment Terms Template" + "options": "Payment Terms Template", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "payment_schedule", @@ -1087,7 +1333,9 @@ "label": "Payment Schedule", "no_copy": 1, "options": "Payment Schedule", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -1095,25 +1343,33 @@ "fieldname": "terms_section_break", "fieldtype": "Section Break", "label": "Terms and Conditions", - "options": "fa fa-legal" + "options": "fa fa-legal", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "tc_name", "fieldtype": "Link", "label": "Terms", "options": "Terms and Conditions", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "terms", "fieldtype": "Text Editor", - "label": "Terms and Conditions1" + "label": "Terms and Conditions1", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "printing_settings", "fieldtype": "Section Break", - "label": "Printing Settings" + "label": "Printing Settings", + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -1121,7 +1377,9 @@ "fieldtype": "Link", "label": "Letter Head", "options": "Letter Head", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -1129,11 +1387,15 @@ "fieldname": "group_same_items", "fieldtype": "Check", "label": "Group same items", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_112", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -1145,14 +1407,18 @@ "oldfieldtype": "Link", "options": "Print Heading", "print_hide": 1, - "report_hide": 1 + "report_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "language", "fieldtype": "Data", "label": "Print Language", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -1161,7 +1427,9 @@ "label": "More Information", "oldfieldtype": "Section Break", "options": "fa fa-file-text", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "credit_to", @@ -1172,7 +1440,9 @@ "options": "Account", "print_hide": 1, "reqd": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "party_account_currency", @@ -1182,7 +1452,9 @@ "no_copy": 1, "options": "Currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "No", @@ -1192,7 +1464,9 @@ "oldfieldname": "is_opening", "oldfieldtype": "Select", "options": "No\nYes", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "against_expense_account", @@ -1202,11 +1476,15 @@ "no_copy": 1, "oldfieldname": "against_expense_account", "oldfieldtype": "Small Text", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_63", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "default": "Draft", @@ -1215,14 +1493,18 @@ "in_standard_filter": 1, "label": "Status", "options": "\nDraft\nReturn\nDebit Note Issued\nSubmitted\nPaid\nUnpaid\nOverdue\nCancelled", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "inter_company_invoice_reference", "fieldtype": "Link", "label": "Inter Company Invoice Reference", "options": "Sales Invoice", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "remarks", @@ -1231,14 +1513,18 @@ "no_copy": 1, "oldfieldname": "remarks", "oldfieldtype": "Text", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "subscription_section", "fieldtype": "Section Break", "label": "Subscription Section", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -1247,7 +1533,9 @@ "fieldtype": "Date", "label": "From Date", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -1256,11 +1544,15 @@ "fieldtype": "Date", "label": "To Date", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_114", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "auto_repeat", @@ -1269,24 +1561,32 @@ "no_copy": 1, "options": "Auto Repeat", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, "depends_on": "eval: doc.auto_repeat", "fieldname": "update_auto_repeat_reference", "fieldtype": "Button", - "label": "Update Auto Repeat Reference" + "label": "Update Auto Repeat Reference", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "accounting_dimensions_section", "fieldtype": "Section Break", - "label": "Accounting Dimensions " + "label": "Accounting Dimensions ", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "dimension_col_break", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", @@ -1294,7 +1594,9 @@ "fieldname": "is_internal_supplier", "fieldtype": "Check", "label": "Is Internal Supplier", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "tax_withholding_category", @@ -1302,14 +1604,32 @@ "hidden": 1, "label": "Tax Withholding Category", "options": "Tax Withholding Category", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "billing_address", + "fieldtype": "Link", + "label": "Select Billing Address", + "options": "Address", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "billing_address_display", + "fieldtype": "Small Text", + "label": "Billing Address", + "read_only": 1, + "show_days": 1, + "show_seconds": 1 } ], "icon": "fa fa-file-text", "idx": 204, "is_submittable": 1, "links": [], - "modified": "2020-04-18 13:05:25.199832", + "modified": "2020-06-13 22:26:30.800199", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice", diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json index a4f60fbba5c..7145fea5c5c 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.json +++ b/erpnext/buying/doctype/purchase_order/purchase_order.json @@ -38,6 +38,8 @@ "col_break_address", "shipping_address", "shipping_address_display", + "billing_address", + "billing_address_display", "currency_and_price_list", "currency", "conversion_rate", @@ -135,7 +137,9 @@ { "fieldname": "supplier_section", "fieldtype": "Section Break", - "options": "fa fa-user" + "options": "fa fa-user", + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -145,7 +149,9 @@ "hidden": 1, "label": "Title", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "naming_series", @@ -157,7 +163,9 @@ "options": "PUR-ORD-.YYYY.-", "print_hide": 1, "reqd": 1, - "set_only_once": 1 + "set_only_once": 1, + "show_days": 1, + "show_seconds": 1 }, { "bold": 1, @@ -170,14 +178,18 @@ "options": "Supplier", "print_hide": 1, "reqd": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.supplier && doc.docstatus===0 && (!(doc.items && doc.items.length) || (doc.items.length==1 && !doc.items[0].item_code))", "description": "Fetch items based on Default Supplier.", "fieldname": "get_items_from_open_material_requests", "fieldtype": "Button", - "label": "Get Items from Open Material Requests" + "label": "Get Items from Open Material Requests", + "show_days": 1, + "show_seconds": 1 }, { "bold": 1, @@ -186,7 +198,9 @@ "fieldtype": "Data", "in_global_search": 1, "label": "Supplier Name", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "company", @@ -198,13 +212,17 @@ "options": "Company", "print_hide": 1, "remember_last_selected_value": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break1", "fieldtype": "Column Break", "oldfieldtype": "Column Break", "print_width": "50%", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -216,27 +234,35 @@ "oldfieldname": "transaction_date", "oldfieldtype": "Date", "reqd": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, "fieldname": "schedule_date", "fieldtype": "Date", - "label": "Required By" + "label": "Required By", + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, "depends_on": "eval:doc.docstatus===1", "fieldname": "order_confirmation_no", "fieldtype": "Data", - "label": "Order Confirmation No" + "label": "Order Confirmation No", + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, "depends_on": "eval:doc.order_confirmation_no", "fieldname": "order_confirmation_date", "fieldtype": "Date", - "label": "Order Confirmation Date" + "label": "Order Confirmation Date", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "amended_from", @@ -248,19 +274,25 @@ "oldfieldtype": "Data", "options": "Purchase Order", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "drop_ship", "fieldtype": "Section Break", - "label": "Drop Ship" + "label": "Drop Ship", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "customer", "fieldtype": "Link", "label": "Customer", "options": "Customer", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "bold": 1, @@ -268,31 +300,41 @@ "fieldtype": "Data", "label": "Customer Name", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_19", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "customer_contact_person", "fieldtype": "Link", "label": "Customer Contact", - "options": "Contact" + "options": "Contact", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "customer_contact_display", "fieldtype": "Small Text", "hidden": 1, "label": "Customer Contact", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "customer_contact_mobile", "fieldtype": "Small Text", "hidden": 1, "label": "Customer Mobile No", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "customer_contact_email", @@ -300,46 +342,60 @@ "hidden": 1, "label": "Customer Contact Email", "options": "Email", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "section_addresses", "fieldtype": "Section Break", - "label": "Address and Contact" + "label": "Address and Contact", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "supplier_address", "fieldtype": "Link", "label": "Select Supplier Address", "options": "Address", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_person", "fieldtype": "Link", "label": "Contact Person", "options": "Contact", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "address_display", "fieldtype": "Small Text", "label": "Address", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_display", "fieldtype": "Small Text", "in_global_search": 1, "label": "Contact", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_mobile", "fieldtype": "Small Text", "label": "Mobile No", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_email", @@ -347,32 +403,42 @@ "label": "Contact Email", "options": "Email", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "col_break_address", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "shipping_address", "fieldtype": "Link", "label": "Select Shipping Address", "options": "Address", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "shipping_address_display", "fieldtype": "Small Text", "label": "Shipping Address", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "currency_and_price_list", "fieldtype": "Section Break", "label": "Currency and Price List", - "options": "fa fa-tag" + "options": "fa fa-tag", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "currency", @@ -382,7 +448,9 @@ "oldfieldtype": "Select", "options": "Currency", "print_hide": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "conversion_rate", @@ -392,18 +460,24 @@ "oldfieldtype": "Currency", "precision": "9", "print_hide": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "cb_price_list", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "buying_price_list", "fieldtype": "Link", "label": "Price List", "options": "Price List", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "price_list_currency", @@ -411,14 +485,18 @@ "label": "Price List Currency", "options": "Currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "plc_conversion_rate", "fieldtype": "Float", "label": "Price List Exchange Rate", "precision": "9", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", @@ -427,11 +505,15 @@ "label": "Ignore Pricing Rule", "no_copy": 1, "permlevel": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "sec_warehouse", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "description": "Sets 'Warehouse' in each row of the Items table.", @@ -439,11 +521,15 @@ "fieldtype": "Link", "label": "Set Target Warehouse", "options": "Warehouse", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "col_break_warehouse", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "default": "No", @@ -452,25 +538,33 @@ "in_standard_filter": 1, "label": "Supply Raw Materials", "options": "No\nYes", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.is_subcontracted==\"Yes\"", "fieldname": "supplier_warehouse", "fieldtype": "Link", "label": "Supplier Warehouse", - "options": "Warehouse" + "options": "Warehouse", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "items_section", "fieldtype": "Section Break", "oldfieldtype": "Section Break", - "options": "fa fa-shopping-cart" + "options": "fa fa-shopping-cart", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "scan_barcode", "fieldtype": "Data", - "label": "Scan Barcode" + "label": "Scan Barcode", + "show_days": 1, + "show_seconds": 1 }, { "allow_bulk_edit": 1, @@ -480,26 +574,34 @@ "oldfieldname": "po_details", "oldfieldtype": "Table", "options": "Purchase Order Item", - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "section_break_48", "fieldtype": "Section Break", - "label": "Pricing Rules" + "label": "Pricing Rules", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "pricing_rules", "fieldtype": "Table", "label": "Purchase Order Pricing Rule", "options": "Pricing Rule Detail", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible_depends_on": "supplied_items", "fieldname": "raw_material_details", "fieldtype": "Section Break", - "label": "Raw Materials Supplied" + "label": "Raw Materials Supplied", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "supplied_items", @@ -509,17 +611,23 @@ "oldfieldtype": "Table", "options": "Purchase Order Item Supplied", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "sb_last_purchase", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_qty", "fieldtype": "Float", "label": "Total Quantity", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_total", @@ -527,7 +635,9 @@ "label": "Total (Company Currency)", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_net_total", @@ -538,18 +648,24 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_26", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total", "fieldtype": "Currency", "label": "Total", "options": "currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "net_total", @@ -559,20 +675,26 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_net_weight", "fieldtype": "Float", "label": "Total Net Weight", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_section", "fieldtype": "Section Break", "oldfieldtype": "Section Break", - "options": "fa fa-money" + "options": "fa fa-money", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_and_charges", @@ -581,22 +703,30 @@ "oldfieldname": "purchase_other_charges", "oldfieldtype": "Link", "options": "Purchase Taxes and Charges Template", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_50", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "shipping_rule", "fieldtype": "Link", "label": "Shipping Rule", "options": "Shipping Rule", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "section_break_52", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes", @@ -604,13 +734,17 @@ "label": "Purchase Taxes and Charges", "oldfieldname": "purchase_tax_details", "oldfieldtype": "Table", - "options": "Purchase Taxes and Charges" + "options": "Purchase Taxes and Charges", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "sec_tax_breakup", "fieldtype": "Section Break", - "label": "Tax Breakup" + "label": "Tax Breakup", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "other_charges_calculation", @@ -619,13 +753,17 @@ "no_copy": 1, "oldfieldtype": "HTML", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "totals", "fieldtype": "Section Break", "oldfieldtype": "Section Break", - "options": "fa fa-money" + "options": "fa fa-money", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_taxes_and_charges_added", @@ -635,7 +773,9 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_taxes_and_charges_deducted", @@ -645,7 +785,9 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_total_taxes_and_charges", @@ -656,11 +798,15 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_39", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_and_charges_added", @@ -670,7 +816,9 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_and_charges_deducted", @@ -680,7 +828,9 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_taxes_and_charges", @@ -688,14 +838,18 @@ "label": "Total Taxes and Charges", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "collapsible_depends_on": "discount_amount", "fieldname": "discount_section", "fieldtype": "Section Break", - "label": "Additional Discount" + "label": "Additional Discount", + "show_days": 1, + "show_seconds": 1 }, { "default": "Grand Total", @@ -703,7 +857,9 @@ "fieldtype": "Select", "label": "Apply Additional Discount On", "options": "\nGrand Total\nNet Total", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_discount_amount", @@ -711,28 +867,38 @@ "label": "Additional Discount Amount (Company Currency)", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_45", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "additional_discount_percentage", "fieldtype": "Float", "label": "Additional Discount Percentage", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "discount_amount", "fieldtype": "Currency", "label": "Additional Discount Amount", "options": "currency", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "totals_section", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_grand_total", @@ -743,7 +909,9 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_rounding_adjustment", @@ -752,7 +920,9 @@ "no_copy": 1, "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "description": "In Words will be visible once you save the Purchase Order.", @@ -762,7 +932,9 @@ "oldfieldname": "in_words", "oldfieldtype": "Data", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_rounded_total", @@ -772,12 +944,16 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break4", "fieldtype": "Column Break", - "oldfieldtype": "Column Break" + "oldfieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "grand_total", @@ -787,7 +963,9 @@ "oldfieldname": "grand_total_import", "oldfieldtype": "Currency", "options": "currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "rounding_adjustment", @@ -796,20 +974,26 @@ "no_copy": 1, "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "rounded_total", "fieldtype": "Currency", "label": "Rounded Total", "options": "currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "disable_rounded_total", "fieldtype": "Check", - "label": "Disable Rounded Total" + "label": "Disable Rounded Total", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "in_words", @@ -818,7 +1002,9 @@ "oldfieldname": "in_words_import", "oldfieldtype": "Data", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "advance_paid", @@ -827,19 +1013,25 @@ "no_copy": 1, "options": "party_account_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "payment_schedule_section", "fieldtype": "Section Break", - "label": "Payment Terms" + "label": "Payment Terms", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "payment_terms_template", "fieldtype": "Link", "label": "Payment Terms Template", - "options": "Payment Terms Template" + "options": "Payment Terms Template", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "payment_schedule", @@ -847,7 +1039,9 @@ "label": "Payment Schedule", "no_copy": 1, "options": "Payment Schedule", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -856,7 +1050,9 @@ "fieldtype": "Section Break", "label": "Terms and Conditions", "oldfieldtype": "Section Break", - "options": "fa fa-legal" + "options": "fa fa-legal", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "tc_name", @@ -865,21 +1061,27 @@ "oldfieldname": "tc_name", "oldfieldtype": "Link", "options": "Terms and Conditions", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "terms", "fieldtype": "Text Editor", "label": "Terms and Conditions", "oldfieldname": "terms", - "oldfieldtype": "Text Editor" + "oldfieldtype": "Text Editor", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "more_info", "fieldtype": "Section Break", "label": "More Information", - "oldfieldtype": "Section Break" + "oldfieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "default": "Draft", @@ -894,7 +1096,9 @@ "print_hide": 1, "read_only": 1, "reqd": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "ref_sq", @@ -905,7 +1109,9 @@ "oldfieldname": "ref_sq", "oldfieldtype": "Data", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "party_account_currency", @@ -915,18 +1121,24 @@ "no_copy": 1, "options": "Currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "inter_company_order_reference", "fieldtype": "Link", "label": "Inter Company Order Reference", "options": "Sales Order", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_74", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:!doc.__islocal", @@ -936,7 +1148,9 @@ "label": "% Received", "no_copy": 1, "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:!doc.__islocal", @@ -946,7 +1160,9 @@ "label": "% Billed", "no_copy": 1, "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -956,6 +1172,8 @@ "oldfieldtype": "Column Break", "print_hide": 1, "print_width": "50%", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -966,7 +1184,9 @@ "oldfieldname": "letter_head", "oldfieldtype": "Select", "options": "Letter Head", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -978,11 +1198,15 @@ "oldfieldtype": "Link", "options": "Print Heading", "print_hide": 1, - "report_hide": 1 + "report_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_86", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -990,19 +1214,25 @@ "fieldname": "group_same_items", "fieldtype": "Check", "label": "Group same items", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "language", "fieldtype": "Data", "label": "Print Language", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "subscription_section", "fieldtype": "Section Break", - "label": "Subscription Section" + "label": "Subscription Section", + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -1010,7 +1240,9 @@ "fieldtype": "Date", "label": "From Date", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -1018,11 +1250,15 @@ "fieldtype": "Date", "label": "To Date", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_97", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "auto_repeat", @@ -1031,44 +1267,72 @@ "no_copy": 1, "options": "Auto Repeat", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, "depends_on": "eval: doc.auto_repeat", "fieldname": "update_auto_repeat_reference", "fieldtype": "Button", - "label": "Update Auto Repeat Reference" + "label": "Update Auto Repeat Reference", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "tax_category", "fieldtype": "Link", "label": "Tax Category", - "options": "Tax Category" + "options": "Tax Category", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "supplied_items", "fieldname": "set_reserve_warehouse", "fieldtype": "Link", "label": "Set Reserve Warehouse", - "options": "Warehouse" + "options": "Warehouse", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "tracking_section", "fieldtype": "Section Break", - "label": "Tracking" + "label": "Tracking", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_75", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "billing_address", + "fieldtype": "Link", + "label": "Select Billing Address", + "options": "Address", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "billing_address_display", + "fieldtype": "Small Text", + "label": "Billing Address", + "read_only": 1, + "show_days": 1, + "show_seconds": 1 } ], "icon": "fa fa-file-text", "idx": 105, "is_submittable": 1, "links": [], - "modified": "2020-04-24 12:13:14.186280", + "modified": "2020-06-13 22:25:47.333850", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order", diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js index 9c561894764..a4cc68b3e2c 100644 --- a/erpnext/public/js/controllers/buying.js +++ b/erpnext/public/js/controllers/buying.js @@ -73,6 +73,8 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ me.frm.set_query('contact_person', erpnext.queries.contact_query); me.frm.set_query('supplier_address', erpnext.queries.address_query); + me.frm.set_query('billing_address', erpnext.queries.company_address_query); + if(this.frm.fields_dict.supplier) { this.frm.set_query("supplier", function() { return{ query: "erpnext.controllers.queries.supplier_query" }}); @@ -283,6 +285,11 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ "shipping_address_display", true); }, + billing_address: function() { + erpnext.utils.get_address_display(this.frm, "billing_address", + "billing_address_display", true); + }, + tc_name: function() { this.get_terms(); }, diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json index 467a206d188..44d5f690285 100755 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json @@ -32,6 +32,8 @@ "col_break_address", "shipping_address", "shipping_address_display", + "billing_address", + "billing_address_display", "currency_and_price_list", "currency", "conversion_rate", @@ -130,13 +132,17 @@ { "fieldname": "supplier_section", "fieldtype": "Section Break", - "options": "fa fa-user" + "options": "fa fa-user", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break0", "fieldtype": "Column Break", "oldfieldtype": "Column Break", "print_width": "50%", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -147,7 +153,9 @@ "hidden": 1, "label": "Title", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "naming_series", @@ -159,7 +167,9 @@ "options": "MAT-PRE-.YYYY.-", "print_hide": 1, "reqd": 1, - "set_only_once": 1 + "set_only_once": 1, + "show_days": 1, + "show_seconds": 1 }, { "bold": 1, @@ -174,6 +184,8 @@ "print_width": "150px", "reqd": 1, "search_index": 1, + "show_days": 1, + "show_seconds": 1, "width": "150px" }, { @@ -184,18 +196,24 @@ "fieldtype": "Data", "in_global_search": 1, "label": "Supplier Name", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "supplier_delivery_note", "fieldtype": "Data", - "label": "Supplier Delivery Note" + "label": "Supplier Delivery Note", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break1", "fieldtype": "Column Break", "oldfieldtype": "Column Break", "print_width": "50%", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -210,6 +228,8 @@ "print_width": "100px", "reqd": 1, "search_index": 1, + "show_days": 1, + "show_seconds": 1, "width": "100px" }, { @@ -223,6 +243,8 @@ "print_hide": 1, "print_width": "100px", "reqd": 1, + "show_days": 1, + "show_seconds": 1, "width": "100px" }, { @@ -231,7 +253,9 @@ "fieldname": "set_posting_time", "fieldtype": "Check", "label": "Edit Posting Date and Time", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "company", @@ -245,6 +269,8 @@ "print_width": "150px", "remember_last_selected_value": 1, "reqd": 1, + "show_days": 1, + "show_seconds": 1, "width": "150px" }, { @@ -254,7 +280,9 @@ "label": "Is Return", "no_copy": 1, "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "is_return", @@ -264,46 +292,60 @@ "no_copy": 1, "options": "Purchase Receipt", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "section_addresses", "fieldtype": "Section Break", - "label": "Address and Contact" + "label": "Address and Contact", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "supplier_address", "fieldtype": "Link", "label": "Select Supplier Address", "options": "Address", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_person", "fieldtype": "Link", "label": "Contact Person", "options": "Contact", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "address_display", "fieldtype": "Small Text", "label": "Address", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_display", "fieldtype": "Small Text", "in_global_search": 1, "label": "Contact", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_mobile", "fieldtype": "Small Text", "label": "Mobile No", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_email", @@ -311,32 +353,42 @@ "label": "Contact Email", "options": "Email", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "col_break_address", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "shipping_address", "fieldtype": "Link", "label": "Select Shipping Address", "options": "Address", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "shipping_address_display", "fieldtype": "Small Text", "label": "Shipping Address", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "currency_and_price_list", "fieldtype": "Section Break", "label": "Currency and Price List", - "options": "fa fa-tag" + "options": "fa fa-tag", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "currency", @@ -346,7 +398,9 @@ "oldfieldtype": "Select", "options": "Currency", "print_hide": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "description": "Rate at which supplier's currency is converted to company's base currency", @@ -357,13 +411,17 @@ "oldfieldtype": "Currency", "precision": "9", "print_hide": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break2", "fieldtype": "Column Break", "oldfieldtype": "Column Break", "print_width": "50%", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -371,7 +429,9 @@ "fieldtype": "Link", "label": "Price List", "options": "Price List", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "buying_price_list", @@ -380,7 +440,9 @@ "label": "Price List Currency", "options": "Currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "buying_price_list", @@ -388,7 +450,9 @@ "fieldtype": "Float", "label": "Price List Exchange Rate", "precision": "9", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", @@ -397,11 +461,15 @@ "label": "Ignore Pricing Rule", "no_copy": 1, "permlevel": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "sec_warehouse", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "description": "Sets 'Accepted Warehouse' in each row of the items table.", @@ -409,7 +477,9 @@ "fieldtype": "Link", "label": "Accepted Warehouse", "options": "Warehouse", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "description": "Sets 'Rejected Warehouse' in each row of the items table.", @@ -420,11 +490,15 @@ "oldfieldname": "rejected_warehouse", "oldfieldtype": "Link", "options": "Warehouse", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "col_break_warehouse", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "default": "No", @@ -434,7 +508,9 @@ "oldfieldname": "is_subcontracted", "oldfieldtype": "Select", "options": "No\nYes", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.is_subcontracted==\"Yes\"", @@ -447,13 +523,17 @@ "options": "Warehouse", "print_hide": 1, "print_width": "50px", + "show_days": 1, + "show_seconds": 1, "width": "50px" }, { "fieldname": "items_section", "fieldtype": "Section Break", "oldfieldtype": "Section Break", - "options": "fa fa-shopping-cart" + "options": "fa fa-shopping-cart", + "show_days": 1, + "show_seconds": 1 }, { "allow_bulk_edit": 1, @@ -463,20 +543,26 @@ "oldfieldname": "purchase_receipt_details", "oldfieldtype": "Table", "options": "Purchase Receipt Item", - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "pricing_rule_details", "fieldtype": "Section Break", - "label": "Pricing Rules" + "label": "Pricing Rules", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "pricing_rules", "fieldtype": "Table", "label": "Pricing Rule Detail", "options": "Pricing Rule Detail", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "supplied_items", @@ -485,7 +571,9 @@ "label": "Get Current Stock", "oldfieldtype": "Button", "options": "get_current_stock", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -496,7 +584,9 @@ "oldfieldtype": "Section Break", "options": "fa fa-table", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "supplied_items", @@ -507,18 +597,24 @@ "oldfieldtype": "Table", "options": "Purchase Receipt Item Supplied", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "section_break0", "fieldtype": "Section Break", - "oldfieldtype": "Section Break" + "oldfieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_qty", "fieldtype": "Float", "label": "Total Quantity", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_total", @@ -526,7 +622,9 @@ "label": "Total (Company Currency)", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_net_total", @@ -539,18 +637,24 @@ "print_width": "150px", "read_only": 1, "reqd": 1, + "show_days": 1, + "show_seconds": 1, "width": "150px" }, { "fieldname": "column_break_27", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total", "fieldtype": "Currency", "label": "Total", "options": "currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "net_total", @@ -560,42 +664,56 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_net_weight", "fieldtype": "Float", "label": "Total Net Weight", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "description": "Add / Edit Taxes and Charges", "fieldname": "taxes_charges_section", "fieldtype": "Section Break", "oldfieldtype": "Section Break", - "options": "fa fa-money" + "options": "fa fa-money", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "tax_category", "fieldtype": "Link", "label": "Tax Category", "options": "Tax Category", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "shipping_col", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "shipping_rule", "fieldtype": "Link", "label": "Shipping Rule", - "options": "Shipping Rule" + "options": "Shipping Rule", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_section", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_and_charges", @@ -604,7 +722,9 @@ "oldfieldname": "purchase_other_charges", "oldfieldtype": "Link", "options": "Purchase Taxes and Charges Template", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes", @@ -612,13 +732,17 @@ "label": "Purchase Taxes and Charges", "oldfieldname": "purchase_tax_details", "oldfieldtype": "Table", - "options": "Purchase Taxes and Charges" + "options": "Purchase Taxes and Charges", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "sec_tax_breakup", "fieldtype": "Section Break", - "label": "Tax Breakup" + "label": "Tax Breakup", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "other_charges_calculation", @@ -627,13 +751,17 @@ "no_copy": 1, "oldfieldtype": "HTML", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "totals", "fieldtype": "Section Break", "oldfieldtype": "Section Break", - "options": "fa fa-money" + "options": "fa fa-money", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_taxes_and_charges_added", @@ -643,7 +771,9 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_taxes_and_charges_deducted", @@ -653,7 +783,9 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_total_taxes_and_charges", @@ -663,12 +795,16 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break3", "fieldtype": "Column Break", "print_width": "50%", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -679,7 +815,9 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_and_charges_deducted", @@ -689,7 +827,9 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_taxes_and_charges", @@ -697,14 +837,18 @@ "label": "Total Taxes and Charges", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "collapsible_depends_on": "discount_amount", "fieldname": "section_break_42", "fieldtype": "Section Break", - "label": "Additional Discount" + "label": "Additional Discount", + "show_days": 1, + "show_seconds": 1 }, { "default": "Grand Total", @@ -712,7 +856,9 @@ "fieldtype": "Select", "label": "Apply Additional Discount On", "options": "\nGrand Total\nNet Total", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_discount_amount", @@ -720,28 +866,38 @@ "label": "Additional Discount Amount (Company Currency)", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_44", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "additional_discount_percentage", "fieldtype": "Float", "label": "Additional Discount Percentage", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "discount_amount", "fieldtype": "Currency", "label": "Additional Discount Amount", "options": "currency", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "section_break_46", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_grand_total", @@ -751,7 +907,9 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_rounding_adjustment", @@ -760,7 +918,9 @@ "no_copy": 1, "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_in_words", @@ -769,7 +929,9 @@ "oldfieldname": "in_words", "oldfieldtype": "Data", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_rounded_total", @@ -779,11 +941,15 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_50", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "grand_total", @@ -793,7 +959,9 @@ "oldfieldname": "grand_total_import", "oldfieldtype": "Currency", "options": "currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "rounding_adjustment", @@ -802,7 +970,9 @@ "no_copy": 1, "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:!doc.disable_rounded_total", @@ -812,7 +982,9 @@ "no_copy": 1, "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "in_words", @@ -821,13 +993,17 @@ "oldfieldname": "in_words_import", "oldfieldtype": "Data", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "disable_rounded_total", "fieldtype": "Check", - "label": "Disable Rounded Total" + "label": "Disable Rounded Total", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -836,7 +1012,9 @@ "fieldtype": "Section Break", "label": "Terms and Conditions", "oldfieldtype": "Section Break", - "options": "fa fa-legal" + "options": "fa fa-legal", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "tc_name", @@ -845,14 +1023,18 @@ "oldfieldname": "tc_name", "oldfieldtype": "Link", "options": "Terms and Conditions", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "terms", "fieldtype": "Text Editor", "label": "Terms and Conditions", "oldfieldname": "terms", - "oldfieldtype": "Text Editor" + "oldfieldtype": "Text Editor", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "bill_no", @@ -861,7 +1043,9 @@ "label": "Bill No", "oldfieldname": "bill_no", "oldfieldtype": "Data", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "bill_date", @@ -870,7 +1054,9 @@ "label": "Bill Date", "oldfieldname": "bill_date", "oldfieldtype": "Date", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -878,7 +1064,9 @@ "fieldtype": "Section Break", "label": "More Information", "oldfieldtype": "Section Break", - "options": "fa fa-file-text" + "options": "fa fa-file-text", + "show_days": 1, + "show_seconds": 1 }, { "default": "Draft", @@ -895,6 +1083,8 @@ "read_only": 1, "reqd": 1, "search_index": 1, + "show_days": 1, + "show_seconds": 1, "width": "150px" }, { @@ -910,6 +1100,8 @@ "print_hide": 1, "print_width": "150px", "read_only": 1, + "show_days": 1, + "show_seconds": 1, "width": "150px" }, { @@ -919,7 +1111,9 @@ "label": "Range", "oldfieldname": "range", "oldfieldtype": "Data", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break4", @@ -927,6 +1121,8 @@ "oldfieldtype": "Column Break", "print_hide": 1, "print_width": "50%", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -935,12 +1131,16 @@ "label": "% Amount Billed", "no_copy": 1, "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "subscription_detail", "fieldtype": "Section Break", - "label": "Auto Repeat Detail" + "label": "Auto Repeat Detail", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "auto_repeat", @@ -949,13 +1149,17 @@ "no_copy": 1, "options": "Auto Repeat", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "printing_settings", "fieldtype": "Section Break", - "label": "Printing Settings" + "label": "Printing Settings", + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -963,7 +1167,9 @@ "fieldtype": "Link", "label": "Letter Head", "options": "Letter Head", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -975,13 +1181,17 @@ "oldfieldtype": "Link", "options": "Print Heading", "print_hide": 1, - "report_hide": 1 + "report_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "language", "fieldtype": "Data", "label": "Print Language", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -989,11 +1199,15 @@ "fieldname": "group_same_items", "fieldtype": "Check", "label": "Group same items", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_97", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "other_details", @@ -1004,6 +1218,8 @@ "options": "
    Other Details
    ", "print_hide": 1, "print_width": "30%", + "show_days": 1, + "show_seconds": 1, "width": "30%" }, { @@ -1011,13 +1227,17 @@ "fieldtype": "Small Text", "label": "Instructions", "oldfieldname": "instructions", - "oldfieldtype": "Text" + "oldfieldtype": "Text", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "remarks", "fieldtype": "Small Text", "label": "Remarks", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -1025,19 +1245,25 @@ "fieldname": "transporter_info", "fieldtype": "Section Break", "label": "Transporter Details", - "options": "fa fa-truck" + "options": "fa fa-truck", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "transporter_name", "fieldtype": "Data", "label": "Transporter Name", "oldfieldname": "transporter_name", - "oldfieldtype": "Data" + "oldfieldtype": "Data", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break5", "fieldtype": "Column Break", "print_width": "50%", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -1048,6 +1274,8 @@ "oldfieldname": "lr_no", "oldfieldtype": "Data", "print_width": "100px", + "show_days": 1, + "show_seconds": 1, "width": "100px" }, { @@ -1058,6 +1286,8 @@ "oldfieldname": "lr_date", "oldfieldtype": "Date", "print_width": "100px", + "show_days": 1, + "show_seconds": 1, "width": "100px" }, { @@ -1066,26 +1296,48 @@ "fieldname": "is_internal_supplier", "fieldtype": "Check", "label": "Is Internal Supplier", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "inter_company_reference", "fieldtype": "Link", "label": "Inter Company Reference", "options": "Delivery Note", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "scan_barcode", "fieldtype": "Data", - "label": "Scan Barcode" + "label": "Scan Barcode", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "billing_address", + "fieldtype": "Link", + "label": "Select Billing Address", + "options": "Address", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "billing_address_display", + "fieldtype": "Small Text", + "label": "Billing Address", + "read_only": 1, + "show_days": 1, + "show_seconds": 1 } ], "icon": "fa fa-truck", "idx": 261, "is_submittable": 1, "links": [], - "modified": "2020-04-18 18:02:18.020763", + "modified": "2020-06-13 22:26:03.600092", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt", @@ -1152,4 +1404,4 @@ "timeline_field": "supplier", "title_field": "title", "track_changes": 1 -} +} \ No newline at end of file From 65e7a2e7a6696c5493d4731149078c2e16816145 Mon Sep 17 00:00:00 2001 From: Anupam K Date: Sat, 13 Jun 2020 13:11:43 +0530 Subject: [PATCH 356/608] opportunity-dashboard-fix --- .../opportunity/opportunity_dashboard.py | 6 +- .../social_media_post/social_media_post.json | 76 ++++++++++++++----- 2 files changed, 58 insertions(+), 24 deletions(-) diff --git a/erpnext/crm/doctype/opportunity/opportunity_dashboard.py b/erpnext/crm/doctype/opportunity/opportunity_dashboard.py index 9ed616afd23..68f0104fd6c 100644 --- a/erpnext/crm/doctype/opportunity/opportunity_dashboard.py +++ b/erpnext/crm/doctype/opportunity/opportunity_dashboard.py @@ -3,11 +3,7 @@ from frappe import _ def get_data(): return { - 'fieldname': 'prevdoc_docname', - 'non_standard_fieldnames': { - 'Supplier Quotation': 'opportunity', - 'Quotation': 'opportunity' - }, + 'fieldname': 'opportunity', 'transactions': [ { 'items': ['Quotation', 'Supplier Quotation'] diff --git a/erpnext/crm/doctype/social_media_post/social_media_post.json b/erpnext/crm/doctype/social_media_post/social_media_post.json index 2601c14b4d6..9c74aaad5fe 100644 --- a/erpnext/crm/doctype/social_media_post/social_media_post.json +++ b/erpnext/crm/doctype/social_media_post/social_media_post.json @@ -30,24 +30,32 @@ "fieldname": "text", "fieldtype": "Small Text", "label": "Tweet", - "mandatory_depends_on": "eval:doc.twitter ==1" + "mandatory_depends_on": "eval:doc.twitter ==1", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "image", "fieldtype": "Attach Image", - "label": "Image" + "label": "Image", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "twitter", "fieldtype": "Check", - "label": "Twitter" + "label": "Twitter", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "linkedin", "fieldtype": "Check", - "label": "LinkedIn" + "label": "LinkedIn", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "amended_from", @@ -56,13 +64,17 @@ "no_copy": 1, "options": "Social Media Post", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.twitter ==1", "fieldname": "content", "fieldtype": "Section Break", - "label": "Twitter" + "label": "Twitter", + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -70,7 +82,9 @@ "fieldtype": "Select", "label": "Post Status", "options": "\nScheduled\nPosted\nError", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -78,7 +92,9 @@ "fieldtype": "Data", "hidden": 1, "label": "Twitter Post Id", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -86,68 +102,89 @@ "fieldtype": "Data", "hidden": 1, "label": "LinkedIn Post Id", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "campaign_name", "fieldtype": "Link", "in_list_view": 1, "label": "Campaign", - "options": "Campaign" + "options": "Campaign", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_6", "fieldtype": "Column Break", - "label": "Share On" + "label": "Share On", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_14", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "tweet_preview", - "fieldtype": "HTML" + "fieldtype": "HTML", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "depends_on": "eval:doc.linkedin==1", "fieldname": "linkedin_section", "fieldtype": "Section Break", - "label": "LinkedIn" + "label": "LinkedIn", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "attachments_section", "fieldtype": "Section Break", - "label": "Attachments" + "label": "Attachments", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "linkedin_post", "fieldtype": "Text", "label": "Post", - "mandatory_depends_on": "eval:doc.linkedin ==1" + "mandatory_depends_on": "eval:doc.linkedin ==1", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_15", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, "fieldname": "scheduled_time", "fieldtype": "Datetime", "label": "Scheduled Time", - "read_only_depends_on": "eval:doc.post_status == \"Posted\"" + "read_only_depends_on": "eval:doc.post_status == \"Posted\"", + "show_days": 1, + "show_seconds": 1 } ], "is_submittable": 1, "links": [], - "modified": "2020-04-21 15:10:04.953713", + "modified": "2020-06-13 20:53:47.670536", "modified_by": "Administrator", "module": "CRM", "name": "Social Media Post", "owner": "Administrator", "permissions": [ { + "cancel": 1, "create": 1, "delete": 1, "email": 1, @@ -157,6 +194,7 @@ "report": 1, "role": "System Manager", "share": 1, + "submit": 1, "write": 1 } ], From 234b5f9a8c869e9fb49fbd16b099153f9972e5ff Mon Sep 17 00:00:00 2001 From: Anupam K Date: Sun, 14 Jun 2020 10:34:58 +0530 Subject: [PATCH 357/608] sm-post-permission-fix --- .../social_media_post/social_media_post.json | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/erpnext/crm/doctype/social_media_post/social_media_post.json b/erpnext/crm/doctype/social_media_post/social_media_post.json index 9c74aaad5fe..0a00dca2808 100644 --- a/erpnext/crm/doctype/social_media_post/social_media_post.json +++ b/erpnext/crm/doctype/social_media_post/social_media_post.json @@ -177,7 +177,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-06-13 20:53:47.670536", + "modified": "2020-06-14 10:31:33.961381", "modified_by": "Administrator", "module": "CRM", "name": "Social Media Post", @@ -196,6 +196,34 @@ "share": 1, "submit": 1, "write": 1 + }, + { + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Sales User", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Sales Manager", + "share": 1, + "submit": 1, + "write": 1 } ], "sort_field": "modified", From 5bf6bec6567fd6f3d6e12af233c0d4c5af872255 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 14 Jun 2020 13:18:08 +0530 Subject: [PATCH 358/608] fix: Consider Overseas category in RCM --- .../doctype/gstr_3b_report/gstr_3b_report.py | 25 ++++++++----------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py index 2691552d802..619734ff263 100644 --- a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py +++ b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py @@ -158,7 +158,7 @@ class GSTR3BReport(Document): self.prepare_data("Sales Invoice", outward_supply_tax_amounts, "sup_details", "osup_det", ["Registered Regular"]) self.prepare_data("Sales Invoice", outward_supply_tax_amounts, "sup_details", "osup_zero", ["SEZ", "Deemed Export", "Overseas"]) - self.prepare_data("Purchase Invoice", inward_supply_tax_amounts, "sup_details", "isup_rev", ["Unregistered"], reverse_charge="Y") + self.prepare_data("Purchase Invoice", inward_supply_tax_amounts, "sup_details", "isup_rev", ["Unregistered", "Overseas"], reverse_charge="Y") self.report_dict["sup_details"]["osup_nil_exmp"]["txval"] = flt(self.get_nil_rated_supply_value(), 2) self.set_itc_details(itc_details) @@ -192,32 +192,27 @@ class GSTR3BReport(Document): for d in self.report_dict["itc_elg"]["itc_avl"]: itc_type = itc_type_map.get(d["ty"]) - gst_category = "Registered Regular" + gst_category = ["Registered Regular"] if d["ty"] == 'ISRC': reverse_charge = "Y" itc_type = 'All Other ITC' - gst_category = 'Unregistered' + gst_category = ['Unregistered', 'Overseas'] else: reverse_charge = "N" for account_head in self.account_heads: - d["iamt"] += flt(itc_details.get((gst_category, itc_type, reverse_charge, account_head.get('igst_account')), {}).get("amount"), 2) - d["camt"] += flt(itc_details.get((gst_category, itc_type, reverse_charge, account_head.get('cgst_account')), {}).get("amount"), 2) - d["samt"] += flt(itc_details.get((gst_category, itc_type, reverse_charge, account_head.get('sgst_account')), {}).get("amount"), 2) - d["csamt"] += flt(itc_details.get((gst_category, itc_type, reverse_charge, account_head.get('cess_account')), {}).get("amount"), 2) + for category in gst_category: + for key in [['iamt', 'igst_account'], ['camt', 'cgst_account'], ['samt', 'sgst_account'], ['csamt', 'cess_account']]: + d[key[0]] += flt(itc_details.get((category, itc_type, reverse_charge, account_head.get(key[1])), {}).get("amount"), 2) - net_itc["iamt"] += flt(d["iamt"], 2) - net_itc["camt"] += flt(d["camt"], 2) - net_itc["samt"] += flt(d["samt"], 2) - net_itc["csamt"] += flt(d["csamt"], 2) + for key in ['iamt', 'camt', 'samt', 'csamt']: + net_itc[key] += flt(d[key], 2) for account_head in self.account_heads: itc_inelg = self.report_dict["itc_elg"]["itc_inelg"][1] - itc_inelg["iamt"] = flt(itc_details.get(("Ineligible", "N", account_head.get("igst_account")), {}).get("amount"), 2) - itc_inelg["camt"] = flt(itc_details.get(("Ineligible", "N", account_head.get("cgst_account")), {}).get("amount"), 2) - itc_inelg["samt"] = flt(itc_details.get(("Ineligible", "N", account_head.get("sgst_account")), {}).get("amount"), 2) - itc_inelg["csamt"] = flt(itc_details.get(("Ineligible", "N", account_head.get("cess_account")), {}).get("amount"), 2) + for key in [['iamt', 'igst_account'], ['camt', 'cgst_account'], ['samt', 'sgst_account'], ['csamt', 'cess_account']]: + itc_inelg[key[0]] = flt(itc_details.get(("Ineligible", "N", account_head.get(key[1])), {}).get("amount"), 2) def prepare_data(self, doctype, tax_details, supply_type, supply_category, gst_category_list, reverse_charge="N"): From d6f9a51cbb2724dd6a3675c8c85b5c40317255cb Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Sun, 14 Jun 2020 14:06:12 +0530 Subject: [PATCH 359/608] fix: item none not found while making sales invoice using opening invoice creation tool --- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 8b5d4d110cc..5e8279bb08d 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -582,14 +582,14 @@ class SalesInvoice(SellingController): def validate_item_code(self): for d in self.get('items'): - if not d.item_code: + if not d.item_code and self.is_opening == "No": msgprint(_("Item Code required at Row No {0}").format(d.idx), raise_exception=True) def validate_warehouse(self): super(SalesInvoice, self).validate_warehouse() for d in self.get_item_list(): - if not d.warehouse and frappe.get_cached_value("Item", d.item_code, "is_stock_item"): + if not d.warehouse and d.item_code and frappe.get_cached_value("Item", d.item_code, "is_stock_item"): frappe.throw(_("Warehouse required for stock Item {0}").format(d.item_code)) def validate_delivery_note(self): From 6c4a24a6c31b5d2a04757191afa1a4fa5670fdc1 Mon Sep 17 00:00:00 2001 From: Abhishek Balam Date: Mon, 15 Jun 2020 11:36:56 +0530 Subject: [PATCH 360/608] cleanup --- .../customer_acquisition_and_loyalty.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py b/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py index 8967c6bad9f..d10b1ca88f6 100644 --- a/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py +++ b/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py @@ -175,11 +175,9 @@ def get_customer_stats(filters, tree_view=False): key = si.territory if tree_view else si.posting_date.strftime('%Y-%m') new_or_repeat = 'new' if si.customer not in customers else 'repeat' - customers_in.setdefault(key, {'new': [0, 0.0], 'repeat': [0, 0.0]}) - revenue_condition = (filters.from_date <= si.posting_date.strftime('%Y-%m-%d')) - if revenue_condition: + if filters.from_date <= si.posting_date.strftime('%Y-%m-%d'): customers_in[key][new_or_repeat][0] += 1 customers_in[key][new_or_repeat][1] += si.base_grand_total if new_or_repeat == 'new': From 359e934cd23ca31839a540791c5694680203543b Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 15 Jun 2020 11:32:42 +0530 Subject: [PATCH 361/608] fix: Travis --- erpnext/buying/doctype/purchase_order/test_purchase_order.py | 1 + erpnext/controllers/accounts_controller.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py index 3d6cba891a6..813286f7fa4 100644 --- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py @@ -200,6 +200,7 @@ class TestPurchaseOrder(unittest.TestCase): # add new item trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 100, 'qty' : 2}]) self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Purchase Order', trans_item, po.name) + frappe.set_user("Administrator") def test_update_qty(self): po = create_purchase_order() diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index c75aff9264e..837ffe3c8cb 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1234,7 +1234,7 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil if parent_doctype == 'Sales Order': prev_date, new_date = child_item.get("delivery_date"), d.get("delivery_date") elif parent_doctype == 'Purchase Order': - prev_date, new_date = child_item.get("schedule_date") == d.get("schedule_date") + prev_date, new_date = child_item.get("schedule_date"), d.get("schedule_date") rate_unchanged = prev_rate == new_rate qty_unchanged = prev_qty == new_qty From 817cbc4b48f7d00bcbfe84c6e2b600e355cce8db Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 15 Jun 2020 12:07:04 +0530 Subject: [PATCH 362/608] fix: Minor fixes in cost center --- erpnext/accounts/doctype/cost_center/cost_center.js | 12 +++++++++--- .../accounts/doctype/cost_center/cost_center.json | 2 +- erpnext/accounts/utils.py | 4 ++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/cost_center/cost_center.js b/erpnext/accounts/doctype/cost_center/cost_center.js index f341f782078..ee23b1be5c5 100644 --- a/erpnext/accounts/doctype/cost_center/cost_center.js +++ b/erpnext/accounts/doctype/cost_center/cost_center.js @@ -71,8 +71,13 @@ frappe.ui.form.on('Cost Center', { "label": "Cost Center Number", "fieldname": "cost_center_number", "fieldtype": "Data", - "reqd": 1, "default": frm.doc.cost_center_number + }, + { + "label": __("Merge with existing"), + "fieldname": "merge", + "fieldtype": "Check", + "default": 0 } ], primary_action: function() { @@ -87,8 +92,9 @@ frappe.ui.form.on('Cost Center', { args: { docname: frm.doc.name, cost_center_name: data.cost_center_name, - cost_center_number: data.cost_center_number, - company: frm.doc.company + cost_center_number: cstr(data.cost_center_number), + company: frm.doc.company, + merge: data.merge }, callback: function(r) { frappe.dom.unfreeze(); diff --git a/erpnext/accounts/doctype/cost_center/cost_center.json b/erpnext/accounts/doctype/cost_center/cost_center.json index c9bbbabe798..9c3573b18f9 100644 --- a/erpnext/accounts/doctype/cost_center/cost_center.json +++ b/erpnext/accounts/doctype/cost_center/cost_center.json @@ -146,7 +146,7 @@ "idx": 1, "is_tree": 1, "links": [], - "modified": "2020-04-29 16:09:30.025214", + "modified": "2020-06-12 16:09:30.025214", "modified_by": "Administrator", "module": "Accounts", "name": "Cost Center", diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 5165495786d..176370c6b6c 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -817,7 +817,7 @@ def create_payment_gateway_account(gateway): pass @frappe.whitelist() -def update_cost_center(docname, cost_center_name, cost_center_number, company): +def update_cost_center(docname, cost_center_name, cost_center_number, company, merge): ''' Renames the document by adding the number as a prefix to the current name and updates all transaction where it was present. @@ -833,7 +833,7 @@ def update_cost_center(docname, cost_center_name, cost_center_number, company): new_name = get_autoname_with_number(cost_center_number, cost_center_name, docname, company) if docname != new_name: - frappe.rename_doc("Cost Center", docname, new_name, force=1) + frappe.rename_doc("Cost Center", docname, new_name, force=1, merge=merge) return new_name def validate_field_number(doctype_name, docname, number_value, company, field_name): From b7e94cc742c6407bb5f223ce9f75ef52583332f0 Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 15 Jun 2020 11:32:42 +0530 Subject: [PATCH 363/608] fix: Travis --- erpnext/buying/doctype/purchase_order/test_purchase_order.py | 1 + erpnext/controllers/accounts_controller.py | 2 +- erpnext/selling/doctype/sales_order/test_sales_order.py | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py index 3d6cba891a6..813286f7fa4 100644 --- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py @@ -200,6 +200,7 @@ class TestPurchaseOrder(unittest.TestCase): # add new item trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 100, 'qty' : 2}]) self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Purchase Order', trans_item, po.name) + frappe.set_user("Administrator") def test_update_qty(self): po = create_purchase_order() diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index c75aff9264e..837ffe3c8cb 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1234,7 +1234,7 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil if parent_doctype == 'Sales Order': prev_date, new_date = child_item.get("delivery_date"), d.get("delivery_date") elif parent_doctype == 'Purchase Order': - prev_date, new_date = child_item.get("schedule_date") == d.get("schedule_date") + prev_date, new_date = child_item.get("schedule_date"), d.get("schedule_date") rate_unchanged = prev_rate == new_rate qty_unchanged = prev_qty == new_qty diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index 90f9b094d1e..74e742fabbb 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -415,6 +415,7 @@ class TestSalesOrder(unittest.TestCase): # add new item trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 100, 'qty' : 2}]) self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Sales Order', trans_item, so.name) + frappe.set_user("Administrator") def test_warehouse_user(self): frappe.permissions.add_user_permission("Warehouse", "_Test Warehouse 1 - _TC", "test@example.com") From 0d880079b197c15374a7fe81f543d58be70ae24c Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 15 Jun 2020 12:23:24 +0530 Subject: [PATCH 364/608] fix: Validation for group cost center --- erpnext/accounts/doctype/gl_entry/gl_entry.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py index 291aff3f5ad..645da341a3e 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.py +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import frappe, erpnext from frappe import _ -from frappe.utils import flt, fmt_money, getdate, formatdate +from frappe.utils import flt, fmt_money, getdate, formatdate, cint from frappe.model.document import Document from frappe.model.naming import set_name_from_naming_options from frappe.model.meta import get_field_precision @@ -134,10 +134,17 @@ class GLEntry(Document): return self.cost_center_company[self.cost_center] + def _check_is_group(): + return cint(frappe.get_cached_value('Cost Center', self.cost_center, 'is_group')) + if self.cost_center and _get_cost_center_company() != self.company: frappe.throw(_("{0} {1}: Cost Center {2} does not belong to Company {3}") .format(self.voucher_type, self.voucher_no, self.cost_center, self.company)) + if self.cost_center and _check_is_group(): + frappe.throw(_("""{0} {1}: Cost Center {2} is a group cost center and group cost centers cannot + be used in transactions""").format(self.voucher_type, self.voucher_no, frappe.bold(self.cost_center))) + def validate_party(self): validate_party_frozen_disabled(self.party_type, self.party) From a2cf79da0f5528e7e5ba38511b8f29075786ad1c Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Mon, 15 Jun 2020 12:28:31 +0530 Subject: [PATCH 365/608] fix: allow to enter Releaving date if status = Left --- erpnext/hr/doctype/employee/employee.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/hr/doctype/employee/employee.json b/erpnext/hr/doctype/employee/employee.json index f575765f69b..2c2b2f6a17b 100644 --- a/erpnext/hr/doctype/employee/employee.json +++ b/erpnext/hr/doctype/employee/employee.json @@ -205,7 +205,7 @@ "label": "Status", "oldfieldname": "status", "oldfieldtype": "Select", - "options": "\nActive\nLeft", + "options": "Active\nLeft", "reqd": 1, "search_index": 1 }, @@ -667,6 +667,7 @@ "oldfieldtype": "Date" }, { + "depends_on": "eval:doc.status == \"Left\"", "fieldname": "relieving_date", "fieldtype": "Date", "label": "Relieving Date", @@ -803,7 +804,7 @@ "idx": 24, "image_field": "image", "links": [], - "modified": "2020-05-05 18:51:03.152503", + "modified": "2020-06-15 12:26:30.003741", "modified_by": "Administrator", "module": "HR", "name": "Employee", From 45b6fed029a2c22f064e372a07b11f005f52ee30 Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Mon, 15 Jun 2020 12:44:55 +0530 Subject: [PATCH 366/608] fix: added some standard filters in expense cliam --- erpnext/hr/doctype/expense_claim/expense_claim.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.json b/erpnext/hr/doctype/expense_claim/expense_claim.json index 96baaab5950..fa28470af89 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.json +++ b/erpnext/hr/doctype/expense_claim/expense_claim.json @@ -66,6 +66,7 @@ "fieldname": "employee", "fieldtype": "Link", "in_global_search": 1, + "in_standard_filter": 1, "label": "From Employee", "oldfieldname": "employee", "oldfieldtype": "Link", @@ -164,6 +165,7 @@ "default": "Today", "fieldname": "posting_date", "fieldtype": "Date", + "in_standard_filter": 1, "label": "Posting Date", "oldfieldname": "posting_date", "oldfieldtype": "Date", @@ -236,6 +238,7 @@ { "fieldname": "company", "fieldtype": "Link", + "in_standard_filter": 1, "label": "Company", "oldfieldname": "company", "oldfieldtype": "Link", @@ -368,7 +371,7 @@ "idx": 1, "is_submittable": 1, "links": [], - "modified": "2019-12-14 23:52:05.388458", + "modified": "2020-06-15 12:43:04.099803", "modified_by": "Administrator", "module": "HR", "name": "Expense Claim", From 2957a631db9c4ed3c9d417aaabedf8e7b4722cfb Mon Sep 17 00:00:00 2001 From: P-Froggy <60393001+P-Froggy@users.noreply.github.com> Date: Mon, 15 Jun 2020 10:11:16 +0200 Subject: [PATCH 367/608] fix: Validation of Purchase Order against Material Request missing (#22192) Validation of Purchase Order and Purchase Order Item against the linked Material Request Item was missing, so it was possible to exchange items in the purcahse order against others and still fullfill the material request. Co-authored-by: Marica --- erpnext/buying/doctype/purchase_order/purchase_order.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index f62df20ae1a..c7efb8a1a17 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -71,6 +71,15 @@ class PurchaseOrder(BuyingController): "compare_fields": [["project", "="], ["item_code", "="], ["uom", "="], ["conversion_factor", "="]], "is_child_table": True + }, + "Material Request": { + "ref_dn_field": "material_request", + "compare_fields": [["company", "="]], + }, + "Material Request Item": { + "ref_dn_field": "material_request_item", + "compare_fields": [["project", "="], ["item_code", "="]], + "is_child_table": True } }) From 30487bd85430b686c957943982eddbdfd7b95a4b Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 15 Jun 2020 13:49:58 +0530 Subject: [PATCH 368/608] fix: codacy issues --- erpnext/support/doctype/issue/issue.py | 2 +- .../doctype/service_level_agreement/service_level_agreement.py | 1 - .../service_level_agreement/test_service_level_agreement.py | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index a23fe0564f5..883e603fd31 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -7,7 +7,7 @@ import json from frappe import _ from frappe import utils from frappe.model.document import Document -from frappe.utils import now, time_diff_in_hours, now_datetime, getdate, get_weekdays, add_to_date, today, get_time, get_datetime, time_diff_in_seconds, time_diff +from frappe.utils import time_diff_in_hours, now_datetime, getdate, get_weekdays, add_to_date, today, get_time, get_datetime, time_diff_in_seconds, time_diff from datetime import datetime, timedelta from frappe.model.mapper import get_mapped_doc from frappe.utils.user import is_website_user diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py index 530230e1e84..c6923157064 100644 --- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py +++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py @@ -7,7 +7,6 @@ import frappe from frappe.model.document import Document from frappe import _ from frappe.utils import getdate, get_weekdays -from datetime import datetime class ServiceLevelAgreement(Document): diff --git a/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py index 0746a9c73ef..07ef368cbe3 100644 --- a/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py +++ b/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py @@ -236,7 +236,6 @@ def create_service_level_agreements_for_issues(): def make_holiday_list(): holiday_list = frappe.db.exists("Holiday List", "__Test Holiday List") if not holiday_list: - now = frappe.utils.now_datetime() holiday_list = frappe.get_doc({ "doctype": "Holiday List", "holiday_list_name": "__Test Holiday List", From 096792791ba459ef83ae8e1e9ecf5cec6488c9e6 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 15 Jun 2020 14:07:06 +0530 Subject: [PATCH 369/608] fix(patch): reload child tables --- erpnext/patches/v13_0/update_sla_enhancements.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/patches/v13_0/update_sla_enhancements.py b/erpnext/patches/v13_0/update_sla_enhancements.py index 3eb0411f827..c156ba95772 100644 --- a/erpnext/patches/v13_0/update_sla_enhancements.py +++ b/erpnext/patches/v13_0/update_sla_enhancements.py @@ -15,7 +15,9 @@ def execute(): }) frappe.reload_doc('support', 'doctype', 'service_level_agreement') + frappe.reload_doc('support', 'doctype', 'pause_sla_on_status') frappe.reload_doc('support', 'doctype', 'service_level_priority') + frappe.reload_doc('support', 'doctype', 'service_day') for entry in sla_details: values = frappe.db.get_value('Service Level', entry.service_level, ['holiday_list', 'employee_group']) From 8a0058787ed054f2951815b2c297df9212675706 Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 15 Jun 2020 14:40:39 +0530 Subject: [PATCH 370/608] fix: Wrong key sent to gte_valuation_rate --- erpnext/controllers/buying_controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index 608e537e1bb..89b48f07ee8 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -349,7 +349,7 @@ class BuyingController(StockController): }) if not rm.rate: - rm.rate = get_valuation_rate(raw_material_data.item_code, self.supplier_warehouse, + rm.rate = get_valuation_rate(raw_material_data.rm_item_code, self.supplier_warehouse, self.doctype, self.name, currency=self.company_currency, company=self.company) rm.amount = qty * flt(rm.rate) From c2496a36007dc4262275af5c0257fd98a9a6db7b Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 15 Jun 2020 15:18:49 +0530 Subject: [PATCH 371/608] fix: Asset maintenance test --- erpnext/assets/doctype/asset_maintenance/asset_maintenance.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py index d6adde6a371..1869a29c8dd 100644 --- a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py +++ b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py @@ -41,7 +41,7 @@ def assign_tasks(asset_maintenance_name, assign_to_member, maintenance_task, nex team_member = frappe.db.get_value('User', assign_to_member, "email") args = { 'doctype' : 'Asset Maintenance', - 'assign_to' : team_member, + 'assign_to' : [team_member], 'name' : asset_maintenance_name, 'description' : maintenance_task, 'date' : next_due_date From a66ce050ff0e52734d39b29f0936e3919b37e221 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 15 Jun 2020 16:03:12 +0530 Subject: [PATCH 372/608] fix: Minor fixes in loan --- erpnext/loan_management/doctype/loan/loan.py | 4 +++- .../loan_management/doctype/loan_repayment/loan_repayment.py | 2 +- .../doctype/loan_security_price/loan_security_price.json | 5 ++++- .../loan_security_shortfall/loan_security_shortfall.py | 4 +++- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/erpnext/loan_management/doctype/loan/loan.py b/erpnext/loan_management/doctype/loan/loan.py index 76e10e5ddd2..4e805d4a27d 100644 --- a/erpnext/loan_management/doctype/loan/loan.py +++ b/erpnext/loan_management/doctype/loan/loan.py @@ -235,8 +235,10 @@ def make_repayment_entry(loan, applicant_type, applicant, loan_type, company, as @frappe.whitelist() def create_loan_security_unpledge(loan, applicant_type, applicant, company, as_dict=1): loan_security_pledge_details = frappe.db.sql(""" - SELECT p.parent, p.loan_security, p.qty as qty FROM `tabLoan Security Pledge` lsp , `tabPledge` p + SELECT p.loan_security, sum(p.qty) as qty + FROM `tabLoan Security Pledge` lsp , `tabPledge` p WHERE p.parent = lsp.name AND lsp.loan = %s AND lsp.docstatus = 1 + GROUP BY p.loan_security """,(loan), as_dict=1) unpledge_request = frappe.new_doc("Loan Security Unpledge") diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py index c28994e280b..9605045777d 100644 --- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py +++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py @@ -116,7 +116,7 @@ class LoanRepayment(AccountsController): def allocate_amounts(self, paid_entries): self.set('repayment_details', []) self.principal_amount_paid = 0 - interest_paid = 0 + interest_paid = self.amount_paid - self.penalty_amount if self.amount_paid - self.penalty_amount > 0 and paid_entries: interest_paid = self.amount_paid - self.penalty_amount diff --git a/erpnext/loan_management/doctype/loan_security_price/loan_security_price.json b/erpnext/loan_management/doctype/loan_security_price/loan_security_price.json index db260a4a9e9..a55b482bd66 100644 --- a/erpnext/loan_management/doctype/loan_security_price/loan_security_price.json +++ b/erpnext/loan_management/doctype/loan_security_price/loan_security_price.json @@ -1,4 +1,5 @@ { + "actions": [], "autoname": "LM-LSP-.####", "creation": "2019-09-03 18:20:31.382887", "doctype": "DocType", @@ -46,6 +47,7 @@ "fieldtype": "Currency", "in_list_view": 1, "label": "Loan Security Price", + "options": "Company:company:default_currency", "reqd": 1 }, { @@ -79,7 +81,8 @@ "read_only": 1 } ], - "modified": "2019-10-26 09:46:46.069667", + "links": [], + "modified": "2020-06-11 03:41:33.900340", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan Security Price", diff --git a/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py b/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py index 308c4385d35..99cb0b3d620 100644 --- a/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py +++ b/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py @@ -19,7 +19,9 @@ def update_shortfall_status(loan, security_value): return if security_value >= loan_security_shortfall.shortfall_amount: - frappe.db.set_value("Loan Security Shortfall", loan_security_shortfall.name, "status", "Completed") + frappe.db.set_value("Loan Security Shortfall", loan_security_shortfall.name, { + "status", "Completed", + "shortfall_value": loan_security_shortfall.shortfall_amount}) else: frappe.db.set_value("Loan Security Shortfall", loan_security_shortfall.name, "shortfall_amount", loan_security_shortfall.shortfall_amount - security_value) From ae862c993ba0123cb19217c2a8b108d1f0297a37 Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 15 Jun 2020 17:38:47 +0530 Subject: [PATCH 373/608] feat: Multi UOM support in Request for Quotation --- .../request_for_quotation.py | 2 + .../test_request_for_quotation.py | 48 ++++++++++++++----- .../request_for_quotation_item.json | 43 ++++++++++++++--- erpnext/patches.txt | 1 + erpnext/patches/v12_0/set_multi_uom_in_rfq.py | 29 +++++++++++ 5 files changed, 104 insertions(+), 19 deletions(-) create mode 100644 erpnext/patches/v12_0/set_multi_uom_in_rfq.py diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py index 95db33b0f8f..dfdb487f9e0 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py @@ -25,6 +25,7 @@ class RequestforQuotation(BuyingController): self.validate_duplicate_supplier() self.validate_supplier_list() validate_for_items(self) + super(RequestforQuotation, self).set_qty_as_per_stock_uom() self.update_email_id() def validate_duplicate_supplier(self): @@ -278,6 +279,7 @@ def create_rfq_items(sq_doc, supplier, data): "description": data.description, "qty": data.qty, "rate": data.rate, + "conversion_factor": data.conversion_factor if data.conversion_factor else None, "supplier_part_no": frappe.db.get_value("Item Supplier", {'parent': data.item_code, 'supplier': supplier}, "supplier_part_no"), "warehouse": data.warehouse or '', "request_for_quotation_item": data.name, diff --git a/erpnext/buying/doctype/request_for_quotation/test_request_for_quotation.py b/erpnext/buying/doctype/request_for_quotation/test_request_for_quotation.py index dbd9f022789..3de9526c4f2 100644 --- a/erpnext/buying/doctype/request_for_quotation/test_request_for_quotation.py +++ b/erpnext/buying/doctype/request_for_quotation/test_request_for_quotation.py @@ -6,12 +6,14 @@ from __future__ import unicode_literals import unittest import frappe -from erpnext.templates.pages.rfq import check_supplier_has_docname_access from frappe.utils import nowdate +from erpnext.stock.doctype.item.test_item import make_item +from erpnext.templates.pages.rfq import check_supplier_has_docname_access +from erpnext.buying.doctype.request_for_quotation.request_for_quotation import make_supplier_quotation +from erpnext.buying.doctype.request_for_quotation.request_for_quotation import create_supplier_quotation class TestRequestforQuotation(unittest.TestCase): def test_quote_status(self): - from erpnext.buying.doctype.request_for_quotation.request_for_quotation import make_supplier_quotation rfq = make_request_for_quotation() self.assertEqual(rfq.get('suppliers')[0].quote_status, 'Pending') @@ -31,7 +33,6 @@ class TestRequestforQuotation(unittest.TestCase): self.assertEqual(rfq.get('suppliers')[1].quote_status, 'No Quote') def test_make_supplier_quotation(self): - from erpnext.buying.doctype.request_for_quotation.request_for_quotation import make_supplier_quotation rfq = make_request_for_quotation() sq = make_supplier_quotation(rfq.name, rfq.get('suppliers')[0].supplier) @@ -51,15 +52,13 @@ class TestRequestforQuotation(unittest.TestCase): self.assertEqual(sq1.get('items')[0].qty, 5) def test_make_supplier_quotation_with_special_characters(self): - from erpnext.buying.doctype.request_for_quotation.request_for_quotation import make_supplier_quotation - frappe.delete_doc_if_exists("Supplier", "_Test Supplier '1", force=1) supplier = frappe.new_doc("Supplier") supplier.supplier_name = "_Test Supplier '1" supplier.supplier_group = "_Test Supplier Group" supplier.insert() - rfq = make_request_for_quotation(supplier_wt_appos) + rfq = make_request_for_quotation(supplier_data=supplier_wt_appos) sq = make_supplier_quotation(rfq.name, supplier_wt_appos[0].get("supplier")) sq.submit() @@ -76,7 +75,6 @@ class TestRequestforQuotation(unittest.TestCase): frappe.form_dict.name = None def test_make_supplier_quotation_from_portal(self): - from erpnext.buying.doctype.request_for_quotation.request_for_quotation import create_supplier_quotation rfq = make_request_for_quotation() rfq.get('items')[0].rate = 100 rfq.supplier = rfq.suppliers[0].supplier @@ -90,12 +88,34 @@ class TestRequestforQuotation(unittest.TestCase): self.assertEqual(supplier_quotation_doc.get('items')[0].qty, 5) self.assertEqual(supplier_quotation_doc.get('items')[0].amount, 500) + def test_make_multi_uom_supplier_quotation(self): + item_code = "_Test Multi UOM RFQ Item" + if not frappe.db.exists('Item', item_code): + item = make_item(item_code, {'stock_uom': '_Test UOM'}) + row = item.append('uoms', { + 'uom': 'Kg', + 'conversion_factor': 2 + }) + row.db_update() -def make_request_for_quotation(supplier_data=None): + rfq = make_request_for_quotation(item_code="_Test Multi UOM RFQ Item", uom="Kg", conversion_factor=2) + rfq.get('items')[0].rate = 100 + rfq.supplier = rfq.suppliers[0].supplier + + self.assertEqual(rfq.items[0].stock_qty, 10) + + supplier_quotation_name = create_supplier_quotation(rfq) + supplier_quotation = frappe.get_doc('Supplier Quotation', supplier_quotation_name) + + self.assertEqual(supplier_quotation.items[0].qty, 5) + self.assertEqual(supplier_quotation.items[0].stock_qty, 10) + +def make_request_for_quotation(**args): """ :param supplier_data: List containing supplier data """ - supplier_data = supplier_data if supplier_data else get_supplier_data() + args = frappe._dict(args) + supplier_data = args.get("supplier_data") if args.get("supplier_data") else get_supplier_data() rfq = frappe.new_doc('Request for Quotation') rfq.transaction_date = nowdate() rfq.status = 'Draft' @@ -106,11 +126,13 @@ def make_request_for_quotation(supplier_data=None): rfq.append('suppliers', data) rfq.append("items", { - "item_code": "_Test Item", + "item_code": args.item_code or "_Test Item", "description": "_Test Item", - "uom": "_Test UOM", - "qty": 5, - "warehouse": "_Test Warehouse - _TC", + "uom": args.uom or "_Test UOM", + "stock_uom": args.stock_uom or "_Test UOM", + "qty": args.qty or 5, + "conversion_factor": args.conversion_factor or 1.0, + "warehouse": args.warehouse or "_Test Warehouse - _TC", "schedule_date": nowdate() }) diff --git a/erpnext/buying/doctype/request_for_quotation_item/request_for_quotation_item.json b/erpnext/buying/doctype/request_for_quotation_item/request_for_quotation_item.json index 0159df962ec..408f49f5233 100644 --- a/erpnext/buying/doctype/request_for_quotation_item/request_for_quotation_item.json +++ b/erpnext/buying/doctype/request_for_quotation_item/request_for_quotation_item.json @@ -1,4 +1,5 @@ { + "actions": [], "autoname": "hash", "creation": "2016-02-25 08:04:02.452958", "doctype": "DocType", @@ -9,6 +10,7 @@ "supplier_part_no", "column_break_3", "item_name", + "schedule_date", "section_break_5", "description", "item_group", @@ -18,9 +20,11 @@ "image_view", "quantity", "qty", + "stock_uom", "col_break2", - "schedule_date", "uom", + "conversion_factor", + "stock_qty", "warehouse_and_reference", "warehouse", "project_name", @@ -33,7 +37,7 @@ "fields": [ { "bold": 1, - "columns": 3, + "columns": 2, "fieldname": "item_code", "fieldtype": "Link", "in_list_view": 1, @@ -98,7 +102,7 @@ { "fieldname": "quantity", "fieldtype": "Section Break", - "label": "Quantity" + "label": "Quantity & Stock" }, { "bold": 1, @@ -129,12 +133,12 @@ { "fieldname": "uom", "fieldtype": "Link", + "in_list_view": 1, "label": "UOM", "oldfieldname": "uom", "oldfieldtype": "Link", "options": "UOM", "print_width": "100px", - "read_only": 1, "reqd": 1, "width": "100px" }, @@ -144,7 +148,7 @@ "label": "Warehouse and Reference" }, { - "columns": 3, + "columns": 2, "fieldname": "warehouse", "fieldtype": "Link", "in_list_view": 1, @@ -202,6 +206,7 @@ }, { "allow_on_submit": 1, + "default": "0", "fieldname": "page_break", "fieldtype": "Check", "label": "Page Break", @@ -219,10 +224,36 @@ { "fieldname": "section_break_23", "fieldtype": "Section Break" + }, + { + "fieldname": "stock_uom", + "fieldtype": "Link", + "label": "Stock UOM", + "options": "UOM", + "print_hide": 1, + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "conversion_factor", + "fieldtype": "Float", + "label": "UOM Conversion Factor", + "print_hide": 1, + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "stock_qty", + "fieldtype": "Float", + "label": "Qty as per Stock UOM", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 } ], "istable": 1, - "modified": "2019-05-01 17:50:23.703801", + "links": [], + "modified": "2020-06-12 19:10:36.333441", "modified_by": "Administrator", "module": "Buying", "name": "Request for Quotation Item", diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 1f5d4d563ae..e999cfedc10 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -696,3 +696,4 @@ execute:frappe.rename_doc("Desk Page", "Loan Management", "Loan", force=True) erpnext.patches.v12_0.update_uom_conversion_factor erpnext.patches.v13_0.delete_old_purchase_reports erpnext.patches.v12_0.set_italian_import_supplier_invoice_permissions +erpnext.patches.v12_0.set_multi_uom_in_rfq diff --git a/erpnext/patches/v12_0/set_multi_uom_in_rfq.py b/erpnext/patches/v12_0/set_multi_uom_in_rfq.py new file mode 100644 index 00000000000..50725bb69dd --- /dev/null +++ b/erpnext/patches/v12_0/set_multi_uom_in_rfq.py @@ -0,0 +1,29 @@ +# Copyright (c) 2017, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe +from frappe.utils import flt +from erpnext.stock.get_item_details import get_conversion_factor + +def execute(): + frappe.reload_doc('buying', 'doctype', 'request_for_quotation_item') + + for rfq_item in frappe.db.sql("""SELECT name, item_code, uom, qty FROM `tabRequest for Quotation Item` WHERE docstatus<2""", as_dict=1): + item_code, uom, qty = rfq_item.get("item_code"), rfq_item.get("uom"), rfq_item.get("qty") + conversion_factor = get_conversion_factor(item_code, uom).get("conversion_factor") or 1.0 + + filters = { + "name" : rfq_item.get("name"), + "stock_uom" : frappe.db.get_value("Item", item_code, "stock_uom"), + "conversion_factor" : conversion_factor, + "stock_qty" : flt(qty) * flt(conversion_factor) + } + + frappe.db.sql("""UPDATE `tabRequest for Quotation Item` + SET + stock_uom= %(stock_uom)s, + conversion_factor = %(conversion_factor)s, + stock_qty = %(stock_qty)s + WHERE + name = %(name)s""", filters) \ No newline at end of file From 2112834743388b08981cd14177908a9bf7d36a1e Mon Sep 17 00:00:00 2001 From: Marica Date: Tue, 16 Jun 2020 00:20:57 +0530 Subject: [PATCH 374/608] fix: Handle unavailable Variants in Website (#22195) * fix: Handle unavailable Variants in Website * fix: Fetch cart setting from argument --- erpnext/portal/product_configurator/utils.py | 3 +++ erpnext/shopping_cart/cart.py | 4 +++- erpnext/templates/generators/item/item_configure.js | 9 ++++++--- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/erpnext/portal/product_configurator/utils.py b/erpnext/portal/product_configurator/utils.py index 0993e69e042..6b6b8c579be 100644 --- a/erpnext/portal/product_configurator/utils.py +++ b/erpnext/portal/product_configurator/utils.py @@ -1,4 +1,5 @@ import frappe +from frappe.utils import cint from erpnext.portal.product_configurator.item_variants_cache import ItemVariantsCacheManager def get_field_filter_data(): @@ -243,6 +244,8 @@ def get_next_attribute_and_values(item_code, selected_attributes): else: product_info = None + product_info["allow_items_not_in_stock"] = cint(data.cart_settings.allow_items_not_in_stock) + return { 'next_attribute': next_attribute, 'valid_options_for_attributes': valid_options_for_attributes, diff --git a/erpnext/shopping_cart/cart.py b/erpnext/shopping_cart/cart.py index 7096c17fb18..5bd30ab2e89 100644 --- a/erpnext/shopping_cart/cart.py +++ b/erpnext/shopping_cart/cart.py @@ -78,8 +78,10 @@ def place_order(): if is_stock_item: item_stock = get_qty_in_stock(item.item_code, "website_warehouse") + if not cint(item_stock.in_stock): + throw(_("{1} Not in Stock").format(item.item_code)) if item.qty > item_stock.stock_qty[0][0]: - throw(_("Only {0} in stock for item {1}").format(item_stock.stock_qty[0][0], item.item_code)) + throw(_("Only {0} in Stock for item {1}").format(item_stock.stock_qty[0][0], item.item_code)) sales_order.flags.ignore_permissions = True sales_order.insert() diff --git a/erpnext/templates/generators/item/item_configure.js b/erpnext/templates/generators/item/item_configure.js index 5fd901169f0..163c955c566 100644 --- a/erpnext/templates/generators/item/item_configure.js +++ b/erpnext/templates/generators/item/item_configure.js @@ -193,14 +193,17 @@ class ItemConfigure { filtered_items_count === 1 ? filtered_items[0] : ''; + // Allow Add to Cart if adding out of stock items enabled in Shopping Cart else check stock. + const in_stock = product_info.allow_items_not_in_stock ? 1 : product_info.in_stock; + const add_to_cart = `${__('Add to cart')}`; + const product_action = in_stock ? add_to_cart : `${__('Not in Stock')}`; + const item_add_to_cart = one_item ? ` `: ''; From fa6e4f62e24c9d0f1c498131e4f198531bc14cff Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 15 Jun 2020 22:21:04 +0530 Subject: [PATCH 375/608] fix: travis for develop --- ...ve_due_advance_amount_to_pending_amount.py | 4 +- .../doctype/timesheet/test_timesheet.py | 38 +++++++++++++------ erpnext/support/doctype/issue/test_issue.py | 6 +-- 3 files changed, 32 insertions(+), 16 deletions(-) diff --git a/erpnext/patches/v12_0/move_due_advance_amount_to_pending_amount.py b/erpnext/patches/v12_0/move_due_advance_amount_to_pending_amount.py index f1ffaf9d2d4..6013eaa29c6 100644 --- a/erpnext/patches/v12_0/move_due_advance_amount_to_pending_amount.py +++ b/erpnext/patches/v12_0/move_due_advance_amount_to_pending_amount.py @@ -6,4 +6,6 @@ import frappe def execute(): ''' Move from due_advance_amount to pending_amount ''' - frappe.db.sql(''' UPDATE `tabEmployee Advance` SET pending_amount=due_advance_amount ''') + + if frappe.db.has_column("Employee Advance", "due_advance_amount"): + frappe.db.sql(''' UPDATE `tabEmployee Advance` SET pending_amount=due_advance_amount ''') diff --git a/erpnext/projects/doctype/timesheet/test_timesheet.py b/erpnext/projects/doctype/timesheet/test_timesheet.py index 32f0428fcd8..cbc624c064f 100644 --- a/erpnext/projects/doctype/timesheet/test_timesheet.py +++ b/erpnext/projects/doctype/timesheet/test_timesheet.py @@ -13,7 +13,7 @@ from erpnext.projects.doctype.timesheet.timesheet import make_salary_slip, make_ from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice from erpnext.hr.doctype.salary_structure.test_salary_structure \ import make_salary_structure, create_salary_structure_assignment - +from erpnext.hr.doctype.employee.test_employee import make_employee class TestTimesheet(unittest.TestCase): def setUp(self): @@ -25,8 +25,10 @@ class TestTimesheet(unittest.TestCase): def test_timesheet_billing_amount(self): - make_salary_structure_for_timesheet("_T-Employee-00001") - timesheet = make_timesheet("_T-Employee-00001", simulate=True, billable=1) + emp = make_employee("test_employee_6@salary.com") + + make_salary_structure_for_timesheet(emp) + timesheet = make_timesheet(emp, simulate=True, billable=1) self.assertEqual(timesheet.total_hours, 2) self.assertEqual(timesheet.total_billable_hours, 2) @@ -35,8 +37,10 @@ class TestTimesheet(unittest.TestCase): self.assertEqual(timesheet.total_billable_amount, 100) def test_timesheet_billing_amount_not_billable(self): - make_salary_structure_for_timesheet("_T-Employee-00001") - timesheet = make_timesheet("_T-Employee-00001", simulate=True, billable=0) + emp = make_employee("test_employee_6@salary.com") + + make_salary_structure_for_timesheet(emp) + timesheet = make_timesheet(emp, simulate=True, billable=0) self.assertEqual(timesheet.total_hours, 2) self.assertEqual(timesheet.total_billable_hours, 0) @@ -45,8 +49,10 @@ class TestTimesheet(unittest.TestCase): self.assertEqual(timesheet.total_billable_amount, 0) def test_salary_slip_from_timesheet(self): - salary_structure = make_salary_structure_for_timesheet("_T-Employee-00001") - timesheet = make_timesheet("_T-Employee-00001", simulate = True, billable=1) + emp = make_employee("test_employee_6@salary.com") + + salary_structure = make_salary_structure_for_timesheet(emp) + timesheet = make_timesheet(emp, simulate = True, billable=1) salary_slip = make_salary_slip(timesheet.name) salary_slip.submit() @@ -65,7 +71,9 @@ class TestTimesheet(unittest.TestCase): self.assertEqual(timesheet.status, 'Submitted') def test_sales_invoice_from_timesheet(self): - timesheet = make_timesheet("_T-Employee-00001", simulate=True, billable=1) + emp = make_employee("test_employee_6@salary.com") + + timesheet = make_timesheet(emp, simulate=True, billable=1) sales_invoice = make_sales_invoice(timesheet.name, '_Test Item', '_Test Customer') sales_invoice.due_date = nowdate() sales_invoice.submit() @@ -80,7 +88,9 @@ class TestTimesheet(unittest.TestCase): self.assertEqual(item.rate, 50.00) def test_timesheet_billing_based_on_project(self): - timesheet = make_timesheet("_T-Employee-00001", simulate=True, billable=1, project = '_Test Project', company='_Test Company') + emp = make_employee("test_employee_6@salary.com") + + timesheet = make_timesheet(emp, simulate=True, billable=1, project = '_Test Project', company='_Test Company') sales_invoice = create_sales_invoice(do_not_save=True) sales_invoice.project = '_Test Project' sales_invoice.submit() @@ -90,6 +100,8 @@ class TestTimesheet(unittest.TestCase): self.assertEqual(ts.time_logs[0].sales_invoice, sales_invoice.name) def test_timesheet_time_overlap(self): + emp = make_employee("test_employee_6@salary.com") + settings = frappe.get_single('Projects Settings') initial_setting = settings.ignore_employee_time_overlap settings.ignore_employee_time_overlap = 0 @@ -97,7 +109,7 @@ class TestTimesheet(unittest.TestCase): update_activity_type("_Test Activity Type") timesheet = frappe.new_doc("Timesheet") - timesheet.employee = "_T-Employee-00001" + timesheet.employee = emp timesheet.append( 'time_logs', { @@ -129,12 +141,14 @@ class TestTimesheet(unittest.TestCase): settings.save() def test_timesheet_std_working_hours(self): + emp = make_employee("test_employee_6@salary.com") + company = frappe.get_doc('Company', "_Test Company") company.standard_working_hours = 8 company.save() timesheet = frappe.new_doc("Timesheet") - timesheet.employee = "_T-Employee-00001" + timesheet.employee = emp timesheet.company = '_Test Company' timesheet.append( 'time_logs', @@ -156,7 +170,7 @@ class TestTimesheet(unittest.TestCase): company.save() timesheet = frappe.new_doc("Timesheet") - timesheet.employee = "_T-Employee-00001" + timesheet.employee = emp timesheet.company = '_Test Company' timesheet.append( 'time_logs', diff --git a/erpnext/support/doctype/issue/test_issue.py b/erpnext/support/doctype/issue/test_issue.py index a0048432705..fb8ceb53b21 100644 --- a/erpnext/support/doctype/issue/test_issue.py +++ b/erpnext/support/doctype/issue/test_issue.py @@ -5,7 +5,7 @@ from __future__ import unicode_literals import frappe import unittest from erpnext.support.doctype.service_level_agreement.test_service_level_agreement import create_service_level_agreements_for_issues -from frappe.utils import now_datetime, get_datetime +from frappe.utils import now_datetime, get_datetime, flt import datetime from datetime import timedelta @@ -120,7 +120,7 @@ class TestIssue(unittest.TestCase): create_communication(issue.name, "test@example.com", "Received", creation) issue.reload() - self.assertEqual(issue.total_hold_time, 2700) + self.assertEqual(flt(issue.total_hold_time, 2), 2700) self.assertEqual(issue.resolution_by, datetime.datetime(2020, 3, 4, 16, 45)) creation = datetime.datetime(2020, 3, 4, 5, 5) @@ -132,7 +132,7 @@ class TestIssue(unittest.TestCase): issue.save() issue.reload() - self.assertEqual(issue.total_hold_time, 2700) + self.assertEqual(flt(issue.total_hold_time, 2), 2700) def make_issue(creation=None, customer=None, index=0): From 58831ecc7d18f477b08e767fbc505fa11a00a9e6 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Tue, 16 Jun 2020 19:23:52 +0530 Subject: [PATCH 376/608] fix(HR): wrong shortcut in desk page (#22269) --- erpnext/hr/desk_page/hr/hr.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/hr/desk_page/hr/hr.json b/erpnext/hr/desk_page/hr/hr.json index 1c24444fdd2..12548d48a74 100644 --- a/erpnext/hr/desk_page/hr/hr.json +++ b/erpnext/hr/desk_page/hr/hr.json @@ -93,7 +93,7 @@ "idx": 0, "is_standard": 1, "label": "HR", - "modified": "2020-06-10 12:41:41.695669", + "modified": "2020-06-16 19:20:50.976045", "modified_by": "Administrator", "module": "HR", "name": "HR", @@ -126,7 +126,7 @@ }, { "label": "Salary Structure", - "link_to": "Payroll Entry", + "link_to": "Salary Structure", "type": "DocType" }, { From c159556c24dd90f18d2b5dafacd94abfacfff4d8 Mon Sep 17 00:00:00 2001 From: Kenneth Sequeira <33246109+kennethsequeira@users.noreply.github.com> Date: Wed, 17 Jun 2020 09:34:58 +0530 Subject: [PATCH 377/608] fix: typo for language in Terms description (#22270) --- .../doctype/terms_and_conditions/terms_and_conditions.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.json b/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.json index aba6a791a4e..28d1d16a051 100644 --- a/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.json +++ b/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_import": 1, "allow_rename": 1, "autoname": "field:title", @@ -49,7 +50,7 @@ "fieldname": "terms_and_conditions_help", "fieldtype": "HTML", "label": "Terms and Conditions Help", - "options": "

    Standard Terms and Conditions Example

    \n\n
    Delivery Terms for Order number {{ name }}\n\n-Order Date : {{ transaction_date }} \n-Expected Delivery Date : {{ delivery_date }}\n
    \n\n

    How to get fieldnames

    \n\n

    The fieldnames you can use in your email template are the fields in the document from which you are sending the email. You can find out the fields of any documents via Setup > Customize Form View and selecting the document type (e.g. Sales Invoice)

    \n\n

    Templating

    \n\n

    Templates are compiled using the Jinja Templating Langauge. To learn more about Jinja, read this documentation.

    " + "options": "

    Standard Terms and Conditions Example

    \n\n
    Delivery Terms for Order number {{ name }}\n\n-Order Date : {{ transaction_date }} \n-Expected Delivery Date : {{ delivery_date }}\n
    \n\n

    How to get fieldnames

    \n\n

    The fieldnames you can use in your email template are the fields in the document from which you are sending the email. You can find out the fields of any documents via Setup > Customize Form View and selecting the document type (e.g. Sales Invoice)

    \n\n

    Templating

    \n\n

    Templates are compiled using the Jinja Templating Language. To learn more about Jinja, read this documentation.

    " }, { "fieldname": "applicable_modules_section", @@ -81,7 +82,8 @@ ], "icon": "icon-legal", "idx": 1, - "modified": "2019-07-04 13:31:30.393425", + "links": [], + "modified": "2020-06-16 22:54:38.094844", "modified_by": "Administrator", "module": "Setup", "name": "Terms and Conditions", From 8d856659afec9bb6bfb17ad28a9697ffc93c0a39 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Wed, 17 Jun 2020 09:37:41 +0530 Subject: [PATCH 378/608] refactor: hide company currency fields in the routing (#22267) (cherry picked from commit fd3ff6be18b0bd5accc88aea22e3adfe48e62aff) --- erpnext/manufacturing/doctype/bom/bom.py | 1 + .../doctype/bom_operation/bom_operation.json | 8 +++++--- erpnext/manufacturing/doctype/routing/routing.js | 1 - 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 2543eec53e4..7d31a1cd15e 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -119,6 +119,7 @@ class BOM(WebsiteGenerator): "description": d.description, "time_in_mins": d.time_in_mins, "batch_size": d.batch_size, + "operating_cost": d.operating_cost, "idx": d.idx }) child.hour_rate = flt(d.hour_rate / self.conversion_rate, 2) diff --git a/erpnext/manufacturing/doctype/bom_operation/bom_operation.json b/erpnext/manufacturing/doctype/bom_operation/bom_operation.json index 3ca851d783b..0350e2cb374 100644 --- a/erpnext/manufacturing/doctype/bom_operation/bom_operation.json +++ b/erpnext/manufacturing/doctype/bom_operation/bom_operation.json @@ -78,6 +78,7 @@ "read_only": 1 }, { + "depends_on": "eval:parent.doctype == 'BOM'", "fieldname": "base_hour_rate", "fieldtype": "Currency", "label": "Base Hour Rate(Company Currency)", @@ -87,6 +88,7 @@ }, { "default": "5", + "depends_on": "eval:parent.doctype == 'BOM'", "fieldname": "base_operating_cost", "fieldtype": "Currency", "label": "Operating Cost(Company Currency)", @@ -108,12 +110,12 @@ ], "idx": 1, "istable": 1, - "modified": "2019-07-16 22:35:55.374037", - "modified_by": "govindsmenokee@gmail.com", + "modified": "2020-06-16 17:01:11.128420", + "modified_by": "Administrator", "module": "Manufacturing", "name": "BOM Operation", "owner": "Administrator", "permissions": [], "sort_field": "modified", "sort_order": "DESC" -} +} \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/routing/routing.js b/erpnext/manufacturing/doctype/routing/routing.js index 6cfd0bae5b5..d7589fa3907 100644 --- a/erpnext/manufacturing/doctype/routing/routing.js +++ b/erpnext/manufacturing/doctype/routing/routing.js @@ -44,7 +44,6 @@ frappe.ui.form.on('BOM Operation', { name: d.workstation }, callback: function (data) { - frappe.model.set_value(d.doctype, d.name, "base_hour_rate", data.message.hour_rate); frappe.model.set_value(d.doctype, d.name, "hour_rate", data.message.hour_rate); frm.events.calculate_operating_cost(frm, d); } From 0a3c34de01fe4a08db9540c3de86debc8b11d3dc Mon Sep 17 00:00:00 2001 From: Anupam Kumar Date: Wed, 17 Jun 2020 10:53:13 +0530 Subject: [PATCH 379/608] address label chages (#22137) Co-authored-by: Marica --- erpnext/patches.txt | 3 ++- .../v12_0/update_address_template_for_india.py | 12 ++++++++++++ .../regional/address_template/templates/india.html | 2 +- erpnext/shopping_cart/cart.py | 4 ++-- erpnext/templates/includes/cart/address_card.html | 2 +- erpnext/templates/includes/cart/cart_address.html | 2 +- 6 files changed, 19 insertions(+), 6 deletions(-) create mode 100644 erpnext/patches/v12_0/update_address_template_for_india.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index db9610b62ba..279c453e350 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -697,4 +697,5 @@ execute:frappe.rename_doc("Desk Page", "Loan Management", "Loan", force=True) erpnext.patches.v12_0.update_uom_conversion_factor erpnext.patches.v13_0.delete_old_purchase_reports erpnext.patches.v12_0.set_italian_import_supplier_invoice_permissions -erpnext.patches.v13_0.update_sla_enhancements \ No newline at end of file +erpnext.patches.v13_0.update_sla_enhancements +erpnext.patches.v12_0.update_address_template_for_india diff --git a/erpnext/patches/v12_0/update_address_template_for_india.py b/erpnext/patches/v12_0/update_address_template_for_india.py new file mode 100644 index 00000000000..0d582da4b5c --- /dev/null +++ b/erpnext/patches/v12_0/update_address_template_for_india.py @@ -0,0 +1,12 @@ +# Copyright (c) 2020, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe +from erpnext.regional.address_template.setup import set_up_address_templates + +def execute(): + if frappe.db.get_value('Company', {'country': 'India'}, 'name'): + address_template = frappe.db.get_value('Address Template', 'India', 'template') + if not address_template or "gstin" not in address_template: + set_up_address_templates(default_country='India') diff --git a/erpnext/regional/address_template/templates/india.html b/erpnext/regional/address_template/templates/india.html index ffb9d0547e0..5d2329efffa 100644 --- a/erpnext/regional/address_template/templates/india.html +++ b/erpnext/regional/address_template/templates/india.html @@ -1,7 +1,7 @@ {{ address_line1 }}
    {% if address_line2 %}{{ address_line2 }}
    {% endif -%}{{ city }}
    {% if gst_state %}{{ gst_state }}{% endif -%} {% if gst_state_number %}, State Code: {{ gst_state_number }}
    {% endif -%} -{% if pincode %}PIN: {{ pincode }}
    {% endif -%} +{% if pincode %}Postal Code: {{ pincode }}
    {% endif -%} {{ country }}
    {% if phone %}Phone: {{ phone }}
    {% endif -%} {% if fax %}Fax: {{ fax }}
    {% endif -%} diff --git a/erpnext/shopping_cart/cart.py b/erpnext/shopping_cart/cart.py index 5bd30ab2e89..a7e8388be9d 100644 --- a/erpnext/shopping_cart/cart.py +++ b/erpnext/shopping_cart/cart.py @@ -42,9 +42,9 @@ def get_cart_quotation(doc=None): return { "doc": decorate_quotation_doc(doc), - "shipping_addresses": [{"name": address.name, "display": address.display} + "shipping_addresses": [{"name": address.name, "title": address.address_title, "display": address.display} for address in addresses if address.address_type == "Shipping"], - "billing_addresses": [{"name": address.name, "display": address.display} + "billing_addresses": [{"name": address.name, "title": address.address_title, "display": address.display} for address in addresses if address.address_type == "Billing"], "shipping_rules": get_applicable_shipping_rules(party), "cart_settings": frappe.get_cached_doc("Shopping Cart Settings") diff --git a/erpnext/templates/includes/cart/address_card.html b/erpnext/templates/includes/cart/address_card.html index c91723e91e7..646210e65f1 100644 --- a/erpnext/templates/includes/cart/address_card.html +++ b/erpnext/templates/includes/cart/address_card.html @@ -3,7 +3,7 @@
    -
    {{ address.name }}
    +
    {{ address.title }}

    {{ address.display }}

    diff --git a/erpnext/templates/includes/cart/cart_address.html b/erpnext/templates/includes/cart/cart_address.html index 60de3af17bf..aa25c885fee 100644 --- a/erpnext/templates/includes/cart/cart_address.html +++ b/erpnext/templates/includes/cart/cart_address.html @@ -109,7 +109,7 @@ frappe.ready(() => { reqd: 1 }, { - label: __('Pin Code'), + label: __('Postal Code'), fieldname: 'pincode', fieldtype: 'Data' }, From 8d4ba6c7ec53674c267ebc8979ef69a3db1b2cb1 Mon Sep 17 00:00:00 2001 From: Anupam K Date: Tue, 16 Jun 2020 16:19:33 +0530 Subject: [PATCH 380/608] Adding filter in academic term --- erpnext/education/doctype/fee_structure/fee_structure.js | 8 ++++++++ .../education/doctype/fee_structure/fee_structure.json | 6 ++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/erpnext/education/doctype/fee_structure/fee_structure.js b/erpnext/education/doctype/fee_structure/fee_structure.js index 7606565fadb..f09d2efcb9e 100644 --- a/erpnext/education/doctype/fee_structure/fee_structure.js +++ b/erpnext/education/doctype/fee_structure/fee_structure.js @@ -9,6 +9,14 @@ frappe.ui.form.on('Fee Structure', { }, onload: function(frm) { + frm.set_query("academic_term", function() { + return { + "filters": { + "academic_year": frm.doc.academic_year + } + }; + }); + frm.set_query("receivable_account", function(doc) { return { filters: { diff --git a/erpnext/education/doctype/fee_structure/fee_structure.json b/erpnext/education/doctype/fee_structure/fee_structure.json index 8ff6851d90a..67e46372f82 100644 --- a/erpnext/education/doctype/fee_structure/fee_structure.json +++ b/erpnext/education/doctype/fee_structure/fee_structure.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_import": 1, "allow_rename": 1, "autoname": "naming_series:", @@ -11,8 +12,8 @@ "program", "student_category", "column_break_2", - "academic_term", "academic_year", + "academic_term", "section_break_4", "components", "section_break_6", @@ -157,7 +158,8 @@ ], "icon": "fa fa-flag", "is_submittable": 1, - "modified": "2019-05-26 09:04:17.765758", + "links": [], + "modified": "2020-06-16 15:34:57.295010", "modified_by": "Administrator", "module": "Education", "name": "Fee Structure", From c6592c880a2873adcb7bf78883e9c3f20b25e59b Mon Sep 17 00:00:00 2001 From: Anupam Kumar Date: Wed, 17 Jun 2020 12:38:31 +0530 Subject: [PATCH 381/608] fix: Student Admission (#22255) * Student Admission fix * adding check for application * adding check for application * updating error message * added date_diff for date comparision --- .../student_admission/student_admission.json | 477 ++++-------------- .../templates/student_admission.html | 13 +- .../test_student_admission.js | 4 +- .../student_admission_program.json | 294 +++-------- .../student_applicant/student_applicant.py | 21 +- .../student_applicant/student_applicant.json | 406 ++++++++------- .../generators/student_admission.html | 6 +- 7 files changed, 416 insertions(+), 805 deletions(-) diff --git a/erpnext/education/doctype/student_admission/student_admission.json b/erpnext/education/doctype/student_admission/student_admission.json index b3c10d43316..1096888d4d2 100644 --- a/erpnext/education/doctype/student_admission/student_admission.json +++ b/erpnext/education/doctype/student_admission/student_admission.json @@ -1,398 +1,119 @@ { - "allow_copy": 0, - "allow_guest_to_view": 1, - "allow_import": 0, - "allow_rename": 1, - "autoname": "", - "beta": 0, - "creation": "2016-09-13 03:05:27.154713", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Document", - "editable_grid": 1, + "actions": [], + "allow_guest_to_view": 1, + "allow_rename": 1, + "creation": "2016-09-13 03:05:27.154713", + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "title", + "route", + "column_break_3", + "academic_year", + "admission_start_date", + "admission_end_date", + "published", + "enable_admission_application", + "section_break_5", + "program_details", + "introduction" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "title", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Title", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "title", + "fieldtype": "Data", + "label": "Title" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "route", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Route", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, + "fieldname": "route", + "fieldtype": "Data", + "label": "Route", + "no_copy": 1, "unique": 1 - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "application_form_route", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Application Form Route", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "academic_year", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Academic Year", + "no_copy": 1, + "options": "Academic Year", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "academic_year", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Academic Year", - "length": 0, - "no_copy": 1, - "options": "Academic Year", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "admission_start_date", + "fieldtype": "Date", + "label": "Admission Start Date", + "no_copy": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "admission_start_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Admission Start Date", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "admission_end_date", + "fieldtype": "Date", + "label": "Admission End Date", + "no_copy": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "admission_end_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Admission End Date", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "published", + "fieldtype": "Check", + "label": "Publish on website" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "published", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Publish on website", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "section_break_5", + "fieldtype": "Section Break", + "label": "Eligibility and Details" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_5", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Eligibility and Details", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "program_details", + "fieldtype": "Table", + "label": "Eligibility and Details", + "options": "Student Admission Program" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "program_details", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Eligibility and Details", - "length": 0, - "no_copy": 0, - "options": "Student Admission Program", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "introduction", + "fieldtype": "Text Editor", + "label": "Introduction" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "introduction", - "fieldtype": "Text Editor", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Introduction", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 + "default": "0", + "fieldname": "enable_admission_application", + "fieldtype": "Check", + "label": "Enable Admission Application" } - ], - "has_web_view": 1, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_published_field": "published", - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2017-11-10 18:57:34.570376", - "modified_by": "Administrator", - "module": "Education", - "name": "Student Admission", - "name_case": "", - "owner": "Administrator", + ], + "has_web_view": 1, + "is_published_field": "published", + "links": [], + "modified": "2020-06-15 20:18:38.591626", + "modified_by": "Administrator", + "module": "Education", + "name": "Student Admission", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Academics User", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Academics User", + "share": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Education", - "route": "admissions", - "show_name_in_global_search": 1, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "title", - "track_changes": 0, - "track_seen": 0 + ], + "restrict_to_domain": "Education", + "route": "admissions", + "show_name_in_global_search": 1, + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "title" } \ No newline at end of file diff --git a/erpnext/education/doctype/student_admission/templates/student_admission.html b/erpnext/education/doctype/student_admission/templates/student_admission.html index 25afaca84dc..e5a9ead31ed 100644 --- a/erpnext/education/doctype/student_admission/templates/student_admission.html +++ b/erpnext/education/doctype/student_admission/templates/student_admission.html @@ -43,8 +43,8 @@ Program/Std. - Minumum Age(DOB) - Maximum Age(DOB) + Minumum Age + Maximum Age Application Fee @@ -52,8 +52,8 @@ {% for row in program_details %} {{ row.program }} - {{ row.minimum_age }} - {{ row.maximum_age }} + {{ row.min_age }} + {{ row.max_age }} {{ row.application_fee }} {% endfor %} @@ -61,12 +61,11 @@
    {% endif %} - - {%- if application_form_route -%} + {%- if doc.enable_admission_application -%}

    + href='/student-applicant?new=1&student_admission={{doc.name}}'> {{ _("Apply Now") }}

    {% endif %} diff --git a/erpnext/education/doctype/student_admission/test_student_admission.js b/erpnext/education/doctype/student_admission/test_student_admission.js index ed794b2482e..3a0bb0b2f23 100644 --- a/erpnext/education/doctype/student_admission/test_student_admission.js +++ b/erpnext/education/doctype/student_admission/test_student_admission.js @@ -11,7 +11,7 @@ QUnit.test('Test: Student Admission', function(assert) { {admission_start_date: '2016-04-20'}, {admission_end_date: '2016-05-31'}, {title: '2016-17 Admissions'}, - {application_form_route: 'student-applicant'}, + {enable_admission_application: 1}, {introduction: 'Test intro'}, {program_details: [ [ @@ -28,7 +28,7 @@ QUnit.test('Test: Student Admission', function(assert) { assert.ok(cur_frm.doc.admission_start_date == '2016-04-20'); assert.ok(cur_frm.doc.admission_end_date == '2016-05-31'); assert.ok(cur_frm.doc.title == '2016-17 Admissions'); - assert.ok(cur_frm.doc.application_form_route == 'student-applicant'); + assert.ok(cur_frm.doc.enable_admission_application == 1); assert.ok(cur_frm.doc.introduction == 'Test intro'); assert.ok(cur_frm.doc.program_details[0].program == 'Standard Test', 'Program correctly selected'); assert.ok(cur_frm.doc.program_details[0].application_fee == 1000); diff --git a/erpnext/education/doctype/student_admission_program/student_admission_program.json b/erpnext/education/doctype/student_admission_program/student_admission_program.json index 97b1bba4217..e9f041e101f 100644 --- a/erpnext/education/doctype/student_admission_program/student_admission_program.json +++ b/erpnext/education/doctype/student_admission_program/student_admission_program.json @@ -1,237 +1,77 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "", - "beta": 0, - "creation": "2017-09-15 12:59:43.207923", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "creation": "2017-09-15 12:59:43.207923", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "program", + "min_age", + "max_age", + "column_break_4", + "application_fee", + "applicant_naming_series" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "program", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Program", - "length": 0, - "no_copy": 0, - "options": "Program", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "program", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Program", + "options": "Program", + "show_days": 1, + "show_seconds": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "minimum_age", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Minimum Age", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_4", + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "maximum_age", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Maximum Age", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "application_fee", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Application Fee", + "show_days": 1, + "show_seconds": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_4", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "applicant_naming_series", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Naming Series (for Student Applicant)", + "show_days": 1, + "show_seconds": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "application_fee", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Application Fee", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "min_age", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Minimum Age", + "show_days": 1, + "show_seconds": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "applicant_naming_series", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Naming Series (for Student Applicant)", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "max_age", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Maximum Age", + "show_days": 1, + "show_seconds": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-11-04 03:37:17.408427", - "modified_by": "Administrator", - "module": "Education", - "name": "Student Admission Program", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Education", - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + ], + "istable": 1, + "links": [], + "modified": "2020-06-10 23:06:30.037404", + "modified_by": "Administrator", + "module": "Education", + "name": "Student Admission Program", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "restrict_to_domain": "Education", + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/education/doctype/student_applicant/student_applicant.py b/erpnext/education/doctype/student_applicant/student_applicant.py index ab947807dd8..211348201e3 100644 --- a/erpnext/education/doctype/student_applicant/student_applicant.py +++ b/erpnext/education/doctype/student_applicant/student_applicant.py @@ -6,7 +6,7 @@ from __future__ import print_function, unicode_literals import frappe from frappe import _ from frappe.model.document import Document -from frappe.utils import getdate +from frappe.utils import getdate, add_years, nowdate, date_diff class StudentApplicant(Document): def autoname(self): @@ -31,6 +31,7 @@ class StudentApplicant(Document): def validate(self): self.validate_dates() self.title = " ".join(filter(None, [self.first_name, self.middle_name, self.last_name])) + if self.student_admission and self.program and self.date_of_birth: self.validation_from_student_admission() @@ -48,16 +49,16 @@ class StudentApplicant(Document): frappe.throw(_("Please select Student Admission which is mandatory for the paid student applicant")) def validation_from_student_admission(self): + student_admission = get_student_admission_data(self.student_admission, self.program) - # different validation for minimum and maximum age so that either min/max can also work independently. - if student_admission and student_admission.minimum_age and \ - getdate(student_admission.minimum_age) < getdate(self.date_of_birth): - frappe.throw(_("Not eligible for the admission in this program as per DOB")) + if student_admission and student_admission.min_age and \ + date_diff(nowdate(), add_years(getdate(self.date_of_birth), student_admission.min_age)) < 0: + frappe.throw(_("Not eligible for the admission in this program as per Date Of Birth")) - if student_admission and student_admission.maximum_age and \ - getdate(student_admission.maximum_age) > getdate(self.date_of_birth): - frappe.throw(_("Not eligible for the admission in this program as per DOB")) + if student_admission and student_admission.max_age and \ + date_diff(nowdate(), add_years(getdate(self.date_of_birth), student_admission.max_age)) > 0: + frappe.throw(_("Not eligible for the admission in this program as per Date Of Birth")) def on_payment_authorized(self, *args, **kwargs): @@ -65,10 +66,12 @@ class StudentApplicant(Document): def get_student_admission_data(student_admission, program): + student_admission = frappe.db.sql("""select sa.admission_start_date, sa.admission_end_date, - sap.program, sap.minimum_age, sap.maximum_age, sap.applicant_naming_series + sap.program, sap.min_age, sap.max_age, sap.applicant_naming_series from `tabStudent Admission` sa, `tabStudent Admission Program` sap where sa.name = sap.parent and sa.name = %s and sap.program = %s""", (student_admission, program), as_dict=1) + if student_admission: return student_admission[0] else: diff --git a/erpnext/education/web_form/student_applicant/student_applicant.json b/erpnext/education/web_form/student_applicant/student_applicant.json index b1ad754c327..1810f07a054 100644 --- a/erpnext/education/web_form/student_applicant/student_applicant.json +++ b/erpnext/education/web_form/student_applicant/student_applicant.json @@ -1,200 +1,248 @@ { - "accept_payment": 0, - "allow_comments": 0, - "allow_delete": 0, - "allow_edit": 1, - "allow_incomplete": 0, - "allow_multiple": 1, - "allow_print": 0, - "amount": 0.0, - "amount_based_on_field": 0, - "creation": "2016-09-22 13:10:10.792735", - "doc_type": "Student Applicant", - "docstatus": 0, - "doctype": "Web Form", - "idx": 0, - "is_standard": 1, - "login_required": 1, - "max_attachment_size": 0, - "modified": "2017-02-21 05:44:46.022738", - "modified_by": "Administrator", - "module": "Education", - "name": "student-applicant", - "owner": "Administrator", - "payment_button_label": "Buy Now", - "published": 1, - "route": "student-applicant", - "show_sidebar": 1, - "sidebar_items": [], - "success_url": "/student-applicant", - "title": "Student Applicant", + "accept_payment": 0, + "allow_comments": 0, + "allow_delete": 0, + "allow_edit": 1, + "allow_incomplete": 0, + "allow_multiple": 1, + "allow_print": 0, + "amount": 0.0, + "amount_based_on_field": 0, + "creation": "2016-09-22 13:10:10.792735", + "doc_type": "Student Applicant", + "docstatus": 0, + "doctype": "Web Form", + "idx": 0, + "is_standard": 1, + "login_required": 1, + "max_attachment_size": 0, + "modified": "2020-06-11 22:53:45.875310", + "modified_by": "Administrator", + "module": "Education", + "name": "student-applicant", + "owner": "Administrator", + "payment_button_label": "Buy Now", + "published": 1, + "route": "student-applicant", + "route_to_success_link": 0, + "show_attachments": 0, + "show_in_grid": 0, + "show_sidebar": 1, + "sidebar_items": [], + "success_url": "/student-applicant", + "title": "Student Applicant", "web_form_fields": [ { - "fieldname": "first_name", - "fieldtype": "Data", - "hidden": 0, - "label": "First Name", - "max_length": 0, - "max_value": 0, - "read_only": 0, - "reqd": 1 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "first_name", + "fieldtype": "Data", + "hidden": 0, + "label": "First Name", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 1, + "show_in_filter": 0 + }, { - "fieldname": "middle_name", - "fieldtype": "Data", - "hidden": 0, - "label": "Middle Name", - "max_length": 0, - "max_value": 0, - "read_only": 0, - "reqd": 0 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "middle_name", + "fieldtype": "Data", + "hidden": 0, + "label": "Middle Name", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, { - "fieldname": "last_name", - "fieldtype": "Data", - "hidden": 0, - "label": "Last Name", - "max_length": 0, - "max_value": 0, - "read_only": 0, - "reqd": 0 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "last_name", + "fieldtype": "Data", + "hidden": 0, + "label": "Last Name", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, { - "fieldname": "image", - "fieldtype": "Data", - "hidden": 0, - "label": "Image", - "max_length": 0, - "max_value": 0, - "read_only": 0, - "reqd": 0 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "image", + "fieldtype": "Data", + "hidden": 0, + "label": "Image", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, { - "fieldname": "program", - "fieldtype": "Link", - "hidden": 0, - "label": "Program", - "max_length": 0, - "max_value": 0, - "options": "Program", - "read_only": 0, - "reqd": 1 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "program", + "fieldtype": "Link", + "hidden": 0, + "label": "Program", + "max_length": 0, + "max_value": 0, + "options": "Program", + "read_only": 0, + "reqd": 1, + "show_in_filter": 0 + }, { - "fieldname": "academic_year", - "fieldtype": "Link", - "hidden": 0, - "label": "Academic Year", - "max_length": 0, - "max_value": 0, - "options": "Academic Year", - "read_only": 0, - "reqd": 0 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "academic_year", + "fieldtype": "Link", + "hidden": 0, + "label": "Academic Year", + "max_length": 0, + "max_value": 0, + "options": "Academic Year", + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, { - "fieldname": "date_of_birth", - "fieldtype": "Date", - "hidden": 0, - "label": "Date of Birth", - "max_length": 0, - "max_value": 0, - "read_only": 0, - "reqd": 0 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "date_of_birth", + "fieldtype": "Date", + "hidden": 0, + "label": "Date of Birth", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, { - "fieldname": "blood_group", - "fieldtype": "Select", - "hidden": 0, - "label": "Blood Group", - "max_length": 0, - "max_value": 0, - "options": "\nA+\nA-\nB+\nB-\nO+\nO-\nAB+\nAB-", - "read_only": 0, - "reqd": 0 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "blood_group", + "fieldtype": "Select", + "hidden": 0, + "label": "Blood Group", + "max_length": 0, + "max_value": 0, + "options": "\nA+\nA-\nB+\nB-\nO+\nO-\nAB+\nAB-", + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, { - "fieldname": "student_email_id", - "fieldtype": "Data", - "hidden": 0, - "label": "Student Email ID", - "max_length": 0, - "max_value": 0, - "read_only": 0, - "reqd": 0 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "student_email_id", + "fieldtype": "Data", + "hidden": 0, + "label": "Student Email ID", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, { - "fieldname": "student_mobile_number", - "fieldtype": "Data", - "hidden": 0, - "label": "Student Mobile Number", - "max_length": 0, - "max_value": 0, - "read_only": 0, - "reqd": 0 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "student_mobile_number", + "fieldtype": "Data", + "hidden": 0, + "label": "Student Mobile Number", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, { - "default": "INDIAN", - "fieldname": "nationality", - "fieldtype": "Data", - "hidden": 0, - "label": "Nationality", - "max_length": 0, - "max_value": 0, - "options": "", - "read_only": 0, - "reqd": 0 - }, + "allow_read_on_all_link_options": 0, + "default": "INDIAN", + "fieldname": "nationality", + "fieldtype": "Data", + "hidden": 0, + "label": "Nationality", + "max_length": 0, + "max_value": 0, + "options": "", + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, { - "fieldname": "address_line_1", - "fieldtype": "Data", - "hidden": 0, - "label": "Address Line 1", - "max_length": 0, - "max_value": 0, - "read_only": 0, - "reqd": 0 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "address_line_1", + "fieldtype": "Data", + "hidden": 0, + "label": "Address Line 1", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, { - "fieldname": "address_line_2", - "fieldtype": "Data", - "hidden": 0, - "label": "Address Line 2", - "max_length": 0, - "max_value": 0, - "read_only": 0, - "reqd": 0 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "address_line_2", + "fieldtype": "Data", + "hidden": 0, + "label": "Address Line 2", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, { - "fieldname": "pincode", - "fieldtype": "Data", - "hidden": 0, - "label": "Pincode", - "max_length": 0, - "max_value": 0, - "read_only": 0, - "reqd": 0 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "pincode", + "fieldtype": "Data", + "hidden": 0, + "label": "Pincode", + "max_length": 0, + "max_value": 0, + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, { - "fieldname": "guardians", - "fieldtype": "Table", - "hidden": 0, - "label": "Guardians", - "max_length": 0, - "max_value": 0, - "options": "Student Guardian", - "read_only": 0, - "reqd": 0 - }, + "allow_read_on_all_link_options": 0, + "fieldname": "guardians", + "fieldtype": "Table", + "hidden": 0, + "label": "Guardians", + "max_length": 0, + "max_value": 0, + "options": "Student Guardian", + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, { - "fieldname": "siblings", - "fieldtype": "Table", - "hidden": 0, - "label": "Siblings", - "max_length": 0, - "max_value": 0, - "options": "Student Sibling", - "read_only": 0, - "reqd": 0 + "allow_read_on_all_link_options": 0, + "fieldname": "siblings", + "fieldtype": "Table", + "hidden": 0, + "label": "Siblings", + "max_length": 0, + "max_value": 0, + "options": "Student Sibling", + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 + }, + { + "allow_read_on_all_link_options": 0, + "fieldname": "student_admission", + "fieldtype": "Link", + "hidden": 0, + "label": "Student Admission", + "max_length": 0, + "max_value": 0, + "options": "Student Admission", + "read_only": 0, + "reqd": 0, + "show_in_filter": 0 } ] } \ No newline at end of file diff --git a/erpnext/templates/generators/student_admission.html b/erpnext/templates/generators/student_admission.html index ae70df8b08b..8b153448eea 100644 --- a/erpnext/templates/generators/student_admission.html +++ b/erpnext/templates/generators/student_admission.html @@ -14,12 +14,12 @@ {%- if introduction -%}
    {{ introduction }}
    -{% endif %} +{% endif %} -{%- if application_form_route -%} +{%- if doc.enable_admission_application -%}

    + href='/student-applicant'> {{ _("Apply Now") }}

    {% endif %} From 17422a3e45fc61cf686931a89321fede0cde8141 Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 17 Jun 2020 13:46:21 +0530 Subject: [PATCH 382/608] fix: Message Formatting --- erpnext/controllers/stock_controller.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 2888c764ef7..759c6cd73eb 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -227,7 +227,9 @@ class StockController(AccountsController): def check_expense_account(self, item): if not item.get("expense_account"): - frappe.throw(_("Expense Account not set for Item {0}. Please set an Expense Account for the item in the Items table").format(item.item_code)) + frappe.throw(_("Row #{0}: Expense Account not set for Item {1}. Please set an Expense \ + Account in the Items table").format(item.idx, frappe.bold(item.item_code)), + title=_("Expense Account Missing")) else: is_expense_account = frappe.db.get_value("Account", From 8546b71358534ecfb13e519ac8d49815f0382d91 Mon Sep 17 00:00:00 2001 From: Afshan <33727827+AfshanKhan@users.noreply.github.com> Date: Wed, 17 Jun 2020 16:16:59 +0530 Subject: [PATCH 383/608] feat: /support (#22194) * added: /support * refactor code and implemented fail checks * removed unused imports * changed filter from from title to name * refactor code to move context inside main * removed unused variable * added: /support * refactor code and implemented fail checks * removed unused imports * changed filter from from title to name * refactor code to move context inside main * removed unused variable * refactor: renamed set to get Co-authored-by: Shivam Mishra Co-authored-by: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> --- .../support_settings/support_settings.json | 37 +++++++++- erpnext/www/support/__init__.py | 0 erpnext/www/support/index.html | 55 ++++++++++++++ erpnext/www/support/index.py | 74 +++++++++++++++++++ 4 files changed, 164 insertions(+), 2 deletions(-) create mode 100644 erpnext/www/support/__init__.py create mode 100644 erpnext/www/support/index.html create mode 100644 erpnext/www/support/index.py diff --git a/erpnext/support/doctype/support_settings/support_settings.json b/erpnext/support/doctype/support_settings/support_settings.json index 1c1b0c3517e..5d3d3ace59d 100644 --- a/erpnext/support/doctype/support_settings/support_settings.json +++ b/erpnext/support/doctype/support_settings/support_settings.json @@ -1,5 +1,5 @@ { - "actions": [], + "actions": "", "creation": "2017-02-17 13:07:35.686409", "doctype": "DocType", "editable_grid": 1, @@ -22,6 +22,10 @@ "post_description_key", "post_route_key", "post_route_string", + "greetings_section_section", + "greeting_title", + "column_break_19", + "greeting_subtitle", "search_apis_sb", "search_apis" ], @@ -127,11 +131,40 @@ "fieldname": "allow_resetting_service_level_agreement", "fieldtype": "Check", "label": "Allow Resetting Service Level Agreement" + }, + { + "default": "We're here to help", + "fieldname": "greeting_title", + "fieldtype": "Data", + "label": "Greeting Title", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "column_break_19", + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 + }, + { + "default": "Browse help topics", + "fieldname": "greeting_subtitle", + "fieldtype": "Data", + "label": "Greeting Subtitle", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "greetings_section_section", + "fieldtype": "Section Break", + "label": "Greetings Section", + "show_days": 1, + "show_seconds": 1 } ], "issingle": 1, "links": [], - "modified": "2020-06-05 17:56:17.491684", + "modified": "2020-06-11 13:08:38.473616", "modified_by": "Administrator", "module": "Support", "name": "Support Settings", diff --git a/erpnext/www/support/__init__.py b/erpnext/www/support/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/www/support/index.html b/erpnext/www/support/index.html new file mode 100644 index 00000000000..824f0bd5ba8 --- /dev/null +++ b/erpnext/www/support/index.html @@ -0,0 +1,55 @@ +{% extends "templates/web.html" %} + +{% block content %} +
    +
    +
    +

    {{ _(greeting_title) or _("We're here to help") }}

    +

    {{ greeting_subtitle or _("Browse help topics.") }}

    +
    +
    +
    + +{% if favorite_article_list %} +
    +
    +

    {{ _("Frequently Read Articles") }}

    +
    + {% for favorite_article in favorite_article_list %} +
    +
    +
    +
    {{ favorite_article['category'] }}
    +

    {{ favorite_article['title'] }}

    +

    {{ favorite_article['description'] }}

    +
    + +
    +
    + {% endfor %} +
    +
    +
    +{% endif %} + +{% if help_article_list %} +
    +
    +

    {{ _("Help Articles") }}

    +
    + {% for item in help_article_list %} +
    +
    {{ item['category'].name }}
    +
    + {% for article in item['articles'] %} + {{ article.title }} + {% endfor %} +
    +
    + {% endfor %} +
    +
    +
    +{% endif %} + +{% endblock %} \ No newline at end of file diff --git a/erpnext/www/support/index.py b/erpnext/www/support/index.py new file mode 100644 index 00000000000..58ca8f72816 --- /dev/null +++ b/erpnext/www/support/index.py @@ -0,0 +1,74 @@ +from __future__ import unicode_literals +import frappe + +def get_context(context): + context.no_cache = 1 + context.align_greeting = '' + setting = frappe.get_doc("Support Settings") + + context.greeting_title = setting.greeting_title + context.greeting_subtitle = setting.greeting_subtitle + + # Support content + favorite_articles = get_favorite_articles_by_page_view() + if len(favorite_articles) < 6: + name_list = [] + if favorite_articles: + for article in favorite_articles: + name_list.append(article.name) + for record in (frappe.get_all("Help Article", + fields=["title", "content", "route", "category"], + filters={"name": ['not in', tuple(name_list)], "published": 1}, + order_by="creation desc", limit=(6-len(favorite_articles)))): + favorite_articles.append(record) + + context.favorite_article_list = get_favorite_articles(favorite_articles) + context.help_article_list = get_help_article_list() + +def get_favorite_articles_by_page_view(): + return frappe.db.sql( + """ + SELECT + t1.name as name, + t1.title as title, + t1.content as content, + t1.route as route, + t1.category as category, + count(t1.route) as count + FROM `tabHelp Article` AS t1 + INNER JOIN + `tabWeb Page View` AS t2 + ON t1.route = t2.path + WHERE t1.published = 1 + GROUP BY route + ORDER BY count DESC + LIMIT 6; + """, as_dict=True) + +def get_favorite_articles(favorite_articles): + favorite_article_list=[] + for article in favorite_articles: + description = frappe.utils.strip_html(article.content) + if len(description) > 175: + description = description[:172] + '...' + favorite_article_dict = { + 'title': article.title, + 'description': description, + 'route': article.route, + 'category': article.category, + } + favorite_article_list.append(favorite_article_dict) + return favorite_article_list + +def get_help_article_list(): + help_article_list=[] + category_list = frappe.get_all("Help Category", fields="name") + for category in category_list: + help_articles = frappe.get_all("Help Article", fields="*", filters={"category": category.name, "published": 1}, order_by="modified desc", limit=5) + if help_articles: + help_aricles_per_caetgory = { + 'category': category, + 'articles': help_articles, + } + help_article_list.append(help_aricles_per_caetgory) + return help_article_list \ No newline at end of file From a4cd9e2389a5485b4079f534b21be4d53bfd3494 Mon Sep 17 00:00:00 2001 From: Abhishek Balam Date: Wed, 17 Jun 2020 17:13:13 +0530 Subject: [PATCH 384/608] use getdate() for comparing dates --- .../customer_acquisition_and_loyalty.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py b/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py index d10b1ca88f6..f15f63d7bb7 100644 --- a/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py +++ b/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.py @@ -5,7 +5,7 @@ from __future__ import unicode_literals import calendar import frappe from frappe import _ -from frappe.utils import cint, cstr +from frappe.utils import cint, cstr, getdate def execute(filters=None): common_columns = [ @@ -177,7 +177,8 @@ def get_customer_stats(filters, tree_view=False): new_or_repeat = 'new' if si.customer not in customers else 'repeat' customers_in.setdefault(key, {'new': [0, 0.0], 'repeat': [0, 0.0]}) - if filters.from_date <= si.posting_date.strftime('%Y-%m-%d'): + # if filters.from_date <= si.posting_date.strftime('%Y-%m-%d'): + if getdate(filters.from_date) <= getdate(si.posting_date): customers_in[key][new_or_repeat][0] += 1 customers_in[key][new_or_repeat][1] += si.base_grand_total if new_or_repeat == 'new': From fec894f530a60a75cdc035d8bc1ffce423245fa8 Mon Sep 17 00:00:00 2001 From: Kaviya Periyasamy <36359901+KaviyaPeriyasamy@users.noreply.github.com> Date: Wed, 17 Jun 2020 17:39:12 +0530 Subject: [PATCH 385/608] fix: modified time updated with the latest date (#22286) --- erpnext/accounts/doctype/cost_center/cost_center.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/cost_center/cost_center.json b/erpnext/accounts/doctype/cost_center/cost_center.json index c9bbbabe798..e7fa954e018 100644 --- a/erpnext/accounts/doctype/cost_center/cost_center.json +++ b/erpnext/accounts/doctype/cost_center/cost_center.json @@ -146,7 +146,7 @@ "idx": 1, "is_tree": 1, "links": [], - "modified": "2020-04-29 16:09:30.025214", + "modified": "2020-06-17 16:09:30.025214", "modified_by": "Administrator", "module": "Accounts", "name": "Cost Center", From 8b19d01d577c565b35c62500266915bfc23bec6a Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 17 Jun 2020 17:26:03 +0530 Subject: [PATCH 386/608] fix: Quality Procedure Fixes - Dont prompt error message if parent is the same in child - filter child procedure field - Disable New button in tree view - Editable grid for Quality Procedure Proces table. --- .../quality_procedure/quality_procedure.js | 9 ++++++++ .../quality_procedure/quality_procedure.json | 3 ++- .../quality_procedure/quality_procedure.py | 23 ++++++++++++------- .../quality_procedure_tree.js | 1 + .../quality_procedure_process.json | 5 +++- 5 files changed, 31 insertions(+), 10 deletions(-) diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.js b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.js index ded3a51dd60..357bd654a7c 100644 --- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.js +++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.js @@ -2,4 +2,13 @@ // For license information, please see license.txt frappe.ui.form.on('Quality Procedure', { + refresh: function(frm) { + frm.set_query("procedure","processes", (frm) =>{ + return { + filters: { + name: ["not in", [frm.parent_quality_procedure, frm.name]] + } + } + }) + } }); \ No newline at end of file diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.json b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.json index 6df116c430c..b3c0d948909 100644 --- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.json +++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.json @@ -1,5 +1,6 @@ { "actions": [], + "allow_rename": 1, "autoname": "format:PRC-{quality_procedure_name}", "creation": "2018-10-06 00:06:29.756804", "doctype": "DocType", @@ -72,7 +73,7 @@ ], "is_tree": 1, "links": [], - "modified": "2020-03-18 18:09:29.371627", + "modified": "2020-06-17 17:25:03.434953", "modified_by": "Administrator", "module": "Quality Management", "name": "Quality Procedure", diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py index 44405c15077..ea544f67339 100644 --- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py +++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py @@ -10,13 +10,8 @@ from frappe import _ class QualityProcedure(NestedSet): nsm_parent_field = 'parent_quality_procedure' - def on_save(self): - for process in self.processes: - if process.procedure: - doc = frappe.get_doc("Quality Procedure", process.procedure) - if doc.parent_quality_procedure: - frappe.throw(_("{0} already has a Parent Procedure {1}.").format(process.procedure, doc.parent_quality_procedure)) - self.is_group = 1 + def before_save(self): + self.check_for_incorrect_child() def on_update(self): self.set_parent() @@ -48,11 +43,23 @@ class QualityProcedure(NestedSet): def set_parent(self): for process in self.processes: - if process.procedure: + # Set parent for only those children who don't have a parent + parent_quality_procedure = frappe.db.get_value("Quality Procedure", process.procedure, "parent_quality_procedure") + if not parent_quality_procedure and process.procedure: doc = frappe.get_doc("Quality Procedure", process.procedure) doc.parent_quality_procedure = self.name doc.save(ignore_permissions=True) + def check_for_incorrect_child(self): + for process in self.processes: + if process.procedure: + # Check if any child process belongs to another parent. + parent_quality_procedure = frappe.db.get_value("Quality Procedure", process.procedure, "parent_quality_procedure") + if parent_quality_procedure and parent_quality_procedure != self.name: + frappe.throw(_("{0} already has a Parent Procedure {1}.".format(frappe.bold(process.procedure), frappe.bold(parent_quality_procedure))), + title=_("Invalid Child Procedure")) + self.is_group = 1 + @frappe.whitelist() def get_children(doctype, parent=None, parent_quality_procedure=None, is_root=False): if parent is None or parent == "All Quality Procedures": diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure_tree.js b/erpnext/quality_management/doctype/quality_procedure/quality_procedure_tree.js index 6df6f656aa1..ef48ab6c6e2 100644 --- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure_tree.js +++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure_tree.js @@ -16,6 +16,7 @@ frappe.treeview_settings["Quality Procedure"] = { }, ], breadcrumb: "Setup", + disable_add_node: true, root_label: "All Quality Procedures", get_tree_root: false, menu_items: [ diff --git a/erpnext/quality_management/doctype/quality_procedure_process/quality_procedure_process.json b/erpnext/quality_management/doctype/quality_procedure_process/quality_procedure_process.json index 0a67fa505ee..3925dbb8aca 100644 --- a/erpnext/quality_management/doctype/quality_procedure_process/quality_procedure_process.json +++ b/erpnext/quality_management/doctype/quality_procedure_process/quality_procedure_process.json @@ -1,6 +1,8 @@ { + "actions": [], "creation": "2019-05-26 00:10:00.248885", "doctype": "DocType", + "editable_grid": 1, "engine": "InnoDB", "field_order": [ "process_description", @@ -23,7 +25,8 @@ } ], "istable": 1, - "modified": "2019-05-26 22:05:49.007189", + "links": [], + "modified": "2020-06-17 15:44:38.937915", "modified_by": "Administrator", "module": "Quality Management", "name": "Quality Procedure Process", From 7057a807ca175cdd1d7f1f20f425c332f6768ab9 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Wed, 17 Jun 2020 18:35:38 +0530 Subject: [PATCH 387/608] refactor: minor layout fixes to portal --- erpnext/public/scss/website.scss | 6 ++++++ erpnext/www/support/index.html | 6 +++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/erpnext/public/scss/website.scss b/erpnext/public/scss/website.scss index 735b417da17..617e916724d 100644 --- a/erpnext/public/scss/website.scss +++ b/erpnext/public/scss/website.scss @@ -81,4 +81,10 @@ .place-order-container { text-align: right; +} + +.kb-card { + .card-body > .card-title { + line-height: 1.3; + } } \ No newline at end of file diff --git a/erpnext/www/support/index.html b/erpnext/www/support/index.html index 824f0bd5ba8..afc437052db 100644 --- a/erpnext/www/support/index.html +++ b/erpnext/www/support/index.html @@ -11,13 +11,13 @@ {% if favorite_article_list %} -
    +

    {{ _("Frequently Read Articles") }}

    {% for favorite_article in favorite_article_list %}
    -
    +
    {{ favorite_article['category'] }}

    {{ favorite_article['title'] }}

    @@ -33,7 +33,7 @@ {% endif %} {% if help_article_list %} -
    +

    {{ _("Help Articles") }}

    From b9c38a13d19f6c7c25234a45ececca3bf1b4f4f2 Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 17 Jun 2020 18:14:47 +0530 Subject: [PATCH 388/608] chore: Remove unnecessary get_doc --- .../doctype/quality_procedure/quality_procedure.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py index ea544f67339..1952e578673 100644 --- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py +++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py @@ -46,9 +46,7 @@ class QualityProcedure(NestedSet): # Set parent for only those children who don't have a parent parent_quality_procedure = frappe.db.get_value("Quality Procedure", process.procedure, "parent_quality_procedure") if not parent_quality_procedure and process.procedure: - doc = frappe.get_doc("Quality Procedure", process.procedure) - doc.parent_quality_procedure = self.name - doc.save(ignore_permissions=True) + frappe.db.set_value(self.doctype, process.procedure, "parent_quality_procedure", self.name) def check_for_incorrect_child(self): for process in self.processes: From 2b18bdfe72479cc3936e5dd7f1d4c164080e7ac6 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Wed, 17 Jun 2020 18:36:51 +0530 Subject: [PATCH 389/608] fix: title for support --- erpnext/www/support/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/www/support/index.html b/erpnext/www/support/index.html index afc437052db..36cd8c68f3e 100644 --- a/erpnext/www/support/index.html +++ b/erpnext/www/support/index.html @@ -4,7 +4,7 @@
    -

    {{ _(greeting_title) or _("We're here to help") }}

    +

    {{ _(greeting_title) or _("We're here to help") }}

    {{ greeting_subtitle or _("Browse help topics.") }}

    From 2462f07c87d1fe3d3d69a8814bd7ef7a9230b05a Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Wed, 17 Jun 2020 18:39:49 +0530 Subject: [PATCH 390/608] refactor: update titles --- erpnext/www/support/index.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/www/support/index.html b/erpnext/www/support/index.html index 36cd8c68f3e..491604f09c0 100644 --- a/erpnext/www/support/index.html +++ b/erpnext/www/support/index.html @@ -4,7 +4,7 @@
    -

    {{ _(greeting_title) or _("We're here to help") }}

    +

    {{ _(greeting_title) or _("We're here to help") }}

    {{ greeting_subtitle or _("Browse help topics.") }}

    @@ -13,7 +13,7 @@ {% if favorite_article_list %}
    -

    {{ _("Frequently Read Articles") }}

    +

    {{ _("Frequently Read Articles") }}

    {% for favorite_article in favorite_article_list %}
    @@ -35,7 +35,7 @@ {% if help_article_list %}
    -

    {{ _("Help Articles") }}

    +

    {{ _("Help Articles") }}

    {% for item in help_article_list %}
    From 6e231ae0cba6f5decb83293c8ebb87ddff5eac8c Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Wed, 17 Jun 2020 18:43:59 +0530 Subject: [PATCH 391/608] refactor: remove translation --- erpnext/www/support/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/www/support/index.html b/erpnext/www/support/index.html index 491604f09c0..2b1c282d184 100644 --- a/erpnext/www/support/index.html +++ b/erpnext/www/support/index.html @@ -4,7 +4,7 @@
    -

    {{ _(greeting_title) or _("We're here to help") }}

    +

    {{ greeting_title or _("We're here to help") }}

    {{ greeting_subtitle or _("Browse help topics.") }}

    From 64d772480b0d0dc7dc5a6ce9f5c99f442322d899 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Wed, 17 Jun 2020 13:18:15 +0000 Subject: [PATCH 392/608] fix: limit max length for cards (#22289) --- erpnext/www/support/index.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/www/support/index.py b/erpnext/www/support/index.py index 58ca8f72816..5d267430c16 100644 --- a/erpnext/www/support/index.py +++ b/erpnext/www/support/index.py @@ -49,8 +49,8 @@ def get_favorite_articles(favorite_articles): favorite_article_list=[] for article in favorite_articles: description = frappe.utils.strip_html(article.content) - if len(description) > 175: - description = description[:172] + '...' + if len(description) > 120: + description = description[:120] + '...' favorite_article_dict = { 'title': article.title, 'description': description, From 91dfd000ede897bc456510456f3bf8e5cafeddc6 Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 17 Jun 2020 19:05:40 +0530 Subject: [PATCH 393/608] fix: Typo --- erpnext/controllers/item_variant.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/item_variant.py b/erpnext/controllers/item_variant.py index 50b17abbe6d..1f95e004244 100644 --- a/erpnext/controllers/item_variant.py +++ b/erpnext/controllers/item_variant.py @@ -102,7 +102,7 @@ def validate_item_attribute_value(attributes_list, attribute, attribute_value, i frappe.throw(_("{0} is not a valid Value for Attribute {1} of Item {2}.").format( frappe.bold(attribute_value), frappe.bold(attribute), frappe.bold(item)), InvalidItemAttributeValueError, title=_("Invalid Value")) else: - msg = _("The value {0} is already assigned to an exisiting Item {1}.").format( + msg = _("The value {0} is already assigned to an existing Item {1}.").format( frappe.bold(attribute_value), frappe.bold(item)) msg += "
    " + _("To still proceed with editing this Attribute Value, enable {0} in Item Variant Settings.").format(frappe.bold("Allow Rename Attribute Value")) From 53b601523b81c32a0b5c10c721f99c184d09bcd8 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Wed, 17 Jun 2020 19:31:57 +0530 Subject: [PATCH 394/608] fix: update shopify api version (#22284) Co-authored-by: Saurabh --- .../shopify_settings/shopify_settings.py | 24 +++++++++++++------ .../doctype/shopify_settings/sync_product.py | 2 +- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py index 64c3b2d2730..25ffd281099 100644 --- a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py +++ b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py @@ -8,6 +8,7 @@ import json from frappe import _ from frappe.model.document import Document from frappe.utils import get_request_session +from requests.exceptions import HTTPError from frappe.custom.doctype.custom_field.custom_field import create_custom_fields from erpnext.erpnext_integrations.utils import get_webhook_address from erpnext.erpnext_integrations.doctype.shopify_log.shopify_log import make_shopify_log @@ -29,19 +30,24 @@ class ShopifySettings(Document): webhooks = ["orders/create", "orders/paid", "orders/fulfilled"] # url = get_shopify_url('admin/webhooks.json', self) created_webhooks = [d.method for d in self.webhooks] - url = get_shopify_url('admin/api/2019-04/webhooks.json', self) + url = get_shopify_url('admin/api/2020-04/webhooks.json', self) for method in webhooks: session = get_request_session() try: - d = session.post(url, data=json.dumps({ + res = session.post(url, data=json.dumps({ "webhook": { "topic": method, "address": get_webhook_address(connector_name='shopify_connection', method='store_request_data'), "format": "json" } }), headers=get_header(self)) - d.raise_for_status() - self.update_webhook_table(method, d.json()) + res.raise_for_status() + self.update_webhook_table(method, res.json()) + + except HTTPError as e: + error_message = res.json().get('errors', e) + make_shopify_log(status="Warning", exception=error_message, rollback=True) + except Exception as e: make_shopify_log(status="Warning", exception=e, rollback=True) @@ -50,13 +56,18 @@ class ShopifySettings(Document): deleted_webhooks = [] for d in self.webhooks: - url = get_shopify_url('admin/api/2019-04/webhooks/{0}.json'.format(d.webhook_id), self) + url = get_shopify_url('admin/api/2020-04/webhooks/{0}.json'.format(d.webhook_id), self) try: res = session.delete(url, headers=get_header(self)) res.raise_for_status() deleted_webhooks.append(d) + + except HTTPError as e: + error_message = res.json().get('errors', e) + make_shopify_log(status="Warning", exception=error_message, rollback=True) + except Exception as e: - frappe.log_error(message=frappe.get_traceback(), title=e) + frappe.log_error(message=e, title='Shopify Webhooks Issue') for d in deleted_webhooks: self.remove(d) @@ -125,4 +136,3 @@ def setup_custom_fields(): } create_custom_fields(custom_fields) - diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/sync_product.py b/erpnext/erpnext_integrations/doctype/shopify_settings/sync_product.py index bde101123db..f9f0bb3cecc 100644 --- a/erpnext/erpnext_integrations/doctype/shopify_settings/sync_product.py +++ b/erpnext/erpnext_integrations/doctype/shopify_settings/sync_product.py @@ -8,7 +8,7 @@ from erpnext.erpnext_integrations.doctype.shopify_settings.shopify_settings impo shopify_variants_attr_list = ["option1", "option2", "option3"] def sync_item_from_shopify(shopify_settings, item): - url = get_shopify_url("admin/api/2019-04/products/{0}.json".format(item.get("product_id")), shopify_settings) + url = get_shopify_url("admin/api/2020-04/products/{0}.json".format(item.get("product_id")), shopify_settings) session = get_request_session() try: From ea78b2403581303973b307abfe4fbedfc494fceb Mon Sep 17 00:00:00 2001 From: Afshan <33727827+AfshanKhan@users.noreply.github.com> Date: Thu, 18 Jun 2020 09:49:35 +0530 Subject: [PATCH 395/608] style: subtitle to be displayed if provided else empty (#22298) * added: /support * refactor code and implemented fail checks * removed unused imports * changed filter from from title to name * refactor code to move context inside main * removed unused variable * added: /support * refactor code and implemented fail checks * removed unused imports * changed filter from from title to name * refactor code to move context inside main * removed unused variable * refactor: renamed set to get * style: subtitle to be displyed if provided else empty Co-authored-by: Shivam Mishra Co-authored-by: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> --- erpnext/www/support/index.html | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/erpnext/www/support/index.html b/erpnext/www/support/index.html index 2b1c282d184..93da503dbb0 100644 --- a/erpnext/www/support/index.html +++ b/erpnext/www/support/index.html @@ -4,8 +4,10 @@
    -

    {{ greeting_title or _("We're here to help") }}

    -

    {{ greeting_subtitle or _("Browse help topics.") }}

    +

    {{ greeting_title or _("We're here to help!") }}

    + {% if greeting_subtitle %} +

    {{ greeting_subtitle }}

    + {% endif %}
    @@ -16,16 +18,17 @@

    {{ _("Frequently Read Articles") }}

    {% for favorite_article in favorite_article_list %} -
    -
    -
    -
    {{ favorite_article['category'] }}
    -

    {{ favorite_article['title'] }}

    -

    {{ favorite_article['description'] }}

    -
    - +
    +
    +
    +
    + {{ favorite_article['category'] }}
    +

    {{ favorite_article['title'] }}

    +

    {{ favorite_article['description'] }}

    +
    +
    {% endfor %}
    From 755e7c812a8a9670b40b208b092d72a1d9fa7488 Mon Sep 17 00:00:00 2001 From: Chinmay Pai Date: Thu, 18 Jun 2020 10:05:53 +0530 Subject: [PATCH 396/608] chore: update travis url for docker build action (#22274) Signed-off-by: Chinmay D. Pai --- .github/workflows/docker-release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-release.yml b/.github/workflows/docker-release.yml index d36b11553ce..8f678583066 100644 --- a/.github/workflows/docker-release.yml +++ b/.github/workflows/docker-release.yml @@ -11,4 +11,4 @@ jobs: - name: curl run: | apk add curl bash - curl -s -X POST -H "Content-Type: application/json" -H "Accept: application/json" -H "Travis-API-Version: 3" -H "Authorization: token ${{ secrets.TRAVIS_CI_TOKEN }}" -d '{"request":{"branch":"master"}}' https://api.travis-ci.org/repo/frappe%2Ffrappe_docker/requests + curl -s -X POST -H "Content-Type: application/json" -H "Accept: application/json" -H "Travis-API-Version: 3" -H "Authorization: token ${{ secrets.TRAVIS_CI_TOKEN }}" -d '{"request":{"branch":"master"}}' https://api.travis-ci.com/repo/frappe%2Ffrappe_docker/requests From 24ee482d8fb1ab6ba7944205f48512487aba27f5 Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 18 Jun 2020 11:46:47 +0530 Subject: [PATCH 397/608] fix: Codacy --- .../doctype/quality_procedure/quality_procedure.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.js b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.js index 357bd654a7c..cf2644e0053 100644 --- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.js +++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.js @@ -8,7 +8,7 @@ frappe.ui.form.on('Quality Procedure', { filters: { name: ["not in", [frm.parent_quality_procedure, frm.name]] } - } - }) + }; + }); } }); \ No newline at end of file From 96100e95077db9d9a2a27b384030460109cdd0f2 Mon Sep 17 00:00:00 2001 From: Chinmay Pai Date: Thu, 18 Jun 2020 11:58:54 +0530 Subject: [PATCH 398/608] chore: add standard queries hooks to whitelist (#21939) standard queries are used within the search widget, and now require to be whitelisted before they can be executed through the search widget. Signed-off-by: Chinmay D. Pai Co-authored-by: sahil28297 <37302950+sahil28297@users.noreply.github.com> --- .../doctype/healthcare_practitioner/healthcare_practitioner.py | 1 + erpnext/selling/doctype/customer/customer.py | 1 + 2 files changed, 2 insertions(+) diff --git a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.py b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.py index 0c13b6af9db..3dc7c1ec393 100644 --- a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.py +++ b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.py @@ -70,6 +70,7 @@ def validate_service_item(item, msg): if frappe.db.get_value('Item', item, 'is_stock_item'): frappe.throw(_(msg)) +@frappe.whitelist() def get_practitioner_list(doctype, txt, searchfield, start, page_len, filters=None): fields = ['name', 'practitioner_name', 'mobile_phone'] diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index ac3bc201e96..682dfede72f 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -339,6 +339,7 @@ def get_loyalty_programs(doc): return lp_details +@frappe.whitelist() def get_customer_list(doctype, txt, searchfield, start, page_len, filters=None): from erpnext.controllers.queries import get_fields From 8d5380a2f0a2e0a160e05092b872302d7d940894 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 18 Jun 2020 14:06:31 +0530 Subject: [PATCH 399/608] fix: incorrect variable used --- erpnext/controllers/accounts_controller.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 837ffe3c8cb..f54b593022c 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1197,11 +1197,11 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil action = "add" if perm_type == 'create' else "update" frappe.throw(_("You do not have permissions to {} items in a Sales Order.").format(action), title=_("Insufficient Permissions")) - def get_new_child_item(): + def get_new_child_item(item_row): if parent_doctype == "Sales Order": - return set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, d) + return set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, item_row) if parent_doctype == "Purchase Order": - return set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, d) + return set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, item_row) def validate_quantity(child_item, d): if parent_doctype == "Sales Order" and flt(d.get("qty")) < flt(child_item.delivered_qty): @@ -1222,7 +1222,7 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil if not d.get("docname"): new_child_flag = True check_permissions(parent, 'create') - child_item = get_new_child_item() + child_item = get_new_child_item(d) else: check_permissions(parent, 'write') child_item = frappe.get_doc(parent_doctype + ' Item', d.get("docname")) From bd449c8d0794e359bf64d5aff8e72e6e91ff3c59 Mon Sep 17 00:00:00 2001 From: Anupam K Date: Thu, 18 Jun 2020 14:20:22 +0530 Subject: [PATCH 400/608] email digest html fix --- erpnext/setup/doctype/email_digest/email_digest.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/setup/doctype/email_digest/email_digest.py b/erpnext/setup/doctype/email_digest/email_digest.py index 4d2d540bbc8..7c0be3bff63 100644 --- a/erpnext/setup/doctype/email_digest/email_digest.py +++ b/erpnext/setup/doctype/email_digest/email_digest.py @@ -101,8 +101,7 @@ class EmailDigest(Document): if not context.purchase_order_list: frappe.throw(_("No items to be received are overdue")) - if not (context.events or context.todo_list or context.notifications or context.cards - or context.purchase_orders_items_overdue_list): + if not context: return None frappe.flags.ignore_account_permission = False From 95e9a4ef2cdb5551c2e6171cc43d75cf2de8ea37 Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 18 Jun 2020 14:43:31 +0530 Subject: [PATCH 401/608] fix: Patch simplification - Also, apply patch to all docs --- erpnext/patches/v12_0/set_multi_uom_in_rfq.py | 21 ++++--------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/erpnext/patches/v12_0/set_multi_uom_in_rfq.py b/erpnext/patches/v12_0/set_multi_uom_in_rfq.py index 50725bb69dd..70ca6b222e9 100644 --- a/erpnext/patches/v12_0/set_multi_uom_in_rfq.py +++ b/erpnext/patches/v12_0/set_multi_uom_in_rfq.py @@ -9,21 +9,8 @@ from erpnext.stock.get_item_details import get_conversion_factor def execute(): frappe.reload_doc('buying', 'doctype', 'request_for_quotation_item') - for rfq_item in frappe.db.sql("""SELECT name, item_code, uom, qty FROM `tabRequest for Quotation Item` WHERE docstatus<2""", as_dict=1): - item_code, uom, qty = rfq_item.get("item_code"), rfq_item.get("uom"), rfq_item.get("qty") - conversion_factor = get_conversion_factor(item_code, uom).get("conversion_factor") or 1.0 - - filters = { - "name" : rfq_item.get("name"), - "stock_uom" : frappe.db.get_value("Item", item_code, "stock_uom"), - "conversion_factor" : conversion_factor, - "stock_qty" : flt(qty) * flt(conversion_factor) - } - - frappe.db.sql("""UPDATE `tabRequest for Quotation Item` + frappe.db.sql("""UPDATE `tabRequest for Quotation Item` SET - stock_uom= %(stock_uom)s, - conversion_factor = %(conversion_factor)s, - stock_qty = %(stock_qty)s - WHERE - name = %(name)s""", filters) \ No newline at end of file + stock_uom = uom, + conversion_factor = 1, + stock_qty = qty""") \ No newline at end of file From 34d2bfbb3e6ce28de2690812191b8885b4f8f6ab Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 18 Jun 2020 09:58:55 +0000 Subject: [PATCH 402/608] refactor: handle exceptions when updating addresses (#22307) * refactor: handle exceptions when updating addresses * refactor: fold common statements in a loop --- .../connectors/woocommerce_connection.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py index 1b0c9f60b6e..6dedaa8c530 100644 --- a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py +++ b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py @@ -73,10 +73,16 @@ def link_customer_and_address(raw_billing_data, raw_shipping_data, customer_name if customer_exists: frappe.rename_doc("Customer", old_name, customer_name) - billing_address = frappe.get_doc("Address", {"woocommerce_email": customer_woo_com_email, "address_type": "Billing"}) - shipping_address = frappe.get_doc("Address", {"woocommerce_email": customer_woo_com_email, "address_type": "Shipping"}) - rename_address(billing_address, customer) - rename_address(shipping_address, customer) + for address_type in ("Billing", "Shipping",): + try: + address = frappe.get_doc("Address", {"woocommerce_email": customer_woo_com_email, "address_type": address_type}) + rename_address(address, customer) + except ( + frappe.DoesNotExistError, + frappe.DuplicateEntryError, + frappe.ValidationError, + ): + pass else: create_address(raw_billing_data, customer, "Billing") create_address(raw_shipping_data, customer, "Shipping") From a529d71ca0bf7517dc9d76e1dfd52359b411b7a8 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 18 Jun 2020 15:38:43 +0530 Subject: [PATCH 403/608] fix(patch): add patch for setting issue metrics (#22296) * patch: set issue metrics in Issue documents * fix: commit after every 100 records --- erpnext/patches.txt | 1 + erpnext/patches/v13_0/update_issue_metrics.py | 33 +++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 erpnext/patches/v13_0/update_issue_metrics.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 279c453e350..d74dc244f3a 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -699,3 +699,4 @@ erpnext.patches.v13_0.delete_old_purchase_reports erpnext.patches.v12_0.set_italian_import_supplier_invoice_permissions erpnext.patches.v13_0.update_sla_enhancements erpnext.patches.v12_0.update_address_template_for_india +erpnext.patches.v13_0.update_issue_metrics diff --git a/erpnext/patches/v13_0/update_issue_metrics.py b/erpnext/patches/v13_0/update_issue_metrics.py new file mode 100644 index 00000000000..6d7623565f0 --- /dev/null +++ b/erpnext/patches/v13_0/update_issue_metrics.py @@ -0,0 +1,33 @@ +from __future__ import unicode_literals +import frappe + +from frappe.core.doctype.communication.communication import set_avg_response_time +from erpnext.support.doctype.issue.issue import set_resolution_time, set_user_resolution_time + +def execute(): + if frappe.db.exists('DocType', 'Issue'): + frappe.reload_doctype('Issue') + + count = 0 + for parent in frappe.get_all('Issue', order_by='creation desc'): + parent_doc = frappe.get_doc('Issue', parent.name) + + communication = frappe.get_all('Communication', filters={ + 'reference_doctype': 'Issue', + 'reference_name': parent.name, + 'communication_medium': 'Email', + 'sent_or_received': 'Sent' + }, order_by = 'creation asc', limit=1) + + if communication: + communication_doc = frappe.get_doc('Communication', communication[0].name) + set_avg_response_time(parent_doc, communication_doc) + + if parent_doc.status in ['Closed', 'Resolved']: + set_resolution_time(parent_doc) + set_user_resolution_time(parent_doc) + + # commit after every 100 records + count += 1 + if count % 100 == 0: + frappe.db.commit() \ No newline at end of file From 1ba7708b602f295d19eae92445f0a33002f45bc4 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 18 Jun 2020 16:45:16 +0530 Subject: [PATCH 404/608] fix: Do not copy Item Tax template from SO to PO --- erpnext/selling/doctype/sales_order/sales_order.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 05e4aa892b0..ffb66354fa0 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -868,7 +868,8 @@ def make_purchase_order(source_name, for_supplier=None, selected_items=[], targe ], "field_no_map": [ "rate", - "price_list_rate" + "price_list_rate", + "item_tax_template" ], "postprocess": update_item, "condition": lambda doc: doc.ordered_qty < doc.qty and doc.supplier == supplier and doc.item_code in selected_items From 77673e27e07b55330ff12db4e0ab4b8e46e26cb5 Mon Sep 17 00:00:00 2001 From: Anupam Kumar Date: Thu, 18 Jun 2020 17:46:20 +0530 Subject: [PATCH 405/608] enable Lead create by email (#22314) Co-authored-by: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> --- erpnext/crm/doctype/lead/lead.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/crm/doctype/lead/lead.json b/erpnext/crm/doctype/lead/lead.json index 6fef0c46437..f5f8b4efb34 100644 --- a/erpnext/crm/doctype/lead/lead.json +++ b/erpnext/crm/doctype/lead/lead.json @@ -6,6 +6,7 @@ "creation": "2013-04-10 11:45:37", "doctype": "DocType", "document_type": "Document", + "email_append_to": 1, "engine": "InnoDB", "field_order": [ "organization_lead", @@ -448,7 +449,7 @@ "idx": 5, "image_field": "image", "links": [], - "modified": "2020-05-11 20:27:45.868960", + "modified": "2020-06-18 14:39:41.835416", "modified_by": "Administrator", "module": "CRM", "name": "Lead", @@ -508,8 +509,10 @@ } ], "search_fields": "lead_name,lead_owner,status", + "sender_field": "email_id", "show_name_in_global_search": 1, "sort_field": "modified", "sort_order": "DESC", + "subject_field": "title", "title_field": "title" } \ No newline at end of file From 82ddef58c01f074f5846ab9115efe8237622b33a Mon Sep 17 00:00:00 2001 From: Prssanna Desai Date: Thu, 18 Jun 2020 18:18:41 +0530 Subject: [PATCH 406/608] feat: date filter for fiscal year (#21880) * feat: date filter for fiscal year * fix: rename fieldtypes to valid_for_fieldtypes * Update utils.py Co-authored-by: Nabin Hait --- erpnext/accounts/utils.py | 22 +++++++++++++++++++++- erpnext/hooks.py | 2 +- erpnext/startup/filters.py | 14 ++++++++++++++ 3 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 erpnext/startup/filters.py diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 5165495786d..f6cd606757e 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -57,6 +57,9 @@ def get_fiscal_years(transaction_date=None, fiscal_year=None, label="Date", verb frappe.cache().hset("fiscal_years", company, fiscal_years) + if not transaction_date and not fiscal_year: + return fiscal_years + if transaction_date: transaction_date = getdate(transaction_date) @@ -79,6 +82,23 @@ def get_fiscal_years(transaction_date=None, fiscal_year=None, label="Date", verb if verbose==1: frappe.msgprint(error_msg) raise FiscalYearError(error_msg) +@frappe.whitelist() +def get_fiscal_year_filter_field(company=None): + field = { + "fieldtype": "Select", + "options": [], + "operator": "Between", + "query_value": True + } + fiscal_years = get_fiscal_years(company=company) + for fiscal_year in fiscal_years: + field["options"].append({ + "label": fiscal_year.name, + "value": fiscal_year.name, + "query_value": [fiscal_year.year_start_date.strftime("%Y-%m-%d"), fiscal_year.year_end_date.strftime("%Y-%m-%d")] + }) + return field + def validate_fiscal_year(date, fiscal_year, company, label="Date", doc=None): years = [f[0] for f in get_fiscal_years(date, label=_(label), company=company)] if fiscal_year not in years: @@ -942,4 +962,4 @@ def get_voucherwise_gl_entries(future_stock_vouchers, posting_date): tuple([posting_date] + [d[1] for d in future_stock_vouchers]), as_dict=1): gl_entries.setdefault((d.voucher_type, d.voucher_no), []).append(d) - return gl_entries \ No newline at end of file + return gl_entries diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 742cc8efbd1..2a695896ed0 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -41,7 +41,7 @@ boot_session = "erpnext.startup.boot.boot_session" notification_config = "erpnext.startup.notifications.get_notification_config" get_help_messages = "erpnext.utilities.activation.get_help_messages" leaderboards = "erpnext.startup.leaderboard.get_leaderboards" - +filters_config = "erpnext.startup.filters.get_filters_config" on_session_creation = [ "erpnext.portal.utils.create_customer_or_supplier", diff --git a/erpnext/startup/filters.py b/erpnext/startup/filters.py new file mode 100644 index 00000000000..a99e49b4917 --- /dev/null +++ b/erpnext/startup/filters.py @@ -0,0 +1,14 @@ + +import frappe + +def get_filters_config(): + filters_config = { + "fiscal year": { + "label": "Fiscal Year", + "get_field": "erpnext.accounts.utils.get_fiscal_year_filter_field", + "valid_for_fieldtypes": ["Date", "Datetime", "DateRange"], + "depends_on": "company", + } + } + + return filters_config \ No newline at end of file From d9b2d635d8c55acc7646c54cb4cb7fee8e125b49 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Thu, 18 Jun 2020 19:13:01 +0530 Subject: [PATCH 407/608] feat: added Expense approver for employee and validation (#22244) * feat: added Expense approver for employee nad validation * fix: requested changes --- .../department_approver/department_approver.py | 16 +++++++++++++++- erpnext/hr/doctype/employee/employee.json | 11 ++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/erpnext/hr/doctype/department_approver/department_approver.py b/erpnext/hr/doctype/department_approver/department_approver.py index df0f75a18c3..d4c118f802d 100644 --- a/erpnext/hr/doctype/department_approver/department_approver.py +++ b/erpnext/hr/doctype/department_approver/department_approver.py @@ -19,7 +19,7 @@ def get_approvers(doctype, txt, searchfield, start, page_len, filters): approvers = [] department_details = {} department_list = [] - employee = frappe.get_value("Employee", filters.get("employee"), ["department", "leave_approver"], as_dict=True) + employee = frappe.get_value("Employee", filters.get("employee"), ["department", "leave_approver", "expense_approver"], as_dict=True) employee_department = filters.get("department") or employee.department if employee_department: @@ -33,10 +33,16 @@ def get_approvers(doctype, txt, searchfield, start, page_len, filters): if filters.get("doctype") == "Leave Application" and employee.leave_approver: approvers.append(frappe.db.get_value("User", employee.leave_approver, ['name', 'first_name', 'last_name'])) + if filters.get("doctype") == "Expense Claim" and employee.expense_approver: + approvers.append(frappe.db.get_value("User", employee.expense_approver, ['name', 'first_name', 'last_name'])) + + if filters.get("doctype") == "Leave Application": parentfield = "leave_approvers" + field_name = "Leave Approver" else: parentfield = "expense_approvers" + field_name = "Expense Approver" if department_list: for d in department_list: approvers += frappe.db.sql("""select user.name, user.first_name, user.last_name from @@ -46,4 +52,12 @@ def get_approvers(doctype, txt, searchfield, start, page_len, filters): and approver.parentfield = %s and approver.approver=user.name""",(d, "%" + txt + "%", parentfield), as_list=True) + if len(approvers) == 0: + frappe.throw(_("Please set {0} for the Employee or for Department: {1}"). + format( + field_name, frappe.bold(employee_department), + frappe.bold(employee.name) + ), + title=_(field_name + " Missing")) + return set(tuple(approver) for approver in approvers) diff --git a/erpnext/hr/doctype/employee/employee.json b/erpnext/hr/doctype/employee/employee.json index 2c2b2f6a17b..7dacacf12b0 100644 --- a/erpnext/hr/doctype/employee/employee.json +++ b/erpnext/hr/doctype/employee/employee.json @@ -62,6 +62,7 @@ "salary_mode", "payroll_cost_center", "column_break_52", + "expense_approver", "bank_name", "bank_ac_no", "health_insurance_section", @@ -798,13 +799,21 @@ { "fieldname": "column_break_52", "fieldtype": "Column Break" + }, + { + "fieldname": "expense_approver", + "fieldtype": "Link", + "label": "Expense Approver", + "options": "User", + "show_days": 1, + "show_seconds": 1 } ], "icon": "fa fa-user", "idx": 24, "image_field": "image", "links": [], - "modified": "2020-06-15 12:26:30.003741", + "modified": "2020-06-18 18:01:27.223535", "modified_by": "Administrator", "module": "HR", "name": "Employee", From 25702a1c550069845d8ce1fea5aade1770ca7c64 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 18 Jun 2020 19:49:46 +0530 Subject: [PATCH 408/608] refactor: show service instead of services --- .../templates/includes/footer/footer_powered.html | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/erpnext/templates/includes/footer/footer_powered.html b/erpnext/templates/includes/footer/footer_powered.html index cf7661ee3fa..4274ba12cf5 100644 --- a/erpnext/templates/includes/footer/footer_powered.html +++ b/erpnext/templates/includes/footer/footer_powered.html @@ -10,9 +10,17 @@ 'Agriculture': '/agriculture', 'Hospitality': '' } %} + {% set link = '' %} +{% set label = domains[0].domain %} {% if domains %} - {% set link = links[domains[0].domain] %} + {% set link = links[label] %} {% endif %} -Powered by ERPNext - {{ '' if domains else 'Open Source' }} ERP Software {{ ('for ' + domains[0].domain + ' Companies') if domains else '' }} +{% if label == "Services" %} + {% set label = "Service" %} +{% endif %} + + + +Powered by ERPNext - {{ '' if domains else 'Open Source' }} ERP Software {{ ('for ' + label + ' Companies') if domains else '' }} From 766f978858b2929ae2caa6a80596b76b850e13fe Mon Sep 17 00:00:00 2001 From: Kenneth Sequeira Date: Thu, 18 Jun 2020 23:23:54 +0530 Subject: [PATCH 409/608] fix: Customer Group label in Itemwise Sales report --- .../report/item_wise_sales_history/item_wise_sales_history.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py b/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py index 405004ece54..08a98ba6c07 100644 --- a/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py +++ b/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py @@ -96,7 +96,7 @@ def get_columns(filters): "label": _("Customer Group"), "fieldtype": "Link", "fieldname": "customer_group", - "options": "customer Group", + "options": "Customer Group", "width": 120 }, { From a9cdc7b6966739bb1771685910bc8a184203beab Mon Sep 17 00:00:00 2001 From: Afshan <33727827+AfshanKhan@users.noreply.github.com> Date: Fri, 19 Jun 2020 11:11:33 +0530 Subject: [PATCH 410/608] style: moved project from reference section to accounting dimensions section (#22309) --- .../doctype/journal_entry_account/journal_entry_account.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json index 26c84a6398c..ff3533a6792 100644 --- a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json +++ b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json @@ -18,6 +18,7 @@ "accounting_dimensions_section", "cost_center", "dimension_col_break", + "project", "currency_section", "account_currency", "column_break_10", @@ -32,7 +33,6 @@ "reference_type", "reference_name", "reference_due_date", - "project", "col_break3", "is_advance", "user_remark", @@ -273,7 +273,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2020-04-25 01:47:49.060128", + "modified": "2020-06-18 14:06:54.833738", "modified_by": "Administrator", "module": "Accounts", "name": "Journal Entry Account", From 428235c478a1dcb0a90a864e9d7dae9d849ff71b Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Fri, 19 Jun 2020 11:15:59 +0530 Subject: [PATCH 411/608] fix: revert issue metrics patch (#22331) --- erpnext/patches.txt | 1 - erpnext/patches/v13_0/update_issue_metrics.py | 33 ------------------- 2 files changed, 34 deletions(-) delete mode 100644 erpnext/patches/v13_0/update_issue_metrics.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index e897260da2c..b3a38b61943 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -700,4 +700,3 @@ erpnext.patches.v12_0.set_italian_import_supplier_invoice_permissions erpnext.patches.v13_0.update_sla_enhancements erpnext.patches.v12_0.update_address_template_for_india erpnext.patches.v12_0.set_multi_uom_in_rfq -erpnext.patches.v13_0.update_issue_metrics diff --git a/erpnext/patches/v13_0/update_issue_metrics.py b/erpnext/patches/v13_0/update_issue_metrics.py deleted file mode 100644 index 6d7623565f0..00000000000 --- a/erpnext/patches/v13_0/update_issue_metrics.py +++ /dev/null @@ -1,33 +0,0 @@ -from __future__ import unicode_literals -import frappe - -from frappe.core.doctype.communication.communication import set_avg_response_time -from erpnext.support.doctype.issue.issue import set_resolution_time, set_user_resolution_time - -def execute(): - if frappe.db.exists('DocType', 'Issue'): - frappe.reload_doctype('Issue') - - count = 0 - for parent in frappe.get_all('Issue', order_by='creation desc'): - parent_doc = frappe.get_doc('Issue', parent.name) - - communication = frappe.get_all('Communication', filters={ - 'reference_doctype': 'Issue', - 'reference_name': parent.name, - 'communication_medium': 'Email', - 'sent_or_received': 'Sent' - }, order_by = 'creation asc', limit=1) - - if communication: - communication_doc = frappe.get_doc('Communication', communication[0].name) - set_avg_response_time(parent_doc, communication_doc) - - if parent_doc.status in ['Closed', 'Resolved']: - set_resolution_time(parent_doc) - set_user_resolution_time(parent_doc) - - # commit after every 100 records - count += 1 - if count % 100 == 0: - frappe.db.commit() \ No newline at end of file From 163a569715c56b23e2001c61b67da34cac973124 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 19 Jun 2020 12:03:36 +0530 Subject: [PATCH 412/608] chore: Delete Bank Reco doctype --- erpnext/patches.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index b3a38b61943..15c4080f976 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -700,3 +700,4 @@ erpnext.patches.v12_0.set_italian_import_supplier_invoice_permissions erpnext.patches.v13_0.update_sla_enhancements erpnext.patches.v12_0.update_address_template_for_india erpnext.patches.v12_0.set_multi_uom_in_rfq +execute:frappe.delete_doc_if_exists("DocType", "Bank Reconciliation") From 1b30ca6a36e2f088439641ba5bbd370475defa85 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 19 Jun 2020 12:12:08 +0530 Subject: [PATCH 413/608] fix: Codacy --- erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py | 4 ++-- erpnext/assets/doctype/asset/depreciation.py | 2 +- erpnext/controllers/stock_controller.py | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index c77e5002dd8..f0585ad4d83 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -500,7 +500,7 @@ class PurchaseInvoice(BuyingController): "account": warehouse_account[item.warehouse]['account'], "against": warehouse_account[item.from_warehouse]["account"], "cost_center": item.cost_center, - "project": item_row.project or self.project, + "project": item.project or self.project, "remarks": self.get("remarks") or _("Accounting Entry for Stock"), "debit": warehouse_debit_amount, }, warehouse_account[item.warehouse]["account_currency"], item=item)) @@ -510,7 +510,7 @@ class PurchaseInvoice(BuyingController): "account": warehouse_account[item.from_warehouse]['account'], "against": warehouse_account[item.warehouse]["account"], "cost_center": item.cost_center, - "project": item_row.project or self.project, + "project": item.project or self.project, "remarks": self.get("remarks") or _("Accounting Entry for Stock"), "debit": -1 * flt(item.base_net_amount, item.precision("base_net_amount")), }, warehouse_account[item.from_warehouse]["account_currency"], item=item)) diff --git a/erpnext/assets/doctype/asset/depreciation.py b/erpnext/assets/doctype/asset/depreciation.py index b7ebb4767e0..8f0afb42b2c 100644 --- a/erpnext/assets/doctype/asset/depreciation.py +++ b/erpnext/assets/doctype/asset/depreciation.py @@ -3,7 +3,7 @@ # For license information, please see license.txt from __future__ import unicode_literals -import frappe, erpnext +import frappe from frappe import _ from frappe.utils import flt, today, getdate, cint from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_checks_for_pl_and_bs_accounts diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index d098c1cfd54..e8483da5441 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -96,7 +96,7 @@ class StockController(AccountsController): "account": warehouse_account[sle.warehouse]["account"], "against": item_row.expense_account, "cost_center": item_row.cost_center, - "project": item_row.project or self.project if hasattr(self, 'project') else None, + "project": item_row.project or self.get('project'), "remarks": self.get("remarks") or "Accounting Entry for Stock", "debit": flt(sle.stock_value_difference, precision), "is_opening": item_row.get("is_opening") or self.get("is_opening") or "No", @@ -107,7 +107,7 @@ class StockController(AccountsController): "account": item_row.expense_account, "against": warehouse_account[sle.warehouse]["account"], "cost_center": item_row.cost_center, - "project": item_row.project or self.project if hasattr(self, 'project') else None, + "project": item_row.project or self.get('project'), "remarks": self.get("remarks") or "Accounting Entry for Stock", "credit": flt(sle.stock_value_difference, precision), "project": item_row.get("project") or self.get("project"), From 7a45c8bc28b286e73c349d82a268758b2c0f75f5 Mon Sep 17 00:00:00 2001 From: Afshan Date: Fri, 19 Jun 2020 12:37:50 +0530 Subject: [PATCH 414/608] fix: replaced "row_id" to "idx" as "row_id" was not available in variable "tax", also replaced "-1" by "-2" as "idx" starts with 0 rather than 1 --- erpnext/public/js/controllers/taxes_and_totals.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index dbe48ec6544..a6eb901fa72 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -345,11 +345,11 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ current_tax_amount = (tax_rate / 100.0) * item.net_amount; } else if(tax.charge_type == "On Previous Row Amount") { current_tax_amount = (tax_rate / 100.0) * - this.frm.doc["taxes"][cint(tax.row_id) - 1].tax_amount_for_current_item; + this.frm.doc["taxes"][cint(tax.idx) - 2].tax_amount_for_current_item; } else if(tax.charge_type == "On Previous Row Total") { current_tax_amount = (tax_rate / 100.0) * - this.frm.doc["taxes"][cint(tax.row_id) - 1].grand_total_for_current_item; + this.frm.doc["taxes"][cint(tax.idx) - 2].grand_total_for_current_item; } this.set_item_wise_tax(item, tax, tax_rate, current_tax_amount); From 6f7652f425e2e58f7059d43c9bf7373af67cac3a Mon Sep 17 00:00:00 2001 From: Afshan Date: Fri, 19 Jun 2020 14:36:56 +0530 Subject: [PATCH 415/608] fix: set row_id by default as previous row for On Previous Row Amount and On Previous Row Total --- erpnext/public/js/controllers/taxes_and_totals.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index a6eb901fa72..6b8dc2804f3 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -335,6 +335,17 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ var tax_rate = this._get_tax_rate(tax, item_tax_map); var current_tax_amount = 0.0; + // To set row_id by default as previous row. + if(["On Previous Row Amount", "On Previous Row Total"].includes(tax.charge_type)) { + if (tax.idx == 1) { + frappe.throw( + __("Cannot select charge type as 'On Previous Row Amount' or 'On Previous Row Total' for first row")) + } + if (!tax.row_id) { + tax.row_id = tax.idx - 1; + } + } + if(tax.charge_type == "Actual") { // distribute the tax amount proportionally to each item row var actual = flt(tax.tax_amount, precision("tax_amount", tax)); @@ -345,11 +356,11 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ current_tax_amount = (tax_rate / 100.0) * item.net_amount; } else if(tax.charge_type == "On Previous Row Amount") { current_tax_amount = (tax_rate / 100.0) * - this.frm.doc["taxes"][cint(tax.idx) - 2].tax_amount_for_current_item; + this.frm.doc["taxes"][cint(tax.row_id) - 1].tax_amount_for_current_item; } else if(tax.charge_type == "On Previous Row Total") { current_tax_amount = (tax_rate / 100.0) * - this.frm.doc["taxes"][cint(tax.idx) - 2].grand_total_for_current_item; + this.frm.doc["taxes"][cint(tax.row_id) - 1].grand_total_for_current_item; } this.set_item_wise_tax(item, tax, tax_rate, current_tax_amount); From 0fe14ce94e1cace40c0a7186ad6a6a837d89143c Mon Sep 17 00:00:00 2001 From: Afshan Date: Fri, 19 Jun 2020 15:10:15 +0530 Subject: [PATCH 416/608] style: formate according to Codacy/PR Quality Review --- erpnext/public/js/controllers/taxes_and_totals.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index 6b8dc2804f3..a449bdcca67 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -337,9 +337,9 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ // To set row_id by default as previous row. if(["On Previous Row Amount", "On Previous Row Total"].includes(tax.charge_type)) { - if (tax.idx == 1) { - frappe.throw( - __("Cannot select charge type as 'On Previous Row Amount' or 'On Previous Row Total' for first row")) + if (tax.idx === 1) { + frappe.throw( + __("Cannot select charge type as 'On Previous Row Amount' or 'On Previous Row Total' for first row")); } if (!tax.row_id) { tax.row_id = tax.idx - 1; From 383807f72e8938b3812e1254f8e82a0609b65903 Mon Sep 17 00:00:00 2001 From: Marica Date: Fri, 19 Jun 2020 15:33:21 +0530 Subject: [PATCH 417/608] feat: Selling Desk, Dashboard and Onboarding (#22055) * feat: Selling Desk, Dashboard and Onboarding * chore: Selling Onboarding and fixes in Other onboardings * chore: Dashboard and Number card Fixtures * fix: Escape filters and Reposition Accounts Dashboard shortcut. Co-authored-by: Nabin Hait --- .../desk_page/accounting/accounting.json | 14 +- .../ordered_items_to_be_billed.js | 8 - .../ordered_items_to_be_billed.json | 27 -- .../ordered_items_to_be_billed.py | 26 -- erpnext/buying/dashboard_fixtures.py | 3 +- .../buying_settings/buying_settings.js | 8 +- .../module_onboarding/buying/buying.json | 2 +- .../buying_settings/buying_settings.json | 10 +- erpnext/patches.txt | 3 +- .../patches/v13_0/delete_old_sales_reports.py | 21 ++ erpnext/selling/dashboard_fixtures.py | 198 +++++++++++++ .../selling/desk_page/selling/selling.json | 70 +++-- .../selling_settings/selling_settings.js | 23 ++ .../selling_settings/selling_settings.json | 8 +- .../module_onboarding/selling/selling.json | 54 ++++ .../create_a_customer/create_a_customer.json | 19 ++ .../create_a_product/create_a_product.json | 19 ++ .../create_a_quotation.json | 19 ++ .../create_product/create_product.json | 19 ++ .../create_your_first_sales_order.json | 19 ++ .../introduction_to_selling.json | 19 ++ .../selling_settings/selling_settings.json | 19 ++ .../setup_your_warehouse.json | 20 ++ .../item_wise_sales_history.js | 40 ++- .../item_wise_sales_history.py | 42 ++- .../quotation_trends/quotation_trends.py | 47 ++- .../report/sales_order_analysis}/__init__.py | 0 .../sales_order_analysis.js | 85 ++++++ .../sales_order_analysis.json | 36 +++ .../sales_order_analysis.py | 279 ++++++++++++++++++ .../sales_order_trends/sales_order_trends.py | 47 ++- erpnext/stock/desk_page/stock/stock.json | 4 +- .../doctype/stock_settings/stock_settings.js | 2 +- .../delivery_note_trends.py | 3 +- .../ordered_items_to_be_delivered/__init__.py | 0 .../ordered_items_to_be_delivered.json | 34 --- 36 files changed, 1079 insertions(+), 168 deletions(-) delete mode 100644 erpnext/accounts/report/ordered_items_to_be_billed/ordered_items_to_be_billed.js delete mode 100644 erpnext/accounts/report/ordered_items_to_be_billed/ordered_items_to_be_billed.json delete mode 100644 erpnext/accounts/report/ordered_items_to_be_billed/ordered_items_to_be_billed.py create mode 100644 erpnext/patches/v13_0/delete_old_sales_reports.py create mode 100644 erpnext/selling/dashboard_fixtures.py create mode 100644 erpnext/selling/module_onboarding/selling/selling.json create mode 100644 erpnext/selling/onboarding_step/create_a_customer/create_a_customer.json create mode 100644 erpnext/selling/onboarding_step/create_a_product/create_a_product.json create mode 100644 erpnext/selling/onboarding_step/create_a_quotation/create_a_quotation.json create mode 100644 erpnext/selling/onboarding_step/create_product/create_product.json create mode 100644 erpnext/selling/onboarding_step/create_your_first_sales_order/create_your_first_sales_order.json create mode 100644 erpnext/selling/onboarding_step/introduction_to_selling/introduction_to_selling.json create mode 100644 erpnext/selling/onboarding_step/selling_settings/selling_settings.json create mode 100644 erpnext/selling/onboarding_step/setup_your_warehouse/setup_your_warehouse.json rename erpnext/{accounts/report/ordered_items_to_be_billed => selling/report/sales_order_analysis}/__init__.py (100%) create mode 100644 erpnext/selling/report/sales_order_analysis/sales_order_analysis.js create mode 100644 erpnext/selling/report/sales_order_analysis/sales_order_analysis.json create mode 100644 erpnext/selling/report/sales_order_analysis/sales_order_analysis.py delete mode 100644 erpnext/stock/report/ordered_items_to_be_delivered/__init__.py delete mode 100644 erpnext/stock/report/ordered_items_to_be_delivered/ordered_items_to_be_delivered.json diff --git a/erpnext/accounts/desk_page/accounting/accounting.json b/erpnext/accounts/desk_page/accounting/accounting.json index 42fb9f4f37d..31315e4c710 100644 --- a/erpnext/accounts/desk_page/accounting/accounting.json +++ b/erpnext/accounts/desk_page/accounting/accounting.json @@ -13,7 +13,7 @@ { "hidden": 0, "label": "Accounts Receivable", - "links": "[\n {\n \"description\": \"Bills raised to Customers.\",\n \"label\": \"Sales Invoice\",\n \"name\": \"Sales Invoice\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Customer database.\",\n \"label\": \"Customer\",\n \"name\": \"Customer\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Bank/Cash transactions against party or for internal transfer\",\n \"label\": \"Payment Entry\",\n \"name\": \"Payment Entry\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Payment Request\",\n \"label\": \"Payment Request\",\n \"name\": \"Payment Request\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Accounts Receivable\",\n \"name\": \"Accounts Receivable\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Accounts Receivable Summary\",\n \"name\": \"Accounts Receivable Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Sales Register\",\n \"name\": \"Sales Register\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Item-wise Sales Register\",\n \"name\": \"Item-wise Sales Register\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Ordered Items To Be Billed\",\n \"name\": \"Ordered Items To Be Billed\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Delivered Items To Be Billed\",\n \"name\": \"Delivered Items To Be Billed\",\n \"type\": \"report\"\n }\n]" + "links": "[\n {\n \"description\": \"Bills raised to Customers.\",\n \"label\": \"Sales Invoice\",\n \"name\": \"Sales Invoice\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Customer database.\",\n \"label\": \"Customer\",\n \"name\": \"Customer\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Bank/Cash transactions against party or for internal transfer\",\n \"label\": \"Payment Entry\",\n \"name\": \"Payment Entry\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Payment Request\",\n \"label\": \"Payment Request\",\n \"name\": \"Payment Request\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Accounts Receivable\",\n \"name\": \"Accounts Receivable\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Accounts Receivable Summary\",\n \"name\": \"Accounts Receivable Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Sales Register\",\n \"name\": \"Sales Register\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Item-wise Sales Register\",\n \"name\": \"Item-wise Sales Register\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Order Analysis\",\n \"name\": \"Sales Order Analysis\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Delivered Items To Be Billed\",\n \"name\": \"Delivered Items To Be Billed\",\n \"type\": \"report\"\n }\n]" }, { "hidden": 0, @@ -98,7 +98,7 @@ "idx": 0, "is_standard": 1, "label": "Accounting", - "modified": "2020-05-27 20:34:50.949772", + "modified": "2020-06-19 12:42:44.054598", "modified_by": "Administrator", "module": "Accounts", "name": "Accounting", @@ -122,11 +122,6 @@ "link_to": "Purchase Invoice", "type": "DocType" }, - { - "label": "Dashboard", - "link_to": "Accounts", - "type": "Dashboard" - }, { "label": "Journal Entry", "link_to": "Journal Entry", @@ -151,6 +146,11 @@ "label": "Trial Balance", "link_to": "Trial Balance", "type": "Report" + }, + { + "label": "Dashboard", + "link_to": "Accounts", + "type": "Dashboard" } ] } \ No newline at end of file diff --git a/erpnext/accounts/report/ordered_items_to_be_billed/ordered_items_to_be_billed.js b/erpnext/accounts/report/ordered_items_to_be_billed/ordered_items_to_be_billed.js deleted file mode 100644 index 6e13d677666..00000000000 --- a/erpnext/accounts/report/ordered_items_to_be_billed/ordered_items_to_be_billed.js +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.query_reports["Ordered Items To Be Billed"] = { - "filters": [ - - ] -} diff --git a/erpnext/accounts/report/ordered_items_to_be_billed/ordered_items_to_be_billed.json b/erpnext/accounts/report/ordered_items_to_be_billed/ordered_items_to_be_billed.json deleted file mode 100644 index c983dc96295..00000000000 --- a/erpnext/accounts/report/ordered_items_to_be_billed/ordered_items_to_be_billed.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "add_total_row": 1, - "apply_user_permissions": 1, - "creation": "2013-02-21 14:26:44", - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 3, - "is_standard": "Yes", - "modified": "2017-11-06 13:04:51.559061", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Ordered Items To Be Billed", - "owner": "Administrator", - "query": "select \n `tabSales Order`.`name` as \"Sales Order:Link/Sales Order:120\",\n `tabSales Order`.`customer` as \"Customer:Link/Customer:120\",\n `tabSales Order`.`customer_name` as \"Customer Name:150\",\n`tabSales Order`.`status` as \"Status\",\n `tabSales Order`.`transaction_date` as \"Date:Date\",\n `tabSales Order`.`project` as \"Project\",\n `tabSales Order Item`.item_code as \"Item:Link/Item:120\",\n `tabSales Order Item`.base_amount as \"Amount:Currency:110\",\n (`tabSales Order Item`.billed_amt * ifnull(`tabSales Order`.conversion_rate, 1)) as \"Billed Amount:Currency:110\",\n (`tabSales Order Item`.base_amount - (`tabSales Order Item`.billed_amt * ifnull(`tabSales Order`.conversion_rate, 1))) as \"Pending Amount:Currency:120\",\n `tabSales Order Item`.item_name as \"Item Name::150\",\n `tabSales Order Item`.description as \"Description::200\",\n `tabSales Order`.`company` as \"Company:Link/Company:\"\nfrom\n `tabSales Order`, `tabSales Order Item`\nwhere\n `tabSales Order Item`.`parent` = `tabSales Order`.`name`\n and `tabSales Order`.docstatus = 1\n and `tabSales Order`.status != \"Closed\"\n and `tabSales Order Item`.amount > 0\n and `tabSales Order Item`.billed_amt < `tabSales Order Item`.amount\norder by `tabSales Order`.transaction_date asc", - "ref_doctype": "Sales Invoice", - "report_name": "Ordered Items To Be Billed", - "report_type": "Script Report", - "roles": [ - { - "role": "Accounts Manager" - }, - { - "role": "Accounts User" - } - ] -} \ No newline at end of file diff --git a/erpnext/accounts/report/ordered_items_to_be_billed/ordered_items_to_be_billed.py b/erpnext/accounts/report/ordered_items_to_be_billed/ordered_items_to_be_billed.py deleted file mode 100644 index ec0d2f39f35..00000000000 --- a/erpnext/accounts/report/ordered_items_to_be_billed/ordered_items_to_be_billed.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - -from __future__ import unicode_literals -import frappe -from frappe import _ -from erpnext.accounts.report.non_billed_report import get_ordered_to_be_billed_data - -def execute(filters=None): - columns = get_column() - args = get_args() - data = get_ordered_to_be_billed_data(args) - return columns, data - -def get_column(): - return [ - _("Sales Order") + ":Link/Sales Order:120", _("Status") + "::120", _("Date") + ":Date:100", - _("Suplier") + ":Link/Customer:120", _("Customer Name") + "::120", - _("Project") + ":Link/Project:120", _("Item Code") + ":Link/Item:120", - _("Amount") + ":Currency:100", _("Billed Amount") + ":Currency:100", _("Pending Amount") + ":Currency:100", - _("Item Name") + "::120", _("Description") + "::120", _("Company") + ":Link/Company:120", - ] - -def get_args(): - return {'doctype': 'Sales Order', 'party': 'customer', - 'date': 'transaction_date', 'order': 'transaction_date', 'order_by': 'asc'} \ No newline at end of file diff --git a/erpnext/buying/dashboard_fixtures.py b/erpnext/buying/dashboard_fixtures.py index 172c936bd2b..c6e2ffa634f 100644 --- a/erpnext/buying/dashboard_fixtures.py +++ b/erpnext/buying/dashboard_fixtures.py @@ -155,8 +155,7 @@ def get_number_cards(company, fiscal_year_name, start_date, end_date): ["Purchase Order", "transaction_date", "Between", [start_date, end_date], False], ["Purchase Order", "status", "not in", ["Draft", "Cancelled", "Closed", None], False], ["Purchase Order", "docstatus", "=", 1, False], - ["Purchase Order", "company", "=", company.name, False], - ["Purchase Order", "transaction_date", "Between", [start_date,end_date], False] + ["Purchase Order", "company", "=", company.name, False] ]), "function": "Sum", "is_public": 1, diff --git a/erpnext/buying/doctype/buying_settings/buying_settings.js b/erpnext/buying/doctype/buying_settings/buying_settings.js index 01b40cd26fe..e496e9628d1 100644 --- a/erpnext/buying/doctype/buying_settings/buying_settings.js +++ b/erpnext/buying/doctype/buying_settings/buying_settings.js @@ -11,21 +11,21 @@ frappe.tour['Buying Settings'] = [ { fieldname: "supp_master_name", title: "Supplier Naming By", - description: __("By default, the Item Name is set as per the Item Code entered. If you want Items to be named by a set ") + "Naming Series" + __(" choose the 'Naming Series' option."), + description: __("By default, the Supplier Name is set as per the Supplier Name entered. If you want Suppliers to be named by a ") + "Naming Series" + __(" choose the 'Naming Series' option."), }, { fieldname: "buying_price_list", title: "Default Buying Price List", - description: __("Configure the default Price List when creating a new Buying transaction, the default is set as 'Standard Buying'. Item prices will be fetched from this Price List.") + description: __("Configure the default Price List when creating a new Purchase transaction. Item prices will be fetched from this Price List.") }, { fieldname: "po_required", title: "Purchase Order Required for Purchase Invoice & Receipt Creation", - description: __("If this option is configured 'Yes', ERPNext will prevent you from creating a Purchase Invoice or Receipt without creating a Purchase Order first. This configuration can be overridden for a particular supplier by enabling the 'Allow Purchase Invoice Creation Without Purchase Order' checkbox in supplier master.") + description: __("If this option is configured 'Yes', ERPNext will prevent you from creating a Purchase Invoice or Receipt without creating a Purchase Order first. This configuration can be overridden for a particular supplier by enabling the 'Allow Purchase Invoice Creation Without Purchase Order' checkbox in the Supplier master.") }, { fieldname: "pr_required", title: "Purchase Receipt Required for Purchase Invoice Creation", - description: __("If this option is configured 'Yes', ERPNext will prevent you from creating a Purchase Invoice without creating a Purchase Receipt first. This configuration can be overridden for a particular supplier by enabling the 'Allow Purchase Invoice Creation Without Purchase Receipt' checkbox in supplier master.") + description: __("If this option is configured 'Yes', ERPNext will prevent you from creating a Purchase Invoice without creating a Purchase Receipt first. This configuration can be overridden for a particular supplier by enabling the 'Allow Purchase Invoice Creation Without Purchase Receipt' checkbox in the Supplier master.") } ]; \ No newline at end of file diff --git a/erpnext/buying/module_onboarding/buying/buying.json b/erpnext/buying/module_onboarding/buying/buying.json index 8fe2f388b0c..6e4bbc95a24 100644 --- a/erpnext/buying/module_onboarding/buying/buying.json +++ b/erpnext/buying/module_onboarding/buying/buying.json @@ -19,7 +19,7 @@ "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/buying", "idx": 0, "is_complete": 0, - "modified": "2020-05-27 17:17:52.075947", + "modified": "2020-06-01 12:55:09.234944", "modified_by": "Administrator", "module": "Buying", "name": "Buying", diff --git a/erpnext/buying/onboarding_step/buying_settings/buying_settings.json b/erpnext/buying/onboarding_step/buying_settings/buying_settings.json index a788ccd4cc9..6d765af1373 100644 --- a/erpnext/buying/onboarding_step/buying_settings/buying_settings.json +++ b/erpnext/buying/onboarding_step/buying_settings/buying_settings.json @@ -1,19 +1,19 @@ { - "action": "Update Settings", + "action": "Show Form Tour", "creation": "2020-05-06 15:53:44.667414", "docstatus": 0, "doctype": "Onboarding Step", "idx": 0, "is_complete": 0, - "is_mandatory": 0, - "is_single": 0, + "is_mandatory": 1, + "is_single": 1, "is_skipped": 0, - "modified": "2020-05-12 18:30:06.323797", + "modified": "2020-06-01 12:52:57.668870", "modified_by": "Administrator", "name": "Buying Settings", "owner": "Administrator", "reference_document": "Buying Settings", "show_full_form": 0, "title": "Configure Buying Settings.", - "validate_action": 1 + "validate_action": 0 } \ No newline at end of file diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 15c4080f976..a7395a476a6 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -700,4 +700,5 @@ erpnext.patches.v12_0.set_italian_import_supplier_invoice_permissions erpnext.patches.v13_0.update_sla_enhancements erpnext.patches.v12_0.update_address_template_for_india erpnext.patches.v12_0.set_multi_uom_in_rfq -execute:frappe.delete_doc_if_exists("DocType", "Bank Reconciliation") +erpnext.patches.v13_0.delete_old_sales_reports +execute:frappe.delete_doc_if_exists("DocType", "Bank Reconciliation") \ No newline at end of file diff --git a/erpnext/patches/v13_0/delete_old_sales_reports.py b/erpnext/patches/v13_0/delete_old_sales_reports.py new file mode 100644 index 00000000000..0f44865808a --- /dev/null +++ b/erpnext/patches/v13_0/delete_old_sales_reports.py @@ -0,0 +1,21 @@ +# Copyright (c) 2019, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals + +import frappe + +def execute(): + reports_to_delete = ["Ordered Items To Be Delivered", "Ordered Items To Be Billed"] + + for report in reports_to_delete: + if frappe.db.exists("Report", report): + delete_auto_email_reports(report) + + frappe.delete_doc("Report", report) + +def delete_auto_email_reports(report): + """ Check for one or multiple Auto Email Reports and delete """ + auto_email_reports = frappe.db.get_values("Auto Email Report", {"report": report}, ["name"]) + for auto_email_report in auto_email_reports: + frappe.delete_doc("Auto Email Report", auto_email_report[0]) \ No newline at end of file diff --git a/erpnext/selling/dashboard_fixtures.py b/erpnext/selling/dashboard_fixtures.py new file mode 100644 index 00000000000..889cb88dce1 --- /dev/null +++ b/erpnext/selling/dashboard_fixtures.py @@ -0,0 +1,198 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +import frappe +import json +from frappe import _ +from frappe.utils import nowdate +from erpnext.accounts.utils import get_fiscal_year + +def get_data(): + return frappe._dict({ + "dashboards": get_dashboards(), + "charts": get_charts(), + "number_cards": get_number_cards(), + }) + +def get_company_for_dashboards(): + company = frappe.defaults.get_defaults().company + if company: + return company + else: + company_list = frappe.get_list("Company") + if company_list: + return company_list[0].name + return None + +company = frappe.get_doc("Company", get_company_for_dashboards()) +fiscal_year = get_fiscal_year(nowdate(), as_dict=1) +fiscal_year_name = fiscal_year.get("name") +start_date = str(fiscal_year.get("year_start_date")) +end_date = str(fiscal_year.get("year_end_date")) + +def get_dashboards(): + return [{ + "name": "Selling", + "dashboard_name": "Selling", + "charts": [ + { "chart": "Sales Order Trends", "width": "Full"}, + { "chart": "Top Customers", "width": "Half"}, + { "chart": "Sales Order Analysis", "width": "Half"}, + { "chart": "Item-wise Annual Sales", "width": "Full"} + ], + "cards": [ + { "card": "Annual Sales"}, + { "card": "Sales Orders to Deliver"}, + { "card": "Sales Orders to Bill"}, + { "card": "Active Customers"} + ] + }] + +def get_charts(): + return [ + { + "name": "Sales Order Analysis", + "chart_name": _("Sales Order Analysis"), + "chart_type": "Report", + "custom_options": json.dumps({ + "type": "donut", + "height": 300, + "axisOptions": {"shortenYAxisNumbers": 1} + }), + "doctype": "Dashboard Chart", + "filters_json": json.dumps({ + "company": company.name, + "from_date": start_date, + "to_date": end_date + }), + "is_custom": 1, + "is_public": 1, + "owner": "Administrator", + "report_name": "Sales Order Analysis", + "type": "Donut" + }, + { + "name": "Item-wise Annual Sales", + "chart_name": _("Item-wise Annual Sales"), + "chart_type": "Report", + "doctype": "Dashboard Chart", + "filters_json": json.dumps({ + "company": company.name, + "from_date": start_date, + "to_date": end_date + }), + "is_custom": 1, + "is_public": 1, + "owner": "Administrator", + "report_name": "Item-wise Sales History", + "type": "Bar" + }, + { + "name": "Sales Order Trends", + "chart_name": _("Sales Order Trends"), + "chart_type": "Report", + "custom_options": json.dumps({ + "type": "line", + "axisOptions": {"shortenYAxisNumbers": 1}, + "tooltipOptions": {}, + "lineOptions": { + "regionFill": 1 + } + }), + "doctype": "Dashboard Chart", + "filters_json": json.dumps({ + "company": company.name, + "period": "Monthly", + "fiscal_year": fiscal_year_name, + "based_on": "Item" + }), + "is_custom": 1, + "is_public": 1, + "owner": "Administrator", + "report_name": "Sales Order Trends", + "type": "Line" + }, + { + "name": "Top Customers", + "chart_name": _("Top Customers"), + "chart_type": "Report", + "doctype": "Dashboard Chart", + "filters_json": json.dumps({ + "company": company.name, + "period": "Monthly", + "fiscal_year": fiscal_year_name, + "based_on": "Customer" + }), + "is_custom": 1, + "is_public": 1, + "owner": "Administrator", + "report_name": "Delivery Note Trends", + "type": "Bar" + } + ] + +def get_number_cards(): + return [ + { + "name": "Annual Sales", + "aggregate_function_based_on": "base_net_total", + "doctype": "Number Card", + "document_type": "Sales Order", + "filters_json": json.dumps([ + ["Sales Order", "transaction_date", "Between", [start_date, end_date], False], + ["Sales Order", "status", "not in", ["Draft", "Cancelled", "Closed", None], False], + ["Sales Order", "docstatus", "=", 1, False], + ["Sales Order", "company", "=", company.name, False] + ]), + "function": "Sum", + "is_public": 1, + "label": _("Annual Sales"), + "owner": "Administrator", + "show_percentage_stats": 1, + "stats_time_interval": "Monthly" + }, + { + "name": "Sales Orders to Deliver", + "doctype": "Number Card", + "document_type": "Sales Order", + "filters_json": json.dumps([ + ["Sales Order", "status", "in", ["To Deliver and Bill", "To Deliver", None], False], + ["Sales Order", "docstatus", "=", 1, False], + ["Sales Order", "company", "=", company.name, False] + ]), + "function": "Count", + "is_public": 1, + "label": _("Sales Orders to Deliver"), + "owner": "Administrator", + "show_percentage_stats": 1, + "stats_time_interval": "Weekly" + }, + { + "name": "Sales Orders to Bill", + "doctype": "Number Card", + "document_type": "Sales Order", + "filters_json": json.dumps([ + ["Sales Order", "status", "in", ["To Deliver and Bill", "To Bill", None], False], + ["Sales Order", "docstatus", "=", 1, False], + ["Sales Order", "company", "=", company.name, False] + ]), + "function": "Count", + "is_public": 1, + "label": _("Sales Orders to Bill"), + "owner": "Administrator", + "show_percentage_stats": 1, + "stats_time_interval": "Weekly" + }, + { + "name": "Active Customers", + "doctype": "Number Card", + "document_type": "Customer", + "filters_json": json.dumps([["Customer", "disabled", "=", "0"]]), + "function": "Count", + "is_public": 1, + "label": "Active Customers", + "owner": "Administrator", + "show_percentage_stats": 1, + "stats_time_interval": "Monthly" + } + ] \ No newline at end of file diff --git a/erpnext/selling/desk_page/selling/selling.json b/erpnext/selling/desk_page/selling/selling.json index 9ec634354d3..60b15326e83 100644 --- a/erpnext/selling/desk_page/selling/selling.json +++ b/erpnext/selling/desk_page/selling/selling.json @@ -1,5 +1,10 @@ { "cards": [ + { + "hidden": 0, + "label": "Selling", + "links": "[\n {\n \"description\": \"Customer Database.\",\n \"label\": \"Customer\",\n \"name\": \"Customer\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Quotes to Leads or Customers.\",\n \"label\": \"Quotation\",\n \"name\": \"Quotation\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Confirmed orders from Customers.\",\n \"label\": \"Sales Order\",\n \"name\": \"Sales Order\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"label\": \"Sales Invoice\",\n \"name\": \"Sales Invoice\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Blanket Orders from Costumers.\",\n \"label\": \"Blanket Order\",\n \"name\": \"Blanket Order\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"description\": \"Manage Sales Partners.\",\n \"label\": \"Sales Partner\",\n \"name\": \"Sales Partner\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Manage Sales Person Tree.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Sales Person\",\n \"link\": \"Tree/Sales Person\",\n \"name\": \"Sales Person\",\n \"type\": \"doctype\"\n }\n]" + }, { "hidden": 0, "label": "Items and Pricing", @@ -10,29 +15,25 @@ "label": "Settings", "links": "[\n {\n \"description\": \"Default settings for selling transactions.\",\n \"label\": \"Selling Settings\",\n \"name\": \"Selling Settings\",\n \"settings\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Template of terms or contract.\",\n \"label\": \"Terms and Conditions Template\",\n \"name\": \"Terms and Conditions\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Tax template for selling transactions.\",\n \"label\": \"Sales Taxes and Charges Template\",\n \"name\": \"Sales Taxes and Charges Template\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Track Leads by Lead Source.\",\n \"label\": \"Lead Source\",\n \"name\": \"Lead Source\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Manage Customer Group Tree.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Customer Group\",\n \"link\": \"Tree/Customer Group\",\n \"name\": \"Customer Group\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"All Contacts.\",\n \"label\": \"Contact\",\n \"name\": \"Contact\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"All Addresses.\",\n \"label\": \"Address\",\n \"name\": \"Address\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Manage Territory Tree.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Territory\",\n \"link\": \"Tree/Territory\",\n \"name\": \"Territory\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Sales campaigns.\",\n \"label\": \"Campaign\",\n \"name\": \"Campaign\",\n \"type\": \"doctype\"\n }\n]" }, - { - "hidden": 0, - "label": "Other Reports", - "links": "[\n {\n \"dependencies\": [\n \"Lead\"\n ],\n \"doctype\": \"Lead\",\n \"is_query_report\": true,\n \"label\": \"Lead Details\",\n \"name\": \"Lead Details\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Address\"\n ],\n \"doctype\": \"Address\",\n \"is_query_report\": true,\n \"label\": \"Customer Addresses And Contacts\",\n \"name\": \"Address And Contacts\",\n \"route_options\": {\n \"party_type\": \"Customer\"\n },\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"BOM\"\n ],\n \"doctype\": \"BOM\",\n \"is_query_report\": true,\n \"label\": \"BOM Search\",\n \"name\": \"BOM Search\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"doctype\": \"Item\",\n \"is_query_report\": true,\n \"label\": \"Available Stock for Packing Items\",\n \"name\": \"Available Stock for Packing Items\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Pending SO Items For Purchase Request\",\n \"name\": \"Pending SO Items For Purchase Request\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Customer Credit Balance\",\n \"name\": \"Customer Credit Balance\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Customers Without Any Sales Transactions\",\n \"name\": \"Customers Without Any Sales Transactions\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Sales Partners Commission\",\n \"name\": \"Sales Partners Commission\",\n \"type\": \"report\"\n }\n]" - }, - { - "hidden": 0, - "label": "Sales", - "links": "[\n {\n \"description\": \"Customer Database.\",\n \"label\": \"Customer\",\n \"name\": \"Customer\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Quotes to Leads or Customers.\",\n \"label\": \"Quotation\",\n \"name\": \"Quotation\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Confirmed orders from Customers.\",\n \"label\": \"Sales Order\",\n \"name\": \"Sales Order\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Invoices for Costumers.\",\n \"label\": \"Sales Invoice\",\n \"name\": \"Sales Invoice\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Blanket Orders from Costumers.\",\n \"label\": \"Blanket Order\",\n \"name\": \"Blanket Order\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"description\": \"Manage Sales Partners.\",\n \"label\": \"Sales Partner\",\n \"name\": \"Sales Partner\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Item\",\n \"Customer\"\n ],\n \"description\": \"Manage Sales Person Tree.\",\n \"icon\": \"fa fa-sitemap\",\n \"label\": \"Sales Person\",\n \"link\": \"Tree/Sales Person\",\n \"name\": \"Sales Person\",\n \"type\": \"doctype\"\n }\n]" - }, { "hidden": 0, "label": "Key Reports", - "links": "[\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Analytics\",\n \"name\": \"Sales Analytics\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"icon\": \"fa fa-bar-chart\",\n \"label\": \"Sales Funnel\",\n \"name\": \"sales-funnel\",\n \"onboard\": 1,\n \"type\": \"page\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"icon\": \"fa fa-bar-chart\",\n \"is_query_report\": true,\n \"label\": \"Customer Acquisition and Loyalty\",\n \"name\": \"Customer Acquisition and Loyalty\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Inactive Customers\",\n \"name\": \"Inactive Customers\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Ordered Items To Be Delivered\",\n \"name\": \"Ordered Items To Be Delivered\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Person-wise Transaction Summary\",\n \"name\": \"Sales Person-wise Transaction Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"doctype\": \"Item\",\n \"is_query_report\": true,\n \"label\": \"Item-wise Sales History\",\n \"name\": \"Item-wise Sales History\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Quotation\"\n ],\n \"doctype\": \"Quotation\",\n \"is_query_report\": true,\n \"label\": \"Quotation Trends\",\n \"name\": \"Quotation Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Order Trends\",\n \"name\": \"Sales Order Trends\",\n \"type\": \"report\"\n }\n]" + "links": "[\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Analytics\",\n \"name\": \"Sales Analytics\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Order Analysis\",\n \"name\": \"Sales Order Analysis\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"icon\": \"fa fa-bar-chart\",\n \"label\": \"Sales Funnel\",\n \"name\": \"sales-funnel\",\n \"onboard\": 1,\n \"type\": \"page\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Order Trends\",\n \"name\": \"Sales Order Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Quotation\"\n ],\n \"doctype\": \"Quotation\",\n \"is_query_report\": true,\n \"label\": \"Quotation Trends\",\n \"name\": \"Quotation Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"icon\": \"fa fa-bar-chart\",\n \"is_query_report\": true,\n \"label\": \"Customer Acquisition and Loyalty\",\n \"name\": \"Customer Acquisition and Loyalty\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Inactive Customers\",\n \"name\": \"Inactive Customers\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Person-wise Transaction Summary\",\n \"name\": \"Sales Person-wise Transaction Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"doctype\": \"Item\",\n \"is_query_report\": true,\n \"label\": \"Item-wise Sales History\",\n \"name\": \"Item-wise Sales History\",\n \"type\": \"report\"\n }\n]" + }, + { + "hidden": 0, + "label": "Other Reports", + "links": "[\n {\n \"dependencies\": [\n \"Lead\"\n ],\n \"doctype\": \"Lead\",\n \"is_query_report\": true,\n \"label\": \"Lead Details\",\n \"name\": \"Lead Details\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Address\"\n ],\n \"doctype\": \"Address\",\n \"is_query_report\": true,\n \"label\": \"Customer Addresses And Contacts\",\n \"name\": \"Address And Contacts\",\n \"route_options\": {\n \"party_type\": \"Customer\"\n },\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"doctype\": \"Item\",\n \"is_query_report\": true,\n \"label\": \"Available Stock for Packing Items\",\n \"name\": \"Available Stock for Packing Items\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Pending SO Items For Purchase Request\",\n \"name\": \"Pending SO Items For Purchase Request\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Customer Credit Balance\",\n \"name\": \"Customer Credit Balance\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Customers Without Any Sales Transactions\",\n \"name\": \"Customers Without Any Sales Transactions\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Sales Partners Commission\",\n \"name\": \"Sales Partners Commission\",\n \"type\": \"report\"\n }\n]" } ], "category": "Modules", "charts": [ { - "chart_name": "Incoming Bills (Purchase Invoice)", - "label": "Income" + "chart_name": "Sales Order Trends", + "label": "Sales Order Trends" } ], + "charts_label": "Selling ", "creation": "2020-01-28 11:49:12.092882", "developer_mode_only": 0, "disable_user_customization": 0, @@ -43,52 +44,49 @@ "idx": 0, "is_standard": 1, "label": "Selling", - "modified": "2020-06-03 13:23:24.861706", + "modified": "2020-06-19 13:23:24.861706", "modified_by": "Administrator", "module": "Selling", "name": "Selling", + "onboarding": "Selling", "owner": "Administrator", "pin_to_bottom": 0, "pin_to_top": 0, "shortcuts": [ { - "color": "#ffe8cd", - "format": "{} Draft", - "label": "Sales Invoice", - "link_to": "Sales Invoice", - "stats_filter": "{ \"status\": \"Draft\" }", + "color": "#cef6d1", + "format": "{} Available", + "label": "Item", + "link_to": "Item", + "stats_filter": "{\n \"disabled\":0\n}", "type": "DocType" }, { "color": "#ffe8cd", - "format": "{} To Deliver", + "format": "{} To Deliver", "label": "Sales Order", "link_to": "Sales Order", - "stats_filter": "{\"Status\": \"To Deliver and Bill\"}", + "stats_filter": "{\n \"company\": [\"like\", '%' + frappe.defaults.get_global_default(\"company\") + '%'],\n \"status\":[\"in\", [\"To Deliver\", \"To Deliver and Bill\"]]\n}", "type": "DocType" }, { "color": "#cef6d1", "format": "{} Open", - "label": "Quotation", - "link_to": "Quotation", + "label": "Sales Analytics", + "link_to": "Sales Analytics", "stats_filter": "{ \"Status\": \"Open\" }", - "type": "DocType" - }, - { - "label": "Delivery Note", - "link_to": "Delivery Note", - "type": "DocType" - }, - { - "label": "Accounts Receivable", - "link_to": "Accounts Receivable", "type": "Report" }, { - "label": "Sales Register", - "link_to": "Sales Register", + "label": "Sales Order Analysis", + "link_to": "Sales Order Analysis", "type": "Report" + }, + { + "label": "Dashboard", + "link_to": "Selling", + "type": "Dashboard" } - ] + ], + "shortcuts_label": "Quick Access" } \ No newline at end of file diff --git a/erpnext/selling/doctype/selling_settings/selling_settings.js b/erpnext/selling/doctype/selling_settings/selling_settings.js index cf6fb2806ee..95a4243fb45 100644 --- a/erpnext/selling/doctype/selling_settings/selling_settings.js +++ b/erpnext/selling/doctype/selling_settings/selling_settings.js @@ -6,3 +6,26 @@ frappe.ui.form.on('Selling Settings', { } }); + +frappe.tour['Selling Settings'] = [ + { + fieldname: "cust_master_name", + title: "Customer Naming By", + description: __("By default, the Customer Name is set as per the Full Name entered. If you want Customers to be named by a ") + "Naming Series" + __(" choose the 'Naming Series' option."), + }, + { + fieldname: "selling_price_list", + title: "Default Selling Price List", + description: __("Configure the default Price List when creating a new Sales transaction. Item prices will be fetched from this Price List.") + }, + { + fieldname: "so_required", + title: "Sales Order Required for Sales Invoice & Delivery Note Creation", + description: __("If this option is configured 'Yes', ERPNext will prevent you from creating a Sales Invoice or Delivery Note without creating a Sales Order first. This configuration can be overridden for a particular Customer by enabling the 'Allow Sales Invoice Creation Without Sales Order' checkbox in the Customer master.") + }, + { + fieldname: "dn_required", + title: "Delivery Note Required for Sales Invoice Creation", + description: __("If this option is configured 'Yes', ERPNext will prevent you from creating a Sales Invoice without creating a Delivery Note first. This configuration can be overridden for a particular Customer by enabling the 'Allow Sales Invoice Creation Without Delivery Note' checkbox in the Customer master.") + } +]; \ No newline at end of file diff --git a/erpnext/selling/doctype/selling_settings/selling_settings.json b/erpnext/selling/doctype/selling_settings/selling_settings.json index c04bfd281e2..dcbc0748f7d 100644 --- a/erpnext/selling/doctype/selling_settings/selling_settings.json +++ b/erpnext/selling/doctype/selling_settings/selling_settings.json @@ -1,4 +1,5 @@ { + "actions": [], "creation": "2013-06-25 10:25:16", "description": "Settings for Selling Module", "doctype": "DocType", @@ -79,13 +80,13 @@ { "fieldname": "so_required", "fieldtype": "Select", - "label": "Sales Order Required", + "label": "Sales Order Required for Sales Invoice & Delivery Note Creation", "options": "No\nYes" }, { "fieldname": "dn_required", "fieldtype": "Select", - "label": "Delivery Note Required", + "label": "Delivery Note Required for Sales Invoice Creation", "options": "No\nYes" }, { @@ -137,7 +138,8 @@ "icon": "fa fa-cog", "idx": 1, "issingle": 1, - "modified": "2019-12-09 13:38:36.486298", + "links": [], + "modified": "2020-06-01 13:58:35.637858", "modified_by": "Administrator", "module": "Selling", "name": "Selling Settings", diff --git a/erpnext/selling/module_onboarding/selling/selling.json b/erpnext/selling/module_onboarding/selling/selling.json new file mode 100644 index 00000000000..10a33c9cf52 --- /dev/null +++ b/erpnext/selling/module_onboarding/selling/selling.json @@ -0,0 +1,54 @@ +{ + "allow_roles": [ + { + "role": "Sales Manager" + }, + { + "role": "Sales User" + }, + { + "role": "Stock Manager" + }, + { + "role": "Stock User" + } + ], + "creation": "2020-06-01 12:44:42.589930", + "docstatus": 0, + "doctype": "Module Onboarding", + "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/selling", + "idx": 0, + "is_complete": 0, + "modified": "2020-06-01 13:35:16.100512", + "modified_by": "Administrator", + "module": "Selling", + "name": "Selling", + "owner": "Administrator", + "steps": [ + { + "step": "Introduction to Selling" + }, + { + "step": "Create a Customer" + }, + { + "step": "Setup your Warehouse" + }, + { + "step": "Create a Product" + }, + { + "step": "Create a Quotation" + }, + { + "step": "Create your first Sales Order" + }, + { + "step": "Selling Settings" + } + ], + "subtitle": "Products, Sales, Analysis and more.", + "success_message": "The Selling Module is all set up!", + "title": "Let's Set Up the Selling Module.", + "user_can_dismiss": 1 +} \ No newline at end of file diff --git a/erpnext/selling/onboarding_step/create_a_customer/create_a_customer.json b/erpnext/selling/onboarding_step/create_a_customer/create_a_customer.json new file mode 100644 index 00000000000..5a403b06cf0 --- /dev/null +++ b/erpnext/selling/onboarding_step/create_a_customer/create_a_customer.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-05-14 17:46:41.831517", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-06-01 13:16:19.731719", + "modified_by": "Administrator", + "name": "Create a Customer", + "owner": "Administrator", + "reference_document": "Customer", + "show_full_form": 0, + "title": "Create a Customer", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/selling/onboarding_step/create_a_product/create_a_product.json b/erpnext/selling/onboarding_step/create_a_product/create_a_product.json new file mode 100644 index 00000000000..d2068e167b7 --- /dev/null +++ b/erpnext/selling/onboarding_step/create_a_product/create_a_product.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-05-12 18:16:06.624554", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-12 18:30:02.489949", + "modified_by": "Administrator", + "name": "Create a Product", + "owner": "Administrator", + "reference_document": "Item", + "show_full_form": 0, + "title": "Create a Product", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/selling/onboarding_step/create_a_quotation/create_a_quotation.json b/erpnext/selling/onboarding_step/create_a_quotation/create_a_quotation.json new file mode 100644 index 00000000000..27253d15b6c --- /dev/null +++ b/erpnext/selling/onboarding_step/create_a_quotation/create_a_quotation.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-06-01 13:34:58.958641", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-06-01 13:34:58.958641", + "modified_by": "Administrator", + "name": "Create a Quotation", + "owner": "Administrator", + "reference_document": "Quotation", + "show_full_form": 1, + "title": "Create a Quotation", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/selling/onboarding_step/create_product/create_product.json b/erpnext/selling/onboarding_step/create_product/create_product.json new file mode 100644 index 00000000000..0ffa30158b0 --- /dev/null +++ b/erpnext/selling/onboarding_step/create_product/create_product.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-05-05 16:42:31.476275", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 1, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-19 12:50:59.010439", + "modified_by": "Administrator", + "name": "Create Product", + "owner": "Administrator", + "reference_document": "Item", + "show_full_form": 0, + "title": "Create a Finished Good", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/selling/onboarding_step/create_your_first_sales_order/create_your_first_sales_order.json b/erpnext/selling/onboarding_step/create_your_first_sales_order/create_your_first_sales_order.json new file mode 100644 index 00000000000..5b601a7a900 --- /dev/null +++ b/erpnext/selling/onboarding_step/create_your_first_sales_order/create_your_first_sales_order.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-06-01 12:52:27.181841", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 1, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-06-01 12:52:27.181841", + "modified_by": "Administrator", + "name": "Create your first Sales Order", + "owner": "Administrator", + "reference_document": "Sales Order", + "show_full_form": 1, + "title": "Create your first Sales Order", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/selling/onboarding_step/introduction_to_selling/introduction_to_selling.json b/erpnext/selling/onboarding_step/introduction_to_selling/introduction_to_selling.json new file mode 100644 index 00000000000..d21c1f4954b --- /dev/null +++ b/erpnext/selling/onboarding_step/introduction_to_selling/introduction_to_selling.json @@ -0,0 +1,19 @@ +{ + "action": "Watch Video", + "creation": "2020-06-01 12:44:32.089234", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-06-01 13:29:13.703177", + "modified_by": "Administrator", + "name": "Introduction to Selling", + "owner": "Administrator", + "show_full_form": 0, + "title": "Introduction to Selling", + "validate_action": 1, + "video_url": "https://youtu.be/1eP90MWoDQM" +} \ No newline at end of file diff --git a/erpnext/selling/onboarding_step/selling_settings/selling_settings.json b/erpnext/selling/onboarding_step/selling_settings/selling_settings.json new file mode 100644 index 00000000000..7996d7b1593 --- /dev/null +++ b/erpnext/selling/onboarding_step/selling_settings/selling_settings.json @@ -0,0 +1,19 @@ +{ + "action": "Show Form Tour", + "creation": "2020-06-01 13:01:45.615189", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 1, + "is_skipped": 0, + "modified": "2020-06-01 13:04:14.980743", + "modified_by": "Administrator", + "name": "Selling Settings", + "owner": "Administrator", + "reference_document": "Selling Settings", + "show_full_form": 0, + "title": "Configure Selling Settings.", + "validate_action": 0 +} \ No newline at end of file diff --git a/erpnext/selling/onboarding_step/setup_your_warehouse/setup_your_warehouse.json b/erpnext/selling/onboarding_step/setup_your_warehouse/setup_your_warehouse.json new file mode 100644 index 00000000000..557c905bd6c --- /dev/null +++ b/erpnext/selling/onboarding_step/setup_your_warehouse/setup_your_warehouse.json @@ -0,0 +1,20 @@ +{ + "action": "Go to Page", + "creation": "2020-05-19 18:54:19.383397", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-19 18:54:19.383397", + "modified_by": "Administrator", + "name": "Setup your Warehouse", + "owner": "Administrator", + "path": "Tree/Warehouse", + "reference_document": "Warehouse", + "show_full_form": 0, + "title": "Setup your Warehouse", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.js b/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.js index daca2e3bd0c..f47d67fe494 100644 --- a/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.js +++ b/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.js @@ -12,12 +12,6 @@ frappe.query_reports["Item-wise Sales History"] = { default: frappe.defaults.get_user_default("Company"), reqd: 1 }, - { - fieldname:"item_group", - label: __("Item Group"), - fieldtype: "Link", - options: "Item Group" - }, { fieldname:"from_date", reqd: 1, @@ -32,6 +26,38 @@ frappe.query_reports["Item-wise Sales History"] = { label: __("To Date"), fieldtype: "Date", }, + { + fieldname:"item_group", + label: __("Item Group"), + fieldtype: "Link", + options: "Item Group" + }, + { + fieldname:"item_code", + label: __("Item"), + fieldtype: "Link", + options: "Item", + get_query: () => { + return { + query: "erpnext.controllers.queries.item_query" + } + } + }, + { + fieldname:"customer", + label: __("Customer"), + fieldtype: "Link", + options: "Customer" + } + ], - ] + "formatter": function (value, row, column, data, default_formatter) { + value = default_formatter(value, row, column, data); + let format_fields = ["delivered_quantity", "billed_amount"]; + + if (in_list(format_fields, column.fieldname) && data && data[column.fieldname] > 0) { + value = "" + value + ""; + } + return value; + } }; \ No newline at end of file diff --git a/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py b/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py index 08a98ba6c07..bd59be663ad 100644 --- a/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py +++ b/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py @@ -11,7 +11,10 @@ def execute(filters=None): filters = frappe._dict(filters or {}) columns = get_columns(filters) data = get_data(filters) - return columns, data + + chart_data = get_chart_data(data) + + return columns, data, None, chart_data def get_columns(filters): return [ @@ -181,6 +184,12 @@ def get_conditions(filters): if filters.get('to_date'): conditions += "AND so.transaction_date <= '%s'" %filters.to_date + if filters.get("item_code"): + conditions += "AND so_item.item_code = '%s'" %frappe.db.escape(filters.item_code) + + if filters.get("customer"): + conditions += "AND so.customer = '%s'" %frappe.db.escape(filters.customer) + return conditions def get_customer_details(): @@ -212,3 +221,34 @@ def get_sales_order_details(company_list, filters): AND so.company in ({0}) AND so.docstatus = 1 {1} """.format(','.join(["%s"] * len(company_list)), conditions), tuple(company_list), as_dict=1) + +def get_chart_data(data): + item_wise_sales_map = {} + labels, datapoints = [], [] + + for row in data: + item_key = row.get("item_code") + + if not item_key in item_wise_sales_map: + item_wise_sales_map[item_key] = 0 + + item_wise_sales_map[item_key] = flt(item_wise_sales_map[item_key]) + flt(row.get("amount")) + + item_wise_sales_map = { item: value for item, value in (sorted(item_wise_sales_map.items(), key = lambda i: i[1], reverse=True))} + + for key in item_wise_sales_map: + labels.append(key) + datapoints.append(item_wise_sales_map[key]) + + return { + "data" : { + "labels" : labels[:30], # show max of 30 items in chart + "datasets" : [ + { + "name" : _(" Total Sales Amount"), + "values" : datapoints[:30] + } + ] + }, + "type" : "bar" + } \ No newline at end of file diff --git a/erpnext/selling/report/quotation_trends/quotation_trends.py b/erpnext/selling/report/quotation_trends/quotation_trends.py index 67375f98b31..968e2ff26f7 100644 --- a/erpnext/selling/report/quotation_trends/quotation_trends.py +++ b/erpnext/selling/report/quotation_trends/quotation_trends.py @@ -3,6 +3,7 @@ from __future__ import unicode_literals import frappe +from frappe import _ from erpnext.controllers.trends import get_columns, get_data def execute(filters=None): @@ -11,4 +12,48 @@ def execute(filters=None): conditions = get_columns(filters, "Quotation") data = get_data(filters, conditions) - return conditions["columns"], data \ No newline at end of file + chart_data = get_chart_data(data, conditions, filters) + + return conditions["columns"], data, None, chart_data + +def get_chart_data(data, conditions, filters): + if not (data and conditions): + return [] + + datapoints = [] + + start = 2 if filters.get("based_on") in ["Item", "Customer"] else 1 + if filters.get("group_by"): + start += 1 + + # fetch only periodic columns as labels + columns = conditions.get("columns")[start:-2][1::2] + labels = [column.split(':')[0] for column in columns] + datapoints = [0] * len(labels) + + for row in data: + # If group by filter, don't add first row of group (it's already summed) + if not row[start-1]: + continue + # Remove None values and compute only periodic data + row = [x if x else 0 for x in row[start:-2]] + row = row[1::2] + + for i in range(len(row)): + datapoints[i] += row[i] + + return { + "data" : { + "labels" : labels, + "datasets" : [ + { + "name" : _("{0}").format(filters.get("period")) + _(" Quoted Amount"), + "values" : datapoints + } + ] + }, + "type" : "line", + "lineOptions": { + "regionFill": 1 + } + } diff --git a/erpnext/accounts/report/ordered_items_to_be_billed/__init__.py b/erpnext/selling/report/sales_order_analysis/__init__.py similarity index 100% rename from erpnext/accounts/report/ordered_items_to_be_billed/__init__.py rename to erpnext/selling/report/sales_order_analysis/__init__.py diff --git a/erpnext/selling/report/sales_order_analysis/sales_order_analysis.js b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.js new file mode 100644 index 00000000000..76a5bb51ca1 --- /dev/null +++ b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.js @@ -0,0 +1,85 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Sales Order Analysis"] = { + "filters": [ + { + "fieldname": "company", + "label": __("Company"), + "fieldtype": "Link", + "width": "80", + "options": "Company", + "reqd": 1, + "default": frappe.defaults.get_default("company") + }, + { + "fieldname":"from_date", + "label": __("From Date"), + "fieldtype": "Date", + "width": "80", + "reqd": 1, + "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1), + }, + { + "fieldname":"to_date", + "label": __("To Date"), + "fieldtype": "Date", + "width": "80", + "reqd": 1, + "default": frappe.datetime.get_today() + }, + { + "fieldname": "sales_order", + "label": __("Sales Order"), + "fieldtype": "MultiSelectList", + "width": "80", + "options": "Sales Order", + "get_data": function(txt) { + return frappe.db.get_link_options("Sales Order", txt); + }, + "get_query": () =>{ + return { + filters: { "docstatus": 1 } + } + } + }, + { + "fieldname": "status", + "label": __("Status"), + "fieldtype": "MultiSelectList", + "width": "80", + get_data: function(txt) { + let status = ["To Bill", "To Deliver", "To Deliver and Bill", "Completed"] + let options = [] + for (let option of status){ + options.push({ + "value": option, + "description": "" + }) + } + return options + } + }, + { + "fieldname": "group_by_so", + "label": __("Group by Sales Order"), + "fieldtype": "Check", + "default": 0 + } + ], + + "formatter": function (value, row, column, data, default_formatter) { + value = default_formatter(value, row, column, data); + let format_fields = ["delivered_qty", "billed_amount"]; + + if (in_list(format_fields, column.fieldname) && data && data[column.fieldname] > 0) { + value = "" + value + ""; + } + + if (column.fieldname == "delay" && data && data[column.fieldname] > 0) { + value = "" + value + ""; + } + return value; + } +}; diff --git a/erpnext/selling/report/sales_order_analysis/sales_order_analysis.json b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.json new file mode 100644 index 00000000000..c0b1d9aa8c2 --- /dev/null +++ b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.json @@ -0,0 +1,36 @@ +{ + "add_total_row": 1, + "creation": "2020-05-29 14:54:53.591445", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "modified": "2020-05-29 14:54:53.591445", + "modified_by": "Administrator", + "module": "Selling", + "name": "Sales Order Analysis", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Sales Order", + "report_name": "Sales Order Analysis", + "report_type": "Script Report", + "roles": [ + { + "role": "Sales User" + }, + { + "role": "Sales Manager" + }, + { + "role": "Maintenance User" + }, + { + "role": "Accounts User" + }, + { + "role": "Stock User" + } + ] +} \ No newline at end of file diff --git a/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py new file mode 100644 index 00000000000..7e8e6e9e8bd --- /dev/null +++ b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py @@ -0,0 +1,279 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +import copy +from frappe import _ +from frappe.utils import flt, date_diff, getdate + +def execute(filters=None): + if not filters: + return [], [], None, [] + + validate_filters(filters) + + columns = get_columns(filters) + conditions = get_conditions(filters) + data = get_data(conditions, filters) + + if not data: + return [], [], None, [] + + data, chart_data = prepare_data(data, filters) + + return columns, data, None, chart_data + +def validate_filters(filters): + from_date, to_date = filters.get("from_date"), filters.get("to_date") + + if not from_date and to_date: + frappe.throw(_("From and To Dates are required.")) + elif date_diff(to_date, from_date) < 0: + frappe.throw(_("To Date cannot be before From Date.")) + +def get_conditions(filters): + conditions = "" + if filters.get("from_date") and filters.get("to_date"): + conditions += " and so.transaction_date between %(from_date)s and %(to_date)s" + + if filters.get("company"): + conditions += " and so.company = %(company)s" + + if filters.get("sales_order"): + conditions += " and so.name in %(sales_order)s" + + if filters.get("status"): + conditions += " and so.status in %(status)s" + + return conditions + +def get_data(conditions, filters): + data = frappe.db.sql(""" + SELECT + so.transaction_date as date, + soi.delivery_date as delivery_date, + so.name as sales_order, + so.status, so.customer, soi.item_code, + DATEDIFF(CURDATE(), soi.delivery_date) as delay_days, + IF(so.status in ('Completed','To Bill'), 0, (SELECT delay_days)) as delay, + soi.qty, soi.delivered_qty, + (soi.qty - soi.delivered_qty) AS pending_qty, + IFNULL(sii.qty, 0) as billed_qty, + soi.base_amount as amount, + (soi.delivered_qty * soi.base_rate) as delivered_qty_amount, + (soi.billed_amt * IFNULL(so.conversion_rate, 1)) as billed_amount, + (soi.base_amount - (soi.billed_amt * IFNULL(so.conversion_rate, 1))) as pending_amount, + soi.warehouse as warehouse, + so.company, soi.name + FROM + `tabSales Order` so, + `tabSales Order Item` soi + LEFT JOIN `tabSales Invoice Item` sii + ON sii.so_detail = soi.name + WHERE + soi.parent = so.name + and so.status not in ('Stopped', 'Closed', 'On Hold') + and so.docstatus = 1 + {conditions} + GROUP BY soi.name + ORDER BY so.transaction_date ASC + """.format(conditions=conditions), filters, as_dict=1) + + return data + +def prepare_data(data, filters): + completed, pending = 0, 0 + + if filters.get("group_by_so"): + sales_order_map = {} + + for row in data: + # sum data for chart + completed += row["billed_amount"] + pending += row["pending_amount"] + + # prepare data for report view + row["qty_to_bill"] = flt(row["qty"]) - flt(row["billed_qty"]) + + row["delay"] = 0 if row["delay"] < 0 else row["delay"] + if filters.get("group_by_so"): + so_name = row["sales_order"] + + if not so_name in sales_order_map: + # create an entry + row_copy = copy.deepcopy(row) + sales_order_map[so_name] = row_copy + else: + # update existing entry + so_row = sales_order_map[so_name] + so_row["required_date"] = max(getdate(so_row["delivery_date"]), getdate(row["delivery_date"])) + so_row["delay"] = min(so_row["delay"], row["delay"]) + + # sum numeric columns + fields = ["qty", "delivered_qty", "pending_qty", "billed_qty", "qty_to_bill", "amount", + "delivered_qty_amount", "billed_amount", "pending_amount"] + for field in fields: + so_row[field] = flt(row[field]) + flt(so_row[field]) + + chart_data = prepare_chart_data(pending, completed) + + if filters.get("group_by_so"): + data = [] + for so in sales_order_map: + data.append(sales_order_map[so]) + return data, chart_data + + return data, chart_data + +def prepare_chart_data(pending, completed): + labels = ["Amount to Bill", "Billed Amount"] + + return { + "data" : { + "labels": labels, + "datasets": [ + {"values": [pending, completed]} + ] + }, + "type": 'donut', + "height": 300 + } + +def get_columns(filters): + columns = [ + { + "label":_("Date"), + "fieldname": "date", + "fieldtype": "Date", + "width": 90 + }, + { + "label": _("Sales Order"), + "fieldname": "sales_order", + "fieldtype": "Link", + "options": "Sales Order", + "width": 160 + }, + { + "label":_("Status"), + "fieldname": "status", + "fieldtype": "Data", + "width": 130 + }, + { + "label": _("Customer"), + "fieldname": "customer", + "fieldtype": "Link", + "options": "Customer", + "width": 130 + }] + + if not filters.get("group_by_so"): + columns.append({ + "label":_("Item Code"), + "fieldname": "item_code", + "fieldtype": "Link", + "options": "Item", + "width": 100 + }) + + columns.extend([ + { + "label": _("Qty"), + "fieldname": "qty", + "fieldtype": "Float", + "width": 120, + "convertible": "qty" + }, + { + "label": _("Delivered Qty"), + "fieldname": "delivered_qty", + "fieldtype": "Float", + "width": 120, + "convertible": "qty" + }, + { + "label": _("Qty to Deliver"), + "fieldname": "pending_qty", + "fieldtype": "Float", + "width": 120, + "convertible": "qty" + }, + { + "label": _("Billed Qty"), + "fieldname": "billed_qty", + "fieldtype": "Float", + "width": 80, + "convertible": "qty" + }, + { + "label": _("Qty to Bill"), + "fieldname": "qty_to_bill", + "fieldtype": "Float", + "width": 80, + "convertible": "qty" + }, + { + "label": _("Amount"), + "fieldname": "amount", + "fieldtype": "Currency", + "width": 110, + "options": "Company:company:default_currency", + "convertible": "rate" + }, + { + "label": _("Billed Amount"), + "fieldname": "billed_amount", + "fieldtype": "Currency", + "width": 110, + "options": "Company:company:default_currency", + "convertible": "rate" + }, + { + "label": _("Pending Amount"), + "fieldname": "pending_amount", + "fieldtype": "Currency", + "width": 130, + "options": "Company:company:default_currency", + "convertible": "rate" + }, + { + "label": _("Amount Delivered"), + "fieldname": "delivered_qty_amount", + "fieldtype": "Currency", + "width": 100, + "options": "Company:company:default_currency", + "convertible": "rate" + }, + { + "label":_("Delivery Date"), + "fieldname": "delivery_date", + "fieldtype": "Date", + "width": 120 + }, + { + "label": _("Delay (in Days)"), + "fieldname": "delay", + "fieldtype": "Data", + "width": 100 + } + ]) + if not filters.get("group_by_so"): + columns.append({ + "label": _("Warehouse"), + "fieldname": "warehouse", + "fieldtype": "Link", + "options": "Warehouse", + "width": 100 + }) + columns.append({ + "label": _("Company"), + "fieldname": "company", + "fieldtype": "Link", + "options": "Company", + "width": 100 + }) + + + return columns \ No newline at end of file diff --git a/erpnext/selling/report/sales_order_trends/sales_order_trends.py b/erpnext/selling/report/sales_order_trends/sales_order_trends.py index c0a0f085d99..de7d3f2f778 100644 --- a/erpnext/selling/report/sales_order_trends/sales_order_trends.py +++ b/erpnext/selling/report/sales_order_trends/sales_order_trends.py @@ -3,6 +3,7 @@ from __future__ import unicode_literals import frappe +from frappe import _ from erpnext.controllers.trends import get_columns,get_data def execute(filters=None): @@ -10,4 +11,48 @@ def execute(filters=None): data = [] conditions = get_columns(filters, "Sales Order") data = get_data(filters, conditions) - return conditions["columns"], data + chart_data = get_chart_data(data, conditions, filters) + + return conditions["columns"], data, None, chart_data + +def get_chart_data(data, conditions, filters): + if not (data and conditions): + return [] + + datapoints = [] + + start = 2 if filters.get("based_on") in ["Item", "Customer"] else 1 + if filters.get("group_by"): + start += 1 + + # fetch only periodic columns as labels + columns = conditions.get("columns")[start:-2][1::2] + labels = [column.split(':')[0] for column in columns] + datapoints = [0] * len(labels) + + for row in data: + # If group by filter, don't add first row of group (it's already summed) + if not row[start-1]: + continue + # Remove None values and compute only periodic data + row = [x if x else 0 for x in row[start:-2]] + row = row[1::2] + + for i in range(len(row)): + datapoints[i] += row[i] + + return { + "data" : { + "labels" : labels, + "datasets" : [ + { + "name" : _("{0}").format(filters.get("period")) + _(" Sales Value"), + "values" : datapoints + } + ] + }, + "type" : "line", + "lineOptions": { + "regionFill": 1 + } + } diff --git a/erpnext/stock/desk_page/stock/stock.json b/erpnext/stock/desk_page/stock/stock.json index 9404292c04f..1bf81f7f0e8 100644 --- a/erpnext/stock/desk_page/stock/stock.json +++ b/erpnext/stock/desk_page/stock/stock.json @@ -33,7 +33,7 @@ { "hidden": 0, "label": "Key Reports", - "links": "[\n {\n \"dependencies\": [\n \"Item Price\"\n ],\n \"doctype\": \"Item Price\",\n \"is_query_report\": false,\n \"label\": \"Item-wise Price List Rate\",\n \"name\": \"Item-wise Price List Rate\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Stock Entry\"\n ],\n \"doctype\": \"Stock Entry\",\n \"is_query_report\": true,\n \"label\": \"Stock Analytics\",\n \"name\": \"Stock Analytics\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Delivery Note\"\n ],\n \"doctype\": \"Delivery Note\",\n \"is_query_report\": true,\n \"label\": \"Delivery Note Trends\",\n \"name\": \"Delivery Note Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Receipt\"\n ],\n \"doctype\": \"Purchase Receipt\",\n \"is_query_report\": true,\n \"label\": \"Purchase Receipt Trends\",\n \"name\": \"Purchase Receipt Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Delivery Note\"\n ],\n \"doctype\": \"Delivery Note\",\n \"is_query_report\": true,\n \"label\": \"Ordered Items To Be Delivered\",\n \"name\": \"Ordered Items To Be Delivered\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Order\"\n ],\n \"doctype\": \"Purchase Order\",\n \"is_query_report\": true,\n \"label\": \"Purchase Order Analysis\",\n \"name\": \"Purchase Order Analysis\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Bin\"\n ],\n \"doctype\": \"Bin\",\n \"is_query_report\": true,\n \"label\": \"Item Shortage Report\",\n \"name\": \"Item Shortage Report\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Batch\"\n ],\n \"doctype\": \"Batch\",\n \"is_query_report\": true,\n \"label\": \"Batch-Wise Balance History\",\n \"name\": \"Batch-Wise Balance History\",\n \"type\": \"report\"\n }\n]" + "links": "[\n {\n \"dependencies\": [\n \"Item Price\"\n ],\n \"doctype\": \"Item Price\",\n \"is_query_report\": false,\n \"label\": \"Item-wise Price List Rate\",\n \"name\": \"Item-wise Price List Rate\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Stock Entry\"\n ],\n \"doctype\": \"Stock Entry\",\n \"is_query_report\": true,\n \"label\": \"Stock Analytics\",\n \"name\": \"Stock Analytics\",\n \"onboard\": 1,\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Delivery Note\"\n ],\n \"doctype\": \"Delivery Note\",\n \"is_query_report\": true,\n \"label\": \"Delivery Note Trends\",\n \"name\": \"Delivery Note Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Receipt\"\n ],\n \"doctype\": \"Purchase Receipt\",\n \"is_query_report\": true,\n \"label\": \"Purchase Receipt Trends\",\n \"name\": \"Purchase Receipt Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Sales Order Analysis\",\n \"name\": \"Sales Order Analysis\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Purchase Order\"\n ],\n \"doctype\": \"Purchase Order\",\n \"is_query_report\": true,\n \"label\": \"Purchase Order Analysis\",\n \"name\": \"Purchase Order Analysis\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Bin\"\n ],\n \"doctype\": \"Bin\",\n \"is_query_report\": true,\n \"label\": \"Item Shortage Report\",\n \"name\": \"Item Shortage Report\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Batch\"\n ],\n \"doctype\": \"Batch\",\n \"is_query_report\": true,\n \"label\": \"Batch-Wise Balance History\",\n \"name\": \"Batch-Wise Balance History\",\n \"type\": \"report\"\n }\n]" }, { "hidden": 0, @@ -58,7 +58,7 @@ "idx": 0, "is_standard": 1, "label": "Stock", - "modified": "2020-05-27 20:38:25.255323", + "modified": "2020-05-30 17:32:11.062681", "modified_by": "Administrator", "module": "Stock", "name": "Stock", diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.js b/erpnext/stock/doctype/stock_settings/stock_settings.js index 877d0c3bbf4..d5049ac6ed0 100644 --- a/erpnext/stock/doctype/stock_settings/stock_settings.js +++ b/erpnext/stock/doctype/stock_settings/stock_settings.js @@ -20,7 +20,7 @@ frappe.tour['Stock Settings'] = [ { fieldname: "item_naming_by", title: __("Item Naming By"), - description: __("By default, the Item Name is set as per the Item Code entered. If you want Items to be named by a set Naming Series choose the 'Naming Series' option.") + description: __("By default, the Item Name is set as per the Item Code entered. If you want Items to be named by a") + "Naming Series" + __(" choose the 'Naming Series' option."), }, { fieldname: "default_warehouse", diff --git a/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py b/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py index 5a931e7efac..446d3049b71 100644 --- a/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py +++ b/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py @@ -26,9 +26,10 @@ def get_chart_data(data, filters): # consider only consolidated row data = [row for row in data if row[0]] + data = sorted(data, key = lambda i: i[-1],reverse=True) + if len(data) > 10: # get top 10 if data too long - data = sorted(data, key = lambda i: i[-1],reverse=True) data = data[:10] for row in data: diff --git a/erpnext/stock/report/ordered_items_to_be_delivered/__init__.py b/erpnext/stock/report/ordered_items_to_be_delivered/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/erpnext/stock/report/ordered_items_to_be_delivered/ordered_items_to_be_delivered.json b/erpnext/stock/report/ordered_items_to_be_delivered/ordered_items_to_be_delivered.json deleted file mode 100644 index aa5fd0f165a..00000000000 --- a/erpnext/stock/report/ordered_items_to_be_delivered/ordered_items_to_be_delivered.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "add_total_row": 1, - "creation": "2018-01-09 18:38:23.540100", - "disable_prepared_report": 0, - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 0, - "is_standard": "Yes", - "modified": "2019-04-01 22:10:09.829361", - "modified_by": "Administrator", - "module": "Stock", - "name": "Ordered Items To Be Delivered", - "owner": "Administrator", - "prepared_report": 0, - "query": "select \n `tabSales Order`.`name` as \"Sales Order:Link/Sales Order:120\",\n `tabSales Order`.`status` as \"Status:Data:120\",\n `tabSales Order`.`customer` as \"Customer:Link/Customer:120\",\n `tabSales Order`.`customer_name` as \"Customer Name::150\",\n `tabSales Order`.`transaction_date` as \"Date:Date\",\n `tabSales Order`.`project` as \"Project:Link/Project:120\",\n `tabSales Order Item`.item_code as \"Item:Link/Item:120\",\n `tabSales Order Item`.qty as \"Qty:Float:140\",\n `tabSales Order Item`.delivered_qty as \"Delivered Qty:Float:140\",\n (`tabSales Order Item`.qty - ifnull(`tabSales Order Item`.delivered_qty, 0)) as \"Qty to Deliver:Float:140\",\n `tabSales Order Item`.base_rate as \"Rate:Float:140\",\n `tabSales Order Item`.base_amount as \"Amount:Float:140\",\n ((`tabSales Order Item`.qty - ifnull(`tabSales Order Item`.delivered_qty, 0))*`tabSales Order Item`.base_rate) as \"Amount to Deliver:Float:140\",\n `tabBin`.actual_qty as \"Available Qty:Float:120\",\n `tabBin`.projected_qty as \"Projected Qty:Float:120\",\n `tabSales Order Item`.`delivery_date` as \"Item Delivery Date:Date:120\",\n DATEDIFF(CURDATE(),`tabSales Order Item`.`delivery_date`) as \"Delay Days:Int:120\",\n `tabSales Order Item`.item_name as \"Item Name::150\",\n `tabSales Order Item`.description as \"Description::200\",\n `tabSales Order Item`.item_group as \"Item Group:Link/Item Group:120\",\n `tabSales Order Item`.warehouse as \"Warehouse:Link/Warehouse:200\"\nfrom\n `tabSales Order` JOIN `tabSales Order Item` \n LEFT JOIN `tabBin` ON (`tabBin`.item_code = `tabSales Order Item`.item_code\n and `tabBin`.warehouse = `tabSales Order Item`.warehouse)\nwhere\n `tabSales Order Item`.`parent` = `tabSales Order`.`name`\n and `tabSales Order`.docstatus = 1\n and `tabSales Order`.status not in (\"Stopped\", \"Closed\")\n and ifnull(`tabSales Order Item`.delivered_qty,0) < ifnull(`tabSales Order Item`.qty,0)\norder by `tabSales Order`.transaction_date asc", - "ref_doctype": "Delivery Note", - "report_name": "Ordered Items To Be Delivered", - "report_type": "Query Report", - "roles": [ - { - "role": "Stock User" - }, - { - "role": "Stock Manager" - }, - { - "role": "Sales User" - }, - { - "role": "Accounts User" - } - ] -} \ No newline at end of file From aca2ca59828932a33ee1e4f029b28564f44cf648 Mon Sep 17 00:00:00 2001 From: Afshan Date: Fri, 19 Jun 2020 16:22:41 +0530 Subject: [PATCH 418/608] style: formate according to Codacy/PR Quality Review --- erpnext/public/js/controllers/taxes_and_totals.js | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index a449bdcca67..b72ceb21139 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -345,7 +345,6 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ tax.row_id = tax.idx - 1; } } - if(tax.charge_type == "Actual") { // distribute the tax amount proportionally to each item row var actual = flt(tax.tax_amount, precision("tax_amount", tax)); From aea450c36af7aa4b103e55f90e9cbe202d2ddede Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Fri, 19 Jun 2020 17:29:49 +0530 Subject: [PATCH 419/608] Revert "fix: update remark on submitting payment entry" --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 15e51bbd995..59611bc74c2 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -453,6 +453,8 @@ class PaymentEntry(AccountsController): frappe.throw(_("Reference No and Reference Date is mandatory for Bank transaction")) def set_remarks(self): + if self.remarks: return + if self.payment_type=="Internal Transfer": remarks = [_("Amount {0} {1} transferred from {2} to {3}") .format(self.paid_from_account_currency, self.paid_amount, self.paid_from, self.paid_to)] From fbd66574ad267370f0955d9d2a59beb447fb59a6 Mon Sep 17 00:00:00 2001 From: Afshan Date: Fri, 19 Jun 2020 18:17:01 +0530 Subject: [PATCH 420/608] Skiping total row for tree-view reports --- .../report/sales_analytics/sales_analytics.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.py b/erpnext/selling/report/sales_analytics/sales_analytics.py index 97d9322918d..cb5d3eccfdc 100644 --- a/erpnext/selling/report/sales_analytics/sales_analytics.py +++ b/erpnext/selling/report/sales_analytics/sales_analytics.py @@ -23,7 +23,14 @@ class Analytics(object): self.get_columns() self.get_data() self.get_chart_data() - return self.columns, self.data, None, self.chart + + # Skiping total row for tree-view reports + skip_total_row = 0 + + if self.filters.tree_type in ["Supplier Group", "Item Group", "Customer Group", "Territory"]: + skip_total_row = 1 + + return self.columns, self.data, None, self.chart, None, skip_total_row def get_columns(self): self.columns = [{ @@ -194,9 +201,6 @@ class Analytics(object): def get_rows(self): self.data = [] self.get_periodic_data() - total_row = { - "entity": "Total", - } for entity, period_data in iteritems(self.entity_periodic_data): row = { @@ -210,9 +214,6 @@ class Analytics(object): row[scrub(period)] = amount total += amount - if not total_row.get(scrub(period)): total_row[scrub(period)] = 0 - total_row[scrub(period)] += amount - row["total"] = total if self.filters.tree_type == "Item": @@ -220,8 +221,6 @@ class Analytics(object): self.data.append(row) - self.data.append(total_row) - def get_rows_by_group(self): self.get_periodic_data() out = [] From 65f00cea15d6d9e89a9123813c0f7fe72ee150c0 Mon Sep 17 00:00:00 2001 From: Afshan Date: Fri, 19 Jun 2020 18:19:06 +0530 Subject: [PATCH 421/608] fix: Skipping* --- erpnext/selling/report/sales_analytics/sales_analytics.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.py b/erpnext/selling/report/sales_analytics/sales_analytics.py index cb5d3eccfdc..4d113c8e9e9 100644 --- a/erpnext/selling/report/sales_analytics/sales_analytics.py +++ b/erpnext/selling/report/sales_analytics/sales_analytics.py @@ -24,7 +24,7 @@ class Analytics(object): self.get_data() self.get_chart_data() - # Skiping total row for tree-view reports + # Skipping total row for tree-view reports skip_total_row = 0 if self.filters.tree_type in ["Supplier Group", "Item Group", "Customer Group", "Territory"]: From 02f3e045ab9e0ff247ad84caff19d3ace528ece6 Mon Sep 17 00:00:00 2001 From: Kenneth Sequeira Date: Fri, 19 Jun 2020 18:22:04 +0530 Subject: [PATCH 422/608] fix: due date filter in purchase invoice --- .../accounts/doctype/purchase_invoice/purchase_invoice_list.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js index 800ed921bdf..e9849c20502 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js @@ -16,7 +16,7 @@ frappe.listview_settings['Purchase Invoice'] = { } else if(frappe.datetime.get_diff(doc.due_date) < 0) { return [__("Overdue"), "red", "outstanding_amount,>,0|due_date,<,Today"]; } else { - return [__("Unpaid"), "orange", "outstanding_amount,>,0|due,>=,Today"]; + return [__("Unpaid"), "orange", "outstanding_amount,>,0|due_date,>=,Today"]; } } else if(cint(doc.is_return)) { return [__("Return"), "darkgrey", "is_return,=,Yes"]; @@ -25,3 +25,4 @@ frappe.listview_settings['Purchase Invoice'] = { } } }; +s \ No newline at end of file From 289c82243f32035043e804786c59594be32b2d10 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Fri, 19 Jun 2020 19:17:57 +0530 Subject: [PATCH 423/608] feat: New Payroll module (#21990) * feat: Moved Document to Payroll Module * feat: Moved Reports to Payroll Module * feat: Moved Print fromat With Patch * feat: Moved Notifiction to Payroll Module and patches * feat: added dashboard and desk page to Payroll * feat: Payroll Dashboard * feat: Module onboarding * feat: Income tax Deductions Report * feat: Ecs Checklist Report * feat: Provident Fund Report * feat: Professional Fund report and commonified Code * feat: Total Payments Based On Payment Mode Report * fix: refactor and added chart Total Payments Based On Payment Mode * feat: Payroll Settings * fix: Bank remittance Report * feat(Payroll based on): Considered unmarked days * feat: Added Help for condition an formula in Salary structure * fix: requested changes * fix: rename report Ecs checklist to salary_payments_via_ecs * fix: renamed report report/total_payments_based_on_payment_mode * fix: added role via setup.py for regional report * feat: added All reports to desk page * fix: frappe.reload doc in all patches * fix: codacy * fix: frappe.reload_doctype for patches * patch: is_income_tax_component and component_type for salary component * fix: uncommented code * test: fixture * fix: test * test: test_payment_days_based_on_attendance --- .../doctype/journal_entry/journal_entry.js | 2 +- .../doctype/journal_entry/journal_entry.py | 2 +- erpnext/hr/dashboard_fixtures.py | 42 +- erpnext/hr/desk_page/hr/hr.json | 40 +- .../employee_benefit_application.json | 576 ----------------- .../employee_benefit_application_detail.json | 177 ------ .../employee_benefit_claim.json | 580 ------------------ .../test_employee_promotion.py | 2 +- .../employee_tax_exemption_category.json | 169 ----- ...ee_tax_exemption_declaration_category.json | 179 ------ ...employee_tax_exemption_proof_submission.py | 54 -- ...tax_exemption_proof_submission_detail.json | 213 ------- .../employee_tax_exemption_sub_category.json | 204 ------ erpnext/hr/doctype/hr_settings/hr_settings.js | 15 - .../hr/doctype/hr_settings/hr_settings.json | 145 ++--- erpnext/hr/doctype/hr_settings/hr_settings.py | 20 - .../leave_encashment/leave_encashment.py | 2 +- .../leave_encashment/test_leave_encashment.py | 2 +- .../payroll_employee_detail.json | 209 ------- .../payroll_period_date.json | 103 ---- .../salary_slip_timesheet.json | 107 ---- .../taxable_salary_slab.json | 232 ------- .../training_event/test_training_event.py | 2 +- .../salary_register/salary_register.json | 26 - .../loan_management/doctype/loan/test_loan.py | 2 +- .../loan_application/test_loan_application.py | 2 +- erpnext/modules.txt | 3 +- erpnext/patches.txt | 7 +- ...ate_department_records_for_each_company.py | 3 +- .../create_salary_structure_assignments.py | 6 +- .../v11_0/inter_state_field_for_gst.py | 4 +- .../v11_0/set_salary_component_properties.py | 4 +- .../patches/v11_1/rename_depends_on_lwp.py | 2 +- .../v13_0/check_is_income_tax_component.py | 24 + ...rts_and_notification_from_hr_to_payroll.py | 52 ++ ...oll_setting_separately_from_hr_settings.py | 27 + ..._from_payroll_period_to_income_tax_slab.py | 4 +- ...itional_salary_encashment_and_incentive.py | 6 +- ...custom_fields_for_india_specific_fields.py | 2 +- .../patches/v5_0/rename_table_fieldnames.py | 2 +- .../patches/v7_0/rename_salary_components.py | 4 +- erpnext/patches/v7_1/update_component_type.py | 2 +- .../update_missing_salary_component_type.py | 30 +- ...ar_leave_encashment_as_salary_component.py | 4 +- .../v7_2/update_abbr_in_salary_slips.py | 2 +- erpnext/patches/v7_2/update_salary_slips.py | 4 +- .../patches/v8_7/sync_india_custom_fields.py | 10 +- .../v9_0/update_employee_loan_details.py | 4 +- .../additional_salary => payroll}/__init__.py | 0 erpnext/payroll/dashboard_fixtures.py | 100 +++ .../payroll/desk_page/payroll/payroll.json | 84 +++ .../doctype}/__init__.py | 0 .../doctype/additional_salary}/__init__.py | 0 .../additional_salary/additional_salary.js | 0 .../additional_salary/additional_salary.json | 6 +- .../additional_salary/additional_salary.py | 0 .../test_additional_salary.js | 0 .../test_additional_salary.py | 4 +- .../employee_benefit_application}/__init__.py | 0 .../employee_benefit_application.js | 8 +- .../employee_benefit_application.json | 191 ++++++ .../employee_benefit_application.py | 4 +- .../test_employee_benefit_application.js | 0 .../test_employee_benefit_application.py | 0 .../__init__.py | 0 .../employee_benefit_application_detail.json | 58 ++ .../employee_benefit_application_detail.py | 4 +- .../employee_benefit_claim}/__init__.py | 0 .../employee_benefit_claim.js | 2 +- .../employee_benefit_claim.json | 194 ++++++ .../employee_benefit_claim.py | 6 +- .../test_employee_benefit_claim.js | 0 .../test_employee_benefit_claim.py | 0 .../doctype/employee_incentive}/__init__.py | 0 .../employee_incentive/employee_incentive.js | 0 .../employee_incentive.json | 4 +- .../employee_incentive/employee_incentive.py | 0 .../test_employee_incentive.js | 0 .../test_employee_incentive.py | 0 .../employee_other_income}/__init__.py | 0 .../employee_other_income.js | 0 .../employee_other_income.json | 4 +- .../employee_other_income.py | 0 .../test_employee_other_income.py | 0 .../__init__.py | 0 .../employee_tax_exemption_category.js | 0 .../employee_tax_exemption_category.json | 74 +++ .../employee_tax_exemption_category.py | 0 .../test_employee_tax_exemption_category.js | 0 .../test_employee_tax_exemption_category.py | 0 .../__init__.py | 0 .../employee_tax_exemption_declaration.js | 2 +- .../employee_tax_exemption_declaration.json | 4 +- .../employee_tax_exemption_declaration.py | 0 ...test_employee_tax_exemption_declaration.js | 0 ...test_employee_tax_exemption_declaration.py | 0 .../__init__.py | 0 ...ee_tax_exemption_declaration_category.json | 61 ++ ...oyee_tax_exemption_declaration_category.py | 4 +- .../__init__.py | 0 ...employee_tax_exemption_proof_submission.js | 2 +- ...ployee_tax_exemption_proof_submission.json | 4 +- ...employee_tax_exemption_proof_submission.py | 0 ...employee_tax_exemption_proof_submission.js | 0 ...employee_tax_exemption_proof_submission.py | 54 ++ .../__init__.py | 0 ...tax_exemption_proof_submission_detail.json | 66 ++ ...e_tax_exemption_proof_submission_detail.py | 4 +- .../__init__.py | 0 .../employee_tax_exemption_sub_category.js | 0 .../employee_tax_exemption_sub_category.json | 86 +++ .../employee_tax_exemption_sub_category.py | 0 ...est_employee_tax_exemption_sub_category.js | 0 ...est_employee_tax_exemption_sub_category.py | 0 .../doctype/income_tax_slab}/__init__.py | 0 .../income_tax_slab/income_tax_slab.js | 0 .../income_tax_slab/income_tax_slab.json | 4 +- .../income_tax_slab/income_tax_slab.py | 0 .../income_tax_slab/test_income_tax_slab.py | 0 .../__init__.py | 0 .../income_tax_slab_other_charges.json | 28 +- .../income_tax_slab_other_charges.py | 0 .../payroll_employee_detail}/__init__.py | 0 .../payroll_employee_detail.json | 66 ++ .../payroll_employee_detail.py | 0 .../doctype/payroll_entry}/__init__.py | 0 .../doctype/payroll_entry/payroll_entry.js | 6 +- .../doctype/payroll_entry/payroll_entry.json | 6 +- .../doctype/payroll_entry/payroll_entry.py | 8 +- .../payroll_entry/payroll_entry_dashboard.py | 0 .../payroll_entry/test_payroll_entry.js | 0 .../payroll_entry/test_payroll_entry.py | 8 +- .../test_set_salary_components.js | 0 .../doctype/payroll_period}/__init__.py | 0 .../doctype/payroll_period/payroll_period.js | 0 .../payroll_period/payroll_period.json | 4 +- .../doctype/payroll_period/payroll_period.py | 2 +- .../payroll_period_dashboard.py | 0 .../payroll_period/test_payroll_period.js | 0 .../payroll_period/test_payroll_period.py | 0 .../doctype/payroll_period_date}/__init__.py | 0 .../payroll_period_date.json | 39 ++ .../payroll_period_date.py | 4 +- .../doctype/payroll_settings}/__init__.py | 0 .../payroll_settings/payroll_settings.js | 19 + .../payroll_settings/payroll_settings.json | 130 ++++ .../payroll_settings/payroll_settings.py | 32 + .../payroll_settings/test_payroll_settings.py | 10 + .../doctype/retention_bonus}/__init__.py | 0 .../retention_bonus/retention_bonus.js | 0 .../retention_bonus/retention_bonus.json | 17 +- .../retention_bonus/retention_bonus.py | 29 +- .../retention_bonus/test_retention_bonus.js | 0 .../retention_bonus/test_retention_bonus.py | 0 .../doctype/salary_component/README.md | 0 .../doctype/salary_component}/__init__.py | 0 .../salary_component/salary_component.js | 0 .../salary_component/salary_component.json | 14 +- .../salary_component/salary_component.py | 0 .../salary_component/test_records.json | 0 .../salary_component/test_salary_component.js | 0 .../salary_component/test_salary_component.py | 0 .../doctype/salary_detail}/__init__.py | 0 .../doctype/salary_detail/salary_detail.json | 6 +- .../doctype/salary_detail/salary_detail.py | 0 .../doctype/salary_slip/README.md | 0 .../doctype/salary_slip/__init__.py | 0 .../doctype/salary_slip/salary_slip.js | 5 +- .../doctype/salary_slip/salary_slip.json | 255 ++++++-- .../doctype/salary_slip/salary_slip.py | 100 ++- .../doctype/salary_slip/salary_slip_list.js | 0 .../doctype/salary_slip/test_salary_slip.js | 0 .../doctype/salary_slip/test_salary_slip.py | 41 +- .../salary_slip_timesheet}/__init__.py | 0 .../salary_slip_timesheet.json | 40 ++ .../salary_slip_timesheet.py | 4 +- .../doctype/salary_structure/README.md | 0 .../doctype/salary_structure/__init__.py | 0 .../condition_and_formula_help.html | 47 ++ .../salary_structure/salary_structure.js | 29 +- .../salary_structure/salary_structure.json | 130 ++-- .../salary_structure/salary_structure.py | 0 .../salary_structure_dashboard.py | 0 .../salary_structure/test_salary_structure.js | 0 .../salary_structure/test_salary_structure.py | 10 +- .../salary_structure_assignment}/__init__.py | 0 .../salary_structure_assignment.js | 0 .../salary_structure_assignment.json | 4 +- .../salary_structure_assignment.py | 0 .../test_salary_structure_assignment.js | 0 .../test_salary_structure_assignment.py | 0 .../doctype/taxable_salary_slab}/__init__.py | 0 .../taxable_salary_slab.json | 64 ++ .../taxable_salary_slab.py | 4 +- .../module_onboarding/payroll/payroll.json | 51 ++ erpnext/payroll/notification/as | 1 + .../notification/retention_bonus}/__init__.py | 0 .../retention_bonus/retention_bonus.json | 6 +- .../retention_bonus/retention_bonus.md | 0 .../retention_bonus/retention_bonus.py | 0 .../assign_salary_structure.json | 19 + .../create_employee/create_employee.json | 19 + .../create_income_tax_slab.json | 19 + .../create_payroll_period.json | 19 + .../create_salary_component.json | 19 + .../create_salary_slip.json | 19 + .../create_salary_structure.json | 19 + .../payroll_settings/payroll_settings.json | 19 + .../__init__.py | 0 .../salary_slip_based_on_timesheet.json | 0 .../salary_slip_standard/__init__.py | 0 .../salary_slip_standard.json | 0 erpnext/payroll/report/__init__.py | 0 .../report/bank_remittance/__init__.py | 0 .../report/bank_remittance/bank_remittance.js | 12 +- .../bank_remittance/bank_remittance.json | 4 +- .../report/bank_remittance/bank_remittance.py | 5 +- .../report/income_tax_deductions/__init__.py | 0 .../income_tax_deductions.js | 7 + .../income_tax_deductions.json | 30 + .../income_tax_deductions.py | 127 ++++ .../__init__.py | 0 .../salary_payments_based_on_payment_mode.js | 7 + ...salary_payments_based_on_payment_mode.json | 30 + .../salary_payments_based_on_payment_mode.py | 177 ++++++ .../salary_payments_via_ecs/__init__.py | 0 .../salary_payments_via_ecs.js | 16 + .../salary_payments_via_ecs.json | 27 + .../salary_payments_via_ecs.py | 146 +++++ .../report/salary_register/__init__.py | 0 .../salary_register/salary_register.html | 0 .../report/salary_register/salary_register.js | 0 .../salary_register/salary_register.json | 27 + .../report/salary_register/salary_register.py | 4 +- .../doctype/timesheet/test_timesheet.py | 2 +- .../salary_slip_deductions_report_filters.js | 47 ++ erpnext/regional/india/setup.py | 54 +- erpnext/regional/india/utils.py | 2 +- .../professional_tax_deductions/__init__.py | 0 .../professional_tax_deductions.js | 7 + .../professional_tax_deductions.json | 20 + .../professional_tax_deductions.py | 69 +++ .../provident_fund_deductions/__init__.py | 0 .../provident_fund_deductions.js | 7 + .../provident_fund_deductions.json | 20 + .../provident_fund_deductions.py | 153 +++++ .../operations/install_fixtures.py | 2 +- 247 files changed, 3360 insertions(+), 3360 deletions(-) delete mode 100644 erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.json delete mode 100644 erpnext/hr/doctype/employee_benefit_application_detail/employee_benefit_application_detail.json delete mode 100644 erpnext/hr/doctype/employee_benefit_claim/employee_benefit_claim.json delete mode 100644 erpnext/hr/doctype/employee_tax_exemption_category/employee_tax_exemption_category.json delete mode 100644 erpnext/hr/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.json delete mode 100644 erpnext/hr/doctype/employee_tax_exemption_proof_submission/test_employee_tax_exemption_proof_submission.py delete mode 100644 erpnext/hr/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.json delete mode 100644 erpnext/hr/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.json delete mode 100644 erpnext/hr/doctype/payroll_employee_detail/payroll_employee_detail.json delete mode 100644 erpnext/hr/doctype/payroll_period_date/payroll_period_date.json delete mode 100644 erpnext/hr/doctype/salary_slip_timesheet/salary_slip_timesheet.json delete mode 100644 erpnext/hr/doctype/taxable_salary_slab/taxable_salary_slab.json delete mode 100644 erpnext/hr/report/salary_register/salary_register.json create mode 100644 erpnext/patches/v13_0/check_is_income_tax_component.py create mode 100644 erpnext/patches/v13_0/move_doctype_reports_and_notification_from_hr_to_payroll.py create mode 100644 erpnext/patches/v13_0/move_payroll_setting_separately_from_hr_settings.py rename erpnext/{hr/doctype/additional_salary => payroll}/__init__.py (100%) create mode 100644 erpnext/payroll/dashboard_fixtures.py create mode 100644 erpnext/payroll/desk_page/payroll/payroll.json rename erpnext/{hr/doctype/employee_benefit_application => payroll/doctype}/__init__.py (100%) rename erpnext/{hr/doctype/employee_benefit_application_detail => payroll/doctype/additional_salary}/__init__.py (100%) rename erpnext/{hr => payroll}/doctype/additional_salary/additional_salary.js (100%) rename erpnext/{hr => payroll}/doctype/additional_salary/additional_salary.json (98%) rename erpnext/{hr => payroll}/doctype/additional_salary/additional_salary.py (100%) rename erpnext/{hr => payroll}/doctype/additional_salary/test_additional_salary.js (100%) rename erpnext/{hr => payroll}/doctype/additional_salary/test_additional_salary.py (87%) rename erpnext/{hr/doctype/employee_benefit_claim => payroll/doctype/employee_benefit_application}/__init__.py (100%) rename erpnext/{hr => payroll}/doctype/employee_benefit_application/employee_benefit_application.js (83%) create mode 100644 erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.json rename erpnext/{hr => payroll}/doctype/employee_benefit_application/employee_benefit_application.py (97%) rename erpnext/{hr => payroll}/doctype/employee_benefit_application/test_employee_benefit_application.js (100%) rename erpnext/{hr => payroll}/doctype/employee_benefit_application/test_employee_benefit_application.py (100%) rename erpnext/{hr/doctype/employee_incentive => payroll/doctype/employee_benefit_application_detail}/__init__.py (100%) create mode 100644 erpnext/payroll/doctype/employee_benefit_application_detail/employee_benefit_application_detail.json rename erpnext/{hr => payroll}/doctype/employee_benefit_application_detail/employee_benefit_application_detail.py (73%) rename erpnext/{hr/doctype/employee_other_income => payroll/doctype/employee_benefit_claim}/__init__.py (100%) rename erpnext/{hr => payroll}/doctype/employee_benefit_claim/employee_benefit_claim.js (77%) create mode 100644 erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.json rename erpnext/{hr => payroll}/doctype/employee_benefit_claim/employee_benefit_claim.py (96%) rename erpnext/{hr => payroll}/doctype/employee_benefit_claim/test_employee_benefit_claim.js (100%) rename erpnext/{hr => payroll}/doctype/employee_benefit_claim/test_employee_benefit_claim.py (100%) rename erpnext/{hr/doctype/employee_tax_exemption_category => payroll/doctype/employee_incentive}/__init__.py (100%) rename erpnext/{hr => payroll}/doctype/employee_incentive/employee_incentive.js (100%) rename erpnext/{hr => payroll}/doctype/employee_incentive/employee_incentive.json (97%) rename erpnext/{hr => payroll}/doctype/employee_incentive/employee_incentive.py (100%) rename erpnext/{hr => payroll}/doctype/employee_incentive/test_employee_incentive.js (100%) rename erpnext/{hr => payroll}/doctype/employee_incentive/test_employee_incentive.py (100%) rename erpnext/{hr/doctype/employee_tax_exemption_declaration => payroll/doctype/employee_other_income}/__init__.py (100%) rename erpnext/{hr => payroll}/doctype/employee_other_income/employee_other_income.js (100%) rename erpnext/{hr => payroll}/doctype/employee_other_income/employee_other_income.json (97%) rename erpnext/{hr => payroll}/doctype/employee_other_income/employee_other_income.py (100%) rename erpnext/{hr => payroll}/doctype/employee_other_income/test_employee_other_income.py (100%) rename erpnext/{hr/doctype/employee_tax_exemption_declaration_category => payroll/doctype/employee_tax_exemption_category}/__init__.py (100%) rename erpnext/{hr => payroll}/doctype/employee_tax_exemption_category/employee_tax_exemption_category.js (100%) create mode 100644 erpnext/payroll/doctype/employee_tax_exemption_category/employee_tax_exemption_category.json rename erpnext/{hr => payroll}/doctype/employee_tax_exemption_category/employee_tax_exemption_category.py (100%) rename erpnext/{hr => payroll}/doctype/employee_tax_exemption_category/test_employee_tax_exemption_category.js (100%) rename erpnext/{hr => payroll}/doctype/employee_tax_exemption_category/test_employee_tax_exemption_category.py (100%) rename erpnext/{hr/doctype/employee_tax_exemption_proof_submission => payroll/doctype/employee_tax_exemption_declaration}/__init__.py (100%) rename erpnext/{hr => payroll}/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.js (88%) rename erpnext/{hr => payroll}/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json (98%) rename erpnext/{hr => payroll}/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py (100%) rename erpnext/{hr => payroll}/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.js (100%) rename erpnext/{hr => payroll}/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py (100%) rename erpnext/{hr/doctype/employee_tax_exemption_proof_submission_detail => payroll/doctype/employee_tax_exemption_declaration_category}/__init__.py (100%) create mode 100644 erpnext/payroll/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.json rename erpnext/{hr => payroll}/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.py (73%) rename erpnext/{hr/doctype/employee_tax_exemption_sub_category => payroll/doctype/employee_tax_exemption_proof_submission}/__init__.py (100%) rename erpnext/{hr => payroll}/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.js (90%) rename erpnext/{hr => payroll}/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.json (98%) rename erpnext/{hr => payroll}/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py (100%) rename erpnext/{hr => payroll}/doctype/employee_tax_exemption_proof_submission/test_employee_tax_exemption_proof_submission.js (100%) create mode 100644 erpnext/payroll/doctype/employee_tax_exemption_proof_submission/test_employee_tax_exemption_proof_submission.py rename erpnext/{hr/doctype/income_tax_slab => payroll/doctype/employee_tax_exemption_proof_submission_detail}/__init__.py (100%) create mode 100644 erpnext/payroll/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.json rename erpnext/{hr => payroll}/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.py (74%) rename erpnext/{hr/doctype/income_tax_slab_other_charges => payroll/doctype/employee_tax_exemption_sub_category}/__init__.py (100%) rename erpnext/{hr => payroll}/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.js (100%) create mode 100644 erpnext/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.json rename erpnext/{hr => payroll}/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.py (100%) rename erpnext/{hr => payroll}/doctype/employee_tax_exemption_sub_category/test_employee_tax_exemption_sub_category.js (100%) rename erpnext/{hr => payroll}/doctype/employee_tax_exemption_sub_category/test_employee_tax_exemption_sub_category.py (100%) rename erpnext/{hr/doctype/payroll_employee_detail => payroll/doctype/income_tax_slab}/__init__.py (100%) rename erpnext/{hr => payroll}/doctype/income_tax_slab/income_tax_slab.js (100%) rename erpnext/{hr => payroll}/doctype/income_tax_slab/income_tax_slab.json (97%) rename erpnext/{hr => payroll}/doctype/income_tax_slab/income_tax_slab.py (100%) rename erpnext/{hr => payroll}/doctype/income_tax_slab/test_income_tax_slab.py (100%) rename erpnext/{hr/doctype/payroll_entry => payroll/doctype/income_tax_slab_other_charges}/__init__.py (100%) rename erpnext/{hr => payroll}/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.json (95%) rename erpnext/{hr => payroll}/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.py (100%) rename erpnext/{hr/doctype/payroll_period => payroll/doctype/payroll_employee_detail}/__init__.py (100%) create mode 100644 erpnext/payroll/doctype/payroll_employee_detail/payroll_employee_detail.json rename erpnext/{hr => payroll}/doctype/payroll_employee_detail/payroll_employee_detail.py (100%) rename erpnext/{hr/doctype/payroll_period_date => payroll/doctype/payroll_entry}/__init__.py (100%) rename erpnext/{hr => payroll}/doctype/payroll_entry/payroll_entry.js (95%) rename erpnext/{hr => payroll}/doctype/payroll_entry/payroll_entry.json (98%) rename erpnext/{hr => payroll}/doctype/payroll_entry/payroll_entry.py (99%) rename erpnext/{hr => payroll}/doctype/payroll_entry/payroll_entry_dashboard.py (100%) rename erpnext/{hr => payroll}/doctype/payroll_entry/test_payroll_entry.js (100%) rename erpnext/{hr => payroll}/doctype/payroll_entry/test_payroll_entry.py (96%) rename erpnext/{hr => payroll}/doctype/payroll_entry/test_set_salary_components.js (100%) rename erpnext/{hr/doctype/retention_bonus => payroll/doctype/payroll_period}/__init__.py (100%) rename erpnext/{hr => payroll}/doctype/payroll_period/payroll_period.js (100%) rename erpnext/{hr => payroll}/doctype/payroll_period/payroll_period.json (96%) rename erpnext/{hr => payroll}/doctype/payroll_period/payroll_period.py (97%) rename erpnext/{hr => payroll}/doctype/payroll_period/payroll_period_dashboard.py (100%) rename erpnext/{hr => payroll}/doctype/payroll_period/test_payroll_period.js (100%) rename erpnext/{hr => payroll}/doctype/payroll_period/test_payroll_period.py (100%) rename erpnext/{hr/doctype/salary_component => payroll/doctype/payroll_period_date}/__init__.py (100%) create mode 100644 erpnext/payroll/doctype/payroll_period_date/payroll_period_date.json rename erpnext/{hr => payroll}/doctype/payroll_period_date/payroll_period_date.py (71%) rename erpnext/{hr/doctype/salary_detail => payroll/doctype/payroll_settings}/__init__.py (100%) create mode 100644 erpnext/payroll/doctype/payroll_settings/payroll_settings.js create mode 100644 erpnext/payroll/doctype/payroll_settings/payroll_settings.json create mode 100644 erpnext/payroll/doctype/payroll_settings/payroll_settings.py create mode 100644 erpnext/payroll/doctype/payroll_settings/test_payroll_settings.py rename erpnext/{hr/doctype/salary_slip_timesheet => payroll/doctype/retention_bonus}/__init__.py (100%) rename erpnext/{hr => payroll}/doctype/retention_bonus/retention_bonus.js (100%) rename erpnext/{hr => payroll}/doctype/retention_bonus/retention_bonus.json (90%) rename erpnext/{hr => payroll}/doctype/retention_bonus/retention_bonus.py (76%) rename erpnext/{hr => payroll}/doctype/retention_bonus/test_retention_bonus.js (100%) rename erpnext/{hr => payroll}/doctype/retention_bonus/test_retention_bonus.py (100%) rename erpnext/{hr => payroll}/doctype/salary_component/README.md (100%) rename erpnext/{hr/doctype/salary_structure_assignment => payroll/doctype/salary_component}/__init__.py (100%) rename erpnext/{hr => payroll}/doctype/salary_component/salary_component.js (100%) rename erpnext/{hr => payroll}/doctype/salary_component/salary_component.json (95%) rename erpnext/{hr => payroll}/doctype/salary_component/salary_component.py (100%) rename erpnext/{hr => payroll}/doctype/salary_component/test_records.json (100%) rename erpnext/{hr => payroll}/doctype/salary_component/test_salary_component.js (100%) rename erpnext/{hr => payroll}/doctype/salary_component/test_salary_component.py (100%) rename erpnext/{hr/doctype/taxable_salary_slab => payroll/doctype/salary_detail}/__init__.py (100%) rename erpnext/{hr => payroll}/doctype/salary_detail/salary_detail.json (99%) rename erpnext/{hr => payroll}/doctype/salary_detail/salary_detail.py (100%) rename erpnext/{hr => payroll}/doctype/salary_slip/README.md (100%) rename erpnext/{hr => payroll}/doctype/salary_slip/__init__.py (100%) rename erpnext/{hr => payroll}/doctype/salary_slip/salary_slip.js (93%) rename erpnext/{hr => payroll}/doctype/salary_slip/salary_slip.json (70%) rename erpnext/{hr => payroll}/doctype/salary_slip/salary_slip.py (92%) rename erpnext/{hr => payroll}/doctype/salary_slip/salary_slip_list.js (100%) rename erpnext/{hr => payroll}/doctype/salary_slip/test_salary_slip.js (100%) rename erpnext/{hr => payroll}/doctype/salary_slip/test_salary_slip.py (94%) rename erpnext/{hr/notification/retention_bonus => payroll/doctype/salary_slip_timesheet}/__init__.py (100%) create mode 100644 erpnext/payroll/doctype/salary_slip_timesheet/salary_slip_timesheet.json rename erpnext/{hr => payroll}/doctype/salary_slip_timesheet/salary_slip_timesheet.py (72%) rename erpnext/{hr => payroll}/doctype/salary_structure/README.md (100%) rename erpnext/{hr => payroll}/doctype/salary_structure/__init__.py (100%) create mode 100644 erpnext/payroll/doctype/salary_structure/condition_and_formula_help.html rename erpnext/{hr => payroll}/doctype/salary_structure/salary_structure.js (91%) rename erpnext/{hr => payroll}/doctype/salary_structure/salary_structure.json (71%) rename erpnext/{hr => payroll}/doctype/salary_structure/salary_structure.py (100%) rename erpnext/{hr => payroll}/doctype/salary_structure/salary_structure_dashboard.py (100%) rename erpnext/{hr => payroll}/doctype/salary_structure/test_salary_structure.js (100%) rename erpnext/{hr => payroll}/doctype/salary_structure/test_salary_structure.py (94%) rename erpnext/{hr/print_format/salary_slip_based_on_timesheet => payroll/doctype/salary_structure_assignment}/__init__.py (100%) rename erpnext/{hr => payroll}/doctype/salary_structure_assignment/salary_structure_assignment.js (100%) rename erpnext/{hr => payroll}/doctype/salary_structure_assignment/salary_structure_assignment.json (98%) rename erpnext/{hr => payroll}/doctype/salary_structure_assignment/salary_structure_assignment.py (100%) rename erpnext/{hr => payroll}/doctype/salary_structure_assignment/test_salary_structure_assignment.js (100%) rename erpnext/{hr => payroll}/doctype/salary_structure_assignment/test_salary_structure_assignment.py (100%) rename erpnext/{hr/print_format/salary_slip_standard => payroll/doctype/taxable_salary_slab}/__init__.py (100%) create mode 100644 erpnext/payroll/doctype/taxable_salary_slab/taxable_salary_slab.json rename erpnext/{hr => payroll}/doctype/taxable_salary_slab/taxable_salary_slab.py (71%) create mode 100644 erpnext/payroll/module_onboarding/payroll/payroll.json create mode 100644 erpnext/payroll/notification/as rename erpnext/{hr/report/bank_remittance => payroll/notification/retention_bonus}/__init__.py (100%) rename erpnext/{hr => payroll}/notification/retention_bonus/retention_bonus.json (86%) rename erpnext/{hr => payroll}/notification/retention_bonus/retention_bonus.md (100%) rename erpnext/{hr => payroll}/notification/retention_bonus/retention_bonus.py (100%) create mode 100644 erpnext/payroll/onboarding_step/assign_salary_structure/assign_salary_structure.json create mode 100644 erpnext/payroll/onboarding_step/create_employee/create_employee.json create mode 100644 erpnext/payroll/onboarding_step/create_income_tax_slab/create_income_tax_slab.json create mode 100644 erpnext/payroll/onboarding_step/create_payroll_period/create_payroll_period.json create mode 100644 erpnext/payroll/onboarding_step/create_salary_component/create_salary_component.json create mode 100644 erpnext/payroll/onboarding_step/create_salary_slip/create_salary_slip.json create mode 100644 erpnext/payroll/onboarding_step/create_salary_structure/create_salary_structure.json create mode 100644 erpnext/payroll/onboarding_step/payroll_settings/payroll_settings.json rename erpnext/{hr/report/salary_register => payroll/print_format/salary_slip_based_on_timesheet}/__init__.py (100%) rename erpnext/{hr => payroll}/print_format/salary_slip_based_on_timesheet/salary_slip_based_on_timesheet.json (100%) create mode 100644 erpnext/payroll/print_format/salary_slip_standard/__init__.py rename erpnext/{hr => payroll}/print_format/salary_slip_standard/salary_slip_standard.json (100%) create mode 100644 erpnext/payroll/report/__init__.py create mode 100644 erpnext/payroll/report/bank_remittance/__init__.py rename erpnext/{hr => payroll}/report/bank_remittance/bank_remittance.js (68%) rename erpnext/{hr => payroll}/report/bank_remittance/bank_remittance.json (87%) rename erpnext/{hr => payroll}/report/bank_remittance/bank_remittance.py (96%) create mode 100644 erpnext/payroll/report/income_tax_deductions/__init__.py create mode 100644 erpnext/payroll/report/income_tax_deductions/income_tax_deductions.js create mode 100644 erpnext/payroll/report/income_tax_deductions/income_tax_deductions.json create mode 100644 erpnext/payroll/report/income_tax_deductions/income_tax_deductions.py create mode 100644 erpnext/payroll/report/salary_payments_based_on_payment_mode/__init__.py create mode 100644 erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.js create mode 100644 erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.json create mode 100644 erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.py create mode 100644 erpnext/payroll/report/salary_payments_via_ecs/__init__.py create mode 100644 erpnext/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.js create mode 100644 erpnext/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.json create mode 100644 erpnext/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.py create mode 100644 erpnext/payroll/report/salary_register/__init__.py rename erpnext/{hr => payroll}/report/salary_register/salary_register.html (100%) rename erpnext/{hr => payroll}/report/salary_register/salary_register.js (100%) create mode 100644 erpnext/payroll/report/salary_register/salary_register.json rename erpnext/{hr => payroll}/report/salary_register/salary_register.py (96%) create mode 100644 erpnext/public/js/salary_slip_deductions_report_filters.js create mode 100644 erpnext/regional/report/professional_tax_deductions/__init__.py create mode 100644 erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.js create mode 100644 erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.json create mode 100644 erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.py create mode 100644 erpnext/regional/report/provident_fund_deductions/__init__.py create mode 100644 erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.js create mode 100644 erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.json create mode 100644 erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.py diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js index 9a832e3c1fe..5685f839fe8 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.js +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js @@ -278,7 +278,7 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({ // payroll entry if(jvd.reference_type==="Payroll Entry") { return { - query: "erpnext.hr.doctype.payroll_entry.payroll_entry.get_payroll_entries_for_jv", + query: "erpnext.payroll.doctype.payroll_entry.payroll_entry.get_payroll_entries_for_jv", }; } diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 41922a2a694..caaf30f11fb 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -54,7 +54,7 @@ class JournalEntry(AccountsController): def on_cancel(self): from erpnext.accounts.utils import unlink_ref_doc_from_payment_entries - from erpnext.hr.doctype.salary_slip.salary_slip import unlink_ref_doc_from_salary_slip + from erpnext.payroll.doctype.salary_slip.salary_slip import unlink_ref_doc_from_salary_slip unlink_ref_doc_from_payment_entries(self) unlink_ref_doc_from_salary_slip(self.name) self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry') diff --git a/erpnext/hr/dashboard_fixtures.py b/erpnext/hr/dashboard_fixtures.py index 6e042ac78d3..6d8091be647 100644 --- a/erpnext/hr/dashboard_fixtures.py +++ b/erpnext/hr/dashboard_fixtures.py @@ -24,24 +24,19 @@ def get_human_resource_dashboard(): "dashboard_name": "Human Resource", "is_default": 1, "charts": [ - { "chart": "Outgoing Salary", "width": "Full"}, + { "chart": "Attendance Count", "width": "Full"}, { "chart": "Gender Diversity Ratio", "width": "Half"}, { "chart": "Job Application Status", "width": "Half"}, { "chart": 'Designation Wise Employee Count', "width": "Half"}, { "chart": 'Department Wise Employee Count', "width": "Half"}, { "chart": 'Designation Wise Openings', "width": "Half"}, - { "chart": 'Department Wise Openings', "width": "Half"}, - { "chart": "Attendance Count", "width": "Full"} + { "chart": 'Department Wise Openings', "width": "Half"} ], "cards": [ {"card": "Total Employees"}, {"card": "New Joinees (Last year)"}, {'card': "Employees Left (Last year)"}, - {'card': "Total Job Openings (Last month)"}, {'card': "Total Applicants (Last month)"}, - {'card': "Shortlisted Candidates (Last month)"}, - {'card': "Rejected Candidates (Last month)"}, - {'card': "Total Job Offered (Last month)"}, ] } @@ -71,13 +66,6 @@ def get_charts(): filters_json = json.dumps([["Job Applicant", "creation", "Previous", "1 month"]])) ) - dashboard_charts.append( - get_dashboards_chart_doc('Outgoing Salary', "Sum", "Line", - document_type = "Salary Slip", based_on="end_date", - value_based_on = "rounded_total", time_interval = "Monthly", timeseries = 1, - filters_json = json.dumps([["Salary Slip", "docstatus", "=", 1]])) - ) - custom_options = '''{ "type": "line", "axisOptions": { @@ -156,32 +144,6 @@ def get_number_cards(): ) ) - number_cards.append( - get_number_cards_doc("Job Opening", "Total Job Openings (Last month)", func = "Sum", - aggregate_function_based_on = "planned_vacancies", - filters_json = json.dumps([["Job Opening", "creation", "Previous", "1 month"]]) - ) - ) - number_cards.append( - get_number_cards_doc("Job Applicant", "Shortlisted Candidates (Last month)", filters_json = json.dumps([ - ["Job Applicant", "status", "=", "Accepted"], - ["Job Applicant", "creation", "Previous", "1 month"] - ]) - ) - ) - number_cards.append( - get_number_cards_doc("Job Applicant", "Rejected Candidates (Last month)", filters_json = json.dumps([ - ["Job Applicant", "status", "=", "Rejected"], - ["Job Applicant", "creation", "Previous", "1 month"] - ]) - ) - ) - number_cards.append( - get_number_cards_doc("Job Offer", "Total Job Offered (Last month)", - filters_json = json.dumps([["Job Offer", "creation", "Previous", "1 month"]]) - ) - ) - return number_cards diff --git a/erpnext/hr/desk_page/hr/hr.json b/erpnext/hr/desk_page/hr/hr.json index 12548d48a74..0fed8d322f5 100644 --- a/erpnext/hr/desk_page/hr/hr.json +++ b/erpnext/hr/desk_page/hr/hr.json @@ -20,11 +20,6 @@ "label": "Leaves", "links": "[\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Leave Application\",\n \"name\": \"Leave Application\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Leave Allocation\",\n \"name\": \"Leave Allocation\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Leave Type\"\n ],\n \"label\": \"Leave Policy\",\n \"name\": \"Leave Policy\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Leave Period\",\n \"name\": \"Leave Period\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Leave Type\",\n \"name\": \"Leave Type\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Holiday List\",\n \"name\": \"Holiday List\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Compensatory Leave Request\",\n \"name\": \"Compensatory Leave Request\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Leave Encashment\",\n \"name\": \"Leave Encashment\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Leave Block List\",\n \"name\": \"Leave Block List\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Leave Application\"\n ],\n \"doctype\": \"Leave Application\",\n \"is_query_report\": true,\n \"label\": \"Employee Leave Balance\",\n \"name\": \"Employee Leave Balance\",\n \"type\": \"report\"\n }\n]" }, - { - "hidden": 0, - "label": "Payroll", - "links": "[\n {\n \"label\": \"Salary Structure\",\n \"name\": \"Salary Structure\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Salary Structure\",\n \"Employee\"\n ],\n \"label\": \"Salary Structure Assignment\",\n \"name\": \"Salary Structure Assignment\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Payroll Entry\",\n \"name\": \"Payroll Entry\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Salary Slip\",\n \"name\": \"Salary Slip\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Payroll Period\",\n \"name\": \"Payroll Period\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Income Tax Slab\",\n \"name\": \"Income Tax Slab\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Salary Component\",\n \"name\": \"Salary Component\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Additional Salary\",\n \"name\": \"Additional Salary\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Retention Bonus\",\n \"name\": \"Retention Bonus\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Incentive\",\n \"name\": \"Employee Incentive\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Salary Slip\"\n ],\n \"doctype\": \"Salary Slip\",\n \"is_query_report\": true,\n \"label\": \"Salary Register\",\n \"name\": \"Salary Register\",\n \"type\": \"report\"\n }\n]" - }, { "hidden": 0, "label": "Attendance", @@ -50,11 +45,6 @@ "label": "Recruitment", "links": "[\n {\n \"label\": \"Job Opening\",\n \"name\": \"Job Opening\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Job Applicant\",\n \"name\": \"Job Applicant\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Job Offer\",\n \"name\": \"Job Offer\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Staffing Plan\",\n \"name\": \"Staffing Plan\",\n \"type\": \"doctype\"\n }\n]" }, - { - "hidden": 0, - "label": "Loans", - "links": "[\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Loan Application\",\n \"name\": \"Loan Application\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Loan\",\n \"name\": \"Loan\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Loan Type\",\n \"name\": \"Loan Type\",\n \"type\": \"doctype\"\n }\n]" - }, { "hidden": 0, "label": "Training", @@ -69,18 +59,13 @@ "hidden": 0, "label": "Performance", "links": "[\n {\n \"label\": \"Appraisal\",\n \"name\": \"Appraisal\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Appraisal Template\",\n \"name\": \"Appraisal Template\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Energy Point Rule\",\n \"name\": \"Energy Point Rule\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Energy Point Log\",\n \"name\": \"Energy Point Log\",\n \"type\": \"doctype\"\n }\n]" - }, - { - "hidden": 0, - "label": "Employee Tax and Benefits", - "links": "[\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Tax Exemption Declaration\",\n \"name\": \"Employee Tax Exemption Declaration\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Tax Exemption Proof Submission\",\n \"name\": \"Employee Tax Exemption Proof Submission\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\",\n \"Payroll Period\"\n ],\n \"label\": \"Employee Other Income\",\n \"name\": \"Employee Other Income\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Benefit Application\",\n \"name\": \"Employee Benefit Application\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Benefit Claim\",\n \"name\": \"Employee Benefit Claim\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Tax Exemption Category\",\n \"name\": \"Employee Tax Exemption Category\",\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"Employee\"\n ],\n \"label\": \"Employee Tax Exemption Sub Category\",\n \"name\": \"Employee Tax Exemption Sub Category\",\n \"type\": \"doctype\"\n }\n]" } ], "category": "Modules", "charts": [ { - "chart_name": "Outgoing Salary", - "label": "Outgoing Salary" + "chart_name": "Attendance Count", + "label": "Attendance Count" } ], "creation": "2020-03-02 15:48:58.322521", @@ -103,21 +88,13 @@ "pin_to_top": 0, "shortcuts": [ { - "color": "#cef6d1", + "color": "#9deca2", "format": "{} Active", "label": "Employee", "link_to": "Employee", "stats_filter": "{\"status\":\"Active\"}", "type": "DocType" }, - { - "color": "#ffe8cd", - "format": "{} Open", - "label": "Leave Application", - "link_to": "Leave Application", - "stats_filter": "{\"status\":\"Open\"}", - "type": "DocType" - }, { "label": "Attendance", "link_to": "Attendance", @@ -125,8 +102,15 @@ "type": "DocType" }, { - "label": "Salary Structure", - "link_to": "Salary Structure", + "format": "{} Open", + "label": "Leave Application", + "link_to": "Leave Application", + "stats_filter": "{\"status\":\"Open\"}", + "type": "DocType" + }, + { + "label": "Job Applicant", + "link_to": "Job Applicant", "type": "DocType" }, { diff --git a/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.json b/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.json deleted file mode 100644 index cf624195b78..00000000000 --- a/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.json +++ /dev/null @@ -1,576 +0,0 @@ -{ - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 1, - "autoname": "HR-BEN-APP-.YY.-.MM.-.#####", - "beta": 0, - "creation": "2018-04-13 16:31:39.190787", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "employee", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Employee", - "length": 0, - "no_copy": 0, - "options": "Employee", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "employee.employee_name", - "fieldname": "employee_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Employee Name", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "max_benefits", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Max Benefits (Yearly)", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "remaining_benefit", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Remaining Benefits (Yearly)", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_2", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Today", - "fieldname": "date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "payroll_period", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Payroll Period", - "length": 0, - "no_copy": 0, - "options": "Payroll Period", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "employee.department", - "fieldname": "department", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Department", - "length": 0, - "no_copy": 0, - "options": "Department", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "amended_from", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Amended From", - "length": 0, - "no_copy": 1, - "options": "Employee Benefit Application", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_4", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Benefits Applied", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "employee_benefits", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Employee Benefits", - "length": 0, - "no_copy": 0, - "options": "Employee Benefit Application Detail", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "totals", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Totals", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "total_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Total Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "pro_rata_dispensed_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Dispensed Amount (Pro-rated)", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-08-21 16:15:39.714081", - "modified_by": "Administrator", - "module": "HR", - "name": "Employee Benefit Application", - "name_case": "", - "owner": "Administrator", - "permissions": [ - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "set_user_permissions": 0, - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Employee", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 1 - } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "employee_name", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_benefit_application_detail/employee_benefit_application_detail.json b/erpnext/hr/doctype/employee_benefit_application_detail/employee_benefit_application_detail.json deleted file mode 100644 index 56421db8c32..00000000000 --- a/erpnext/hr/doctype/employee_benefit_application_detail/employee_benefit_application_detail.json +++ /dev/null @@ -1,177 +0,0 @@ -{ - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "", - "beta": 0, - "creation": "2018-04-13 16:36:18.389786", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "earning_component", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Earning Component", - "length": 0, - "no_copy": 0, - "options": "Salary Component", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "earning_component.pay_against_benefit_claim", - "fieldname": "pay_against_benefit_claim", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Pay Against Benefit Claim", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "earning_component.max_benefit_amount", - "fieldname": "max_benefit_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Max Benefit Amount", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-08-21 16:15:42.111118", - "modified_by": "Administrator", - "module": "HR", - "name": "Employee Benefit Application Detail", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "search_fields": "", - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_benefit_claim/employee_benefit_claim.json b/erpnext/hr/doctype/employee_benefit_claim/employee_benefit_claim.json deleted file mode 100644 index 1aa69d02a5a..00000000000 --- a/erpnext/hr/doctype/employee_benefit_claim/employee_benefit_claim.json +++ /dev/null @@ -1,580 +0,0 @@ -{ - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 0, - "autoname": "HR-BEN-CLM-.YY.-.MM.-.#####", - "beta": 0, - "creation": "2018-04-13 16:43:10.386409", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "employee", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Employee", - "length": 0, - "no_copy": 0, - "options": "Employee", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "employee.employee_name", - "fieldname": "employee_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Employee Name", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "employee.department", - "fieldname": "department", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Department", - "length": 0, - "no_copy": 0, - "options": "Department", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Today", - "fieldname": "claim_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Claim Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "benefit_type_and_amount", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Benefit Type and Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "earning_component", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Claim Benefit For", - "length": 0, - "no_copy": 0, - "options": "Salary Component", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "earning_component.max_benefit_amount", - "fieldname": "max_amount_eligible", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Max Amount Eligible", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "earning_component.pay_against_benefit_claim", - "fieldname": "pay_against_benefit_claim", - "fieldtype": "Check", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Pay Against Benefit Claim", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "claimed_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Claimed Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "salary_slip", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Salary Slip", - "length": 0, - "no_copy": 0, - "options": "Salary Slip", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "amended_from", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Amended From", - "length": 0, - "no_copy": 1, - "options": "Employee Benefit Claim", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_9", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Expense Proof", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "attachments", - "fieldtype": "Attach", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Attachments", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-08-21 16:15:35.942067", - "modified_by": "Administrator", - "module": "HR", - "name": "Employee Benefit Claim", - "name_case": "", - "owner": "Administrator", - "permissions": [ - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "set_user_permissions": 0, - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Employee", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 1 - } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "employee_name", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_promotion/test_employee_promotion.py b/erpnext/hr/doctype/employee_promotion/test_employee_promotion.py index 420bbe6b1a9..9e7d3186b88 100644 --- a/erpnext/hr/doctype/employee_promotion/test_employee_promotion.py +++ b/erpnext/hr/doctype/employee_promotion/test_employee_promotion.py @@ -6,7 +6,7 @@ from __future__ import unicode_literals import frappe import unittest from frappe.utils import getdate, add_days -from erpnext.hr.doctype.salary_structure.test_salary_structure import make_employee +from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_employee class TestEmployeePromotion(unittest.TestCase): def setUp(self): diff --git a/erpnext/hr/doctype/employee_tax_exemption_category/employee_tax_exemption_category.json b/erpnext/hr/doctype/employee_tax_exemption_category/employee_tax_exemption_category.json deleted file mode 100644 index 66fac5bee5c..00000000000 --- a/erpnext/hr/doctype/employee_tax_exemption_category/employee_tax_exemption_category.json +++ /dev/null @@ -1,169 +0,0 @@ -{ - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 1, - "autoname": "Prompt", - "beta": 0, - "creation": "2018-04-13 16:51:36.971140", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "max_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Max Exemption Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "1", - "fetch_if_empty": 0, - "fieldname": "is_active", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Is Active", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2019-04-25 13:20:31.367158", - "modified_by": "Administrator", - "module": "HR", - "name": "Employee Tax Exemption Category", - "name_case": "", - "owner": "Administrator", - "permissions": [ - { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 1 - }, - { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 1 - }, - { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 1 - } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.json b/erpnext/hr/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.json deleted file mode 100644 index 7b3b8f5caae..00000000000 --- a/erpnext/hr/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.json +++ /dev/null @@ -1,179 +0,0 @@ -{ - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2018-04-13 16:56:23.333041", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "exemption_sub_category", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Exemption Sub Category", - "length": 0, - "no_copy": 0, - "options": "Employee Tax Exemption Sub Category", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "exemption_sub_category.exemption_category", - "fetch_if_empty": 0, - "fieldname": "exemption_category", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Exemption Category", - "length": 0, - "no_copy": 0, - "options": "Employee Tax Exemption Category", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "exemption_sub_category.max_amount", - "fetch_if_empty": 0, - "fieldname": "max_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Maximum Exempted Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Declared Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2019-04-26 11:28:14.023086", - "modified_by": "Administrator", - "module": "HR", - "name": "Employee Tax Exemption Declaration Category", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_tax_exemption_proof_submission/test_employee_tax_exemption_proof_submission.py b/erpnext/hr/doctype/employee_tax_exemption_proof_submission/test_employee_tax_exemption_proof_submission.py deleted file mode 100644 index e54d9193ba7..00000000000 --- a/erpnext/hr/doctype/employee_tax_exemption_proof_submission/test_employee_tax_exemption_proof_submission.py +++ /dev/null @@ -1,54 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt -from __future__ import unicode_literals - -import frappe -import unittest -# from erpnext.hr.doctype.employee_tax_exemption_declaration.test_employee_tax_exemption_declaration import create_exemption_category, create_payroll_period -# -# class TestEmployeeTaxExemptionProofSubmission(unittest.TestCase): -# def setup(self): -# make_employee("employee@proofsubmission.com") -# create_payroll_period() -# create_exemption_category() -# frappe.db.sql("""delete from `tabEmployee Tax Exemption Proof Submission`""") -# -# def test_exemption_amount_lesser_than_category_max(self): -# declaration = frappe.get_doc({ -# "doctype": "Employee Tax Exemption Proof Submission", -# "employee": frappe.get_value("Employee", {"user_id":"employee@proofsubmission.com"}, "name"), -# "payroll_period": "Test Payroll Period", -# "tax_exemption_proofs": [dict(exemption_sub_category = "_Test Sub Category", -# type_of_proof = "Test Proof", -# exemption_category = "_Test Category", -# amount = 150000)] -# }) -# self.assertRaises(frappe.ValidationError, declaration.save) -# declaration = frappe.get_doc({ -# "doctype": "Employee Tax Exemption Proof Submission", -# "payroll_period": "Test Payroll Period", -# "employee": frappe.get_value("Employee", {"user_id":"employee@proofsubmission.com"}, "name"), -# "tax_exemption_proofs": [dict(exemption_sub_category = "_Test Sub Category", -# type_of_proof = "Test Proof", -# exemption_category = "_Test Category", -# amount = 100000)] -# }) -# self.assertTrue(declaration.save) -# self.assertTrue(declaration.submit) -# -# def test_duplicate_category_in_proof_submission(self): -# declaration = frappe.get_doc({ -# "doctype": "Employee Tax Exemption Proof Submission", -# "employee": frappe.get_value("Employee", {"user_id":"employee@proofsubmission.com"}, "name"), -# "payroll_period": "Test Payroll Period", -# "tax_exemption_proofs": [dict(exemption_sub_category = "_Test Sub Category", -# exemption_category = "_Test Category", -# type_of_proof = "Test Proof", -# amount = 100000), -# dict(exemption_sub_category = "_Test Sub Category", -# exemption_category = "_Test Category", -# amount = 50000), -# ] -# }) -# self.assertRaises(frappe.ValidationError, declaration.save) diff --git a/erpnext/hr/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.json b/erpnext/hr/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.json deleted file mode 100644 index b9254afad07..00000000000 --- a/erpnext/hr/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.json +++ /dev/null @@ -1,213 +0,0 @@ -{ - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2018-04-13 17:19:03.006149", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "exemption_sub_category", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Exemption Sub Category", - "length": 0, - "no_copy": 0, - "options": "Employee Tax Exemption Sub Category", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "exemption_sub_category.exemption_category", - "fetch_if_empty": 0, - "fieldname": "exemption_category", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Exemption Category", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "exemption_sub_category.max_amount", - "fetch_if_empty": 0, - "fieldname": "max_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Maximum Exemption Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "type_of_proof", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Type of Proof", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Actual Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2019-04-25 15:45:03.154904", - "modified_by": "Administrator", - "module": "HR", - "name": "Employee Tax Exemption Proof Submission Detail", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.json b/erpnext/hr/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.json deleted file mode 100644 index b0e492e7ca4..00000000000 --- a/erpnext/hr/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.json +++ /dev/null @@ -1,204 +0,0 @@ -{ - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 1, - "autoname": "Prompt", - "beta": 0, - "creation": "2018-05-09 12:47:26.983095", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "exemption_category", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Tax Exemption Category", - "length": 0, - "no_copy": 0, - "options": "Employee Tax Exemption Category", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "exemption_category.max_amount", - "fetch_if_empty": 1, - "fieldname": "max_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Max Exemption Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "1", - "fetch_if_empty": 0, - "fieldname": "is_active", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Is Active", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2019-04-25 13:24:05.164877", - "modified_by": "Administrator", - "module": "HR", - "name": "Employee Tax Exemption Sub Category", - "name_case": "", - "owner": "Administrator", - "permissions": [ - { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 1 - }, - { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 1 - }, - { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 1 - } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/hr_settings/hr_settings.js b/erpnext/hr/doctype/hr_settings/hr_settings.js index b629b42f9b1..fd082fda09b 100644 --- a/erpnext/hr/doctype/hr_settings/hr_settings.js +++ b/erpnext/hr/doctype/hr_settings/hr_settings.js @@ -2,21 +2,6 @@ // For license information, please see license.txt frappe.ui.form.on('HR Settings', { - encrypt_salary_slips_in_emails: function(frm) { - let encrypt_state = frm.doc.encrypt_salary_slips_in_emails; - frm.set_df_property('password_policy', 'reqd', encrypt_state); - }, - - validate: function(frm) { - let policy = frm.doc.password_policy; - if (policy) { - if (policy.includes(' ') || policy.includes('--')) { - frappe.msgprint(__("Password policy cannot contain spaces or simultaneous hyphens. The format will be restructured automatically")); - } - frm.set_value('password_policy', policy.split(new RegExp(" |-", 'g')).filter((token) => token).join('-')); - } - }, - restrict_backdated_leave_application: function(frm) { frm.toggle_reqd("role_allowed_to_create_backdated_leave_application", frm.doc.restrict_backdated_leave_application); } diff --git a/erpnext/hr/doctype/hr_settings/hr_settings.json b/erpnext/hr/doctype/hr_settings/hr_settings.json index ebf8723be65..c42e1d72fcc 100644 --- a/erpnext/hr/doctype/hr_settings/hr_settings.json +++ b/erpnext/hr/doctype/hr_settings/hr_settings.json @@ -12,16 +12,6 @@ "column_break_4", "stop_birthday_reminders", "expense_approver_mandatory_in_expense_claim", - "payroll_settings", - "payroll_based_on", - "max_working_hours_against_timesheet", - "include_holidays_in_total_working_days", - "disable_rounded_total", - "column_break_11", - "daily_wages_fraction_for_half_day", - "email_salary_slip_to_employee", - "encrypt_salary_slips_in_emails", - "password_policy", "leave_settings", "leave_approval_notification_template", "leave_status_notification_template", @@ -38,13 +28,17 @@ { "fieldname": "employee_settings", "fieldtype": "Section Break", - "label": "Employee Settings" + "label": "Employee Settings", + "show_days": 1, + "show_seconds": 1 }, { "description": "Enter retirement age in years", "fieldname": "retirement_age", "fieldtype": "Data", - "label": "Retirement Age" + "label": "Retirement Age", + "show_days": 1, + "show_seconds": 1 }, { "default": "Naming Series", @@ -52,161 +46,126 @@ "fieldname": "emp_created_by", "fieldtype": "Select", "label": "Employee Records to be created by", - "options": "Naming Series\nEmployee Number\nFull Name" + "options": "Naming Series\nEmployee Number\nFull Name", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_4", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "description": "Don't send Employee Birthday Reminders", "fieldname": "stop_birthday_reminders", "fieldtype": "Check", - "label": "Stop Birthday Reminders" + "label": "Stop Birthday Reminders", + "show_days": 1, + "show_seconds": 1 }, { "default": "1", "fieldname": "expense_approver_mandatory_in_expense_claim", "fieldtype": "Check", - "label": "Expense Approver Mandatory In Expense Claim" - }, - { - "fieldname": "payroll_settings", - "fieldtype": "Section Break", - "label": "Payroll Settings" - }, - { - "default": "0", - "description": "If checked, Total no. of Working Days will include holidays, and this will reduce the value of Salary Per Day", - "fieldname": "include_holidays_in_total_working_days", - "fieldtype": "Check", - "label": "Include holidays in Total no. of Working Days" - }, - { - "fieldname": "max_working_hours_against_timesheet", - "fieldtype": "Float", - "label": "Max working hours against Timesheet" - }, - { - "fieldname": "column_break_11", - "fieldtype": "Column Break" - }, - { - "default": "1", - "description": "Emails salary slip to employee based on preferred email selected in Employee", - "fieldname": "email_salary_slip_to_employee", - "fieldtype": "Check", - "label": "Email Salary Slip to Employee" - }, - { - "default": "0", - "depends_on": "eval: doc.email_salary_slip_to_employee == 1;", - "description": "The salary slip emailed to the employee will be password protected, the password will be generated based on the password policy.", - "fieldname": "encrypt_salary_slips_in_emails", - "fieldtype": "Check", - "label": "Encrypt Salary Slips in Emails" - }, - { - "depends_on": "eval: doc.encrypt_salary_slips_in_emails == 1", - "description": "Example: SAL-{first_name}-{date_of_birth.year}
    This will generate a password like SAL-Jane-1972", - "fieldname": "password_policy", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Password Policy" + "label": "Expense Approver Mandatory In Expense Claim", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "leave_settings", "fieldtype": "Section Break", - "label": "Leave Settings" + "label": "Leave Settings", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "leave_approval_notification_template", "fieldtype": "Link", "label": "Leave Approval Notification Template", - "options": "Email Template" + "options": "Email Template", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "leave_status_notification_template", "fieldtype": "Link", "label": "Leave Status Notification Template", - "options": "Email Template" + "options": "Email Template", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_18", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "default": "1", "fieldname": "leave_approver_mandatory_in_leave_application", "fieldtype": "Check", - "label": "Leave Approver Mandatory In Leave Application" + "label": "Leave Approver Mandatory In Leave Application", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "show_leaves_of_all_department_members_in_calendar", "fieldtype": "Check", - "label": "Show Leaves Of All Department Members In Calendar" + "label": "Show Leaves Of All Department Members In Calendar", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "hiring_settings", "fieldtype": "Section Break", - "label": "Hiring Settings" + "label": "Hiring Settings", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "check_vacancies", "fieldtype": "Check", - "label": "Check Vacancies On Job Offer Creation" + "label": "Check Vacancies On Job Offer Creation", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "auto_leave_encashment", "fieldtype": "Check", - "label": "Auto Leave Encashment" - }, - { - "default": "0", - "description": "If checked, hides and disables Rounded Total field in Salary Slips", - "fieldname": "disable_rounded_total", - "fieldtype": "Check", - "label": "Disable Rounded Total" + "label": "Auto Leave Encashment", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "restrict_backdated_leave_application", "fieldtype": "Check", - "label": "Restrict Backdated Leave Application" + "label": "Restrict Backdated Leave Application", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.restrict_backdated_leave_application == 1", "fieldname": "role_allowed_to_create_backdated_leave_application", "fieldtype": "Link", "label": "Role Allowed to Create Backdated Leave Application", - "options": "Role" - }, - { - "default": "Leave", - "fieldname": "payroll_based_on", - "fieldtype": "Select", - "label": "Calculate Payroll Working Days Based On", - "options": "Leave\nAttendance" - }, - { - "default": "0.5", - "description": "The fraction of daily wages to be paid for half-day attendance", - "fieldname": "daily_wages_fraction_for_half_day", - "fieldtype": "Float", - "label": "Daily Wages Fraction for Half Day" + "options": "Role", + "show_days": 1, + "show_seconds": 1 } ], "icon": "fa fa-cog", "idx": 1, "issingle": 1, "links": [], - "modified": "2020-05-11 13:02:51.274347", + "modified": "2020-06-04 15:15:09.865476", "modified_by": "Administrator", "module": "HR", "name": "HR Settings", diff --git a/erpnext/hr/doctype/hr_settings/hr_settings.py b/erpnext/hr/doctype/hr_settings/hr_settings.py index 5ed4c87c62f..ced98fb9a58 100644 --- a/erpnext/hr/doctype/hr_settings/hr_settings.py +++ b/erpnext/hr/doctype/hr_settings/hr_settings.py @@ -5,34 +5,14 @@ from __future__ import unicode_literals import frappe -from frappe import _ from frappe.model.document import Document -from frappe.utils import cint -from frappe.custom.doctype.property_setter.property_setter import make_property_setter class HRSettings(Document): def validate(self): self.set_naming_series() - self.validate_password_policy() - - if not self.daily_wages_fraction_for_half_day: - self.daily_wages_fraction_for_half_day = 0.5 def set_naming_series(self): from erpnext.setup.doctype.naming_series.naming_series import set_by_naming_series set_by_naming_series("Employee", "employee_number", self.get("emp_created_by")=="Naming Series", hide_name_field=True) - def validate_password_policy(self): - if self.email_salary_slip_to_employee and self.encrypt_salary_slips_in_emails: - if not self.password_policy: - frappe.throw(_("Password policy for Salary Slips is not set")) - - def on_update(self): - self.toggle_rounded_total() - frappe.clear_cache() - - def toggle_rounded_total(self): - self.disable_rounded_total = cint(self.disable_rounded_total) - make_property_setter("Salary Slip", "rounded_total", "hidden", self.disable_rounded_total, "Check") - make_property_setter("Salary Slip", "rounded_total", "print_hide", self.disable_rounded_total, "Check") diff --git a/erpnext/hr/doctype/leave_encashment/leave_encashment.py b/erpnext/hr/doctype/leave_encashment/leave_encashment.py index 50a08b12bc0..8913c648c52 100644 --- a/erpnext/hr/doctype/leave_encashment/leave_encashment.py +++ b/erpnext/hr/doctype/leave_encashment/leave_encashment.py @@ -8,7 +8,7 @@ from frappe import _ from frappe.model.document import Document from frappe.utils import getdate, nowdate, flt from erpnext.hr.utils import set_employee_name -from erpnext.hr.doctype.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure +from erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure from erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry import create_leave_ledger_entry from erpnext.hr.doctype.leave_allocation.leave_allocation import get_unused_leaves diff --git a/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py b/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py index ac7755b23a7..99f64634161 100644 --- a/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py +++ b/erpnext/hr/doctype/leave_encashment/test_leave_encashment.py @@ -7,7 +7,7 @@ import frappe import unittest from frappe.utils import today, add_months from erpnext.hr.doctype.employee.test_employee import make_employee -from erpnext.hr.doctype.salary_structure.test_salary_structure import make_salary_structure +from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure from erpnext.hr.doctype.leave_period.test_leave_period import create_leave_period from erpnext.hr.doctype.leave_policy.test_leave_policy import create_leave_policy\ diff --git a/erpnext/hr/doctype/payroll_employee_detail/payroll_employee_detail.json b/erpnext/hr/doctype/payroll_employee_detail/payroll_employee_detail.json deleted file mode 100644 index 0dd3403d665..00000000000 --- a/erpnext/hr/doctype/payroll_employee_detail/payroll_employee_detail.json +++ /dev/null @@ -1,209 +0,0 @@ -{ - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2017-11-30 06:07:33.477781", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "employee", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Employee", - "length": 0, - "no_copy": 0, - "options": "Employee", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "employee.employee_name", - "fieldname": "employee_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Employee Name", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "employee.department", - "fieldname": "department", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Department", - "length": 0, - "no_copy": 0, - "options": "Department", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "employee.designation", - "fieldname": "designation", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Designation", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2019-01-30 11:28:16.544471", - "modified_by": "Administrator", - "module": "HR", - "name": "Payroll Employee Detail", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 1, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/payroll_period_date/payroll_period_date.json b/erpnext/hr/doctype/payroll_period_date/payroll_period_date.json deleted file mode 100644 index 29bd2a33220..00000000000 --- a/erpnext/hr/doctype/payroll_period_date/payroll_period_date.json +++ /dev/null @@ -1,103 +0,0 @@ -{ - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2018-04-13 15:17:30.513630", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "start_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Start Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "end_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "End Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-04-13 19:39:37.473294", - "modified_by": "Administrator", - "module": "HR", - "name": "Payroll Period Date", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/salary_slip_timesheet/salary_slip_timesheet.json b/erpnext/hr/doctype/salary_slip_timesheet/salary_slip_timesheet.json deleted file mode 100644 index 797f8f7c028..00000000000 --- a/erpnext/hr/doctype/salary_slip_timesheet/salary_slip_timesheet.json +++ /dev/null @@ -1,107 +0,0 @@ -{ - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2016-06-14 19:22:29.811658", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "fields": [ - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "time_sheet", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Time Sheet", - "length": 0, - "no_copy": 0, - "options": "Timesheet", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "working_hours", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Working Hours", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2019-02-19 08:33:41.762144", - "modified_by": "Administrator", - "module": "HR", - "name": "Salary Slip Timesheet", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 -} \ No newline at end of file diff --git a/erpnext/hr/doctype/taxable_salary_slab/taxable_salary_slab.json b/erpnext/hr/doctype/taxable_salary_slab/taxable_salary_slab.json deleted file mode 100644 index a094f8a1971..00000000000 --- a/erpnext/hr/doctype/taxable_salary_slab/taxable_salary_slab.json +++ /dev/null @@ -1,232 +0,0 @@ -{ - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2018-04-13 17:42:13.516032", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "from_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "From Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "to_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "To Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "percent_deduction", - "fieldtype": "Percent", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Percent Deduction", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "condition", - "fieldtype": "Code", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Condition", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_5", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "html_6", - "fieldtype": "HTML", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "options": "

    Condition Examples

    \n
      \n
    1. Applying tax if employee born between 31-12-1937 and 01-01-1958 (Employees aged 60 to 80)
      \nCondition: date_of_birth>date(1937, 12, 31) and date_of_birth<date(1958, 01, 01)

    2. Applying tax by employee gender
      \nCondition: gender==\"Male\"

    3. \n
    4. Applying tax by Salary Component
      \nCondition: base > 10000
    ", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-06-19 10:10:23.732132", - "modified_by": "Administrator", - "module": "HR", - "name": "Taxable Salary Slab", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 -} diff --git a/erpnext/hr/doctype/training_event/test_training_event.py b/erpnext/hr/doctype/training_event/test_training_event.py index 57123e304f5..313f90eba85 100644 --- a/erpnext/hr/doctype/training_event/test_training_event.py +++ b/erpnext/hr/doctype/training_event/test_training_event.py @@ -6,7 +6,7 @@ from __future__ import unicode_literals import frappe import unittest from frappe.utils import today, add_days -from erpnext.hr.doctype.salary_structure.test_salary_structure import make_employee +from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_employee class TestTrainingEvent(unittest.TestCase): def setUp(self): diff --git a/erpnext/hr/report/salary_register/salary_register.json b/erpnext/hr/report/salary_register/salary_register.json deleted file mode 100644 index 89a7ba29101..00000000000 --- a/erpnext/hr/report/salary_register/salary_register.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "add_total_row": 1, - "apply_user_permissions": 1, - "creation": "2017-01-10 17:36:58.153863", - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 2, - "is_standard": "Yes", - "modified": "2017-02-24 19:58:33.143974", - "modified_by": "Administrator", - "module": "HR", - "name": "Salary Register", - "owner": "Administrator", - "ref_doctype": "Salary Slip", - "report_name": "Salary Register", - "report_type": "Script Report", - "roles": [ - { - "role": "HR User" - }, - { - "role": "HR Manager" - } - ] -} \ No newline at end of file diff --git a/erpnext/loan_management/doctype/loan/test_loan.py b/erpnext/loan_management/doctype/loan/test_loan.py index 364e2ffecf8..3f37a26418b 100644 --- a/erpnext/loan_management/doctype/loan/test_loan.py +++ b/erpnext/loan_management/doctype/loan/test_loan.py @@ -9,7 +9,7 @@ import unittest from frappe.utils import (nowdate, add_days, getdate, now_datetime, add_to_date, get_datetime, add_months, get_first_day, get_last_day, flt, date_diff) from erpnext.selling.doctype.customer.test_customer import get_customer_dict -from erpnext.hr.doctype.salary_structure.test_salary_structure import make_employee +from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_employee from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import (process_loan_interest_accrual_for_demand_loans, process_loan_interest_accrual_for_term_loans) from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import days_in_year diff --git a/erpnext/loan_management/doctype/loan_application/test_loan_application.py b/erpnext/loan_management/doctype/loan_application/test_loan_application.py index 99c807b2cd3..687c58000e2 100644 --- a/erpnext/loan_management/doctype/loan_application/test_loan_application.py +++ b/erpnext/loan_management/doctype/loan_application/test_loan_application.py @@ -5,7 +5,7 @@ from __future__ import unicode_literals import frappe import unittest -from erpnext.hr.doctype.salary_structure.test_salary_structure import make_employee +from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_employee from erpnext.loan_management.doctype.loan.test_loan import create_loan_type, create_loan_accounts class TestLoanApplication(unittest.TestCase): diff --git a/erpnext/modules.txt b/erpnext/modules.txt index 3b347582c35..1e2aeea36a8 100644 --- a/erpnext/modules.txt +++ b/erpnext/modules.txt @@ -24,4 +24,5 @@ Hotels Hub Node Quality Management Communication -Loan Management \ No newline at end of file +Loan Management +Payroll \ No newline at end of file diff --git a/erpnext/patches.txt b/erpnext/patches.txt index a7395a476a6..b17cc474447 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -333,7 +333,7 @@ erpnext.patches.v7_0.update_mode_of_payment_type execute:frappe.reload_doctype('Employee') #2016-10-18 execute:frappe.db.sql("update `tabEmployee` set prefered_contact_email = IFNULL(prefered_contact_email,'') ") -execute:frappe.reload_doctype("Salary Slip") +execute:frappe.reload_doc("Payroll", "doctype", "salary_slip") execute:frappe.db.sql("update `tabSalary Slip` set posting_date=creation") execute:frappe.reload_doc("stock", "doctype", "stock_settings") erpnext.patches.v8_0.create_domain_docs #16-05-2017 @@ -701,4 +701,7 @@ erpnext.patches.v13_0.update_sla_enhancements erpnext.patches.v12_0.update_address_template_for_india erpnext.patches.v12_0.set_multi_uom_in_rfq erpnext.patches.v13_0.delete_old_sales_reports -execute:frappe.delete_doc_if_exists("DocType", "Bank Reconciliation") \ No newline at end of file +execute:frappe.delete_doc_if_exists("DocType", "Bank Reconciliation") +erpnext.patches.v13_0.move_doctype_reports_and_notification_from_hr_to_payroll +erpnext.patches.v13_0.move_payroll_setting_separately_from_hr_settings +erpnext.patches.v13_0.check_is_income_tax_component diff --git a/erpnext/patches/v11_0/create_department_records_for_each_company.py b/erpnext/patches/v11_0/create_department_records_for_each_company.py index f09c5b2c5df..e9b5950a133 100644 --- a/erpnext/patches/v11_0/create_department_records_for_each_company.py +++ b/erpnext/patches/v11_0/create_department_records_for_each_company.py @@ -6,8 +6,9 @@ from frappe.utils.nestedset import rebuild_tree def execute(): frappe.local.lang = frappe.db.get_default("lang") or 'en' - for doctype in ['department', 'leave_period', 'staffing_plan', 'job_opening', 'payroll_entry']: + for doctype in ['department', 'leave_period', 'staffing_plan', 'job_opening']: frappe.reload_doc("hr", "doctype", doctype) + frappe.reload_doc("Payroll", "doctype", 'payroll_entry') companies = frappe.db.get_all("Company", fields=["name", "abbr"]) departments = frappe.db.get_all("Department") diff --git a/erpnext/patches/v11_0/create_salary_structure_assignments.py b/erpnext/patches/v11_0/create_salary_structure_assignments.py index 610fa85172f..c51c38182cc 100644 --- a/erpnext/patches/v11_0/create_salary_structure_assignments.py +++ b/erpnext/patches/v11_0/create_salary_structure_assignments.py @@ -5,11 +5,11 @@ from __future__ import unicode_literals import frappe from datetime import datetime from frappe.utils import getdate -from erpnext.hr.doctype.salary_structure_assignment.salary_structure_assignment import DuplicateAssignment +from erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment import DuplicateAssignment def execute(): - frappe.reload_doc('hr', 'doctype', 'salary_structure') - frappe.reload_doc("hr", "doctype", "salary_structure_assignment") + frappe.reload_doc('Payroll', 'doctype', 'salary_structure') + frappe.reload_doc("Payroll", "doctype", "salary_structure_assignment") frappe.db.sql(""" delete from `tabSalary Structure Assignment` where salary_structure in (select name from `tabSalary Structure` where is_active='No' or docstatus!=1) diff --git a/erpnext/patches/v11_0/inter_state_field_for_gst.py b/erpnext/patches/v11_0/inter_state_field_for_gst.py index 48249c9bbf5..730eebc01ce 100644 --- a/erpnext/patches/v11_0/inter_state_field_for_gst.py +++ b/erpnext/patches/v11_0/inter_state_field_for_gst.py @@ -6,8 +6,8 @@ def execute(): company = frappe.get_all('Company', filters = {'country': 'India'}) if not company: return - frappe.reload_doc("hr", "doctype", "Employee Tax Exemption Declaration") - frappe.reload_doc("hr", "doctype", "Employee Tax Exemption Proof Submission") + frappe.reload_doc("Payroll", "doctype", "Employee Tax Exemption Declaration") + frappe.reload_doc("Payroll", "doctype", "Employee Tax Exemption Proof Submission") frappe.reload_doc("hr", "doctype", "Employee Grade") frappe.reload_doc("hr", "doctype", "Leave Policy") diff --git a/erpnext/patches/v11_0/set_salary_component_properties.py b/erpnext/patches/v11_0/set_salary_component_properties.py index 83fb53d2a73..2498888273d 100644 --- a/erpnext/patches/v11_0/set_salary_component_properties.py +++ b/erpnext/patches/v11_0/set_salary_component_properties.py @@ -2,8 +2,8 @@ from __future__ import unicode_literals import frappe def execute(): - frappe.reload_doc('hr', 'doctype', 'salary_detail') - frappe.reload_doc('hr', 'doctype', 'salary_component') + frappe.reload_doc('Payroll', 'doctype', 'salary_detail') + frappe.reload_doc('Payroll', 'doctype', 'salary_component') frappe.db.sql("update `tabSalary Component` set is_tax_applicable=1 where type='Earning'") diff --git a/erpnext/patches/v11_1/rename_depends_on_lwp.py b/erpnext/patches/v11_1/rename_depends_on_lwp.py index 20d8867e1d2..a0f2536f7d8 100644 --- a/erpnext/patches/v11_1/rename_depends_on_lwp.py +++ b/erpnext/patches/v11_1/rename_depends_on_lwp.py @@ -9,5 +9,5 @@ from frappe.model.utils.rename_field import rename_field def execute(): for doctype in ("Salary Component", "Salary Detail"): if "depends_on_lwp" in frappe.db.get_table_columns(doctype): - frappe.reload_doc("hr", "doctype", scrub(doctype)) + frappe.reload_doc("Payroll", "doctype", scrub(doctype)) rename_field(doctype, "depends_on_lwp", "depends_on_payment_days") \ No newline at end of file diff --git a/erpnext/patches/v13_0/check_is_income_tax_component.py b/erpnext/patches/v13_0/check_is_income_tax_component.py new file mode 100644 index 00000000000..f69412c5383 --- /dev/null +++ b/erpnext/patches/v13_0/check_is_income_tax_component.py @@ -0,0 +1,24 @@ +# Copyright (c) 2019, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals + +import frappe, erpnext + +def execute(): + frappe.reload_doc('Payroll', 'doctype', 'salary_structure') + + if frappe.db.exists("Salary Component", "Income Tax"): + frappe.db.set_value("Salary Component", "Income Tax", "is_income_tax_component", 1) + if frappe.db.exists("Salary Component", "TDS"): + frappe.db.set_value("Salary Component", "TDS", "is_income_tax_component", 1) + + components = frappe.db.sql("select name from `tabSalary Component` where variable_based_on_taxable_salary = 1", as_dict=1) + for component in components: + frappe.db.set_value("Salary Component", component.name, "is_income_tax_component", 1) + + if erpnext.get_region() == "India": + if frappe.db.exists("Salary Component", "Provident Fund"): + frappe.db.set_value("Salary Component", "Provident Fund", "component_type", "Provident Fund") + if frappe.db.exists("Salary Component", "Professional Tax"): + frappe.db.set_value("Salary Component", "Professional Tax", "component_type", "Professional Tax") \ No newline at end of file diff --git a/erpnext/patches/v13_0/move_doctype_reports_and_notification_from_hr_to_payroll.py b/erpnext/patches/v13_0/move_doctype_reports_and_notification_from_hr_to_payroll.py new file mode 100644 index 00000000000..4d7c85ce2d1 --- /dev/null +++ b/erpnext/patches/v13_0/move_doctype_reports_and_notification_from_hr_to_payroll.py @@ -0,0 +1,52 @@ +# Copyright (c) 2019, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals + +import frappe + +def execute(): + frappe.db.sql("""UPDATE `tabPrint Format` + SET module = 'Payroll' + WHERE name IN ('Salary Slip Based On Timesheet', 'Salary Slip Standard')""" + ) + + frappe.db.sql("""UPDATE `tabNotification` SET module='Payroll' WHERE name='Retention Bonus';""" + ) + + doctypes_moved = [ + 'Employee Benefit Application Detail', + 'Employee Tax Exemption Declaration Category', + 'Salary Component', + 'Employee Tax Exemption Proof Submission Detail', + 'Income Tax Slab Other Charges', + 'Taxable Salary Slab', + 'Payroll Period Date', + 'Salary Slip Timesheet', + 'Payroll Employee Detail', + 'Salary Detail', + 'Employee Tax Exemption Sub Category', + 'Employee Tax Exemption Category', + 'Employee Benefit Claim', + 'Employee Benefit Application', + 'Employee Other Income', + 'Employee Tax Exemption Proof Submission', + 'Employee Tax Exemption Declaration', + 'Employee Incentive', + 'Retention Bonus', + 'Additional Salary', + 'Income Tax Slab', + 'Payroll Period', + 'Salary Slip', + 'Payroll Entry', + 'Salary Structure Assignment', + 'Salary Structure' + ] + + for doctype in doctypes_moved: + frappe.delete_doc_if_exists("DocType", doctype) + + reports = ["Salary Register", "Bank Remittance"] + + for report in reports: + frappe.delete_doc_if_exists("Report", report) diff --git a/erpnext/patches/v13_0/move_payroll_setting_separately_from_hr_settings.py b/erpnext/patches/v13_0/move_payroll_setting_separately_from_hr_settings.py new file mode 100644 index 00000000000..a901064b889 --- /dev/null +++ b/erpnext/patches/v13_0/move_payroll_setting_separately_from_hr_settings.py @@ -0,0 +1,27 @@ +# Copyright (c) 2019, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals + +import frappe + +def execute(): + data = frappe.db.sql('''SELECT * + FROM `tabSingles` + WHERE + doctype = "HR Settings" + AND + field in ( + "encrypt_salary_slips_in_emails", + "email_salary_slip_to_employee", + "daily_wages_fraction_for_half_day", + "disable_rounded_total", + "include_holidays_in_total_working_days", + "max_working_hours_against_timesheet", + "payroll_based_on", + "password_policy" + ) + ''', as_dict=1) + + for d in data: + frappe.db.set_value("Payroll Settings", None, d.field, d.value) diff --git a/erpnext/patches/v13_0/move_tax_slabs_from_payroll_period_to_income_tax_slab.py b/erpnext/patches/v13_0/move_tax_slabs_from_payroll_period_to_income_tax_slab.py index 5ade8ca0f4c..1a91d218ba3 100644 --- a/erpnext/patches/v13_0/move_tax_slabs_from_payroll_period_to_income_tax_slab.py +++ b/erpnext/patches/v13_0/move_tax_slabs_from_payroll_period_to_income_tax_slab.py @@ -11,7 +11,7 @@ def execute(): return for doctype in ("income_tax_slab", "salary_structure_assignment", "employee_other_income", "income_tax_slab_other_charges"): - frappe.reload_doc("hr", "doctype", doctype) + frappe.reload_doc("Payroll", "doctype", doctype) standard_tax_exemption_amount_exists = frappe.db.has_column("Payroll Period", "standard_tax_exemption_amount") @@ -29,7 +29,7 @@ def execute(): WHERE company=%s ORDER BY start_date DESC """.format(select_fields), company.name, as_dict = 1) - + for i, period in enumerate(payroll_periods): income_tax_slab = frappe.new_doc("Income Tax Slab") income_tax_slab.name = "Tax Slab:" + period.name diff --git a/erpnext/patches/v13_0/patch_to_fix_reverse_linking_in_additional_salary_encashment_and_incentive.py b/erpnext/patches/v13_0/patch_to_fix_reverse_linking_in_additional_salary_encashment_and_incentive.py index ddcadcb4de9..fde8f864703 100644 --- a/erpnext/patches/v13_0/patch_to_fix_reverse_linking_in_additional_salary_encashment_and_incentive.py +++ b/erpnext/patches/v13_0/patch_to_fix_reverse_linking_in_additional_salary_encashment_and_incentive.py @@ -6,8 +6,10 @@ def execute(): if not frappe.db.table_exists("Additional Salary"): return - for doctype in ("Additional Salary", "Leave Encashment", "Employee Incentive", "Salary Detail"): - frappe.reload_doc("hr", "doctype", doctype) + for doctype in ("Additional Salary", "Employee Incentive", "Salary Detail"): + frappe.reload_doc("Payroll", "doctype", doctype) + + frappe.reload_doc("hr", "doctype", "Leave Encashment") additional_salaries = frappe.get_all("Additional Salary", fields = ['name', "salary_slip", "type", "salary_component"], diff --git a/erpnext/patches/v4_0/create_custom_fields_for_india_specific_fields.py b/erpnext/patches/v4_0/create_custom_fields_for_india_specific_fields.py index 9c602a3f782..fe50e444b52 100644 --- a/erpnext/patches/v4_0/create_custom_fields_for_india_specific_fields.py +++ b/erpnext/patches/v4_0/create_custom_fields_for_india_specific_fields.py @@ -8,7 +8,7 @@ from frappe.custom.doctype.custom_field.custom_field import create_custom_field_ def execute(): frappe.reload_doc("stock", "doctype", "purchase_receipt") frappe.reload_doc("hr", "doctype", "employee") - frappe.reload_doc("hr", "doctype", "salary_slip") + frappe.reload_doc("Payroll", "doctype", "salary_slip") india_specific_fields = { "Purchase Receipt": [{ diff --git a/erpnext/patches/v5_0/rename_table_fieldnames.py b/erpnext/patches/v5_0/rename_table_fieldnames.py index 59f534303f1..aefb0a20372 100644 --- a/erpnext/patches/v5_0/rename_table_fieldnames.py +++ b/erpnext/patches/v5_0/rename_table_fieldnames.py @@ -220,7 +220,7 @@ def execute(): frappe.reload_doc("manufacturing", "doctype", "work_order_operation") frappe.reload_doc("manufacturing", "doctype", "workstation_working_hour") frappe.reload_doc("stock", "doctype", "item_variant") - frappe.reload_doc("hr", "doctype", "salary_detail") + frappe.reload_doc("Payroll", "doctype", "salary_detail") frappe.reload_doc("accounts", "doctype", "party_account") frappe.reload_doc("accounts", "doctype", "fiscal_year_company") diff --git a/erpnext/patches/v7_0/rename_salary_components.py b/erpnext/patches/v7_0/rename_salary_components.py index bc48e343174..1693f3bdf1f 100644 --- a/erpnext/patches/v7_0/rename_salary_components.py +++ b/erpnext/patches/v7_0/rename_salary_components.py @@ -6,8 +6,8 @@ def execute(): if not frappe.db.exists("DocType", "Salary Structure Earning"): return - frappe.reload_doc("hr", "doctype", "salary_detail") - frappe.reload_doc("hr", "doctype", "salary_component") + frappe.reload_doc("Payroll", "doctype", "salary_detail") + frappe.reload_doc("Payroll", "doctype", "salary_component") standard_cols = ["name", "creation", "modified", "owner", "modified_by", "parent", "parenttype", "parentfield", "idx"] diff --git a/erpnext/patches/v7_1/update_component_type.py b/erpnext/patches/v7_1/update_component_type.py index 552fc894676..24ca0570e01 100644 --- a/erpnext/patches/v7_1/update_component_type.py +++ b/erpnext/patches/v7_1/update_component_type.py @@ -3,7 +3,7 @@ import frappe from frappe.utils import flt def execute(): - frappe.reload_doc('hr', 'doctype', 'salary_component') + frappe.reload_doc('Payroll', 'doctype', 'salary_component') sal_components = frappe.db.sql(""" select DISTINCT salary_component, parentfield from `tabSalary Detail`""", as_dict=True) diff --git a/erpnext/patches/v7_1/update_missing_salary_component_type.py b/erpnext/patches/v7_1/update_missing_salary_component_type.py index 7d50ee4dff7..824f2b881f0 100644 --- a/erpnext/patches/v7_1/update_missing_salary_component_type.py +++ b/erpnext/patches/v7_1/update_missing_salary_component_type.py @@ -10,12 +10,12 @@ earnings or deductions in existing salary slips def execute(): frappe.reload_doc("accounts", "doctype", "salary_component_account") - frappe.reload_doc("hr", "doctype", "salary_component") - frappe.reload_doc("hr", "doctype", "taxable_salary_slab") - - for s in frappe.db.sql('''select name, type, salary_component_abbr from `tabSalary Component` + frappe.reload_doc("Payroll", "doctype", "salary_component") + frappe.reload_doc("Payroll", "doctype", "taxable_salary_slab") + + for s in frappe.db.sql('''select name, type, salary_component_abbr from `tabSalary Component` where ifnull(type, "")="" or ifnull(salary_component_abbr, "") = ""''', as_dict=1): - + component = frappe.get_doc('Salary Component', s.name) # guess @@ -29,22 +29,22 @@ def execute(): else: component.type = 'Deduction' - + if not s.salary_component_abbr: abbr = ''.join([c[0] for c in component.salary_component.split()]).upper() - + abbr_count = frappe.db.sql(""" - select - count(name) - from - `tabSalary Component` - where + select + count(name) + from + `tabSalary Component` + where salary_component_abbr = %s or salary_component_abbr like %s """, (abbr, abbr + "-%%")) - + if abbr_count and abbr_count[0][0] > 0: abbr = abbr + "-" + cstr(abbr_count[0][0]) - + component.salary_component_abbr = abbr - + component.save() diff --git a/erpnext/patches/v7_2/arrear_leave_encashment_as_salary_component.py b/erpnext/patches/v7_2/arrear_leave_encashment_as_salary_component.py index 3b9642dd3b8..d2583b94224 100644 --- a/erpnext/patches/v7_2/arrear_leave_encashment_as_salary_component.py +++ b/erpnext/patches/v7_2/arrear_leave_encashment_as_salary_component.py @@ -2,7 +2,9 @@ from __future__ import unicode_literals import frappe def execute(): - frappe.reload_doctype('Salary Slip', 'Salary Component') + # frappe.reload_doctype('Salary Slip', 'Salary Component') + frappe.reload_doc("Payroll", "doctype", "Salary Slip") + frappe.reload_doc("Payroll", "doctype", "Salary Component") salary_components = [['Arrear', "ARR"], ['Leave Encashment', 'LENC']] for salary_component, salary_abbr in salary_components: if not frappe.db.exists('Salary Component', salary_component): diff --git a/erpnext/patches/v7_2/update_abbr_in_salary_slips.py b/erpnext/patches/v7_2/update_abbr_in_salary_slips.py index 19dcb5e3b22..57432fe9861 100644 --- a/erpnext/patches/v7_2/update_abbr_in_salary_slips.py +++ b/erpnext/patches/v7_2/update_abbr_in_salary_slips.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals import frappe def execute(): - frappe.reload_doctype('Salary Slip') + frappe.reload_doc('Payroll', 'doctype', 'Salary Slip') if not frappe.db.has_column('Salary Detail', 'abbr'): return diff --git a/erpnext/patches/v7_2/update_salary_slips.py b/erpnext/patches/v7_2/update_salary_slips.py index 11a52f95876..9fcce62d8ff 100644 --- a/erpnext/patches/v7_2/update_salary_slips.py +++ b/erpnext/patches/v7_2/update_salary_slips.py @@ -1,10 +1,10 @@ from __future__ import unicode_literals import frappe -from erpnext.hr.doctype.payroll_entry.payroll_entry import get_month_details +from erpnext.payroll.doctype.payroll_entry.payroll_entry import get_month_details from frappe.utils import cint def execute(): - frappe.reload_doctype('Salary Slip') + frappe.reload_doc("Payroll", "doctype", "Salary Slip") if not frappe.db.has_column('Salary Slip', 'fiscal_year'): return diff --git a/erpnext/patches/v8_7/sync_india_custom_fields.py b/erpnext/patches/v8_7/sync_india_custom_fields.py index 73e1b182e82..eb24a90f013 100644 --- a/erpnext/patches/v8_7/sync_india_custom_fields.py +++ b/erpnext/patches/v8_7/sync_india_custom_fields.py @@ -7,11 +7,11 @@ def execute(): if not company: return - frappe.reload_doc('hr', 'doctype', 'payroll_period') - frappe.reload_doc('hr', 'doctype', 'employee_tax_exemption_declaration') - frappe.reload_doc('hr', 'doctype', 'employee_tax_exemption_proof_submission') - frappe.reload_doc('hr', 'doctype', 'employee_tax_exemption_declaration_category') - frappe.reload_doc('hr', 'doctype', 'employee_tax_exemption_proof_submission_detail') + frappe.reload_doc('Payroll', 'doctype', 'payroll_period') + frappe.reload_doc('Payroll', 'doctype', 'employee_tax_exemption_declaration') + frappe.reload_doc('Payroll', 'doctype', 'employee_tax_exemption_proof_submission') + frappe.reload_doc('Payroll', 'doctype', 'employee_tax_exemption_declaration_category') + frappe.reload_doc('Payroll', 'doctype', 'employee_tax_exemption_proof_submission_detail') frappe.reload_doc('accounts', 'doctype', 'tax_category') diff --git a/erpnext/patches/v9_0/update_employee_loan_details.py b/erpnext/patches/v9_0/update_employee_loan_details.py index 86690fc1f83..ef8d32855fb 100644 --- a/erpnext/patches/v9_0/update_employee_loan_details.py +++ b/erpnext/patches/v9_0/update_employee_loan_details.py @@ -5,8 +5,8 @@ from __future__ import unicode_literals import frappe def execute(): - frappe.reload_doc('hr', 'doctype', 'salary_slip_loan') - frappe.reload_doc('hr', 'doctype', 'salary_slip') + frappe.reload_doc('Payroll', 'doctype', 'salary_slip_loan') + frappe.reload_doc('Payroll', 'doctype', 'salary_slip') for data in frappe.db.sql(""" select name, start_date, end_date, total_loan_repayment diff --git a/erpnext/hr/doctype/additional_salary/__init__.py b/erpnext/payroll/__init__.py similarity index 100% rename from erpnext/hr/doctype/additional_salary/__init__.py rename to erpnext/payroll/__init__.py diff --git a/erpnext/payroll/dashboard_fixtures.py b/erpnext/payroll/dashboard_fixtures.py new file mode 100644 index 00000000000..ae7a9ff51af --- /dev/null +++ b/erpnext/payroll/dashboard_fixtures.py @@ -0,0 +1,100 @@ +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +import frappe +import erpnext +from erpnext.hr.dashboard_fixtures import get_dashboards_chart_doc, get_number_cards_doc +import json +from frappe import _ + +def get_data(): + return frappe._dict({ + "dashboards": get_dashboards(), + "charts": get_charts(), + "number_cards": get_number_cards(), + }) + +def get_dashboards(): + dashboards = [] + dashboards.append(get_payroll_dashboard()) + return dashboards + +def get_payroll_dashboard(): + return { + "name": "Payroll", + "dashboard_name": "Payroll", + "is_default": 1, + "charts": [ + { "chart": "Outgoing Salary", "width": "Full"}, + { "chart": "Designation Wise Salary(Last Month)", "width": "Half"}, + { "chart": "Department Wise Salary(Last Month)", "width": "Half"}, + ], + "cards": [ + {"card": "Total Declaration Submitted"}, + {"card": "Total Salary Structure"}, + {"card": "Total Incentive Given(Last month)"}, + {"card": "Total Outgoing Salary(Last month)"}, + ] + } + +def get_charts(): + dashboard_charts= [ + get_dashboards_chart_doc('Outgoing Salary', "Sum", "Line", + document_type = "Salary Slip", based_on="end_date", + value_based_on = "rounded_total", time_interval = "Monthly", timeseries = 1, + filters_json = json.dumps([["Salary Slip", "docstatus", "=", 1]])) + ] + + dashboard_charts.append( + get_dashboards_chart_doc('Department Wise Salary(Last Month)', "Group By", "Bar", + document_type = "Salary Slip", group_by_type="Sum", group_by_based_on="department", + time_interval = "Monthly", aggregate_function_based_on = "rounded_total", + filters_json = json.dumps([ + ["Salary Slip", "docstatus", "=", 1], + ["Salary Slip", "start_date", "Previous","1 month"] + ]) + ) + ) + + dashboard_charts.append( + get_dashboards_chart_doc('Designation Wise Salary(Last Month)', "Group By", "Bar", + document_type = "Salary Slip", group_by_type="Sum", group_by_based_on="designation", + time_interval = "Monthly", aggregate_function_based_on = "rounded_total", + filters_json = json.dumps([ + ["Salary Slip", "docstatus", "=", 1], + ["Salary Slip", "start_date", "Previous","1 month"] + ]) + ) + ) + + return dashboard_charts + +def get_number_cards(): + number_cards = [get_number_cards_doc("Employee Tax Exemption Declaration", "Total Declaration Submitted", filters_json = json.dumps([ + ["Employee Tax Exemption Declaration", "docstatus", "=","1"], + ["Employee Tax Exemption Declaration","creation","Previous","1 year"] + ]) + )] + + number_cards.append(get_number_cards_doc("Employee Incentive", "Total Incentive Given(Last month)", + time_interval = "Monthly", func = "Sum", aggregate_function_based_on = "incentive_amount", + filters_json = json.dumps([ + ["Employee Incentive", "docstatus", "=", 1], + ["Employee Incentive","payroll_date","Previous","1 year"] + ])) + ) + + number_cards.append(get_number_cards_doc("Salary Slip", "Total Outgoing Salary(Last month)", + time_interval = "Monthly", time_span= "Monthly", func = "Sum", aggregate_function_based_on = "rounded_total", + filters_json = json.dumps([ + ["Salary Slip", "docstatus", "=", 1], + ["Salary Slip", "start_date","Previous","1 month"] + ])) + ) + number_cards.append(get_number_cards_doc("Salary Structure", "Total Salary Structure", + filters_json = json.dumps([ + ["Salary Structure", "docstatus", "=", 1] + ])) + ) + + return number_cards \ No newline at end of file diff --git a/erpnext/payroll/desk_page/payroll/payroll.json b/erpnext/payroll/desk_page/payroll/payroll.json new file mode 100644 index 00000000000..b5eac465c82 --- /dev/null +++ b/erpnext/payroll/desk_page/payroll/payroll.json @@ -0,0 +1,84 @@ +{ + "cards": [ + { + "hidden": 0, + "label": "Payroll", + "links": "[\n {\n \"label\": \"Salary Component\",\n \"name\": \"Salary Component\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n \n },\n {\n \"label\": \"Salary Structure\",\n \"name\": \"Salary Structure\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Salary Structure Assignment\",\n \"name\": \"Salary Structure Assignment\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Payroll Entry\",\n \"name\": \"Payroll Entry\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Salary Slip\",\n \"name\": \"Salary Slip\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n }\n]" + }, + { + "hidden": 0, + "label": "Taxation", + "links": "[\n {\n \"label\": \"Payroll Period\",\n \"name\": \"Payroll Period\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n \n },\n {\n \"label\": \"Income Tax Slab\",\n \"name\": \"Income Tax Slab\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n \n },\n {\n \"label\": \"Employee Tax Exemption Declaration\",\n \"name\": \"Employee Tax Exemption Declaration\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n \n },\n {\n \"label\": \"Employee Tax Exemption Proof Submission\",\n \"name\": \"Employee Tax Exemption Proof Submission\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n \n },\n {\n \"label\": \"Employee Tax Exemption Category\",\n \"name\": \"Employee Tax Exemption Category\",\n \"type\": \"doctype\"\n \n },\n {\n \"label\": \"Employee Tax Exemption Sub Category\",\n \"name\": \"Employee Tax Exemption Sub Category\",\n \"type\": \"doctype\"\n \n }\n]" + }, + { + "hidden": 0, + "label": "Compensations", + "links": "[\n {\n \"label\": \"Additional Salary\",\n \"name\": \"Additional Salary\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n \n },\n {\n \"label\": \"Retention Bonus\",\n \"name\": \"Retention Bonus\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Employee Incentive\",\n \"name\": \"Employee Incentive\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Employee Benefit Application\",\n \"name\": \"Employee Benefit Application\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Employee Benefit Claim\",\n \"name\": \"Employee Benefit Claim\",\n \"type\": \"doctype\"\n }\n]" + }, + { + "hidden": 0, + "label": "Reports", + "links": "[\n {\n \"dependencies\": [\n \"Salary Slip\"\n ],\n \"doctype\": \"Salary Slip\",\n \"is_query_report\": true,\n \"label\": \"Salary Register\",\n \"name\": \"Salary Register\",\n \"type\": \"report\"\n \n },\n {\n \"dependencies\": [\n \"Salary Slip\"\n ],\n \"doctype\": \"Salary Slip\",\n \"label\": \"Salary Payments Based On Payment Mode\",\n \"is_query_report\": true,\n \"name\": \"Salary Payments Based On Payment Mode\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Salary Slip\"\n ],\n \"doctype\": \"Salary Slip\",\n \"label\": \"Salary Payments via ECS\",\n \"is_query_report\": true,\n \"name\": \"Salary Payments via ECS\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Salary Slip\"\n ],\n \"doctype\": \"Salary Slip\",\n \"label\": \"Income Tax Deductions\",\n \"is_query_report\": true,\n \"name\": \"Income Tax Deductions\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Salary Slip\"\n ],\n \"doctype\": \"Salary Slip\",\n \"label\": \"Professional Tax Deductions\",\n \"is_query_report\": true,\n \"name\": \"Professional Tax Deductions\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Salary Slip\"\n ],\n \"doctype\": \"Salary Slip\",\n \"label\": \"Provident Fund Deductions\",\n \"is_query_report\": true,\n \"name\": \"Provident Fund Deductions\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Payroll Entry\"\n ],\n \"doctype\": \"Payroll Entry\",\n \"is_query_report\": true,\n \"label\": \"Bank Remittance\",\n \"name\": \"Bank Remittance\",\n \"type\": \"report\"\n \n }\n]" + } + ], + "category": "Modules", + "charts": [ + { + "chart_name": "Outgoing Salary", + "label": "Outgoing Salary" + } + ], + "creation": "2020-05-27 19:54:23.405607", + "developer_mode_only": 0, + "disable_user_customization": 0, + "docstatus": 0, + "doctype": "Desk Page", + "extends_another_page": 0, + "hide_custom": 0, + "idx": 0, + "is_standard": 1, + "label": "Payroll", + "modified": "2020-06-19 12:23:06.034046", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Payroll", + "onboarding": "Payroll", + "owner": "Administrator", + "pin_to_bottom": 0, + "pin_to_top": 0, + "shortcuts": [ + { + "label": "Salary Structure", + "link_to": "Salary Structure", + "type": "DocType" + }, + { + "label": "Payroll Entry", + "link_to": "Payroll Entry", + "type": "DocType" + }, + { + "color": "", + "format": "{} Pending", + "label": "Salary Slip", + "link_to": "Salary Slip", + "stats_filter": "{\"status\": \"Draft\"}", + "type": "DocType" + }, + { + "label": "Income Tax Slab", + "link_to": "Income Tax Slab", + "type": "DocType" + }, + { + "label": "Salary Register", + "link_to": "Salary Register", + "type": "Report" + }, + { + "label": "Dashboard", + "link_to": "Payroll", + "type": "Dashboard" + } + ] +} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_benefit_application/__init__.py b/erpnext/payroll/doctype/__init__.py similarity index 100% rename from erpnext/hr/doctype/employee_benefit_application/__init__.py rename to erpnext/payroll/doctype/__init__.py diff --git a/erpnext/hr/doctype/employee_benefit_application_detail/__init__.py b/erpnext/payroll/doctype/additional_salary/__init__.py similarity index 100% rename from erpnext/hr/doctype/employee_benefit_application_detail/__init__.py rename to erpnext/payroll/doctype/additional_salary/__init__.py diff --git a/erpnext/hr/doctype/additional_salary/additional_salary.js b/erpnext/payroll/doctype/additional_salary/additional_salary.js similarity index 100% rename from erpnext/hr/doctype/additional_salary/additional_salary.js rename to erpnext/payroll/doctype/additional_salary/additional_salary.js diff --git a/erpnext/hr/doctype/additional_salary/additional_salary.json b/erpnext/payroll/doctype/additional_salary/additional_salary.json similarity index 98% rename from erpnext/hr/doctype/additional_salary/additional_salary.json rename to erpnext/payroll/doctype/additional_salary/additional_salary.json index bfb543f49ad..ad64289f0d2 100644 --- a/erpnext/hr/doctype/additional_salary/additional_salary.json +++ b/erpnext/payroll/doctype/additional_salary/additional_salary.json @@ -146,7 +146,7 @@ "label": "To Date", "mandatory_depends_on": "eval:(doc.is_recurring==1)" }, - { + { "fieldname": "ref_doctype", "fieldtype": "Link", "label": "Reference Document Type", @@ -163,9 +163,9 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-04-04 18:06:29.170878", + "modified": "2020-05-27 21:10:50.374063", "modified_by": "Administrator", - "module": "HR", + "module": "Payroll", "name": "Additional Salary", "owner": "Administrator", "permissions": [ diff --git a/erpnext/hr/doctype/additional_salary/additional_salary.py b/erpnext/payroll/doctype/additional_salary/additional_salary.py similarity index 100% rename from erpnext/hr/doctype/additional_salary/additional_salary.py rename to erpnext/payroll/doctype/additional_salary/additional_salary.py diff --git a/erpnext/hr/doctype/additional_salary/test_additional_salary.js b/erpnext/payroll/doctype/additional_salary/test_additional_salary.js similarity index 100% rename from erpnext/hr/doctype/additional_salary/test_additional_salary.js rename to erpnext/payroll/doctype/additional_salary/test_additional_salary.js diff --git a/erpnext/hr/doctype/additional_salary/test_additional_salary.py b/erpnext/payroll/doctype/additional_salary/test_additional_salary.py similarity index 87% rename from erpnext/hr/doctype/additional_salary/test_additional_salary.py rename to erpnext/payroll/doctype/additional_salary/test_additional_salary.py index 6f93fb5df8d..de26543b571 100644 --- a/erpnext/hr/doctype/additional_salary/test_additional_salary.py +++ b/erpnext/payroll/doctype/additional_salary/test_additional_salary.py @@ -6,8 +6,8 @@ import unittest import frappe, erpnext from frappe.utils import nowdate, add_days from erpnext.hr.doctype.employee.test_employee import make_employee -from erpnext.hr.doctype.salary_component.test_salary_component import create_salary_component -from erpnext.hr.doctype.salary_slip.test_salary_slip import make_employee_salary_slip, setup_test +from erpnext.payroll.doctype.salary_component.test_salary_component import create_salary_component +from erpnext.payroll.doctype.salary_slip.test_salary_slip import make_employee_salary_slip, setup_test class TestAdditionalSalary(unittest.TestCase): diff --git a/erpnext/hr/doctype/employee_benefit_claim/__init__.py b/erpnext/payroll/doctype/employee_benefit_application/__init__.py similarity index 100% rename from erpnext/hr/doctype/employee_benefit_claim/__init__.py rename to erpnext/payroll/doctype/employee_benefit_application/__init__.py diff --git a/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.js b/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.js similarity index 83% rename from erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.js rename to erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.js index b73dcf8ac36..f509df31e83 100644 --- a/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.js +++ b/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.js @@ -6,7 +6,7 @@ frappe.ui.form.on('Employee Benefit Application', { frm.trigger('set_earning_component'); var method, args; if(frm.doc.employee && frm.doc.date && frm.doc.payroll_period){ - method = "erpnext.hr.doctype.employee_benefit_application.employee_benefit_application.get_max_benefits_remaining"; + method = "erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application.get_max_benefits_remaining"; args = { employee: frm.doc.employee, on_date: frm.doc.date, @@ -15,7 +15,7 @@ frappe.ui.form.on('Employee Benefit Application', { get_max_benefits(frm, method, args); } else if(frm.doc.employee && frm.doc.date){ - method = "erpnext.hr.doctype.employee_benefit_application.employee_benefit_application.get_max_benefits"; + method = "erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application.get_max_benefits"; args = { employee: frm.doc.employee, on_date: frm.doc.date @@ -32,7 +32,7 @@ frappe.ui.form.on('Employee Benefit Application', { if(!frm.doc.employee && !frm.doc.date) return; frm.set_query("earning_component", "employee_benefits", function() { return { - query : "erpnext.hr.doctype.employee_benefit_application.employee_benefit_application.get_earning_components", + query : "erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application.get_earning_components", filters: {date: frm.doc.date, employee: frm.doc.employee} }; }); @@ -41,7 +41,7 @@ frappe.ui.form.on('Employee Benefit Application', { payroll_period: function(frm) { var method, args; if(frm.doc.employee && frm.doc.date && frm.doc.payroll_period){ - method = "erpnext.hr.doctype.employee_benefit_application.employee_benefit_application.get_max_benefits_remaining"; + method = "erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application.get_max_benefits_remaining"; args = { employee: frm.doc.employee, on_date: frm.doc.date, diff --git a/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.json b/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.json new file mode 100644 index 00000000000..275c855b687 --- /dev/null +++ b/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.json @@ -0,0 +1,191 @@ +{ + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "HR-BEN-APP-.YY.-.MM.-.#####", + "creation": "2018-04-13 16:31:39.190787", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "employee", + "employee_name", + "max_benefits", + "remaining_benefit", + "column_break_2", + "date", + "payroll_period", + "department", + "amended_from", + "section_break_4", + "employee_benefits", + "totals", + "total_amount", + "pro_rata_dispensed_amount" + ], + "fields": [ + { + "fieldname": "employee", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Employee", + "options": "Employee", + "reqd": 1 + }, + { + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Data", + "label": "Employee Name", + "read_only": 1 + }, + { + "fieldname": "max_benefits", + "fieldtype": "Currency", + "label": "Max Benefits (Yearly)", + "read_only": 1 + }, + { + "fieldname": "remaining_benefit", + "fieldtype": "Currency", + "label": "Remaining Benefits (Yearly)", + "read_only": 1 + }, + { + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, + { + "default": "Today", + "fieldname": "date", + "fieldtype": "Date", + "label": "Date", + "reqd": 1 + }, + { + "fieldname": "payroll_period", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Payroll Period", + "options": "Payroll Period", + "reqd": 1 + }, + { + "fetch_from": "employee.department", + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "options": "Department", + "read_only": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Employee Benefit Application", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "section_break_4", + "fieldtype": "Section Break", + "label": "Benefits Applied" + }, + { + "fieldname": "employee_benefits", + "fieldtype": "Table", + "label": "Employee Benefits", + "options": "Employee Benefit Application Detail", + "reqd": 1 + }, + { + "fieldname": "totals", + "fieldtype": "Section Break", + "label": "Totals" + }, + { + "fieldname": "total_amount", + "fieldtype": "Currency", + "label": "Total Amount", + "read_only": 1 + }, + { + "fieldname": "pro_rata_dispensed_amount", + "fieldtype": "Currency", + "label": "Dispensed Amount (Pro-rated)", + "read_only": 1 + } + ], + "is_submittable": 1, + "links": [], + "modified": "2020-05-27 22:58:31.271922", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Employee Benefit Application", + "owner": "Administrator", + "permissions": [ + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Employee", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "employee_name", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.py b/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.py similarity index 97% rename from erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.py rename to erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.py index feaa92590a3..e166a704d60 100644 --- a/erpnext/hr/doctype/employee_benefit_application/employee_benefit_application.py +++ b/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.py @@ -7,8 +7,8 @@ import frappe from frappe import _ from frappe.utils import date_diff, getdate, rounded, add_days, cstr, cint, flt from frappe.model.document import Document -from erpnext.hr.doctype.payroll_period.payroll_period import get_payroll_period_days, get_period_factor -from erpnext.hr.doctype.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure +from erpnext.payroll.doctype.payroll_period.payroll_period import get_payroll_period_days, get_period_factor +from erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure from erpnext.hr.utils import get_sal_slip_total_benefit_given, get_holidays_for_employee, get_previous_claimed_amount class EmployeeBenefitApplication(Document): diff --git a/erpnext/hr/doctype/employee_benefit_application/test_employee_benefit_application.js b/erpnext/payroll/doctype/employee_benefit_application/test_employee_benefit_application.js similarity index 100% rename from erpnext/hr/doctype/employee_benefit_application/test_employee_benefit_application.js rename to erpnext/payroll/doctype/employee_benefit_application/test_employee_benefit_application.js diff --git a/erpnext/hr/doctype/employee_benefit_application/test_employee_benefit_application.py b/erpnext/payroll/doctype/employee_benefit_application/test_employee_benefit_application.py similarity index 100% rename from erpnext/hr/doctype/employee_benefit_application/test_employee_benefit_application.py rename to erpnext/payroll/doctype/employee_benefit_application/test_employee_benefit_application.py diff --git a/erpnext/hr/doctype/employee_incentive/__init__.py b/erpnext/payroll/doctype/employee_benefit_application_detail/__init__.py similarity index 100% rename from erpnext/hr/doctype/employee_incentive/__init__.py rename to erpnext/payroll/doctype/employee_benefit_application_detail/__init__.py diff --git a/erpnext/payroll/doctype/employee_benefit_application_detail/employee_benefit_application_detail.json b/erpnext/payroll/doctype/employee_benefit_application_detail/employee_benefit_application_detail.json new file mode 100644 index 00000000000..f0415d2b108 --- /dev/null +++ b/erpnext/payroll/doctype/employee_benefit_application_detail/employee_benefit_application_detail.json @@ -0,0 +1,58 @@ +{ + "actions": [], + "creation": "2018-04-13 16:36:18.389786", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "earning_component", + "pay_against_benefit_claim", + "max_benefit_amount", + "amount" + ], + "fields": [ + { + "fieldname": "earning_component", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Earning Component", + "options": "Salary Component", + "reqd": 1 + }, + { + "default": "0", + "fetch_from": "earning_component.pay_against_benefit_claim", + "fieldname": "pay_against_benefit_claim", + "fieldtype": "Check", + "label": "Pay Against Benefit Claim", + "read_only": 1 + }, + { + "fetch_from": "earning_component.max_benefit_amount", + "fieldname": "max_benefit_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Max Benefit Amount", + "read_only": 1 + }, + { + "fieldname": "amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Amount", + "reqd": 1 + } + ], + "istable": 1, + "links": [], + "modified": "2020-05-27 23:45:00.519134", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Employee Benefit Application Detail", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_benefit_application_detail/employee_benefit_application_detail.py b/erpnext/payroll/doctype/employee_benefit_application_detail/employee_benefit_application_detail.py similarity index 73% rename from erpnext/hr/doctype/employee_benefit_application_detail/employee_benefit_application_detail.py rename to erpnext/payroll/doctype/employee_benefit_application_detail/employee_benefit_application_detail.py index 3a502fe524c..65405feaf19 100644 --- a/erpnext/hr/doctype/employee_benefit_application_detail/employee_benefit_application_detail.py +++ b/erpnext/payroll/doctype/employee_benefit_application_detail/employee_benefit_application_detail.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt from __future__ import unicode_literals -import frappe +# import frappe from frappe.model.document import Document class EmployeeBenefitApplicationDetail(Document): diff --git a/erpnext/hr/doctype/employee_other_income/__init__.py b/erpnext/payroll/doctype/employee_benefit_claim/__init__.py similarity index 100% rename from erpnext/hr/doctype/employee_other_income/__init__.py rename to erpnext/payroll/doctype/employee_benefit_claim/__init__.py diff --git a/erpnext/hr/doctype/employee_benefit_claim/employee_benefit_claim.js b/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.js similarity index 77% rename from erpnext/hr/doctype/employee_benefit_claim/employee_benefit_claim.js rename to erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.js index 5e12828ba4c..6db6cb86b3d 100644 --- a/erpnext/hr/doctype/employee_benefit_claim/employee_benefit_claim.js +++ b/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.js @@ -5,7 +5,7 @@ frappe.ui.form.on('Employee Benefit Claim', { setup: function(frm) { frm.set_query("earning_component", function() { return { - query : "erpnext.hr.doctype.employee_benefit_application.employee_benefit_application.get_earning_components", + query : "erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application.get_earning_components", filters: {date: frm.doc.claim_date, employee: frm.doc.employee} }; }); diff --git a/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.json b/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.json new file mode 100644 index 00000000000..7fea59fabf4 --- /dev/null +++ b/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.json @@ -0,0 +1,194 @@ +{ + "actions": [], + "allow_import": 1, + "autoname": "HR-BEN-CLM-.YY.-.MM.-.#####", + "creation": "2018-04-13 16:43:10.386409", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "employee", + "employee_name", + "department", + "column_break_3", + "claim_date", + "benefit_type_and_amount", + "earning_component", + "max_amount_eligible", + "pay_against_benefit_claim", + "claimed_amount", + "salary_slip", + "amended_from", + "section_break_9", + "attachments" + ], + "fields": [ + { + "fieldname": "employee", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Employee", + "options": "Employee", + "reqd": 1 + }, + { + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Data", + "label": "Employee Name", + "read_only": 1 + }, + { + "fetch_from": "employee.department", + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "options": "Department", + "read_only": 1 + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "default": "Today", + "fieldname": "claim_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Claim Date", + "reqd": 1 + }, + { + "fieldname": "benefit_type_and_amount", + "fieldtype": "Section Break", + "label": "Benefit Type and Amount" + }, + { + "fieldname": "earning_component", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Claim Benefit For", + "options": "Salary Component", + "reqd": 1 + }, + { + "fetch_from": "earning_component.max_benefit_amount", + "fieldname": "max_amount_eligible", + "fieldtype": "Currency", + "label": "Max Amount Eligible", + "read_only": 1 + }, + { + "default": "0", + "fetch_from": "earning_component.pay_against_benefit_claim", + "fieldname": "pay_against_benefit_claim", + "fieldtype": "Check", + "hidden": 1, + "label": "Pay Against Benefit Claim", + "read_only": 1 + }, + { + "fieldname": "claimed_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Claimed Amount", + "reqd": 1 + }, + { + "fieldname": "salary_slip", + "fieldtype": "Link", + "label": "Salary Slip", + "options": "Salary Slip", + "read_only": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Employee Benefit Claim", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "section_break_9", + "fieldtype": "Section Break", + "label": "Expense Proof" + }, + { + "fieldname": "attachments", + "fieldtype": "Attach", + "label": "Attachments" + } + ], + "is_submittable": 1, + "links": [], + "modified": "2020-05-27 23:01:50.791676", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Employee Benefit Claim", + "owner": "Administrator", + "permissions": [ + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Employee", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "employee_name", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_benefit_claim/employee_benefit_claim.py b/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.py similarity index 96% rename from erpnext/hr/doctype/employee_benefit_claim/employee_benefit_claim.py rename to erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.py index 3a12c9c9f9c..d9937a7bb97 100644 --- a/erpnext/hr/doctype/employee_benefit_claim/employee_benefit_claim.py +++ b/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.py @@ -7,10 +7,10 @@ import frappe from frappe import _ from frappe.utils import flt from frappe.model.document import Document -from erpnext.hr.doctype.employee_benefit_application.employee_benefit_application import get_max_benefits +from erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application import get_max_benefits from erpnext.hr.utils import get_previous_claimed_amount -from erpnext.hr.doctype.payroll_period.payroll_period import get_payroll_period -from erpnext.hr.doctype.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure +from erpnext.payroll.doctype.payroll_period.payroll_period import get_payroll_period +from erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure class EmployeeBenefitClaim(Document): def validate(self): diff --git a/erpnext/hr/doctype/employee_benefit_claim/test_employee_benefit_claim.js b/erpnext/payroll/doctype/employee_benefit_claim/test_employee_benefit_claim.js similarity index 100% rename from erpnext/hr/doctype/employee_benefit_claim/test_employee_benefit_claim.js rename to erpnext/payroll/doctype/employee_benefit_claim/test_employee_benefit_claim.js diff --git a/erpnext/hr/doctype/employee_benefit_claim/test_employee_benefit_claim.py b/erpnext/payroll/doctype/employee_benefit_claim/test_employee_benefit_claim.py similarity index 100% rename from erpnext/hr/doctype/employee_benefit_claim/test_employee_benefit_claim.py rename to erpnext/payroll/doctype/employee_benefit_claim/test_employee_benefit_claim.py diff --git a/erpnext/hr/doctype/employee_tax_exemption_category/__init__.py b/erpnext/payroll/doctype/employee_incentive/__init__.py similarity index 100% rename from erpnext/hr/doctype/employee_tax_exemption_category/__init__.py rename to erpnext/payroll/doctype/employee_incentive/__init__.py diff --git a/erpnext/hr/doctype/employee_incentive/employee_incentive.js b/erpnext/payroll/doctype/employee_incentive/employee_incentive.js similarity index 100% rename from erpnext/hr/doctype/employee_incentive/employee_incentive.js rename to erpnext/payroll/doctype/employee_incentive/employee_incentive.js diff --git a/erpnext/hr/doctype/employee_incentive/employee_incentive.json b/erpnext/payroll/doctype/employee_incentive/employee_incentive.json similarity index 97% rename from erpnext/hr/doctype/employee_incentive/employee_incentive.json rename to erpnext/payroll/doctype/employee_incentive/employee_incentive.json index e2d8a11f47d..81ff86506a2 100644 --- a/erpnext/hr/doctype/employee_incentive/employee_incentive.json +++ b/erpnext/payroll/doctype/employee_incentive/employee_incentive.json @@ -74,9 +74,9 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-03-05 18:59:40.526014", + "modified": "2020-05-27 22:42:51.209630", "modified_by": "Administrator", - "module": "HR", + "module": "Payroll", "name": "Employee Incentive", "owner": "Administrator", "permissions": [ diff --git a/erpnext/hr/doctype/employee_incentive/employee_incentive.py b/erpnext/payroll/doctype/employee_incentive/employee_incentive.py similarity index 100% rename from erpnext/hr/doctype/employee_incentive/employee_incentive.py rename to erpnext/payroll/doctype/employee_incentive/employee_incentive.py diff --git a/erpnext/hr/doctype/employee_incentive/test_employee_incentive.js b/erpnext/payroll/doctype/employee_incentive/test_employee_incentive.js similarity index 100% rename from erpnext/hr/doctype/employee_incentive/test_employee_incentive.js rename to erpnext/payroll/doctype/employee_incentive/test_employee_incentive.js diff --git a/erpnext/hr/doctype/employee_incentive/test_employee_incentive.py b/erpnext/payroll/doctype/employee_incentive/test_employee_incentive.py similarity index 100% rename from erpnext/hr/doctype/employee_incentive/test_employee_incentive.py rename to erpnext/payroll/doctype/employee_incentive/test_employee_incentive.py diff --git a/erpnext/hr/doctype/employee_tax_exemption_declaration/__init__.py b/erpnext/payroll/doctype/employee_other_income/__init__.py similarity index 100% rename from erpnext/hr/doctype/employee_tax_exemption_declaration/__init__.py rename to erpnext/payroll/doctype/employee_other_income/__init__.py diff --git a/erpnext/hr/doctype/employee_other_income/employee_other_income.js b/erpnext/payroll/doctype/employee_other_income/employee_other_income.js similarity index 100% rename from erpnext/hr/doctype/employee_other_income/employee_other_income.js rename to erpnext/payroll/doctype/employee_other_income/employee_other_income.js diff --git a/erpnext/hr/doctype/employee_other_income/employee_other_income.json b/erpnext/payroll/doctype/employee_other_income/employee_other_income.json similarity index 97% rename from erpnext/hr/doctype/employee_other_income/employee_other_income.json rename to erpnext/payroll/doctype/employee_other_income/employee_other_income.json index 8abfe1e93a8..c5a2a73e423 100644 --- a/erpnext/hr/doctype/employee_other_income/employee_other_income.json +++ b/erpnext/payroll/doctype/employee_other_income/employee_other_income.json @@ -76,9 +76,9 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-05-14 17:17:38.883126", + "modified": "2020-05-27 22:55:17.604688", "modified_by": "Administrator", - "module": "HR", + "module": "Payroll", "name": "Employee Other Income", "owner": "Administrator", "permissions": [ diff --git a/erpnext/hr/doctype/employee_other_income/employee_other_income.py b/erpnext/payroll/doctype/employee_other_income/employee_other_income.py similarity index 100% rename from erpnext/hr/doctype/employee_other_income/employee_other_income.py rename to erpnext/payroll/doctype/employee_other_income/employee_other_income.py diff --git a/erpnext/hr/doctype/employee_other_income/test_employee_other_income.py b/erpnext/payroll/doctype/employee_other_income/test_employee_other_income.py similarity index 100% rename from erpnext/hr/doctype/employee_other_income/test_employee_other_income.py rename to erpnext/payroll/doctype/employee_other_income/test_employee_other_income.py diff --git a/erpnext/hr/doctype/employee_tax_exemption_declaration_category/__init__.py b/erpnext/payroll/doctype/employee_tax_exemption_category/__init__.py similarity index 100% rename from erpnext/hr/doctype/employee_tax_exemption_declaration_category/__init__.py rename to erpnext/payroll/doctype/employee_tax_exemption_category/__init__.py diff --git a/erpnext/hr/doctype/employee_tax_exemption_category/employee_tax_exemption_category.js b/erpnext/payroll/doctype/employee_tax_exemption_category/employee_tax_exemption_category.js similarity index 100% rename from erpnext/hr/doctype/employee_tax_exemption_category/employee_tax_exemption_category.js rename to erpnext/payroll/doctype/employee_tax_exemption_category/employee_tax_exemption_category.js diff --git a/erpnext/payroll/doctype/employee_tax_exemption_category/employee_tax_exemption_category.json b/erpnext/payroll/doctype/employee_tax_exemption_category/employee_tax_exemption_category.json new file mode 100644 index 00000000000..c09708279a0 --- /dev/null +++ b/erpnext/payroll/doctype/employee_tax_exemption_category/employee_tax_exemption_category.json @@ -0,0 +1,74 @@ +{ + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "Prompt", + "creation": "2018-04-13 16:51:36.971140", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "max_amount", + "is_active" + ], + "fields": [ + { + "fieldname": "max_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Max Exemption Amount" + }, + { + "default": "1", + "fieldname": "is_active", + "fieldtype": "Check", + "label": "Is Active" + } + ], + "links": [], + "modified": "2020-05-27 23:16:47.472910", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Employee Tax Exemption Category", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC" +} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_tax_exemption_category/employee_tax_exemption_category.py b/erpnext/payroll/doctype/employee_tax_exemption_category/employee_tax_exemption_category.py similarity index 100% rename from erpnext/hr/doctype/employee_tax_exemption_category/employee_tax_exemption_category.py rename to erpnext/payroll/doctype/employee_tax_exemption_category/employee_tax_exemption_category.py diff --git a/erpnext/hr/doctype/employee_tax_exemption_category/test_employee_tax_exemption_category.js b/erpnext/payroll/doctype/employee_tax_exemption_category/test_employee_tax_exemption_category.js similarity index 100% rename from erpnext/hr/doctype/employee_tax_exemption_category/test_employee_tax_exemption_category.js rename to erpnext/payroll/doctype/employee_tax_exemption_category/test_employee_tax_exemption_category.js diff --git a/erpnext/hr/doctype/employee_tax_exemption_category/test_employee_tax_exemption_category.py b/erpnext/payroll/doctype/employee_tax_exemption_category/test_employee_tax_exemption_category.py similarity index 100% rename from erpnext/hr/doctype/employee_tax_exemption_category/test_employee_tax_exemption_category.py rename to erpnext/payroll/doctype/employee_tax_exemption_category/test_employee_tax_exemption_category.py diff --git a/erpnext/hr/doctype/employee_tax_exemption_proof_submission/__init__.py b/erpnext/payroll/doctype/employee_tax_exemption_declaration/__init__.py similarity index 100% rename from erpnext/hr/doctype/employee_tax_exemption_proof_submission/__init__.py rename to erpnext/payroll/doctype/employee_tax_exemption_declaration/__init__.py diff --git a/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.js b/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.js similarity index 88% rename from erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.js rename to erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.js index a827eca1c46..0e0c9b5a1ac 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.js +++ b/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.js @@ -42,7 +42,7 @@ frappe.ui.form.on('Employee Tax Exemption Declaration', { if(frm.doc.docstatus==1) { frm.add_custom_button(__('Submit Proof'), function() { frappe.model.open_mapped_doc({ - method: "erpnext.hr.doctype.employee_tax_exemption_declaration.employee_tax_exemption_declaration.make_proof_submission", + method: "erpnext.payroll.doctype.employee_tax_exemption_declaration.employee_tax_exemption_declaration.make_proof_submission", frm: frm }); }).addClass("btn-primary"); diff --git a/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json b/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json similarity index 98% rename from erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json rename to erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json index 18fad85c4b3..5423365bdb9 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json +++ b/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json @@ -107,9 +107,9 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-03-18 14:56:25.625717", + "modified": "2020-05-27 22:49:43.829892", "modified_by": "Administrator", - "module": "HR", + "module": "Payroll", "name": "Employee Tax Exemption Declaration", "owner": "Administrator", "permissions": [ diff --git a/erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py b/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py similarity index 100% rename from erpnext/hr/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py rename to erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py diff --git a/erpnext/hr/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.js b/erpnext/payroll/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.js similarity index 100% rename from erpnext/hr/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.js rename to erpnext/payroll/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.js diff --git a/erpnext/hr/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py b/erpnext/payroll/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py similarity index 100% rename from erpnext/hr/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py rename to erpnext/payroll/doctype/employee_tax_exemption_declaration/test_employee_tax_exemption_declaration.py diff --git a/erpnext/hr/doctype/employee_tax_exemption_proof_submission_detail/__init__.py b/erpnext/payroll/doctype/employee_tax_exemption_declaration_category/__init__.py similarity index 100% rename from erpnext/hr/doctype/employee_tax_exemption_proof_submission_detail/__init__.py rename to erpnext/payroll/doctype/employee_tax_exemption_declaration_category/__init__.py diff --git a/erpnext/payroll/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.json b/erpnext/payroll/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.json new file mode 100644 index 00000000000..eddaec28970 --- /dev/null +++ b/erpnext/payroll/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.json @@ -0,0 +1,61 @@ +{ + "actions": [], + "creation": "2018-04-13 16:56:23.333041", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "exemption_sub_category", + "exemption_category", + "max_amount", + "amount" + ], + "fields": [ + { + "fieldname": "exemption_sub_category", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Exemption Sub Category", + "options": "Employee Tax Exemption Sub Category", + "reqd": 1 + }, + { + "fetch_from": "exemption_sub_category.exemption_category", + "fieldname": "exemption_category", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Exemption Category", + "options": "Employee Tax Exemption Category", + "read_only": 1, + "reqd": 1 + }, + { + "fetch_from": "exemption_sub_category.max_amount", + "fieldname": "max_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Maximum Exempted Amount", + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Declared Amount", + "reqd": 1 + } + ], + "istable": 1, + "links": [], + "modified": "2020-05-27 23:41:03.638739", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Employee Tax Exemption Declaration Category", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.py b/erpnext/payroll/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.py similarity index 73% rename from erpnext/hr/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.py rename to erpnext/payroll/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.py index 362677e8872..bff747f90de 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.py +++ b/erpnext/payroll/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt from __future__ import unicode_literals -import frappe +# import frappe from frappe.model.document import Document class EmployeeTaxExemptionDeclarationCategory(Document): diff --git a/erpnext/hr/doctype/employee_tax_exemption_sub_category/__init__.py b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/__init__.py similarity index 100% rename from erpnext/hr/doctype/employee_tax_exemption_sub_category/__init__.py rename to erpnext/payroll/doctype/employee_tax_exemption_proof_submission/__init__.py diff --git a/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.js b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.js similarity index 90% rename from erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.js rename to erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.js index 66118c08111..715d7553b00 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.js +++ b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.js @@ -43,7 +43,7 @@ frappe.ui.form.on('Employee Tax Exemption Proof Submission', { frm.add_custom_button(__('Get Details From Declaration'), function() { erpnext.utils.map_current_doc({ - method: "erpnext.hr.doctype.employee_tax_exemption_declaration.employee_tax_exemption_declaration.make_proof_submission", + method: "erpnext.payroll.doctype.employee_tax_exemption_declaration.employee_tax_exemption_declaration.make_proof_submission", source_doctype: "Employee Tax Exemption Declaration", target: frm, date_field: "creation", diff --git a/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.json b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.json similarity index 98% rename from erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.json rename to erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.json index 8b117a25b54..de8fa09a83b 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.json +++ b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.json @@ -130,9 +130,9 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-03-18 14:55:51.420016", + "modified": "2020-05-27 22:53:10.412321", "modified_by": "Administrator", - "module": "HR", + "module": "Payroll", "name": "Employee Tax Exemption Proof Submission", "owner": "Administrator", "permissions": [ diff --git a/erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py similarity index 100% rename from erpnext/hr/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py rename to erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py diff --git a/erpnext/hr/doctype/employee_tax_exemption_proof_submission/test_employee_tax_exemption_proof_submission.js b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/test_employee_tax_exemption_proof_submission.js similarity index 100% rename from erpnext/hr/doctype/employee_tax_exemption_proof_submission/test_employee_tax_exemption_proof_submission.js rename to erpnext/payroll/doctype/employee_tax_exemption_proof_submission/test_employee_tax_exemption_proof_submission.js diff --git a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/test_employee_tax_exemption_proof_submission.py b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/test_employee_tax_exemption_proof_submission.py new file mode 100644 index 00000000000..cb9ed5f971c --- /dev/null +++ b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/test_employee_tax_exemption_proof_submission.py @@ -0,0 +1,54 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest +from erpnext.payroll.doctype.employee_tax_exemption_declaration.test_employee_tax_exemption_declaration import create_exemption_category, create_payroll_period + +class TestEmployeeTaxExemptionProofSubmission(unittest.TestCase): + def setup(self): + make_employee("employee@proofsubmission.com") + create_payroll_period() + create_exemption_category() + frappe.db.sql("""delete from `tabEmployee Tax Exemption Proof Submission`""") + + def test_exemption_amount_lesser_than_category_max(self): + declaration = frappe.get_doc({ + "doctype": "Employee Tax Exemption Proof Submission", + "employee": frappe.get_value("Employee", {"user_id":"employee@proofsubmission.com"}, "name"), + "payroll_period": "Test Payroll Period", + "tax_exemption_proofs": [dict(exemption_sub_category = "_Test Sub Category", + type_of_proof = "Test Proof", + exemption_category = "_Test Category", + amount = 150000)] + }) + self.assertRaises(frappe.ValidationError, declaration.save) + declaration = frappe.get_doc({ + "doctype": "Employee Tax Exemption Proof Submission", + "payroll_period": "Test Payroll Period", + "employee": frappe.get_value("Employee", {"user_id":"employee@proofsubmission.com"}, "name"), + "tax_exemption_proofs": [dict(exemption_sub_category = "_Test Sub Category", + type_of_proof = "Test Proof", + exemption_category = "_Test Category", + amount = 100000)] + }) + self.assertTrue(declaration.save) + self.assertTrue(declaration.submit) + + def test_duplicate_category_in_proof_submission(self): + declaration = frappe.get_doc({ + "doctype": "Employee Tax Exemption Proof Submission", + "employee": frappe.get_value("Employee", {"user_id":"employee@proofsubmission.com"}, "name"), + "payroll_period": "Test Payroll Period", + "tax_exemption_proofs": [dict(exemption_sub_category = "_Test Sub Category", + exemption_category = "_Test Category", + type_of_proof = "Test Proof", + amount = 100000), + dict(exemption_sub_category = "_Test Sub Category", + exemption_category = "_Test Category", + amount = 50000), + ] + }) + self.assertRaises(frappe.ValidationError, declaration.save) diff --git a/erpnext/hr/doctype/income_tax_slab/__init__.py b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission_detail/__init__.py similarity index 100% rename from erpnext/hr/doctype/income_tax_slab/__init__.py rename to erpnext/payroll/doctype/employee_tax_exemption_proof_submission_detail/__init__.py diff --git a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.json b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.json new file mode 100644 index 00000000000..4c53bd3f5d1 --- /dev/null +++ b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.json @@ -0,0 +1,66 @@ +{ + "actions": [], + "creation": "2018-04-13 17:19:03.006149", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "exemption_sub_category", + "exemption_category", + "max_amount", + "type_of_proof", + "amount" + ], + "fields": [ + { + "fieldname": "exemption_sub_category", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Exemption Sub Category", + "options": "Employee Tax Exemption Sub Category", + "reqd": 1 + }, + { + "fetch_from": "exemption_sub_category.exemption_category", + "fieldname": "exemption_category", + "fieldtype": "Read Only", + "in_list_view": 1, + "label": "Exemption Category", + "reqd": 1 + }, + { + "fetch_from": "exemption_sub_category.max_amount", + "fieldname": "max_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Maximum Exemption Amount", + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "type_of_proof", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Type of Proof", + "reqd": 1 + }, + { + "fieldname": "amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Actual Amount" + } + ], + "istable": 1, + "links": [], + "modified": "2020-05-27 23:37:08.265600", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Employee Tax Exemption Proof Submission Detail", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.py b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.py similarity index 74% rename from erpnext/hr/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.py rename to erpnext/payroll/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.py index c5d1a8fe5d9..0244ae66468 100644 --- a/erpnext/hr/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.py +++ b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt from __future__ import unicode_literals -import frappe +# import frappe from frappe.model.document import Document class EmployeeTaxExemptionProofSubmissionDetail(Document): diff --git a/erpnext/hr/doctype/income_tax_slab_other_charges/__init__.py b/erpnext/payroll/doctype/employee_tax_exemption_sub_category/__init__.py similarity index 100% rename from erpnext/hr/doctype/income_tax_slab_other_charges/__init__.py rename to erpnext/payroll/doctype/employee_tax_exemption_sub_category/__init__.py diff --git a/erpnext/hr/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.js b/erpnext/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.js similarity index 100% rename from erpnext/hr/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.js rename to erpnext/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.js diff --git a/erpnext/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.json b/erpnext/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.json new file mode 100644 index 00000000000..b89d9c197f3 --- /dev/null +++ b/erpnext/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.json @@ -0,0 +1,86 @@ +{ + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "Prompt", + "creation": "2018-05-09 12:47:26.983095", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "exemption_category", + "max_amount", + "is_active" + ], + "fields": [ + { + "fieldname": "exemption_category", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Tax Exemption Category", + "options": "Employee Tax Exemption Category", + "reqd": 1 + }, + { + "fetch_from": "exemption_category.max_amount", + "fetch_if_empty": 1, + "fieldname": "max_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Max Exemption Amount" + }, + { + "default": "1", + "fieldname": "is_active", + "fieldtype": "Check", + "label": "Is Active" + } + ], + "links": [], + "modified": "2020-05-27 23:18:08.254645", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Employee Tax Exemption Sub Category", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC" +} \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.py b/erpnext/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.py similarity index 100% rename from erpnext/hr/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.py rename to erpnext/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.py diff --git a/erpnext/hr/doctype/employee_tax_exemption_sub_category/test_employee_tax_exemption_sub_category.js b/erpnext/payroll/doctype/employee_tax_exemption_sub_category/test_employee_tax_exemption_sub_category.js similarity index 100% rename from erpnext/hr/doctype/employee_tax_exemption_sub_category/test_employee_tax_exemption_sub_category.js rename to erpnext/payroll/doctype/employee_tax_exemption_sub_category/test_employee_tax_exemption_sub_category.js diff --git a/erpnext/hr/doctype/employee_tax_exemption_sub_category/test_employee_tax_exemption_sub_category.py b/erpnext/payroll/doctype/employee_tax_exemption_sub_category/test_employee_tax_exemption_sub_category.py similarity index 100% rename from erpnext/hr/doctype/employee_tax_exemption_sub_category/test_employee_tax_exemption_sub_category.py rename to erpnext/payroll/doctype/employee_tax_exemption_sub_category/test_employee_tax_exemption_sub_category.py diff --git a/erpnext/hr/doctype/payroll_employee_detail/__init__.py b/erpnext/payroll/doctype/income_tax_slab/__init__.py similarity index 100% rename from erpnext/hr/doctype/payroll_employee_detail/__init__.py rename to erpnext/payroll/doctype/income_tax_slab/__init__.py diff --git a/erpnext/hr/doctype/income_tax_slab/income_tax_slab.js b/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.js similarity index 100% rename from erpnext/hr/doctype/income_tax_slab/income_tax_slab.js rename to erpnext/payroll/doctype/income_tax_slab/income_tax_slab.js diff --git a/erpnext/hr/doctype/income_tax_slab/income_tax_slab.json b/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.json similarity index 97% rename from erpnext/hr/doctype/income_tax_slab/income_tax_slab.json rename to erpnext/payroll/doctype/income_tax_slab/income_tax_slab.json index f74315f32e9..72b43326c32 100644 --- a/erpnext/hr/doctype/income_tax_slab/income_tax_slab.json +++ b/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.json @@ -94,9 +94,9 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-04-29 15:08:21.436120", + "modified": "2020-05-27 20:27:13.425084", "modified_by": "Administrator", - "module": "HR", + "module": "Payroll", "name": "Income Tax Slab", "owner": "Administrator", "permissions": [ diff --git a/erpnext/hr/doctype/income_tax_slab/income_tax_slab.py b/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.py similarity index 100% rename from erpnext/hr/doctype/income_tax_slab/income_tax_slab.py rename to erpnext/payroll/doctype/income_tax_slab/income_tax_slab.py diff --git a/erpnext/hr/doctype/income_tax_slab/test_income_tax_slab.py b/erpnext/payroll/doctype/income_tax_slab/test_income_tax_slab.py similarity index 100% rename from erpnext/hr/doctype/income_tax_slab/test_income_tax_slab.py rename to erpnext/payroll/doctype/income_tax_slab/test_income_tax_slab.py diff --git a/erpnext/hr/doctype/payroll_entry/__init__.py b/erpnext/payroll/doctype/income_tax_slab_other_charges/__init__.py similarity index 100% rename from erpnext/hr/doctype/payroll_entry/__init__.py rename to erpnext/payroll/doctype/income_tax_slab_other_charges/__init__.py diff --git a/erpnext/hr/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.json b/erpnext/payroll/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.json similarity index 95% rename from erpnext/hr/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.json rename to erpnext/payroll/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.json index b23fb3dc317..2531c79828a 100644 --- a/erpnext/hr/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.json +++ b/erpnext/payroll/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.json @@ -14,18 +14,6 @@ "max_taxable_income" ], "fields": [ - { - "fieldname": "column_break_2", - "fieldtype": "Column Break" - }, - { - "columns": 2, - "fieldname": "min_taxable_income", - "fieldtype": "Currency", - "in_list_view": 1, - "label": "Min Taxable Income", - "options": "Company:company:default_currency" - }, { "columns": 4, "fieldname": "description", @@ -34,6 +22,10 @@ "label": "Description", "reqd": 1 }, + { + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, { "columns": 2, "fieldname": "percent", @@ -47,6 +39,14 @@ "fieldtype": "Section Break", "label": "Conditions" }, + { + "columns": 2, + "fieldname": "min_taxable_income", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Min Taxable Income", + "options": "Company:company:default_currency" + }, { "fieldname": "column_break_7", "fieldtype": "Column Break" @@ -62,9 +62,9 @@ ], "istable": 1, "links": [], - "modified": "2020-04-24 13:27:43.598967", + "modified": "2020-05-27 23:33:17.931912", "modified_by": "Administrator", - "module": "HR", + "module": "Payroll", "name": "Income Tax Slab Other Charges", "owner": "Administrator", "permissions": [], diff --git a/erpnext/hr/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.py b/erpnext/payroll/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.py similarity index 100% rename from erpnext/hr/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.py rename to erpnext/payroll/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.py diff --git a/erpnext/hr/doctype/payroll_period/__init__.py b/erpnext/payroll/doctype/payroll_employee_detail/__init__.py similarity index 100% rename from erpnext/hr/doctype/payroll_period/__init__.py rename to erpnext/payroll/doctype/payroll_employee_detail/__init__.py diff --git a/erpnext/payroll/doctype/payroll_employee_detail/payroll_employee_detail.json b/erpnext/payroll/doctype/payroll_employee_detail/payroll_employee_detail.json new file mode 100644 index 00000000000..b22e5de3243 --- /dev/null +++ b/erpnext/payroll/doctype/payroll_employee_detail/payroll_employee_detail.json @@ -0,0 +1,66 @@ +{ + "actions": [], + "creation": "2017-11-30 06:07:33.477781", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "employee", + "employee_name", + "column_break_3", + "department", + "designation" + ], + "fields": [ + { + "fieldname": "employee", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Employee", + "options": "Employee", + "read_only": 1 + }, + { + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Employee Name", + "read_only": 1 + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "fetch_from": "employee.department", + "fieldname": "department", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Department", + "options": "Department", + "read_only": 1 + }, + { + "fetch_from": "employee.designation", + "fieldname": "designation", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Designation", + "read_only": 1 + } + ], + "istable": 1, + "links": [], + "modified": "2020-05-27 23:25:13.779032", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Payroll Employee Detail", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "read_only": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/hr/doctype/payroll_employee_detail/payroll_employee_detail.py b/erpnext/payroll/doctype/payroll_employee_detail/payroll_employee_detail.py similarity index 100% rename from erpnext/hr/doctype/payroll_employee_detail/payroll_employee_detail.py rename to erpnext/payroll/doctype/payroll_employee_detail/payroll_employee_detail.py diff --git a/erpnext/hr/doctype/payroll_period_date/__init__.py b/erpnext/payroll/doctype/payroll_entry/__init__.py similarity index 100% rename from erpnext/hr/doctype/payroll_period_date/__init__.py rename to erpnext/payroll/doctype/payroll_entry/__init__.py diff --git a/erpnext/hr/doctype/payroll_entry/payroll_entry.js b/erpnext/payroll/doctype/payroll_entry/payroll_entry.js similarity index 95% rename from erpnext/hr/doctype/payroll_entry/payroll_entry.js rename to erpnext/payroll/doctype/payroll_entry/payroll_entry.js index da25d7574e0..1ae3553b9b9 100644 --- a/erpnext/hr/doctype/payroll_entry/payroll_entry.js +++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.js @@ -84,7 +84,7 @@ frappe.ui.form.on('Payroll Entry', { add_bank_entry_button: function(frm) { frappe.call({ - method: 'erpnext.hr.doctype.payroll_entry.payroll_entry.payroll_entry_has_bank_entries', + method: 'erpnext.payroll.doctype.payroll_entry.payroll_entry.payroll_entry_has_bank_entries', args: { 'name': frm.doc.name }, @@ -170,7 +170,7 @@ frappe.ui.form.on('Payroll Entry', { set_start_end_dates: function (frm) { if (!frm.doc.salary_slip_based_on_timesheet) { frappe.call({ - method: 'erpnext.hr.doctype.payroll_entry.payroll_entry.get_start_end_dates', + method: 'erpnext.payroll.doctype.payroll_entry.payroll_entry.get_start_end_dates', args: { payroll_frequency: frm.doc.payroll_frequency, start_date: frm.doc.posting_date @@ -188,7 +188,7 @@ frappe.ui.form.on('Payroll Entry', { set_end_date: function(frm){ frappe.call({ - method: 'erpnext.hr.doctype.payroll_entry.payroll_entry.get_end_date', + method: 'erpnext.payroll.doctype.payroll_entry.payroll_entry.get_end_date', args: { frequency: frm.doc.payroll_frequency, start_date: frm.doc.start_date diff --git a/erpnext/hr/doctype/payroll_entry/payroll_entry.json b/erpnext/payroll/doctype/payroll_entry/payroll_entry.json similarity index 98% rename from erpnext/hr/doctype/payroll_entry/payroll_entry.json rename to erpnext/payroll/doctype/payroll_entry/payroll_entry.json index 9356f3e7b3b..4888be29871 100644 --- a/erpnext/hr/doctype/payroll_entry/payroll_entry.json +++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_copy": 1, "autoname": "HR-PRUN-.YYYY.-.#####", "creation": "2017-10-23 15:22:29.291323", @@ -260,9 +261,10 @@ ], "icon": "fa fa-cog", "is_submittable": 1, - "modified": "2019-09-12 15:46:31.436381", + "links": [], + "modified": "2020-05-27 20:06:06.953904", "modified_by": "Administrator", - "module": "HR", + "module": "Payroll", "name": "Payroll Entry", "owner": "Administrator", "permissions": [ diff --git a/erpnext/hr/doctype/payroll_entry/payroll_entry.py b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py similarity index 99% rename from erpnext/hr/doctype/payroll_entry/payroll_entry.py rename to erpnext/payroll/doctype/payroll_entry/payroll_entry.py index 656de0170dd..e6bb708e418 100644 --- a/erpnext/hr/doctype/payroll_entry/payroll_entry.py +++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py @@ -55,7 +55,7 @@ class PayrollEntry(Document): ifnull(salary_slip_based_on_timesheet,0) = %(salary_slip_based_on_timesheet)s {condition}""".format(condition=condition), {"company": self.company, "salary_slip_based_on_timesheet":self.salary_slip_based_on_timesheet}) - + if sal_struct: cond += "and t2.salary_structure IN %(sal_struct)s " cond += "and %(from_date)s >= t2.from_date" @@ -154,7 +154,7 @@ class PayrollEntry(Document): submit_salary_slips_for_employees(self, ss_list, publish_progress=False) def email_salary_slip(self, submitted_ss): - if frappe.db.get_single_value("HR Settings", "email_salary_slip_to_employee"): + if frappe.db.get_single_value("Payroll Settings", "email_salary_slip_to_employee"): for ss in submitted_ss: ss.email_salary_slip() @@ -170,7 +170,7 @@ class PayrollEntry(Document): def get_salary_components(self, component_type): salary_slips = self.get_sal_slip_list(ss_status = 1, as_dict = True) - if salary_slips: + if salary_slips: salary_components = frappe.db.sql(""" select ssd.salary_component, ssd.amount, ssd.parentfield, ss.payroll_cost_center from `tabSalary Slip` ss, `tabSalary Detail` ssd @@ -197,7 +197,7 @@ class PayrollEntry(Document): return account_details def get_account(self, component_dict = None): - account_dict = {} + account_dict = {} for key, amount in component_dict.items(): account = self.get_salary_component_account(key[0]) account_dict[(account, key[1])] = account_dict.get((account, key[1]), 0) + amount diff --git a/erpnext/hr/doctype/payroll_entry/payroll_entry_dashboard.py b/erpnext/payroll/doctype/payroll_entry/payroll_entry_dashboard.py similarity index 100% rename from erpnext/hr/doctype/payroll_entry/payroll_entry_dashboard.py rename to erpnext/payroll/doctype/payroll_entry/payroll_entry_dashboard.py diff --git a/erpnext/hr/doctype/payroll_entry/test_payroll_entry.js b/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.js similarity index 100% rename from erpnext/hr/doctype/payroll_entry/test_payroll_entry.js rename to erpnext/payroll/doctype/payroll_entry/test_payroll_entry.js diff --git a/erpnext/hr/doctype/payroll_entry/test_payroll_entry.py b/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py similarity index 96% rename from erpnext/hr/doctype/payroll_entry/test_payroll_entry.py rename to erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py index 3c318e78a24..b0f225d909d 100644 --- a/erpnext/hr/doctype/payroll_entry/test_payroll_entry.py +++ b/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py @@ -7,11 +7,11 @@ import frappe from dateutil.relativedelta import relativedelta from erpnext.accounts.utils import get_fiscal_year, getdate, nowdate from frappe.utils import add_months -from erpnext.hr.doctype.payroll_entry.payroll_entry import get_start_end_dates, get_end_date +from erpnext.payroll.doctype.payroll_entry.payroll_entry import get_start_end_dates, get_end_date from erpnext.hr.doctype.employee.test_employee import make_employee -from erpnext.hr.doctype.salary_slip.test_salary_slip import get_salary_component_account, \ +from erpnext.payroll.doctype.salary_slip.test_salary_slip import get_salary_component_account, \ make_earning_salary_component, make_deduction_salary_component, create_account -from erpnext.hr.doctype.salary_structure.test_salary_structure import make_salary_structure +from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure from erpnext.loan_management.doctype.loan.test_loan import create_loan, make_loan_disbursement_entry from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual_for_term_loans @@ -24,7 +24,7 @@ class TestPayrollEntry(unittest.TestCase): make_earning_salary_component(setup=True, company_list=["_Test Company"]) make_deduction_salary_component(setup=True, company_list=["_Test Company"]) - frappe.db.set_value("HR Settings", None, "email_salary_slip_to_employee", 0) + frappe.db.set_value("Payroll Settings", None, "email_salary_slip_to_employee", 0) def test_payroll_entry(self): # pylint: disable=no-self-use company = erpnext.get_default_company() diff --git a/erpnext/hr/doctype/payroll_entry/test_set_salary_components.js b/erpnext/payroll/doctype/payroll_entry/test_set_salary_components.js similarity index 100% rename from erpnext/hr/doctype/payroll_entry/test_set_salary_components.js rename to erpnext/payroll/doctype/payroll_entry/test_set_salary_components.js diff --git a/erpnext/hr/doctype/retention_bonus/__init__.py b/erpnext/payroll/doctype/payroll_period/__init__.py similarity index 100% rename from erpnext/hr/doctype/retention_bonus/__init__.py rename to erpnext/payroll/doctype/payroll_period/__init__.py diff --git a/erpnext/hr/doctype/payroll_period/payroll_period.js b/erpnext/payroll/doctype/payroll_period/payroll_period.js similarity index 100% rename from erpnext/hr/doctype/payroll_period/payroll_period.js rename to erpnext/payroll/doctype/payroll_period/payroll_period.js diff --git a/erpnext/hr/doctype/payroll_period/payroll_period.json b/erpnext/payroll/doctype/payroll_period/payroll_period.json similarity index 96% rename from erpnext/hr/doctype/payroll_period/payroll_period.json rename to erpnext/payroll/doctype/payroll_period/payroll_period.json index c0fa506e7f0..345a2415a2c 100644 --- a/erpnext/hr/doctype/payroll_period/payroll_period.json +++ b/erpnext/payroll/doctype/payroll_period/payroll_period.json @@ -53,9 +53,9 @@ } ], "links": [], - "modified": "2020-03-18 18:13:23.859980", + "modified": "2020-05-27 20:12:32.684189", "modified_by": "Administrator", - "module": "HR", + "module": "Payroll", "name": "Payroll Period", "owner": "Administrator", "permissions": [ diff --git a/erpnext/hr/doctype/payroll_period/payroll_period.py b/erpnext/payroll/doctype/payroll_period/payroll_period.py similarity index 97% rename from erpnext/hr/doctype/payroll_period/payroll_period.py rename to erpnext/payroll/doctype/payroll_period/payroll_period.py index 6956c382854..d7893d06572 100644 --- a/erpnext/hr/doctype/payroll_period/payroll_period.py +++ b/erpnext/payroll/doctype/payroll_period/payroll_period.py @@ -64,7 +64,7 @@ def get_payroll_period_days(start_date, end_date, employee, company=None): if len(payroll_period) > 0: actual_no_of_days = date_diff(getdate(payroll_period[0][2]), getdate(payroll_period[0][1])) + 1 working_days = actual_no_of_days - if not cint(frappe.db.get_value("HR Settings", None, "include_holidays_in_total_working_days")): + if not cint(frappe.db.get_value("Payroll Settings", None, "include_holidays_in_total_working_days")): holidays = get_holidays_for_employee(employee, getdate(payroll_period[0][1]), getdate(payroll_period[0][2])) working_days -= len(holidays) return payroll_period[0][0], working_days, actual_no_of_days diff --git a/erpnext/hr/doctype/payroll_period/payroll_period_dashboard.py b/erpnext/payroll/doctype/payroll_period/payroll_period_dashboard.py similarity index 100% rename from erpnext/hr/doctype/payroll_period/payroll_period_dashboard.py rename to erpnext/payroll/doctype/payroll_period/payroll_period_dashboard.py diff --git a/erpnext/hr/doctype/payroll_period/test_payroll_period.js b/erpnext/payroll/doctype/payroll_period/test_payroll_period.js similarity index 100% rename from erpnext/hr/doctype/payroll_period/test_payroll_period.js rename to erpnext/payroll/doctype/payroll_period/test_payroll_period.js diff --git a/erpnext/hr/doctype/payroll_period/test_payroll_period.py b/erpnext/payroll/doctype/payroll_period/test_payroll_period.py similarity index 100% rename from erpnext/hr/doctype/payroll_period/test_payroll_period.py rename to erpnext/payroll/doctype/payroll_period/test_payroll_period.py diff --git a/erpnext/hr/doctype/salary_component/__init__.py b/erpnext/payroll/doctype/payroll_period_date/__init__.py similarity index 100% rename from erpnext/hr/doctype/salary_component/__init__.py rename to erpnext/payroll/doctype/payroll_period_date/__init__.py diff --git a/erpnext/payroll/doctype/payroll_period_date/payroll_period_date.json b/erpnext/payroll/doctype/payroll_period_date/payroll_period_date.json new file mode 100644 index 00000000000..d745fcd5325 --- /dev/null +++ b/erpnext/payroll/doctype/payroll_period_date/payroll_period_date.json @@ -0,0 +1,39 @@ +{ + "actions": [], + "creation": "2018-04-13 15:17:30.513630", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "start_date", + "end_date" + ], + "fields": [ + { + "fieldname": "start_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Start Date", + "reqd": 1 + }, + { + "fieldname": "end_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "End Date", + "reqd": 1 + } + ], + "istable": 1, + "links": [], + "modified": "2020-05-27 23:30:15.943356", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Payroll Period Date", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/hr/doctype/payroll_period_date/payroll_period_date.py b/erpnext/payroll/doctype/payroll_period_date/payroll_period_date.py similarity index 71% rename from erpnext/hr/doctype/payroll_period_date/payroll_period_date.py rename to erpnext/payroll/doctype/payroll_period_date/payroll_period_date.py index 06ecb495f31..a3ee269d8e6 100644 --- a/erpnext/hr/doctype/payroll_period_date/payroll_period_date.py +++ b/erpnext/payroll/doctype/payroll_period_date/payroll_period_date.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt from __future__ import unicode_literals -import frappe +# import frappe from frappe.model.document import Document class PayrollPeriodDate(Document): diff --git a/erpnext/hr/doctype/salary_detail/__init__.py b/erpnext/payroll/doctype/payroll_settings/__init__.py similarity index 100% rename from erpnext/hr/doctype/salary_detail/__init__.py rename to erpnext/payroll/doctype/payroll_settings/__init__.py diff --git a/erpnext/payroll/doctype/payroll_settings/payroll_settings.js b/erpnext/payroll/doctype/payroll_settings/payroll_settings.js new file mode 100644 index 00000000000..941464dc51f --- /dev/null +++ b/erpnext/payroll/doctype/payroll_settings/payroll_settings.js @@ -0,0 +1,19 @@ +// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Payroll Settings', { + encrypt_salary_slips_in_emails: function(frm) { + let encrypt_state = frm.doc.encrypt_salary_slips_in_emails; + frm.set_df_property('password_policy', 'reqd', encrypt_state); + }, + + validate: function(frm) { + let policy = frm.doc.password_policy; + if (policy) { + if (policy.includes(' ') || policy.includes('--')) { + frappe.msgprint(__("Password policy cannot contain spaces or simultaneous hyphens. The format will be restructured automatically")); + } + frm.set_value('password_policy', policy.split(new RegExp(" |-", 'g')).filter((token) => token).join('-')); + } + }, +}); diff --git a/erpnext/payroll/doctype/payroll_settings/payroll_settings.json b/erpnext/payroll/doctype/payroll_settings/payroll_settings.json new file mode 100644 index 00000000000..e3b8b3e7e04 --- /dev/null +++ b/erpnext/payroll/doctype/payroll_settings/payroll_settings.json @@ -0,0 +1,130 @@ +{ + "actions": [], + "creation": "2020-06-04 15:13:33.589685", + "doctype": "DocType", + "document_type": "Other", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "payroll_based_on", + "consider_unmarked_attendance_as", + "max_working_hours_against_timesheet", + "include_holidays_in_total_working_days", + "disable_rounded_total", + "column_break_11", + "daily_wages_fraction_for_half_day", + "email_salary_slip_to_employee", + "encrypt_salary_slips_in_emails", + "password_policy" + ], + "fields": [ + { + "default": "Leave", + "fieldname": "payroll_based_on", + "fieldtype": "Select", + "label": "Calculate Payroll Working Days Based On", + "options": "Leave\nAttendance", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "max_working_hours_against_timesheet", + "fieldtype": "Float", + "label": "Max working hours against Timesheet", + "show_days": 1, + "show_seconds": 1 + }, + { + "default": "0", + "description": "If checked, Total no. of Working Days will include holidays, and this will reduce the value of Salary Per Day", + "fieldname": "include_holidays_in_total_working_days", + "fieldtype": "Check", + "label": "Include holidays in Total no. of Working Days", + "show_days": 1, + "show_seconds": 1 + }, + { + "default": "0", + "description": "If checked, hides and disables Rounded Total field in Salary Slips", + "fieldname": "disable_rounded_total", + "fieldtype": "Check", + "label": "Disable Rounded Total", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "column_break_11", + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 + }, + { + "default": "0.5", + "description": "The fraction of daily wages to be paid for half-day attendance", + "fieldname": "daily_wages_fraction_for_half_day", + "fieldtype": "Float", + "label": "Daily Wages Fraction for Half Day", + "show_days": 1, + "show_seconds": 1 + }, + { + "default": "1", + "description": "Emails salary slip to employee based on preferred email selected in Employee", + "fieldname": "email_salary_slip_to_employee", + "fieldtype": "Check", + "label": "Email Salary Slip to Employee", + "show_days": 1, + "show_seconds": 1 + }, + { + "default": "0", + "depends_on": "eval: doc.email_salary_slip_to_employee == 1;", + "description": "The salary slip emailed to the employee will be password protected, the password will be generated based on the password policy.", + "fieldname": "encrypt_salary_slips_in_emails", + "fieldtype": "Check", + "label": "Encrypt Salary Slips in Emails", + "show_days": 1, + "show_seconds": 1 + }, + { + "depends_on": "eval: doc.encrypt_salary_slips_in_emails == 1", + "description": "Example: SAL-{first_name}-{date_of_birth.year}
    This will generate a password like SAL-Jane-1972", + "fieldname": "password_policy", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Password Policy", + "show_days": 1, + "show_seconds": 1 + }, + { + "depends_on": "eval:doc.payroll_based_on == 'Attendance'", + "fieldname": "consider_unmarked_attendance_as", + "fieldtype": "Select", + "label": "Consider Unmarked Attendance As", + "options": "Present\nAbsent", + "show_days": 1, + "show_seconds": 1 + } + ], + "icon": "fa fa-cog", + "issingle": 1, + "links": [], + "modified": "2020-06-05 12:35:34.861674", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Payroll Settings", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "email": 1, + "print": 1, + "read": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "ASC" +} \ No newline at end of file diff --git a/erpnext/payroll/doctype/payroll_settings/payroll_settings.py b/erpnext/payroll/doctype/payroll_settings/payroll_settings.py new file mode 100644 index 00000000000..5efa41db1f7 --- /dev/null +++ b/erpnext/payroll/doctype/payroll_settings/payroll_settings.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.model.document import Document +from frappe.utils import cint +from frappe.custom.doctype.property_setter.property_setter import make_property_setter +from frappe import _ + +class PayrollSettings(Document): + def validate(self): + self.validate_password_policy() + + if not self.daily_wages_fraction_for_half_day: + self.daily_wages_fraction_for_half_day = 0.5 + + def validate_password_policy(self): + if self.email_salary_slip_to_employee and self.encrypt_salary_slips_in_emails: + if not self.password_policy: + frappe.throw(_("Password policy for Salary Slips is not set")) + + + def on_update(self): + self.toggle_rounded_total() + frappe.clear_cache() + + def toggle_rounded_total(self): + self.disable_rounded_total = cint(self.disable_rounded_total) + make_property_setter("Salary Slip", "rounded_total", "hidden", self.disable_rounded_total, "Check") + make_property_setter("Salary Slip", "rounded_total", "print_hide", self.disable_rounded_total, "Check") diff --git a/erpnext/payroll/doctype/payroll_settings/test_payroll_settings.py b/erpnext/payroll/doctype/payroll_settings/test_payroll_settings.py new file mode 100644 index 00000000000..314866e128e --- /dev/null +++ b/erpnext/payroll/doctype/payroll_settings/test_payroll_settings.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +# import frappe +import unittest + +class TestPayrollSettings(unittest.TestCase): + pass diff --git a/erpnext/hr/doctype/salary_slip_timesheet/__init__.py b/erpnext/payroll/doctype/retention_bonus/__init__.py similarity index 100% rename from erpnext/hr/doctype/salary_slip_timesheet/__init__.py rename to erpnext/payroll/doctype/retention_bonus/__init__.py diff --git a/erpnext/hr/doctype/retention_bonus/retention_bonus.js b/erpnext/payroll/doctype/retention_bonus/retention_bonus.js similarity index 100% rename from erpnext/hr/doctype/retention_bonus/retention_bonus.js rename to erpnext/payroll/doctype/retention_bonus/retention_bonus.js diff --git a/erpnext/hr/doctype/retention_bonus/retention_bonus.json b/erpnext/payroll/doctype/retention_bonus/retention_bonus.json similarity index 90% rename from erpnext/hr/doctype/retention_bonus/retention_bonus.json rename to erpnext/payroll/doctype/retention_bonus/retention_bonus.json index 7781053e137..53fe17f9f66 100644 --- a/erpnext/hr/doctype/retention_bonus/retention_bonus.json +++ b/erpnext/payroll/doctype/retention_bonus/retention_bonus.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_import": 1, "allow_rename": 1, "autoname": "HR-RTB-.YYYY.-.#####", @@ -16,8 +17,7 @@ "column_break_6", "employee_name", "department", - "date_of_joining", - "additional_salary" + "date_of_joining" ], "fields": [ { @@ -83,14 +83,6 @@ "label": "Date of Joining", "read_only": 1 }, - { - "fieldname": "additional_salary", - "fieldtype": "Link", - "label": "Additional Salary", - "no_copy": 1, - "options": "Additional Salary", - "read_only": 1 - }, { "fieldname": "salary_component", "fieldtype": "Link", @@ -100,9 +92,10 @@ } ], "is_submittable": 1, - "modified": "2019-09-03 16:47:24.210422", + "links": [], + "modified": "2020-05-27 22:42:05.251951", "modified_by": "Administrator", - "module": "HR", + "module": "Payroll", "name": "Retention Bonus", "owner": "Administrator", "permissions": [ diff --git a/erpnext/hr/doctype/retention_bonus/retention_bonus.py b/erpnext/payroll/doctype/retention_bonus/retention_bonus.py similarity index 76% rename from erpnext/hr/doctype/retention_bonus/retention_bonus.py rename to erpnext/payroll/doctype/retention_bonus/retention_bonus.py index 48637a350cd..ed0d36cfa5f 100644 --- a/erpnext/hr/doctype/retention_bonus/retention_bonus.py +++ b/erpnext/payroll/doctype/retention_bonus/retention_bonus.py @@ -17,13 +17,7 @@ class RetentionBonus(Document): def on_submit(self): company = frappe.db.get_value('Employee', self.employee, 'company') - additional_salary = frappe.db.exists('Additional Salary', { - 'employee': self.employee, - 'salary_component': self.salary_component, - 'payroll_date': self.bonus_payment_date, - 'company': company, - 'docstatus': 1 - }) + additional_salary = self.get_additional_salary() if not additional_salary: additional_salary = frappe.new_doc('Additional Salary') @@ -32,8 +26,10 @@ class RetentionBonus(Document): additional_salary.amount = self.bonus_amount additional_salary.payroll_date = self.bonus_payment_date additional_salary.company = company + additional_salary.ref_doctype = self.doctype + additional_salary.ref_docname = self.name additional_salary.submit() - self.db_set('additional_salary', additional_salary.name) + # self.db_set('additional_salary', additional_salary.name) else: bonus_added = frappe.db.get_value('Additional Salary', additional_salary, 'amount') + self.bonus_amount @@ -41,11 +37,24 @@ class RetentionBonus(Document): self.db_set('additional_salary', additional_salary) def on_cancel(self): + + additional_salary = self.get_additional_salary() if self.additional_salary: bonus_removed = frappe.db.get_value('Additional Salary', self.additional_salary, 'amount') - self.bonus_amount if bonus_removed == 0: frappe.get_doc('Additional Salary', self.additional_salary).cancel() else: frappe.db.set_value('Additional Salary', self.additional_salary, 'amount', bonus_removed) - - self.db_set('additional_salary', '') \ No newline at end of file + + # self.db_set('additional_salary', '') + + def get_additional_salary(self): + return frappe.db.exists('Additional Salary', { + 'employee': self.employee, + 'salary_component': self.salary_component, + 'payroll_date': self.bonus_payment_date, + 'company': company, + 'docstatus': 1, + 'ref_doctype': self.doctype, + 'ref_docname': self.name + }) diff --git a/erpnext/hr/doctype/retention_bonus/test_retention_bonus.js b/erpnext/payroll/doctype/retention_bonus/test_retention_bonus.js similarity index 100% rename from erpnext/hr/doctype/retention_bonus/test_retention_bonus.js rename to erpnext/payroll/doctype/retention_bonus/test_retention_bonus.js diff --git a/erpnext/hr/doctype/retention_bonus/test_retention_bonus.py b/erpnext/payroll/doctype/retention_bonus/test_retention_bonus.py similarity index 100% rename from erpnext/hr/doctype/retention_bonus/test_retention_bonus.py rename to erpnext/payroll/doctype/retention_bonus/test_retention_bonus.py diff --git a/erpnext/hr/doctype/salary_component/README.md b/erpnext/payroll/doctype/salary_component/README.md similarity index 100% rename from erpnext/hr/doctype/salary_component/README.md rename to erpnext/payroll/doctype/salary_component/README.md diff --git a/erpnext/hr/doctype/salary_structure_assignment/__init__.py b/erpnext/payroll/doctype/salary_component/__init__.py similarity index 100% rename from erpnext/hr/doctype/salary_structure_assignment/__init__.py rename to erpnext/payroll/doctype/salary_component/__init__.py diff --git a/erpnext/hr/doctype/salary_component/salary_component.js b/erpnext/payroll/doctype/salary_component/salary_component.js similarity index 100% rename from erpnext/hr/doctype/salary_component/salary_component.js rename to erpnext/payroll/doctype/salary_component/salary_component.js diff --git a/erpnext/hr/doctype/salary_component/salary_component.json b/erpnext/payroll/doctype/salary_component/salary_component.json similarity index 95% rename from erpnext/hr/doctype/salary_component/salary_component.json rename to erpnext/payroll/doctype/salary_component/salary_component.json index 97c46c829e7..f1e5cf090dd 100644 --- a/erpnext/hr/doctype/salary_component/salary_component.json +++ b/erpnext/payroll/doctype/salary_component/salary_component.json @@ -16,6 +16,7 @@ "column_break_4", "depends_on_payment_days", "is_tax_applicable", + "is_income_tax_component", "deduct_full_tax_on_selected_payroll_date", "variable_based_on_taxable_salary", "exempted_from_income_tax", @@ -231,13 +232,22 @@ "fieldname": "exempted_from_income_tax", "fieldtype": "Check", "label": "Exempted from Income Tax" + }, + { + "default": "0", + "depends_on": "eval:doc.type == \"Deduction\"", + "fieldname": "is_income_tax_component", + "fieldtype": "Check", + "label": "Is Income Tax Component", + "show_days": 1, + "show_seconds": 1 } ], "icon": "fa fa-flag", "links": [], - "modified": "2020-04-28 15:46:45.252945", + "modified": "2020-06-01 15:39:20.826565", "modified_by": "Administrator", - "module": "HR", + "module": "Payroll", "name": "Salary Component", "owner": "Administrator", "permissions": [ diff --git a/erpnext/hr/doctype/salary_component/salary_component.py b/erpnext/payroll/doctype/salary_component/salary_component.py similarity index 100% rename from erpnext/hr/doctype/salary_component/salary_component.py rename to erpnext/payroll/doctype/salary_component/salary_component.py diff --git a/erpnext/hr/doctype/salary_component/test_records.json b/erpnext/payroll/doctype/salary_component/test_records.json similarity index 100% rename from erpnext/hr/doctype/salary_component/test_records.json rename to erpnext/payroll/doctype/salary_component/test_records.json diff --git a/erpnext/hr/doctype/salary_component/test_salary_component.js b/erpnext/payroll/doctype/salary_component/test_salary_component.js similarity index 100% rename from erpnext/hr/doctype/salary_component/test_salary_component.js rename to erpnext/payroll/doctype/salary_component/test_salary_component.js diff --git a/erpnext/hr/doctype/salary_component/test_salary_component.py b/erpnext/payroll/doctype/salary_component/test_salary_component.py similarity index 100% rename from erpnext/hr/doctype/salary_component/test_salary_component.py rename to erpnext/payroll/doctype/salary_component/test_salary_component.py diff --git a/erpnext/hr/doctype/taxable_salary_slab/__init__.py b/erpnext/payroll/doctype/salary_detail/__init__.py similarity index 100% rename from erpnext/hr/doctype/taxable_salary_slab/__init__.py rename to erpnext/payroll/doctype/salary_detail/__init__.py diff --git a/erpnext/hr/doctype/salary_detail/salary_detail.json b/erpnext/payroll/doctype/salary_detail/salary_detail.json similarity index 99% rename from erpnext/hr/doctype/salary_detail/salary_detail.json rename to erpnext/payroll/doctype/salary_detail/salary_detail.json index fe5f83b5328..b7d2bc12725 100644 --- a/erpnext/hr/doctype/salary_detail/salary_detail.json +++ b/erpnext/payroll/doctype/salary_detail/salary_detail.json @@ -211,13 +211,13 @@ ], "istable": 1, "links": [], - "modified": "2020-04-04 20:00:16.475295", + "modified": "2020-05-27 23:21:26.300951", "modified_by": "Administrator", - "module": "HR", + "module": "Payroll", "name": "Salary Detail", "owner": "Administrator", "permissions": [], "quick_entry": 1, "sort_field": "modified", "sort_order": "DESC" -} +} \ No newline at end of file diff --git a/erpnext/hr/doctype/salary_detail/salary_detail.py b/erpnext/payroll/doctype/salary_detail/salary_detail.py similarity index 100% rename from erpnext/hr/doctype/salary_detail/salary_detail.py rename to erpnext/payroll/doctype/salary_detail/salary_detail.py diff --git a/erpnext/hr/doctype/salary_slip/README.md b/erpnext/payroll/doctype/salary_slip/README.md similarity index 100% rename from erpnext/hr/doctype/salary_slip/README.md rename to erpnext/payroll/doctype/salary_slip/README.md diff --git a/erpnext/hr/doctype/salary_slip/__init__.py b/erpnext/payroll/doctype/salary_slip/__init__.py similarity index 100% rename from erpnext/hr/doctype/salary_slip/__init__.py rename to erpnext/payroll/doctype/salary_slip/__init__.py diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.js b/erpnext/payroll/doctype/salary_slip/salary_slip.js similarity index 93% rename from erpnext/hr/doctype/salary_slip/salary_slip.js rename to erpnext/payroll/doctype/salary_slip/salary_slip.js index 1c4d4e34c56..4b623e57b48 100644 --- a/erpnext/hr/doctype/salary_slip/salary_slip.js +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.js @@ -56,7 +56,7 @@ frappe.ui.form.on("Salary Slip", { set_end_date: function(frm){ frappe.call({ - method: 'erpnext.hr.doctype.payroll_entry.payroll_entry.get_end_date', + method: 'erpnext.payroll.doctype.payroll_entry.payroll_entry.get_end_date', args: { frequency: frm.doc.payroll_frequency, start_date: frm.doc.start_date @@ -123,6 +123,9 @@ frappe.ui.form.on("Salary Slip", { doc: frm.doc, callback: function(r, rt) { frm.refresh(); + if (frm.doc.absent_days){ + frm.fields_dict.absent_days.set_description("Unmarked Days is treated as "+ r.message +". You can can change this in " + frappe.utils.get_form_link("Payroll Settings", "Payroll Settings", true)); + } } }); } diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.json b/erpnext/payroll/doctype/salary_slip/salary_slip.json similarity index 70% rename from erpnext/hr/doctype/salary_slip/salary_slip.json rename to erpnext/payroll/doctype/salary_slip/salary_slip.json index cfd4d897314..a6337deef58 100644 --- a/erpnext/hr/doctype/salary_slip/salary_slip.json +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.json @@ -23,11 +23,13 @@ "salary_slip_based_on_timesheet", "start_date", "end_date", - "column_break_15", "salary_structure", "payroll_frequency", + "column_break_15", "total_working_days", + "unmarked_days", "leave_without_pay", + "absent_days", "payment_days", "hourly_wages", "timesheets", @@ -37,6 +39,7 @@ "section_break_26", "bank_name", "bank_account_no", + "mode_of_payment", "section_break_32", "deduct_tax_for_unclaimed_employee_benefits", "deduct_tax_for_unsubmitted_tax_exemption_proof", @@ -71,7 +74,9 @@ "fieldtype": "Date", "in_list_view": 1, "label": "Posting Date", - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "employee", @@ -84,7 +89,9 @@ "oldfieldtype": "Link", "options": "Employee", "reqd": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "fetch_from": "employee.employee_name", @@ -95,7 +102,9 @@ "label": "Employee Name", "oldfieldname": "employee_name", "oldfieldtype": "Data", - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fetch_from": "employee.department", @@ -106,7 +115,9 @@ "oldfieldname": "department", "oldfieldtype": "Link", "options": "Department", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.designation", @@ -115,7 +126,9 @@ "fieldtype": "Read Only", "label": "Designation", "oldfieldname": "designation", - "oldfieldtype": "Link" + "oldfieldtype": "Link", + "show_days": 1, + "show_seconds": 1 }, { "fetch_from": "employee.branch", @@ -126,12 +139,16 @@ "oldfieldname": "branch", "oldfieldtype": "Link", "options": "Branch", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break1", "fieldtype": "Column Break", "oldfieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -139,21 +156,27 @@ "fieldtype": "Select", "label": "Status", "options": "Draft\nSubmitted\nCancelled", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "journal_entry", "fieldtype": "Link", "label": "Journal Entry", "options": "Journal Entry", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "payroll_entry", "fieldtype": "Link", "label": "Payroll Entry", "options": "Payroll Entry", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "company", @@ -163,7 +186,9 @@ "label": "Company", "options": "Company", "remember_last_selected_value": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -172,46 +197,62 @@ "ignore_user_permissions": 1, "label": "Letter Head", "options": "Letter Head", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "section_break_10", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "salary_slip_based_on_timesheet", "fieldtype": "Check", "label": "Salary Slip Based on Timesheet", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "start_date", "fieldtype": "Date", - "label": "Start Date" + "label": "Start Date", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "end_date", "fieldtype": "Date", - "label": "End Date" + "label": "End Date", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_15", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "salary_structure", "fieldtype": "Link", "label": "Salary Structure", "options": "Salary Structure", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:(!doc.salary_slip_based_on_timesheet)", "fieldname": "payroll_frequency", "fieldtype": "Select", "label": "Payroll Frequency", - "options": "\nMonthly\nFortnightly\nBimonthly\nWeekly\nDaily" + "options": "\nMonthly\nFortnightly\nBimonthly\nWeekly\nDaily", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_working_days", @@ -220,14 +261,18 @@ "oldfieldname": "total_days_in_month", "oldfieldtype": "Int", "read_only": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "leave_without_pay", "fieldtype": "Float", "label": "Leave Without Pay", "oldfieldname": "leave_without_pay", - "oldfieldtype": "Currency" + "oldfieldtype": "Currency", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "payment_days", @@ -236,38 +281,52 @@ "oldfieldname": "payment_days", "oldfieldtype": "Float", "read_only": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "hourly_wages", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "timesheets", "fieldtype": "Table", "label": "Salary Slip Timesheet", - "options": "Salary Slip Timesheet" + "options": "Salary Slip Timesheet", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_20", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_working_hours", "fieldtype": "Float", "label": "Total Working Hours", - "print_hide_if_no_value": 1 + "print_hide_if_no_value": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "hour_rate", "fieldtype": "Currency", "label": "Hour Rate", "options": "Company:company:default_currency", - "print_hide_if_no_value": 1 + "print_hide_if_no_value": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "section_break_26", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "bank_name", @@ -275,7 +334,9 @@ "label": "Bank Name", "oldfieldname": "bank_name", "oldfieldtype": "Data", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "bank_account_no", @@ -283,35 +344,47 @@ "label": "Bank Account No.", "oldfieldname": "bank_account_no", "oldfieldtype": "Data", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "section_break_32", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "deduct_tax_for_unclaimed_employee_benefits", "fieldtype": "Check", - "label": "Deduct Tax For Unclaimed Employee Benefits" + "label": "Deduct Tax For Unclaimed Employee Benefits", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "deduct_tax_for_unsubmitted_tax_exemption_proof", "fieldtype": "Check", - "label": "Deduct Tax For Unsubmitted Tax Exemption Proof" + "label": "Deduct Tax For Unsubmitted Tax Exemption Proof", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "earning_deduction", "fieldtype": "Section Break", "label": "Earning & Deduction", - "oldfieldtype": "Section Break" + "oldfieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "earning", "fieldtype": "Column Break", "label": "Earning", "oldfieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -320,13 +393,17 @@ "label": "Earnings", "oldfieldname": "earning_details", "oldfieldtype": "Table", - "options": "Salary Detail" + "options": "Salary Detail", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "deduction", "fieldtype": "Column Break", "label": "Deduction", "oldfieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -335,12 +412,16 @@ "label": "Deductions", "oldfieldname": "deduction_details", "oldfieldtype": "Table", - "options": "Salary Detail" + "options": "Salary Detail", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "totals", "fieldtype": "Section Break", - "oldfieldtype": "Section Break" + "oldfieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "gross_pay", @@ -349,11 +430,15 @@ "oldfieldname": "gross_pay", "oldfieldtype": "Currency", "options": "Company:company:default_currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_25", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_deduction", @@ -362,24 +447,32 @@ "oldfieldname": "total_deduction", "oldfieldtype": "Currency", "options": "Company:company:default_currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "total_loan_repayment", "fieldname": "loan_repayment", "fieldtype": "Section Break", - "label": "Loan repayment" + "label": "Loan repayment", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "loans", "fieldtype": "Table", "label": "Employee Loan", "options": "Salary Slip Loan", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "section_break_43", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", @@ -387,7 +480,9 @@ "fieldtype": "Currency", "label": "Total Principal Amount", "options": "Company:company:default_currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", @@ -395,11 +490,15 @@ "fieldtype": "Currency", "label": "Total Interest Amount", "options": "Company:company:default_currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_45", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", @@ -407,12 +506,16 @@ "fieldtype": "Currency", "label": "Total Loan Repayment", "options": "Company:company:default_currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "net_pay_info", "fieldtype": "Section Break", - "label": "net pay info" + "label": "net pay info", + "show_days": 1, + "show_seconds": 1 }, { "description": "Gross Pay - Total Deduction - Loan Repayment", @@ -422,11 +525,15 @@ "oldfieldname": "net_pay", "oldfieldtype": "Currency", "options": "Company:company:default_currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_53", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "bold": 1, @@ -434,11 +541,15 @@ "fieldtype": "Currency", "label": "Rounded Total", "options": "Company:company:default_currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "section_break_55", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "description": "Net Pay (in words) will be visible once you save the Salary Slip.", @@ -447,7 +558,9 @@ "label": "Total in words", "oldfieldname": "net_pay_in_words", "oldfieldtype": "Data", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "amended_from", @@ -459,7 +572,9 @@ "oldfieldtype": "Data", "options": "Salary Slip", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fetch_from": "employee.payroll_cost_center", @@ -468,16 +583,42 @@ "fieldtype": "Link", "label": "Payroll Cost Center", "options": "Cost Center", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "mode_of_payment", + "fieldtype": "Select", + "label": "Mode Of Payment", + "read_only": 1, + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "absent_days", + "fieldtype": "Float", + "label": "Absent Days", + "read_only": 1, + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "unmarked_days", + "fieldtype": "Float", + "hidden": 1, + "label": "Unmarked days", + "show_days": 1, + "show_seconds": 1 } ], "icon": "fa fa-file-text", "idx": 9, "is_submittable": 1, "links": [], - "modified": "2020-05-05 18:55:26.173629", + "modified": "2020-06-05 14:42:43.921828", "modified_by": "Administrator", - "module": "HR", + "module": "Payroll", "name": "Salary Slip", "owner": "Administrator", "permissions": [ diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.py b/erpnext/payroll/doctype/salary_slip/salary_slip.py similarity index 92% rename from erpnext/hr/doctype/salary_slip/salary_slip.py rename to erpnext/payroll/doctype/salary_slip/salary_slip.py index 4d5c8437c67..2da19b03978 100644 --- a/erpnext/hr/doctype/salary_slip/salary_slip.py +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py @@ -9,14 +9,14 @@ from frappe.utils import add_days, cint, cstr, flt, getdate, rounded, date_diff, from frappe.model.naming import make_autoname from frappe import msgprint, _ -from erpnext.hr.doctype.payroll_entry.payroll_entry import get_start_end_dates +from erpnext.payroll.doctype.payroll_entry.payroll_entry import get_start_end_dates from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee from erpnext.utilities.transaction_base import TransactionBase from frappe.utils.background_jobs import enqueue -from erpnext.hr.doctype.additional_salary.additional_salary import get_additional_salary_component -from erpnext.hr.doctype.payroll_period.payroll_period import get_period_factor, get_payroll_period -from erpnext.hr.doctype.employee_benefit_application.employee_benefit_application import get_benefit_component_amount -from erpnext.hr.doctype.employee_benefit_claim.employee_benefit_claim import get_benefit_claim_amount, get_last_payroll_period_benefits +from erpnext.payroll.doctype.additional_salary.additional_salary import get_additional_salary_component +from erpnext.payroll.doctype.payroll_period.payroll_period import get_period_factor, get_payroll_period +from erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application import get_benefit_component_amount +from erpnext.payroll.doctype.employee_benefit_claim.employee_benefit_claim import get_benefit_claim_amount, get_last_payroll_period_benefits from erpnext.loan_management.doctype.loan_repayment.loan_repayment import calculate_amounts, create_repayment_entry class SalarySlip(TransactionBase): @@ -54,8 +54,8 @@ class SalarySlip(TransactionBase): total = self.net_pay if self.is_rounding_total_disabled() else self.rounded_total self.total_in_words = money_in_words(total, company_currency) - if frappe.db.get_single_value("HR Settings", "max_working_hours_against_timesheet"): - max_working_hours = frappe.db.get_single_value("HR Settings", "max_working_hours_against_timesheet") + if frappe.db.get_single_value("Payroll Settings", "max_working_hours_against_timesheet"): + max_working_hours = frappe.db.get_single_value("Payroll Settings", "max_working_hours_against_timesheet") if self.salary_slip_based_on_timesheet and (self.total_working_hours > int(max_working_hours)): frappe.msgprint(_("Total working hours should not be greater than max working hours {0}"). format(max_working_hours), alert=True) @@ -67,7 +67,7 @@ class SalarySlip(TransactionBase): self.set_status() self.update_status(self.name) self.make_loan_repayment_entry() - if (frappe.db.get_single_value("HR Settings", "email_salary_slip_to_employee")) and not frappe.flags.via_payroll_entry: + if (frappe.db.get_single_value("Payroll Settings", "email_salary_slip_to_employee")) and not frappe.flags.via_payroll_entry: self.email_salary_slip() def on_cancel(self): @@ -93,7 +93,7 @@ class SalarySlip(TransactionBase): frappe.throw(_("To date cannot be before From date")) def is_rounding_total_disabled(self): - return cint(frappe.db.get_single_value("HR Settings", "disable_rounded_total")) + return cint(frappe.db.get_single_value("Payroll Settings", "disable_rounded_total")) def check_existing(self): if not self.salary_slip_based_on_timesheet: @@ -136,6 +136,8 @@ class SalarySlip(TransactionBase): self.salary_slip_based_on_timesheet = self._salary_structure_doc.salary_slip_based_on_timesheet or 0 self.set_time_sheet() self.pull_sal_struct() + consider_unmarked_attendance_as = frappe.db.get_value("Payroll Settings", None, "consider_unmarked_attendance_as") or "Present" + return consider_unmarked_attendance_as def set_time_sheet(self): if self.salary_slip_based_on_timesheet: @@ -175,7 +177,7 @@ class SalarySlip(TransactionBase): .format(self.employee), title=_('Salary Structure Missing')) def pull_sal_struct(self): - from erpnext.hr.doctype.salary_structure.salary_structure import make_salary_slip + from erpnext.payroll.doctype.salary_structure.salary_structure import make_salary_slip if self.salary_slip_based_on_timesheet: self.salary_structure = self._salary_structure_doc.name @@ -188,8 +190,8 @@ class SalarySlip(TransactionBase): make_salary_slip(self._salary_structure_doc.name, self) def get_working_days_details(self, joining_date=None, relieving_date=None, lwp=None, for_preview=0): - payroll_based_on = frappe.db.get_value("HR Settings", None, "payroll_based_on") - include_holidays_in_total_working_days = frappe.db.get_single_value("HR Settings", "include_holidays_in_total_working_days") + payroll_based_on = frappe.db.get_value("Payroll Settings", None, "payroll_based_on") + include_holidays_in_total_working_days = frappe.db.get_single_value("Payroll Settings", "include_holidays_in_total_working_days") working_days = date_diff(self.end_date, self.start_date) + 1 if for_preview: @@ -198,7 +200,7 @@ class SalarySlip(TransactionBase): return holidays = self.get_holidays_for_employee(self.start_date, self.end_date) - + if not cint(include_holidays_in_total_working_days): working_days -= len(holidays) if working_days < 0: @@ -206,9 +208,10 @@ class SalarySlip(TransactionBase): if not payroll_based_on: frappe.throw(_("Please set Payroll based on in HR settings")) - + if payroll_based_on == "Attendance": - actual_lwp = self.calculate_lwp_based_on_attendance(holidays) + actual_lwp, absent = self.calculate_lwp_and_absent_days_based_on_attendance(holidays) + self.absent_days = absent else: actual_lwp = self.calculate_lwp_based_on_leave_application(holidays, working_days) @@ -226,9 +229,36 @@ class SalarySlip(TransactionBase): if flt(payment_days) > flt(lwp): self.payment_days = flt(payment_days) - flt(lwp) + + if payroll_based_on == "Attendance": + self.payment_days -= flt(absent) + + unmarked_days = self.get_unmarked_days() + consider_unmarked_attendance_as = frappe.db.get_value("Payroll Settings", None, "consider_unmarked_attendance_as") or "Present" + + if payroll_based_on == "Attendance" and consider_unmarked_attendance_as =="Absent": + self.absent_days += unmarked_days #will be treated as absent + self.payment_days -= unmarked_days + if include_holidays_in_total_working_days: + self.absent_days -= len(holidays) + for holiday in holidays: + if not frappe.db.exists("Attendance", {"employee": self.employee, "attendance_date": holiday, "docstatus": 1 }): + self.payment_days += 1 + + else: self.payment_days = 0 + def get_unmarked_days(self): + marked_days = frappe.get_all("Attendance", filters = { + "attendance_date": ["between", ['2020-05-1',"2020-05-30"]], + "employee": 'HR-EMP-00003', + "docstatus": 1 + }, fields = ["COUNT(*) as marked_days"])[0].marked_days + + return self.total_working_days - marked_days + + def get_payment_days(self, joining_date, relieving_date, include_holidays_in_total_working_days): if not joining_date: joining_date, relieving_date = frappe.get_cached_value("Employee", self.employee, @@ -277,7 +307,7 @@ class SalarySlip(TransactionBase): lwp = 0 holidays = "','".join(holidays) daily_wages_fraction_for_half_day = \ - flt(frappe.db.get_value("HR Settings", None, "daily_wages_fraction_for_half_day")) or 0.5 + flt(frappe.db.get_value("Payroll Settings", None, "daily_wages_fraction_for_half_day")) or 0.5 for d in range(working_days): dt = add_days(cstr(getdate(self.start_date)), d) @@ -304,15 +334,15 @@ class SalarySlip(TransactionBase): lwp += (1 - daily_wages_fraction_for_half_day) if is_half_day_leave else 1 return lwp - - def calculate_lwp_based_on_attendance(self, holidays): + + def calculate_lwp_and_absent_days_based_on_attendance(self, holidays): lwp = 0 + absent = 0 daily_wages_fraction_for_half_day = \ - flt(frappe.db.get_value("HR Settings", None, "daily_wages_fraction_for_half_day")) or 0.5 + flt(frappe.db.get_value("Payroll Settings", None, "daily_wages_fraction_for_half_day")) or 0.5 lwp_leave_types = dict(frappe.get_all("Leave Type", {"is_lwp": 1}, ["name", "include_holiday"], as_list=1)) - attendances = frappe.db.sql(''' SELECT attendance_date, status, leave_type FROM `tabAttendance` @@ -322,7 +352,7 @@ class SalarySlip(TransactionBase): AND docstatus = 1 AND attendance_date between %s and %s ''', values=(self.employee, self.start_date, self.end_date), as_dict=1) - + for d in attendances: if d.status in ('Half Day', 'On Leave') and d.leave_type and d.leave_type not in lwp_leave_types: continue @@ -332,9 +362,14 @@ class SalarySlip(TransactionBase): (d.leave_type and d.leave_type in lwp_leave_types and not lwp_leave_types[d.leave_type]): continue - lwp += (1 - daily_wages_fraction_for_half_day) if d.status == "Half Day" else 1 + if d.status == "Half Day": + lwp += (1 - daily_wages_fraction_for_half_day) + elif d.status == "On Leave" and d.leave_type in lwp_leave_types: + lwp += 1 + elif d.status == "Absent": + absent += 1 - return lwp + return lwp, absent def add_earning_for_hourly_wages(self, doc, salary_component, amount): row_exists = False @@ -578,7 +613,7 @@ class SalarySlip(TransactionBase): # Total taxable earnings including additional and other incomes total_taxable_earnings = previous_taxable_earnings + current_structured_taxable_earnings + future_structured_taxable_earnings \ + current_additional_earnings + other_incomes + unclaimed_taxable_benefits - total_exemption_amount - + # Total taxable earnings without additional earnings with full tax total_taxable_earnings_without_full_tax_addl_components = total_taxable_earnings - current_additional_earnings_with_full_tax @@ -586,7 +621,7 @@ class SalarySlip(TransactionBase): total_structured_tax_amount = self.calculate_tax_by_tax_slab( total_taxable_earnings_without_full_tax_addl_components, tax_slab) current_structured_tax_amount = (total_structured_tax_amount - previous_total_paid_taxes) / remaining_sub_periods - + # Total taxable earnings with additional earnings with full tax full_tax_on_additional_earnings = 0.0 if current_additional_earnings_with_full_tax: @@ -622,7 +657,7 @@ class SalarySlip(TransactionBase): select sum(sd.amount) from `tabSalary Detail` sd join `tabSalary Slip` ss on sd.parent=ss.name - where + where sd.parentfield='earnings' and sd.is_tax_applicable=1 and is_flexible_benefit=0 @@ -841,7 +876,7 @@ class SalarySlip(TransactionBase): if flt(d.max_taxable_income) and flt(d.max_taxable_income) < tax_amount: continue - + tax_amount += tax_amount * flt(d.percent) / 100 return tax_amount @@ -955,13 +990,13 @@ class SalarySlip(TransactionBase): def email_salary_slip(self): receiver = frappe.db.get_value("Employee", self.employee, "prefered_email") - hr_settings = frappe.get_single("HR Settings") + payroll_settings = frappe.get_single("Payroll Settings") message = "Please see attachment" password = None - if hr_settings.encrypt_salary_slips_in_emails: - password = generate_password_for_pdf(hr_settings.password_policy, self.employee) + if payroll_settings.encrypt_salary_slips_in_emails: + password = generate_password_for_pdf(payroll_settings.password_policy, self.employee) message += """
    Note: Your salary slip is password protected, - the password to unlock the PDF is of the format {0}. """.format(hr_settings.password_policy) + the password to unlock the PDF is of the format {0}. """.format(payroll_settings.password_policy) if receiver: email_args = { @@ -1004,8 +1039,9 @@ class SalarySlip(TransactionBase): self.calculate_net_pay() def pull_emp_details(self): - emp = frappe.db.get_value("Employee", self.employee, ["bank_name", "bank_ac_no"], as_dict=1) + emp = frappe.db.get_value("Employee", self.employee, ["bank_name", "bank_ac_no", "salary_mode"], as_dict=1) if emp: + self.mode_of_payment = emp.salary_mode self.bank_name = emp.bank_name self.bank_account_no = emp.bank_ac_no diff --git a/erpnext/hr/doctype/salary_slip/salary_slip_list.js b/erpnext/payroll/doctype/salary_slip/salary_slip_list.js similarity index 100% rename from erpnext/hr/doctype/salary_slip/salary_slip_list.js rename to erpnext/payroll/doctype/salary_slip/salary_slip_list.js diff --git a/erpnext/hr/doctype/salary_slip/test_salary_slip.js b/erpnext/payroll/doctype/salary_slip/test_salary_slip.js similarity index 100% rename from erpnext/hr/doctype/salary_slip/test_salary_slip.js rename to erpnext/payroll/doctype/salary_slip/test_salary_slip.js diff --git a/erpnext/hr/doctype/salary_slip/test_salary_slip.py b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py similarity index 94% rename from erpnext/hr/doctype/salary_slip/test_salary_slip.py rename to erpnext/payroll/doctype/salary_slip/test_salary_slip.py index 3eff738ec8d..f42b9ad11dc 100644 --- a/erpnext/hr/doctype/salary_slip/test_salary_slip.py +++ b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py @@ -10,17 +10,17 @@ import random from erpnext.accounts.utils import get_fiscal_year from frappe.utils.make_random import get_random from frappe.utils import getdate, nowdate, add_days, add_months, flt, get_first_day, get_last_day -from erpnext.hr.doctype.salary_structure.salary_structure import make_salary_slip -from erpnext.hr.doctype.payroll_entry.payroll_entry import get_month_details +from erpnext.payroll.doctype.salary_structure.salary_structure import make_salary_slip +from erpnext.payroll.doctype.payroll_entry.payroll_entry import get_month_details from erpnext.hr.doctype.employee.test_employee import make_employee -from erpnext.hr.doctype.employee_tax_exemption_declaration.test_employee_tax_exemption_declaration \ +from erpnext.payroll.doctype.employee_tax_exemption_declaration.test_employee_tax_exemption_declaration \ import create_payroll_period, create_exemption_category class TestSalarySlip(unittest.TestCase): def setUp(self): setup_test() def tearDown(self): - frappe.db.set_value("HR Settings", None, "include_holidays_in_total_working_days", 0) + frappe.db.set_value("Payroll Settings", None, "include_holidays_in_total_working_days", 0) frappe.set_user("Administrator") def test_payment_days_based_on_attendance(self): @@ -28,8 +28,8 @@ class TestSalarySlip(unittest.TestCase): no_of_days = self.get_no_of_days() # Payroll based on attendance - frappe.db.set_value("HR Settings", None, "payroll_based_on", "Attendance") - frappe.db.set_value("HR Settings", None, "daily_wages_fraction_for_half_day", 0.75) + frappe.db.set_value("Payroll Settings", None, "payroll_based_on", "Attendance") + frappe.db.set_value("Payroll Settings", None, "daily_wages_fraction_for_half_day", 0.75) emp_id = make_employee("test_for_attendance@salary.com") frappe.db.set_value("Employee", emp_id, {"relieving_date": None, "status": "Active"}) @@ -47,7 +47,7 @@ class TestSalarySlip(unittest.TestCase): """, (month_start_date, month_end_date))[0][0] mark_attendance(emp_id, first_sunday, 'Absent', ignore_validate=True) # invalid lwp - mark_attendance(emp_id, add_days(first_sunday, 1), 'Absent', ignore_validate=True) # valid lwp + mark_attendance(emp_id, add_days(first_sunday, 1), 'Absent', ignore_validate=True) # counted as absent mark_attendance(emp_id, add_days(first_sunday, 2), 'Half Day', leave_type='Leave Without Pay', ignore_validate=True) # valid 0.75 lwp mark_attendance(emp_id, add_days(first_sunday, 3), 'On Leave', leave_type='Leave Without Pay', ignore_validate=True) # valid lwp mark_attendance(emp_id, add_days(first_sunday, 4), 'On Leave', leave_type='Casual Leave', ignore_validate=True) # invalid lwp @@ -55,7 +55,8 @@ class TestSalarySlip(unittest.TestCase): ss = make_employee_salary_slip("test_for_attendance@salary.com", "Monthly") - self.assertEqual(ss.leave_without_pay, 2.25) + self.assertEqual(ss.leave_without_pay, 1.25) + self.assertEqual(ss.absent_days, 1) days_in_month = no_of_days[0] no_of_holidays = no_of_days[1] @@ -63,17 +64,17 @@ class TestSalarySlip(unittest.TestCase): self.assertEqual(ss.payment_days, days_in_month - no_of_holidays - 2.25) #Gross pay calculation based on attendances - gross_pay = 78000 - ((78000 / (days_in_month - no_of_holidays)) * flt(ss.leave_without_pay)) + gross_pay = 78000 - ((78000 / (days_in_month - no_of_holidays)) * flt(ss.leave_without_pay + ss.absent_days)) self.assertEqual(ss.gross_pay, gross_pay) - frappe.db.set_value("HR Settings", None, "payroll_based_on", "Leave") + frappe.db.set_value("Payroll Settings", None, "payroll_based_on", "Leave") def test_payment_days_based_on_leave_application(self): no_of_days = self.get_no_of_days() # Payroll based on attendance - frappe.db.set_value("HR Settings", None, "payroll_based_on", "Leave") + frappe.db.set_value("Payroll Settings", None, "payroll_based_on", "Leave") emp_id = make_employee("test_for_attendance@salary.com") frappe.db.set_value("Employee", emp_id, {"relieving_date": None, "status": "Active"}) @@ -106,11 +107,11 @@ class TestSalarySlip(unittest.TestCase): self.assertEqual(ss.gross_pay, gross_pay) - frappe.db.set_value("HR Settings", None, "payroll_based_on", "Leave") + frappe.db.set_value("Payroll Settings", None, "payroll_based_on", "Leave") def test_salary_slip_with_holidays_included(self): no_of_days = self.get_no_of_days() - frappe.db.set_value("HR Settings", None, "include_holidays_in_total_working_days", 1) + frappe.db.set_value("Payroll Settings", None, "include_holidays_in_total_working_days", 1) make_employee("test_employee@salary.com") frappe.db.set_value("Employee", frappe.get_value("Employee", {"employee_name":"test_employee@salary.com"}, "name"), "relieving_date", None) @@ -126,7 +127,7 @@ class TestSalarySlip(unittest.TestCase): def test_salary_slip_with_holidays_excluded(self): no_of_days = self.get_no_of_days() - frappe.db.set_value("HR Settings", None, "include_holidays_in_total_working_days", 0) + frappe.db.set_value("Payroll Settings", None, "include_holidays_in_total_working_days", 0) make_employee("test_employee@salary.com") frappe.db.set_value("Employee", frappe.get_value("Employee", {"employee_name":"test_employee@salary.com"}, "name"), "relieving_date", None) @@ -144,7 +145,7 @@ class TestSalarySlip(unittest.TestCase): def test_payment_days(self): no_of_days = self.get_no_of_days() # Holidays not included in working days - frappe.db.set_value("HR Settings", None, "include_holidays_in_total_working_days", 1) + frappe.db.set_value("Payroll Settings", None, "include_holidays_in_total_working_days", 1) # set joinng date in the same month make_employee("test_employee@salary.com") @@ -200,7 +201,7 @@ class TestSalarySlip(unittest.TestCase): def test_email_salary_slip(self): frappe.db.sql("delete from `tabEmail Queue`") - frappe.db.set_value("HR Settings", None, "email_salary_slip_to_employee", 1) + frappe.db.set_value("Payroll Settings", None, "email_salary_slip_to_employee", 1) make_employee("test_employee@salary.com") ss = make_employee_salary_slip("test_employee@salary.com", "Monthly") @@ -270,7 +271,7 @@ class TestSalarySlip(unittest.TestCase): # as per assigned salary structure 40500 in monthly salary so 236000*5/100/12 frappe.db.sql("""delete from `tabPayroll Period`""") frappe.db.sql("""delete from `tabSalary Component`""") - + payroll_period = create_payroll_period() create_tax_slab(payroll_period, allow_tax_exemption=True) @@ -287,7 +288,7 @@ class TestSalarySlip(unittest.TestCase): for doc in delete_docs: frappe.db.sql("delete from `tab%s` where employee='%s'" % (doc, employee)) - from erpnext.hr.doctype.salary_structure.test_salary_structure import \ + from erpnext.payroll.doctype.salary_structure.test_salary_structure import \ make_salary_structure, create_salary_structure_assignment salary_structure = make_salary_structure("Stucture to test tax", "Monthly", other_details={"max_benefits": 100000}, test_tax=True) @@ -378,7 +379,7 @@ class TestSalarySlip(unittest.TestCase): return [no_of_days_in_month[1], no_of_holidays_in_month] def make_employee_salary_slip(user, payroll_frequency, salary_structure=None): - from erpnext.hr.doctype.salary_structure.test_salary_structure import make_salary_structure + from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure if not salary_structure: salary_structure = payroll_frequency + " Salary Structure Test for Salary Slip" @@ -699,7 +700,7 @@ def setup_test(): make_holiday_list() frappe.db.set_value("Company", erpnext.get_default_company(), "default_holiday_list", "Salary Slip Test Holiday List") - frappe.db.set_value("HR Settings", None, "email_salary_slip_to_employee", 0) + frappe.db.set_value("Payroll Settings", None, "email_salary_slip_to_employee", 0) frappe.db.set_value('HR Settings', None, 'leave_status_notification_template', None) frappe.db.set_value('HR Settings', None, 'leave_approval_notification_template', None) diff --git a/erpnext/hr/notification/retention_bonus/__init__.py b/erpnext/payroll/doctype/salary_slip_timesheet/__init__.py similarity index 100% rename from erpnext/hr/notification/retention_bonus/__init__.py rename to erpnext/payroll/doctype/salary_slip_timesheet/__init__.py diff --git a/erpnext/payroll/doctype/salary_slip_timesheet/salary_slip_timesheet.json b/erpnext/payroll/doctype/salary_slip_timesheet/salary_slip_timesheet.json new file mode 100644 index 00000000000..028c195a2fe --- /dev/null +++ b/erpnext/payroll/doctype/salary_slip_timesheet/salary_slip_timesheet.json @@ -0,0 +1,40 @@ +{ + "actions": [], + "creation": "2016-06-14 19:22:29.811658", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "time_sheet", + "working_hours" + ], + "fields": [ + { + "fieldname": "time_sheet", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Time Sheet", + "options": "Timesheet", + "reqd": 1 + }, + { + "fieldname": "working_hours", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Working Hours", + "no_copy": 1, + "read_only": 1 + } + ], + "istable": 1, + "links": [], + "modified": "2020-05-27 23:27:43.463532", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Salary Slip Timesheet", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC" +} \ No newline at end of file diff --git a/erpnext/hr/doctype/salary_slip_timesheet/salary_slip_timesheet.py b/erpnext/payroll/doctype/salary_slip_timesheet/salary_slip_timesheet.py similarity index 72% rename from erpnext/hr/doctype/salary_slip_timesheet/salary_slip_timesheet.py rename to erpnext/payroll/doctype/salary_slip_timesheet/salary_slip_timesheet.py index 1bbfc53a32d..7adb12e83a4 100644 --- a/erpnext/hr/doctype/salary_slip_timesheet/salary_slip_timesheet.py +++ b/erpnext/payroll/doctype/salary_slip_timesheet/salary_slip_timesheet.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt from __future__ import unicode_literals -import frappe +# import frappe from frappe.model.document import Document class SalarySlipTimesheet(Document): diff --git a/erpnext/hr/doctype/salary_structure/README.md b/erpnext/payroll/doctype/salary_structure/README.md similarity index 100% rename from erpnext/hr/doctype/salary_structure/README.md rename to erpnext/payroll/doctype/salary_structure/README.md diff --git a/erpnext/hr/doctype/salary_structure/__init__.py b/erpnext/payroll/doctype/salary_structure/__init__.py similarity index 100% rename from erpnext/hr/doctype/salary_structure/__init__.py rename to erpnext/payroll/doctype/salary_structure/__init__.py diff --git a/erpnext/payroll/doctype/salary_structure/condition_and_formula_help.html b/erpnext/payroll/doctype/salary_structure/condition_and_formula_help.html new file mode 100644 index 00000000000..e59d78d1b8b --- /dev/null +++ b/erpnext/payroll/doctype/salary_structure/condition_and_formula_help.html @@ -0,0 +1,47 @@ +

    Variables

    +
      +
    • + Variables from Salary Structure Assignment:
      + base = Base, variable = Variable etc. +
    • +
    • + Variables from Employee:
      Employment Type = employment_type, Branch = branch etc. +
    • +
    • + Variables Salary Slip:
      + Payment Days = payment_days, Leave without pay = leave_without_pay etc. +
    • +
    • + Abbreviation from Salary Component:
      + BS = Basic Salary etc. +
    • +
    • + Some additional variable:
      + gross_pay and annual_taxable_earning can also be used. +
    • +
    • Direct Amount can also be used
    • +
    + +

    Examples for Conditions and formula

    +
      +
    • + Calculating Basic Salary based on base +
      Condition: base < 10000
      +
      Formula: base * .2
      +
    • +
    • + Calculating HRA based on Basic SalaryBS +
      Condition: BS > 2000
      +
      Formula: BS * .1
      +
    • +
    • + Calculating TDS based on Employment Typeemployment_type +
      Condition: employment_type=="Intern"
      +
      Amount: 1000
      +
    • +
    • + Calculating Income Tax based on annual_taxable_earning +
      Condition: annual_taxable_earning > 20000000
      +
      Formula: annual_taxable_earning * 0.10 
      +
    • +
    \ No newline at end of file diff --git a/erpnext/hr/doctype/salary_structure/salary_structure.js b/erpnext/payroll/doctype/salary_structure/salary_structure.js similarity index 91% rename from erpnext/hr/doctype/salary_structure/salary_structure.js rename to erpnext/payroll/doctype/salary_structure/salary_structure.js index 7748403513f..ca458f976f6 100755 --- a/erpnext/hr/doctype/salary_structure/salary_structure.js +++ b/erpnext/payroll/doctype/salary_structure/salary_structure.js @@ -14,7 +14,30 @@ cur_frm.cscript.onload = function(doc, dt, dn){ frappe.ui.form.on('Salary Structure', { onload: function(frm) { - frm.toggle_reqd(['payroll_frequency'], !frm.doc.salary_slip_based_on_timesheet), + + let help_button = $(` + Condition and Formula Help + `).click(()=>{ + + let d = new frappe.ui.Dialog({ + title: 'Condition and Formula Help', + fields: [ + { + fieldname: 'msg_wrapper', + fieldtype: 'HTML' + } + ] + }); + + let message_html = frappe.render_template("condition_and_formula_help") + + d.fields_dict.msg_wrapper.$wrapper.append(message_html) + + d.show() + }); + frm.get_field("conditions_and_formula_variable_and_example").$wrapper.append(frm.doc.filters_html).append(help_button) + + frm.toggle_reqd(['payroll_frequency'], !frm.doc.salary_slip_based_on_timesheet) frm.set_query("salary_component", "earnings", function() { return { @@ -114,7 +137,7 @@ frappe.ui.form.on('Salary Structure', { preview_salary_slip: function(frm) { frappe.call({ - method: "erpnext.hr.doctype.salary_structure.salary_structure.get_employees", + method: "erpnext.payroll.doctype.salary_structure.salary_structure.get_employees", args: { salary_structure: frm.doc.name }, @@ -155,7 +178,7 @@ frappe.ui.form.on('Salary Structure', { open_salary_slip: function(frm, employee){ var print_format = frm.doc.salary_slip_based_on_timesheet ? "Salary Slip based on Timesheet" : "Salary Slip Standard"; frappe.call({ - method: "erpnext.hr.doctype.salary_structure.salary_structure.make_salary_slip", + method: "erpnext.payroll.doctype.salary_structure.salary_structure.make_salary_slip", args: { source_name: frm.doc.name, employee: employee, diff --git a/erpnext/hr/doctype/salary_structure/salary_structure.json b/erpnext/payroll/doctype/salary_structure/salary_structure.json similarity index 71% rename from erpnext/hr/doctype/salary_structure/salary_structure.json rename to erpnext/payroll/doctype/salary_structure/salary_structure.json index 58c4044093e..e710f6b72cb 100644 --- a/erpnext/hr/doctype/salary_structure/salary_structure.json +++ b/erpnext/payroll/doctype/salary_structure/salary_structure.json @@ -22,10 +22,9 @@ "leave_encashment_amount_per_day", "max_benefits", "earning_deduction", - "earning", "earnings", - "deduction", "deductions", + "conditions_and_formula_variable_and_example", "net_pay_detail", "column_break2", "total_earning", @@ -44,17 +43,23 @@ "label": "Company", "options": "Company", "remember_last_selected_value": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "letter_head", "fieldtype": "Link", "label": "Letter Head", - "options": "Letter Head" + "options": "Letter Head", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break1", "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -67,7 +72,9 @@ "oldfieldname": "is_active", "oldfieldtype": "Select", "options": "\nYes\nNo", - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "Monthly", @@ -75,7 +82,9 @@ "fieldname": "payroll_frequency", "fieldtype": "Select", "label": "Payroll Frequency", - "options": "\nMonthly\nFortnightly\nBimonthly\nWeekly\nDaily" + "options": "\nMonthly\nFortnightly\nBimonthly\nWeekly\nDaily", + "show_days": 1, + "show_seconds": 1 }, { "default": "No", @@ -86,46 +95,62 @@ "no_copy": 1, "options": "Yes\nNo", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "time_sheet_earning_detail", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "salary_slip_based_on_timesheet", "fieldtype": "Check", - "label": "Salary Slip Based on Timesheet" + "label": "Salary Slip Based on Timesheet", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_17", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "description": "Salary Component for timesheet based payroll.", "fieldname": "salary_component", "fieldtype": "Link", "label": "Salary Component", - "options": "Salary Component" + "options": "Salary Component", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "hour_rate", "fieldtype": "Currency", "label": "Hour Rate", - "options": "Company:company:default_currency" + "options": "Company:company:default_currency", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "leave_encashment_amount_per_day", "fieldtype": "Currency", "label": "Leave Encashment Amount Per Day", - "options": "Company:company:default_currency" + "options": "Company:company:default_currency", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "max_benefits", "fieldtype": "Currency", "label": "Max Benefits (Amount)", - "options": "Company:company:default_currency" + "options": "Company:company:default_currency", + "show_days": 1, + "show_seconds": 1 }, { "description": "Salary breakup based on Earning and Deduction.", @@ -133,15 +158,9 @@ "fieldtype": "Section Break", "oldfieldname": "earning_deduction", "oldfieldtype": "Section Break", - "precision": "2" - }, - { - "fieldname": "earning", - "fieldtype": "Section Break", - "label": "Earning", - "oldfieldname": "col_brk2", - "oldfieldtype": "Column Break", - "width": "50%" + "precision": "2", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "earnings", @@ -149,15 +168,9 @@ "label": "Earnings", "oldfieldname": "earning_details", "oldfieldtype": "Table", - "options": "Salary Detail" - }, - { - "fieldname": "deduction", - "fieldtype": "Section Break", - "label": "Deduction", - "oldfieldname": "col_brk3", - "oldfieldtype": "Column Break", - "width": "50%" + "options": "Salary Detail", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "deductions", @@ -165,16 +178,22 @@ "label": "Deductions", "oldfieldname": "deduction_details", "oldfieldtype": "Table", - "options": "Salary Detail" + "options": "Salary Detail", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "net_pay_detail", "fieldtype": "Section Break", - "options": "Simple" + "options": "Simple", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break2", "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -185,7 +204,9 @@ "oldfieldname": "total_earning", "oldfieldtype": "Currency", "options": "Company:company:default_currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_deduction", @@ -195,7 +216,9 @@ "oldfieldname": "total_deduction", "oldfieldtype": "Currency", "options": "Company:company:default_currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "net_pay", @@ -203,28 +226,38 @@ "hidden": 1, "label": "Net Pay", "options": "Company:company:default_currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "account", "fieldtype": "Section Break", - "label": "Account" + "label": "Account", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "mode_of_payment", "fieldtype": "Link", "label": "Mode of Payment", - "options": "Mode of Payment" + "options": "Mode of Payment", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_28", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "payment_account", "fieldtype": "Link", "label": "Payment Account", - "options": "Account" + "options": "Account", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "amended_from", @@ -233,16 +266,25 @@ "no_copy": 1, "options": "Salary Structure", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "conditions_and_formula_variable_and_example", + "fieldtype": "HTML", + "label": "Conditions and Formula variable and example", + "show_days": 1, + "show_seconds": 1 } ], "icon": "fa fa-file-text", "idx": 1, "is_submittable": 1, "links": [], - "modified": "2019-12-31 16:34:35.087658", + "modified": "2020-06-05 17:07:26.129355", "modified_by": "Administrator", - "module": "HR", + "module": "Payroll", "name": "Salary Structure", "owner": "Administrator", "permissions": [ diff --git a/erpnext/hr/doctype/salary_structure/salary_structure.py b/erpnext/payroll/doctype/salary_structure/salary_structure.py similarity index 100% rename from erpnext/hr/doctype/salary_structure/salary_structure.py rename to erpnext/payroll/doctype/salary_structure/salary_structure.py diff --git a/erpnext/hr/doctype/salary_structure/salary_structure_dashboard.py b/erpnext/payroll/doctype/salary_structure/salary_structure_dashboard.py similarity index 100% rename from erpnext/hr/doctype/salary_structure/salary_structure_dashboard.py rename to erpnext/payroll/doctype/salary_structure/salary_structure_dashboard.py diff --git a/erpnext/hr/doctype/salary_structure/test_salary_structure.js b/erpnext/payroll/doctype/salary_structure/test_salary_structure.js similarity index 100% rename from erpnext/hr/doctype/salary_structure/test_salary_structure.js rename to erpnext/payroll/doctype/salary_structure/test_salary_structure.js diff --git a/erpnext/hr/doctype/salary_structure/test_salary_structure.py b/erpnext/payroll/doctype/salary_structure/test_salary_structure.py similarity index 94% rename from erpnext/hr/doctype/salary_structure/test_salary_structure.py rename to erpnext/payroll/doctype/salary_structure/test_salary_structure.py index eb5311e3b81..e04fda81202 100644 --- a/erpnext/hr/doctype/salary_structure/test_salary_structure.py +++ b/erpnext/payroll/doctype/salary_structure/test_salary_structure.py @@ -7,11 +7,11 @@ import unittest import erpnext from frappe.utils.make_random import get_random from frappe.utils import nowdate, add_days, add_years, getdate, add_months -from erpnext.hr.doctype.salary_structure.salary_structure import make_salary_slip -from erpnext.hr.doctype.salary_slip.test_salary_slip import make_earning_salary_component,\ +from erpnext.payroll.doctype.salary_structure.salary_structure import make_salary_slip +from erpnext.payroll.doctype.salary_slip.test_salary_slip import make_earning_salary_component,\ make_deduction_salary_component, make_employee_salary_slip, create_tax_slab from erpnext.hr.doctype.employee.test_employee import make_employee -from erpnext.hr.doctype.employee_tax_exemption_declaration.test_employee_tax_exemption_declaration import create_payroll_period +from erpnext.payroll.doctype.employee_tax_exemption_declaration.test_employee_tax_exemption_declaration import create_payroll_period test_dependencies = ["Fiscal Year"] @@ -62,7 +62,7 @@ class TestSalaryStructure(unittest.TestCase): self.assertEqual(assignment.base * 0.2, ss.deductions[0].amount) def test_amount_totals(self): - frappe.db.set_value("HR Settings", None, "include_holidays_in_total_working_days", 0) + frappe.db.set_value("Payroll Settings", None, "include_holidays_in_total_working_days", 0) sal_slip = frappe.get_value("Salary Slip", {"employee_name":"test_employee_2@salary.com"}) if not sal_slip: sal_slip = make_employee_salary_slip("test_employee_2@salary.com", "Monthly", "Salary Structure Sample") @@ -128,7 +128,7 @@ def make_salary_structure(salary_structure, payroll_frequency, employee=None, do salary_structure_doc.insert() if not dont_submit: salary_structure_doc.submit() - + else: salary_structure_doc = frappe.get_doc("Salary Structure", salary_structure) diff --git a/erpnext/hr/print_format/salary_slip_based_on_timesheet/__init__.py b/erpnext/payroll/doctype/salary_structure_assignment/__init__.py similarity index 100% rename from erpnext/hr/print_format/salary_slip_based_on_timesheet/__init__.py rename to erpnext/payroll/doctype/salary_structure_assignment/__init__.py diff --git a/erpnext/hr/doctype/salary_structure_assignment/salary_structure_assignment.js b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.js similarity index 100% rename from erpnext/hr/doctype/salary_structure_assignment/salary_structure_assignment.js rename to erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.js diff --git a/erpnext/hr/doctype/salary_structure_assignment/salary_structure_assignment.json b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.json similarity index 98% rename from erpnext/hr/doctype/salary_structure_assignment/salary_structure_assignment.json rename to erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.json index 0098aa8ec80..4f74a7fecf4 100644 --- a/erpnext/hr/doctype/salary_structure_assignment/salary_structure_assignment.json +++ b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.json @@ -124,9 +124,9 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-04-25 18:24:23.617088", + "modified": "2020-05-27 19:58:09.964692", "modified_by": "Administrator", - "module": "HR", + "module": "Payroll", "name": "Salary Structure Assignment", "owner": "Administrator", "permissions": [ diff --git a/erpnext/hr/doctype/salary_structure_assignment/salary_structure_assignment.py b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py similarity index 100% rename from erpnext/hr/doctype/salary_structure_assignment/salary_structure_assignment.py rename to erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py diff --git a/erpnext/hr/doctype/salary_structure_assignment/test_salary_structure_assignment.js b/erpnext/payroll/doctype/salary_structure_assignment/test_salary_structure_assignment.js similarity index 100% rename from erpnext/hr/doctype/salary_structure_assignment/test_salary_structure_assignment.js rename to erpnext/payroll/doctype/salary_structure_assignment/test_salary_structure_assignment.js diff --git a/erpnext/hr/doctype/salary_structure_assignment/test_salary_structure_assignment.py b/erpnext/payroll/doctype/salary_structure_assignment/test_salary_structure_assignment.py similarity index 100% rename from erpnext/hr/doctype/salary_structure_assignment/test_salary_structure_assignment.py rename to erpnext/payroll/doctype/salary_structure_assignment/test_salary_structure_assignment.py diff --git a/erpnext/hr/print_format/salary_slip_standard/__init__.py b/erpnext/payroll/doctype/taxable_salary_slab/__init__.py similarity index 100% rename from erpnext/hr/print_format/salary_slip_standard/__init__.py rename to erpnext/payroll/doctype/taxable_salary_slab/__init__.py diff --git a/erpnext/payroll/doctype/taxable_salary_slab/taxable_salary_slab.json b/erpnext/payroll/doctype/taxable_salary_slab/taxable_salary_slab.json new file mode 100644 index 00000000000..277576eff97 --- /dev/null +++ b/erpnext/payroll/doctype/taxable_salary_slab/taxable_salary_slab.json @@ -0,0 +1,64 @@ +{ + "actions": [], + "creation": "2018-04-13 17:42:13.516032", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "from_amount", + "to_amount", + "percent_deduction", + "condition", + "column_break_5", + "html_6" + ], + "fields": [ + { + "fieldname": "from_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "From Amount", + "reqd": 1 + }, + { + "fieldname": "to_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "To Amount" + }, + { + "fieldname": "percent_deduction", + "fieldtype": "Percent", + "in_list_view": 1, + "label": "Percent Deduction", + "reqd": 1 + }, + { + "fieldname": "condition", + "fieldtype": "Code", + "in_list_view": 1, + "label": "Condition" + }, + { + "fieldname": "column_break_5", + "fieldtype": "Column Break" + }, + { + "fieldname": "html_6", + "fieldtype": "HTML", + "options": "

    Condition Examples

    \n
      \n
    1. Applying tax if employee born between 31-12-1937 and 01-01-1958 (Employees aged 60 to 80)
      \nCondition: date_of_birth>date(1937, 12, 31) and date_of_birth<date(1958, 01, 01)

    2. Applying tax by employee gender
      \nCondition: gender==\"Male\"

    3. \n
    4. Applying tax by Salary Component
      \nCondition: base > 10000
    " + } + ], + "istable": 1, + "links": [], + "modified": "2020-05-27 23:32:47.253106", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Taxable Salary Slab", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/hr/doctype/taxable_salary_slab/taxable_salary_slab.py b/erpnext/payroll/doctype/taxable_salary_slab/taxable_salary_slab.py similarity index 71% rename from erpnext/hr/doctype/taxable_salary_slab/taxable_salary_slab.py rename to erpnext/payroll/doctype/taxable_salary_slab/taxable_salary_slab.py index 23e5ffbe629..49c52557dbc 100644 --- a/erpnext/hr/doctype/taxable_salary_slab/taxable_salary_slab.py +++ b/erpnext/payroll/doctype/taxable_salary_slab/taxable_salary_slab.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt from __future__ import unicode_literals -import frappe +# import frappe from frappe.model.document import Document class TaxableSalarySlab(Document): diff --git a/erpnext/payroll/module_onboarding/payroll/payroll.json b/erpnext/payroll/module_onboarding/payroll/payroll.json new file mode 100644 index 00000000000..a4ea53640eb --- /dev/null +++ b/erpnext/payroll/module_onboarding/payroll/payroll.json @@ -0,0 +1,51 @@ +{ + "allow_roles": [ + { + "role": "HR Manager" + }, + { + "role": "HR User" + } + ], + "creation": "2020-06-01 12:10:52.560472", + "docstatus": 0, + "doctype": "Module Onboarding", + "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/human-resources/payroll-entry", + "idx": 0, + "is_complete": 0, + "modified": "2020-06-04 16:35:30.650792", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Payroll", + "owner": "Administrator", + "steps": [ + { + "step": "Create Employee" + }, + { + "step": "Create Salary Component" + }, + { + "step": "Create Payroll Period" + }, + { + "step": "Create Income Tax Slab" + }, + { + "step": "Create Salary Structure" + }, + { + "step": "Assign Salary Structure" + }, + { + "step": "Create Salary Slip" + }, + { + "step": "Payroll Settings" + } + ], + "subtitle": "Salary, Compensations and more.", + "success_message": "The Payroll is all set up!", + "title": "Let's Setup the Payroll Module. ", + "user_can_dismiss": 1 +} \ No newline at end of file diff --git a/erpnext/payroll/notification/as b/erpnext/payroll/notification/as new file mode 100644 index 00000000000..7a395572613 --- /dev/null +++ b/erpnext/payroll/notification/as @@ -0,0 +1 @@ +update from `tabNotification` set module='Payroll' where name = "Retention Bonus" \ No newline at end of file diff --git a/erpnext/hr/report/bank_remittance/__init__.py b/erpnext/payroll/notification/retention_bonus/__init__.py similarity index 100% rename from erpnext/hr/report/bank_remittance/__init__.py rename to erpnext/payroll/notification/retention_bonus/__init__.py diff --git a/erpnext/hr/notification/retention_bonus/retention_bonus.json b/erpnext/payroll/notification/retention_bonus/retention_bonus.json similarity index 86% rename from erpnext/hr/notification/retention_bonus/retention_bonus.json rename to erpnext/payroll/notification/retention_bonus/retention_bonus.json index cbc8e2d0781..50db0338c4a 100644 --- a/erpnext/hr/notification/retention_bonus/retention_bonus.json +++ b/erpnext/payroll/notification/retention_bonus/retention_bonus.json @@ -13,10 +13,10 @@ "is_standard": 1, "message": "

    {{ _(\"Hello\") }},

    \n\n

    {{ _(\"Retention Bonus for\") }} {{ doc.employee_name }} {{ _(\"due on\") }} {{ doc.bonus_payment_date }}

    ", "modified": "2018-05-15 19:00:24.294418", - "modified_by": "ranjith@earthianslive.com", - "module": "HR", + "modified_by": "Administrator", + "module": "Payroll", "name": "Retention Bonus", - "owner": "ranjith@earthianslive.com", + "owner": "Administrator", "recipients": [ { "email_by_role": "HR Manager" diff --git a/erpnext/hr/notification/retention_bonus/retention_bonus.md b/erpnext/payroll/notification/retention_bonus/retention_bonus.md similarity index 100% rename from erpnext/hr/notification/retention_bonus/retention_bonus.md rename to erpnext/payroll/notification/retention_bonus/retention_bonus.md diff --git a/erpnext/hr/notification/retention_bonus/retention_bonus.py b/erpnext/payroll/notification/retention_bonus/retention_bonus.py similarity index 100% rename from erpnext/hr/notification/retention_bonus/retention_bonus.py rename to erpnext/payroll/notification/retention_bonus/retention_bonus.py diff --git a/erpnext/payroll/onboarding_step/assign_salary_structure/assign_salary_structure.json b/erpnext/payroll/onboarding_step/assign_salary_structure/assign_salary_structure.json new file mode 100644 index 00000000000..8a07b10276a --- /dev/null +++ b/erpnext/payroll/onboarding_step/assign_salary_structure/assign_salary_structure.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-06-01 11:58:43.927590", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 1, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-06-01 11:58:43.927590", + "modified_by": "Administrator", + "name": "Assign Salary Structure", + "owner": "Administrator", + "reference_document": "Salary Structure Assignment", + "show_full_form": 1, + "title": "Assign Salary Structure", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/payroll/onboarding_step/create_employee/create_employee.json b/erpnext/payroll/onboarding_step/create_employee/create_employee.json new file mode 100644 index 00000000000..5839ae6ca4a --- /dev/null +++ b/erpnext/payroll/onboarding_step/create_employee/create_employee.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-05-14 11:43:25.561152", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 1, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-14 12:26:28.629074", + "modified_by": "Administrator", + "name": "Create Employee", + "owner": "Administrator", + "reference_document": "Employee", + "show_full_form": 0, + "title": "Create Employee", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/payroll/onboarding_step/create_income_tax_slab/create_income_tax_slab.json b/erpnext/payroll/onboarding_step/create_income_tax_slab/create_income_tax_slab.json new file mode 100644 index 00000000000..faada7e4119 --- /dev/null +++ b/erpnext/payroll/onboarding_step/create_income_tax_slab/create_income_tax_slab.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-06-01 11:54:54.823796", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-06-01 11:54:54.823796", + "modified_by": "Administrator", + "name": "Create Income Tax Slab", + "owner": "Administrator", + "reference_document": "Income Tax Slab", + "show_full_form": 1, + "title": "Create Income Tax Slab", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/payroll/onboarding_step/create_payroll_period/create_payroll_period.json b/erpnext/payroll/onboarding_step/create_payroll_period/create_payroll_period.json new file mode 100644 index 00000000000..4bae67546c7 --- /dev/null +++ b/erpnext/payroll/onboarding_step/create_payroll_period/create_payroll_period.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-06-01 11:53:54.553947", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 1, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-06-01 11:53:54.553947", + "modified_by": "Administrator", + "name": "Create Payroll Period", + "owner": "Administrator", + "reference_document": "Payroll Period", + "show_full_form": 0, + "title": "Create Payroll Period", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/payroll/onboarding_step/create_salary_component/create_salary_component.json b/erpnext/payroll/onboarding_step/create_salary_component/create_salary_component.json new file mode 100644 index 00000000000..002d819618e --- /dev/null +++ b/erpnext/payroll/onboarding_step/create_salary_component/create_salary_component.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-06-01 11:57:04.002073", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-06-01 11:57:04.002073", + "modified_by": "Administrator", + "name": "Create Salary Component", + "owner": "Administrator", + "reference_document": "Salary Component", + "show_full_form": 1, + "title": "Create Salary Component", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/payroll/onboarding_step/create_salary_slip/create_salary_slip.json b/erpnext/payroll/onboarding_step/create_salary_slip/create_salary_slip.json new file mode 100644 index 00000000000..2aa31f485f3 --- /dev/null +++ b/erpnext/payroll/onboarding_step/create_salary_slip/create_salary_slip.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-06-01 11:59:29.972393", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 1, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-06-01 11:59:29.972393", + "modified_by": "Administrator", + "name": "Create Salary Slip", + "owner": "Administrator", + "reference_document": "Salary Slip", + "show_full_form": 1, + "title": "Create Salary Slip", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/payroll/onboarding_step/create_salary_structure/create_salary_structure.json b/erpnext/payroll/onboarding_step/create_salary_structure/create_salary_structure.json new file mode 100644 index 00000000000..11d8327259a --- /dev/null +++ b/erpnext/payroll/onboarding_step/create_salary_structure/create_salary_structure.json @@ -0,0 +1,19 @@ +{ + "action": "Create Entry", + "creation": "2020-06-01 11:57:54.527808", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 1, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-06-01 11:57:54.527808", + "modified_by": "Administrator", + "name": "Create Salary Structure", + "owner": "Administrator", + "reference_document": "Salary Structure", + "show_full_form": 1, + "title": "Create Salary Structure", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/payroll/onboarding_step/payroll_settings/payroll_settings.json b/erpnext/payroll/onboarding_step/payroll_settings/payroll_settings.json new file mode 100644 index 00000000000..946b8c8707a --- /dev/null +++ b/erpnext/payroll/onboarding_step/payroll_settings/payroll_settings.json @@ -0,0 +1,19 @@ +{ + "action": "Go to Page", + "creation": "2020-06-04 16:34:29.664917", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-06-04 16:34:29.664917", + "modified_by": "Administrator", + "name": "Payroll Settings", + "owner": "Administrator", + "path": "#Form/Payroll Settings", + "show_full_form": 0, + "title": "Payroll Settings", + "validate_action": 1 +} \ No newline at end of file diff --git a/erpnext/hr/report/salary_register/__init__.py b/erpnext/payroll/print_format/salary_slip_based_on_timesheet/__init__.py similarity index 100% rename from erpnext/hr/report/salary_register/__init__.py rename to erpnext/payroll/print_format/salary_slip_based_on_timesheet/__init__.py diff --git a/erpnext/hr/print_format/salary_slip_based_on_timesheet/salary_slip_based_on_timesheet.json b/erpnext/payroll/print_format/salary_slip_based_on_timesheet/salary_slip_based_on_timesheet.json similarity index 100% rename from erpnext/hr/print_format/salary_slip_based_on_timesheet/salary_slip_based_on_timesheet.json rename to erpnext/payroll/print_format/salary_slip_based_on_timesheet/salary_slip_based_on_timesheet.json diff --git a/erpnext/payroll/print_format/salary_slip_standard/__init__.py b/erpnext/payroll/print_format/salary_slip_standard/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/hr/print_format/salary_slip_standard/salary_slip_standard.json b/erpnext/payroll/print_format/salary_slip_standard/salary_slip_standard.json similarity index 100% rename from erpnext/hr/print_format/salary_slip_standard/salary_slip_standard.json rename to erpnext/payroll/print_format/salary_slip_standard/salary_slip_standard.json diff --git a/erpnext/payroll/report/__init__.py b/erpnext/payroll/report/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/payroll/report/bank_remittance/__init__.py b/erpnext/payroll/report/bank_remittance/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/hr/report/bank_remittance/bank_remittance.js b/erpnext/payroll/report/bank_remittance/bank_remittance.js similarity index 68% rename from erpnext/hr/report/bank_remittance/bank_remittance.js rename to erpnext/payroll/report/bank_remittance/bank_remittance.js index 1e10f24301e..6482ed34516 100644 --- a/erpnext/hr/report/bank_remittance/bank_remittance.js +++ b/erpnext/payroll/report/bank_remittance/bank_remittance.js @@ -5,12 +5,12 @@ frappe.query_reports["Bank Remittance"] = { "filters": [ { - "fieldname":"company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "default": frappe.defaults.get_user_default("Company"), - "reqd": 1 + fieldname:"company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1 }, { fieldname:"from_date", diff --git a/erpnext/hr/report/bank_remittance/bank_remittance.json b/erpnext/payroll/report/bank_remittance/bank_remittance.json similarity index 87% rename from erpnext/hr/report/bank_remittance/bank_remittance.json rename to erpnext/payroll/report/bank_remittance/bank_remittance.json index b8aa4e98d27..2a697b25892 100644 --- a/erpnext/hr/report/bank_remittance/bank_remittance.json +++ b/erpnext/payroll/report/bank_remittance/bank_remittance.json @@ -7,9 +7,9 @@ "doctype": "Report", "idx": 0, "is_standard": "Yes", - "modified": "2019-04-26 16:57:52.558895", + "modified": "2020-05-28 00:08:08.097494", "modified_by": "Administrator", - "module": "HR", + "module": "Payroll", "name": "Bank Remittance", "owner": "Administrator", "prepared_report": 0, diff --git a/erpnext/hr/report/bank_remittance/bank_remittance.py b/erpnext/payroll/report/bank_remittance/bank_remittance.py similarity index 96% rename from erpnext/hr/report/bank_remittance/bank_remittance.py rename to erpnext/payroll/report/bank_remittance/bank_remittance.py index b2d2c530247..a35d8e550ec 100644 --- a/erpnext/hr/report/bank_remittance/bank_remittance.py +++ b/erpnext/payroll/report/bank_remittance/bank_remittance.py @@ -125,7 +125,10 @@ def get_salary_slips(payroll_entries): # appending company debit accounts for slip in salary_slips: - slip["debit_acc_no"] = payroll_entry_map[slip.payroll_entry]['company_account'] + if slip.payroll_entry: + slip["debit_acc_no"] = payroll_entry_map[slip.payroll_entry]['company_account'] + else: + slip["debit_acc_no"] = None return salary_slips diff --git a/erpnext/payroll/report/income_tax_deductions/__init__.py b/erpnext/payroll/report/income_tax_deductions/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.js b/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.js new file mode 100644 index 00000000000..4bbb7f6a1be --- /dev/null +++ b/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.js @@ -0,0 +1,7 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.require("assets/erpnext/js/salary_slip_deductions_report_filters.js", function() { + frappe.query_reports["Income Tax Deductions"] = erpnext.salary_slip_deductions_report_filters; +}); \ No newline at end of file diff --git a/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.json b/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.json new file mode 100644 index 00000000000..cf80398f984 --- /dev/null +++ b/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.json @@ -0,0 +1,30 @@ +{ + "add_total_row": 0, + "creation": "2020-05-30 00:07:56.744372", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "modified": "2020-05-30 00:07:56.744372", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Income Tax Deductions", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Salary Slip", + "report_name": "Income Tax Deductions", + "report_type": "Script Report", + "roles": [ + { + "role": "HR User" + }, + { + "role": "HR Manager" + }, + { + "role": "Employee" + } + ] +} \ No newline at end of file diff --git a/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.py b/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.py new file mode 100644 index 00000000000..3bad5879bb3 --- /dev/null +++ b/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.py @@ -0,0 +1,127 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe, erpnext +from frappe import _ + +def execute(filters=None): + columns = get_columns(filters) + data = get_data(filters) + + return columns, data + +def get_columns(filters): + columns = [ + { + "label": _("Employee"), + "options": "Employee", + "fieldname": "employee", + "fieldtype": "Link", + "width": 200 + }, + { + "label": _("Employee Name"), + "options": "Employee", + "fieldname": "employee_name", + "fieldtype": "Link", + "width": 160 + }] + + if erpnext.get_region() == "India": + columns.append({ + "label": _("PAN Number"), + "fieldname": "pan_number", + "fieldtype": "Data", + "width": 140 + }) + + columns += [{ + "label": _("Income Tax Component"), + "fieldname": "it_comp", + "fieldtype": "Data", + "width": 170 + }, + { + "label": _("Income Tax Amount"), + "fieldname": "it_amount", + "fieldtype": "Currency", + "options": "currency", + "width": 140 + }, + { + "label": _("Gross Pay"), + "fieldname": "gross_pay", + "fieldtype": "Currency", + "options": "currency", + "width": 140 + }, + { + "label": _("Posting Date"), + "fieldname": "posting_date", + "fieldtype": "Date", + "width": 140 + } + ] + + return columns + +def get_conditions(filters): + conditions = [""] + + if filters.get("department"): + conditions.append("sal.department = '%s' " % (filters["department"]) ) + + if filters.get("branch"): + conditions.append("sal.branch = '%s' " % (filters["branch"]) ) + + if filters.get("company"): + conditions.append("sal.company = '%s' " % (filters["company"]) ) + + if filters.get("period"): + conditions.append("month(sal.start_date) = '%s' " % (filters["period"])) + + return " and ".join(conditions) + + +def get_data(filters): + + data = [] + + if erpnext.get_region() == "India": + employee_pan_dict = frappe._dict(frappe.db.sql(""" select employee, pan_number from `tabEmployee`""")) + + component_types = frappe.db.sql(""" select name from `tabSalary Component` + where is_income_tax_component = 1 """) + + component_types = [comp_type[0] for comp_type in component_types] + + conditions = get_conditions(filters) + + entry = frappe.db.sql(""" select sal.employee, sal.employee_name, sal.posting_date, ded.salary_component, ded.amount,sal.gross_pay + from `tabSalary Slip` sal, `tabSalary Detail` ded + where sal.name = ded.parent + and ded.parentfield = 'deductions' + and ded.parenttype = 'Salary Slip' + and sal.docstatus = 1 %s + and ded.salary_component in (%s) + """ % (conditions , ", ".join(['%s']*len(component_types))), tuple(component_types), as_dict=1) + + for d in entry: + + employee = { + "employee": d.employee, + "employee_name": d.employee_name, + "it_comp": d.salary_component, + "posting_date": d.posting_date, + # "pan_number": employee_pan_dict.get(d.employee), + "it_amount": d.amount, + "gross_pay": d.gross_pay + } + + if erpnext.get_region() == "India": + employee["pan_number"] = employee_pan_dict.get(d.employee) + + data.append(employee) + + return data diff --git a/erpnext/payroll/report/salary_payments_based_on_payment_mode/__init__.py b/erpnext/payroll/report/salary_payments_based_on_payment_mode/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.js b/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.js new file mode 100644 index 00000000000..166d982c9c6 --- /dev/null +++ b/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.js @@ -0,0 +1,7 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.require("assets/erpnext/js/salary_slip_deductions_report_filters.js", function() { + frappe.query_reports["Salary Payments Based On Payment Mode"] = erpnext.salary_slip_deductions_report_filters; +}); \ No newline at end of file diff --git a/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.json b/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.json new file mode 100644 index 00000000000..c04cc32b9b0 --- /dev/null +++ b/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.json @@ -0,0 +1,30 @@ +{ + "add_total_row": 0, + "creation": "2020-06-16 18:43:43.107246", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "modified": "2020-06-16 18:43:43.107246", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Salary Payments Based On Payment Mode", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Salary Slip", + "report_name": "Salary Payments Based On Payment Mode", + "report_type": "Script Report", + "roles": [ + { + "role": "HR User" + }, + { + "role": "HR Manager" + }, + { + "role": "Employee" + } + ] +} \ No newline at end of file diff --git a/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.py b/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.py new file mode 100644 index 00000000000..7f0c2e2e000 --- /dev/null +++ b/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.py @@ -0,0 +1,177 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe, erpnext +from frappe import _ +from erpnext.regional.report.provident_fund_deductions.provident_fund_deductions import get_conditions + +def execute(filters=None): + mode_of_payments = get_payment_modes() + + if not len(mode_of_payments): + return [], [] + + columns = get_columns(filters, mode_of_payments) + data, total_rows, report_summary = get_data(filters, mode_of_payments) + chart = get_chart(mode_of_payments, total_rows) + + return columns, data, None, chart, report_summary + +def get_columns(filters, mode_of_payments): + columns = [{ + "label": _("Branch"), + "options": "Branch", + "fieldname": "branch", + "fieldtype": "Link", + "width": 200 + }] + + for mode in mode_of_payments: + columns.append({ + "label": _(mode), + "fieldname": mode, + "fieldtype": "Currency", + "width": 160 + }) + + columns.append({ + "label": _("Total"), + "fieldname": "total", + "fieldtype": "Currency", + "width": 140 + }) + + return columns + +def get_payment_modes(): + mode_of_payments = frappe.db.sql_list(""" + select distinct mode_of_payment from `tabSalary Slip` where docstatus = 1 + """) + return mode_of_payments + +def prepare_data(entry): + branch_wise_entries = {} + gross_pay = 0 + + for d in entry: + gross_pay += d.gross_pay + if branch_wise_entries.get(d.branch): + branch_wise_entries[d.branch][d.mode_of_payment] = d.net_pay + else: + branch_wise_entries.setdefault(d.branch, {}).setdefault(d.mode_of_payment, d.net_pay) + + return branch_wise_entries, gross_pay + +def get_data(filters, mode_of_payments): + data = [] + + conditions = get_conditions(filters) + + entry = frappe.db.sql(""" + select branch, mode_of_payment, sum(net_pay) as net_pay, sum(gross_pay) as gross_pay + from `tabSalary Slip` sal + where docstatus = 1 %s + group by branch, mode_of_payment + """ % (conditions), as_dict=1) + + branch_wise_entries, gross_pay = prepare_data(entry) + + branches = frappe.db.sql_list(""" + select distinct branch from `tabSalary Slip` sal + where docstatus = 1 %s + """ % (conditions)) + + total_row = {"total": 0, "branch": "Total"} + + for branch in branches: + total = 0 + row = { + "branch": branch + } + for mode in mode_of_payments: + if branch_wise_entries.get(branch).get(mode): + row[mode] = branch_wise_entries.get(branch).get(mode) + total += branch_wise_entries.get(branch).get(mode) + + row["total"] = total + data.append(row) + + total_row = get_total_based_on_mode_of_payment(data, mode_of_payments) + total_deductions = gross_pay - total_row.get("total") + + if data: + data.append(total_row) + data.append({}) + data.append({ + "branch": "Total Gross Pay", + mode_of_payments[0]:gross_pay + }) + data.append({ + "branch": "Total Deductions", + mode_of_payments[0]:total_deductions + }) + data.append({ + "branch": "Total Net Pay", + mode_of_payments[0]:total_row.get("total") + }) + + currency = erpnext.get_company_currency(filters.company) + report_summary = get_report_summary(gross_pay, total_deductions, total_row.get("total"), currency) + + return data, total_row, report_summary + +def get_total_based_on_mode_of_payment(data, mode_of_payments): + + total = 0 + total_row = {"branch": "Total"} + for mode in mode_of_payments: + sum_of_payment = sum([detail[mode] for detail in data if mode in detail.keys()]) + total_row[mode] = sum_of_payment + total += sum_of_payment + + total_row["total"] = total + return total_row + +def get_report_summary(gross_pay, total_deductions, net_pay, currency): + return [ + { + "value": gross_pay, + "label": "Total Gross Pay", + "indicator": "Green", + "datatype": "Currency", + "currency": currency + }, + { + "value": total_deductions, + "label": "Total Deduction", + "datatype": "Currency", + "indicator": "Red", + "currency": currency + }, + { + "value": net_pay, + "label": "Total Net Pay", + "datatype": "Currency", + "indicator": "Blue", + "currency": currency + } + ] + +def get_chart(mode_of_payments, data): + if data: + values = [] + labels = [] + + for mode in mode_of_payments: + values.append(data[mode]) + labels.append([mode]) + + chart = { + "data": { + "labels": labels, + "datasets": [{'name': 'Mode Of Payments', "values": values}] + } + } + chart['type'] = "bar" + return chart diff --git a/erpnext/payroll/report/salary_payments_via_ecs/__init__.py b/erpnext/payroll/report/salary_payments_via_ecs/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.js b/erpnext/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.js new file mode 100644 index 00000000000..e49fc112ff4 --- /dev/null +++ b/erpnext/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.js @@ -0,0 +1,16 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.require("assets/erpnext/js/salary_slip_deductions_report_filters.js", function() { + + let ecs_checklist_filter = erpnext.salary_slip_deductions_report_filters + ecs_checklist_filter['filters'].push({ + fieldname: "type", + label: __("Type"), + fieldtype: "Select", + options:["", "Bank", "Cash", "Cheque"] + }) + + frappe.query_reports["Salary Payments via ECS"] = ecs_checklist_filter +}); diff --git a/erpnext/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.json b/erpnext/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.json new file mode 100644 index 00000000000..dd0ac7c4ef7 --- /dev/null +++ b/erpnext/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.json @@ -0,0 +1,27 @@ +{ + "add_total_row": 0, + "creation": "2020-06-16 18:35:30.508143", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "modified": "2020-06-16 18:38:23.680185", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Salary Payments via ECS", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Salary Slip", + "report_name": "Salary Payments via ECS", + "report_type": "Script Report", + "roles": [ + { + "role": "HR Manager" + }, + { + "role": "HR User" + } + ] +} \ No newline at end of file diff --git a/erpnext/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.py b/erpnext/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.py new file mode 100644 index 00000000000..afd5c13b32e --- /dev/null +++ b/erpnext/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.py @@ -0,0 +1,146 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe, erpnext +from frappe import _ + +def execute(filters=None): + columns = get_columns(filters) + data = get_data(filters) + + return columns, data + +def get_columns(filters): + columns = [ + { + "label": _("Branch"), + "options": "Branch", + "fieldname": "branch", + "fieldtype": "Link", + "width": 200 + }, + { + "label": _("Employee Name"), + "options": "Employee", + "fieldname": "employee_name", + "fieldtype": "Link", + "width": 160 + }, + { + "label": _("Employee"), + "options":"Employee", + "fieldname": "employee", + "fieldtype": "Link", + "width": 140 + }, + { + "label": _("Gross Pay"), + "fieldname": "gross_pay", + "fieldtype": "Currency", + "options": "currency", + "width": 140 + }, + { + "label": _("Bank"), + "fieldname": "bank", + "fieldtype": "Data", + "width": 140 + }, + { + "label": _("Account No"), + "fieldname": "account_no", + "fieldtype": "Data", + "width": 140 + }, + ] + if erpnext.get_region() == "India": + columns += [ + { + "label": _("IFSC"), + "fieldname": "ifsc", + "fieldtype": "Data", + "width": 140 + }, + { + "label": _("MICR"), + "fieldname": "micr", + "fieldtype": "Data", + "width": 140 + } + ] + + return columns + +def get_conditions(filters): + conditions = [""] + + if filters.get("department"): + conditions.append("department = '%s' " % (filters["department"]) ) + + if filters.get("branch"): + conditions.append("branch = '%s' " % (filters["branch"]) ) + + if filters.get("company"): + conditions.append("company = '%s' " % (filters["company"]) ) + + if filters.get("period"): + conditions.append("month(start_date) = '%s' " % (filters["period"])) + + return " and ".join(conditions) + +def get_data(filters): + + data = [] + + fields = ["employee", "branch", "bank_name", "bank_ac_no", "salary_mode"] + if erpnext.get_region() == "India": + fields += ["ifsc_code", "micr_code"] + + + employee_details = frappe.get_list("Employee", fields = fields) + employee_data_dict = {} + + for d in employee_details: + employee_data_dict.setdefault( + d.employee,{ + "bank_ac_no" : d.bank_ac_no, + "ifsc_code" : d.ifsc_code or None, + "micr_code" : d.micr_code or None, + "branch" : d.branch, + "salary_mode" : d.salary_mode, + "bank_name": d.bank_name + } + ) + + conditions = get_conditions(filters) + + entry = frappe.db.sql(""" select employee, employee_name, gross_pay + from `tabSalary Slip` + where docstatus = 1 %s """ + %(conditions), as_dict =1) + + for d in entry: + + employee = { + "branch" : employee_data_dict.get(d.employee).get("branch"), + "employee_name" : d.employee_name, + "employee" : d.employee, + "gross_pay" : d.gross_pay, + } + + if employee_data_dict.get(d.employee).get("salary_mode") == "Bank": + employee["bank"] = employee_data_dict.get(d.employee).get("bank_name") + employee["account_no"] = employee_data_dict.get(d.employee).get("bank_ac_no") + if erpnext.get_region() == "India": + employee["ifsc"] = employee_data_dict.get(d.employee).get("ifsc_code") + employee["micr"] = employee_data_dict.get(d.employee).get("micr_code") + else: + employee["account_no"] = employee_data_dict.get(d.employee).get("salary_mode") + + if filters.get("type") and employee_data_dict.get(d.employee).get("salary_mode") == filters.get("type"): + data.append(employee) + elif not filters.get("type"): + data.append(employee) + + return data diff --git a/erpnext/payroll/report/salary_register/__init__.py b/erpnext/payroll/report/salary_register/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/hr/report/salary_register/salary_register.html b/erpnext/payroll/report/salary_register/salary_register.html similarity index 100% rename from erpnext/hr/report/salary_register/salary_register.html rename to erpnext/payroll/report/salary_register/salary_register.html diff --git a/erpnext/hr/report/salary_register/salary_register.js b/erpnext/payroll/report/salary_register/salary_register.js similarity index 100% rename from erpnext/hr/report/salary_register/salary_register.js rename to erpnext/payroll/report/salary_register/salary_register.js diff --git a/erpnext/payroll/report/salary_register/salary_register.json b/erpnext/payroll/report/salary_register/salary_register.json new file mode 100644 index 00000000000..5a70c325939 --- /dev/null +++ b/erpnext/payroll/report/salary_register/salary_register.json @@ -0,0 +1,27 @@ +{ + "add_total_row": 1, + "creation": "2017-01-10 17:36:58.153863", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 2, + "is_standard": "Yes", + "modified": "2020-05-28 00:07:18.576661", + "modified_by": "Administrator", + "module": "Payroll", + "name": "Salary Register", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Salary Slip", + "report_name": "Salary Register", + "report_type": "Script Report", + "roles": [ + { + "role": "HR User" + }, + { + "role": "HR Manager" + } + ] +} \ No newline at end of file diff --git a/erpnext/hr/report/salary_register/salary_register.py b/erpnext/payroll/report/salary_register/salary_register.py similarity index 96% rename from erpnext/hr/report/salary_register/salary_register.py rename to erpnext/payroll/report/salary_register/salary_register.py index ea7fc06cff1..87010855fdb 100644 --- a/erpnext/hr/report/salary_register/salary_register.py +++ b/erpnext/payroll/report/salary_register/salary_register.py @@ -55,8 +55,8 @@ def get_columns(salary_slips): columns = [ _("Salary Slip ID") + ":Link/Salary Slip:150",_("Employee") + ":Link/Employee:120", _("Employee Name") + "::140", _("Date of Joining") + "::80", _("Branch") + ":Link/Branch:-1", _("Department") + ":Link/Department:-1", - _("Designation") + ":Link/Designation:-1", _("Company") + ":Link/Company:120", _("Start Date") + "::80", - _("End Date") + "::80", _("Leave Without Pay") + ":Float:-1", _("Payment Days") + ":Float:120" + _("Designation") + ":Link/Designation:120", _("Company") + ":Link/Company:120", _("Start Date") + "::80", + _("End Date") + "::80", _("Leave Without Pay") + ":Float:50", _("Payment Days") + ":Float:120" ] salary_components = {_("Earning"): [], _("Deduction"): []} diff --git a/erpnext/projects/doctype/timesheet/test_timesheet.py b/erpnext/projects/doctype/timesheet/test_timesheet.py index cbc624c064f..03b67b10231 100644 --- a/erpnext/projects/doctype/timesheet/test_timesheet.py +++ b/erpnext/projects/doctype/timesheet/test_timesheet.py @@ -11,7 +11,7 @@ from frappe.utils import now_datetime, nowdate, add_days, add_months from erpnext.projects.doctype.timesheet.timesheet import OverlapError from erpnext.projects.doctype.timesheet.timesheet import make_salary_slip, make_sales_invoice from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice -from erpnext.hr.doctype.salary_structure.test_salary_structure \ +from erpnext.payroll.doctype.salary_structure.test_salary_structure \ import make_salary_structure, create_salary_structure_assignment from erpnext.hr.doctype.employee.test_employee import make_employee diff --git a/erpnext/public/js/salary_slip_deductions_report_filters.js b/erpnext/public/js/salary_slip_deductions_report_filters.js new file mode 100644 index 00000000000..242037991aa --- /dev/null +++ b/erpnext/public/js/salary_slip_deductions_report_filters.js @@ -0,0 +1,47 @@ +frappe.provide("erpnext.salary_slip_deductions_report_filters"); + +erpnext.salary_slip_deductions_report_filters = { + "filters": [ + { + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + reqd:1, + default: frappe.defaults.get_user_default("Company"), + }, + { + fieldname: "period", + label: __("Period"), + fieldtype: "Select", + reqd: 1 , + options: [ + { "value": 1, "label": __("Jan") }, + { "value": 2, "label": __("Feb") }, + { "value": 3, "label": __("Mar") }, + { "value": 4, "label": __("Apr") }, + { "value": 5, "label": __("May") }, + { "value": 6, "label": __("June") }, + { "value": 7, "label": __("July") }, + { "value": 8, "label": __("Aug") }, + { "value": 9, "label": __("Sep") }, + { "value": 10, "label": __("Oct") }, + { "value": 11, "label": __("Nov") }, + { "value": 12, "label": __("Dec") }, + ], + default: frappe.datetime.str_to_obj(frappe.datetime.get_today()).getMonth() + 1 + }, + { + fieldname: "department", + label: __("Department"), + fieldtype: "Link", + options: "Department", + }, + { + fieldname: "branch", + label: __("Barnch"), + fieldtype: "Link", + options: "Branch", + } + ] +} \ No newline at end of file diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py index 8593966cc3a..290694a7899 100644 --- a/erpnext/regional/india/setup.py +++ b/erpnext/regional/india/setup.py @@ -60,6 +60,19 @@ def add_custom_roles_for_reports(): ] )).insert() + for report_name in ('Professional Tax Deductions', 'Provident Fund Deductions'): + + if not frappe.db.get_value('Custom Role', dict(report=report_name)): + frappe.get_doc(dict( + doctype='Custom Role', + report=report_name, + roles= [ + dict(role='HR User'), + dict(role='HR Manager'), + dict(role='Employee') + ] + )).insert() + def add_permissions(): for doctype in ('GST HSN Code', 'GST Settings', 'GSTR 3B Report', 'Lower Deduction Certificate'): add_permission(doctype, 'All', 0) @@ -402,10 +415,45 @@ def make_custom_fields(update=True): 'Purchase Receipt Item': [hsn_sac_field, nil_rated_exempt, is_non_gst], 'Purchase Invoice Item': [hsn_sac_field, nil_rated_exempt, is_non_gst], 'Material Request Item': [hsn_sac_field, nil_rated_exempt, is_non_gst], + 'Salary Component': [ + dict(fieldname= 'component_type', + label= 'Component Type', + fieldtype= 'Select', + insert_after= 'description', + options= "\nProvident Fund\nAdditional Provident Fund\nProvident Fund Loan\nProfessional Tax", + depends_on = 'eval:doc.type == "Deduction"' + ) + ], 'Employee': [ - dict(fieldname='ifsc_code', label='IFSC Code', - fieldtype='Data', insert_after='bank_ac_no', print_hide=1, - depends_on='eval:doc.salary_mode == "Bank"') + dict(fieldname='ifsc_code', + label='IFSC Code', + fieldtype='Data', + insert_after='bank_ac_no', + print_hide=1, + depends_on='eval:doc.salary_mode == "Bank"' + ), + dict( + fieldname = 'pan_number', + label = 'PAN Number', + fieldtype = 'Data', + insert_after = 'payroll_cost_center', + print_hide = 1 + ), + dict( + fieldname = 'micr_code', + label = 'MICR Code', + fieldtype = 'Data', + insert_after = 'ifsc_code', + print_hide = 1, + depends_on='eval:doc.salary_mode == "Bank"' + ), + dict( + fieldname = 'provident_fund_account', + label = 'Provident Fund Account', + fieldtype = 'Data', + insert_after = 'pan_number' + ) + ], 'Company': [ dict(fieldname='hra_section', label='HRA Settings', diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index 9fe29eba1b8..05ffa87f144 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -6,7 +6,7 @@ from erpnext.regional.india import states, state_numbers from erpnext.controllers.taxes_and_totals import get_itemised_tax, get_itemised_taxable_amount from erpnext.controllers.accounts_controller import get_taxes_and_charges from erpnext.hr.utils import get_salary_assignment -from erpnext.hr.doctype.salary_structure.salary_structure import make_salary_slip +from erpnext.payroll.doctype.salary_structure.salary_structure import make_salary_slip from erpnext.regional.india import number_state_mapping from six import string_types from erpnext.accounts.general_ledger import make_gl_entries diff --git a/erpnext/regional/report/professional_tax_deductions/__init__.py b/erpnext/regional/report/professional_tax_deductions/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.js b/erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.js new file mode 100644 index 00000000000..29c7dbf43c6 --- /dev/null +++ b/erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.js @@ -0,0 +1,7 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.require("assets/erpnext/js/salary_slip_deductions_report_filters.js", function() { + frappe.query_reports["Professional Tax Deductions"] = erpnext.salary_slip_deductions_report_filters; +}); \ No newline at end of file diff --git a/erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.json b/erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.json new file mode 100644 index 00000000000..9938e9db524 --- /dev/null +++ b/erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.json @@ -0,0 +1,20 @@ +{ + "add_total_row": 0, + "creation": "2020-06-02 00:37:44.537355", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "modified": "2020-06-16 19:02:26.306348", + "modified_by": "Administrator", + "module": "Regional", + "name": "Professional Tax Deductions", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Salary Slip", + "report_name": "Professional Tax Deductions", + "report_type": "Script Report", + "roles": [] +} \ No newline at end of file diff --git a/erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.py b/erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.py new file mode 100644 index 00000000000..900fe963b4b --- /dev/null +++ b/erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.py @@ -0,0 +1,69 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe import _ +from erpnext.regional.report.provident_fund_deductions.provident_fund_deductions import get_conditions + +def execute(filters=None): + columns = get_columns(filters) + data = get_data(filters) + + return columns, data + +def get_columns(filters): + columns = [ + { + "label": _("Employee"), + "options": "Employee", + "fieldname": "employee", + "fieldtype": "Link", + "width": 200 + }, + { + "label": _("Employee Name"), + "options": "Employee", + "fieldname": "employee_name", + "fieldtype": "Link", + "width": 160 + }, + { + "label": _("Amount"), + "fieldname": "amount", + "fieldtype": "Currency", + "width": 140 + } + ] + + return columns + +def get_data(filters): + + data = [] + + component_type_dict = frappe._dict(frappe.db.sql(""" select name, component_type from `tabSalary Component` + where component_type = 'Professional Tax' """)) + + conditions = get_conditions(filters) + + entry = frappe.db.sql(""" select sal.employee, sal.employee_name, ded.salary_component, ded.amount + from `tabSalary Slip` sal, `tabSalary Detail` ded + where sal.name = ded.parent + and ded.parentfield = 'deductions' + and ded.parenttype = 'Salary Slip' + and sal.docstatus = 1 %s + and ded.salary_component in (%s) + """ % (conditions , ", ".join(['%s']*len(component_type_dict))), tuple(component_type_dict.keys()), as_dict=1) + + for d in entry: + + employee = { + "employee": d.employee, + "employee_name": d.employee_name, + "amount": d.amount + } + + data.append(employee) + + return data \ No newline at end of file diff --git a/erpnext/regional/report/provident_fund_deductions/__init__.py b/erpnext/regional/report/provident_fund_deductions/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.js b/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.js new file mode 100644 index 00000000000..b4dc28d177d --- /dev/null +++ b/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.js @@ -0,0 +1,7 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.require("assets/erpnext/js/salary_slip_deductions_report_filters.js", function() { + frappe.query_reports["Provident Fund Deductions"] = erpnext.salary_slip_deductions_report_filters; +}); \ No newline at end of file diff --git a/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.json b/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.json new file mode 100644 index 00000000000..e25d335f9bd --- /dev/null +++ b/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.json @@ -0,0 +1,20 @@ +{ + "add_total_row": 0, + "creation": "2020-06-01 23:44:07.919117", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "modified": "2020-06-16 18:54:19.305763", + "modified_by": "Administrator", + "module": "Regional", + "name": "Provident Fund Deductions", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Salary Slip", + "report_name": "Provident Fund Deductions", + "report_type": "Script Report", + "roles": [] +} \ No newline at end of file diff --git a/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.py b/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.py new file mode 100644 index 00000000000..9f58957fed8 --- /dev/null +++ b/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.py @@ -0,0 +1,153 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe import _ + +def execute(filters=None): + columns = get_columns(filters) + data = get_data(filters) + + return columns, data + +def get_columns(filters): + columns = [ + { + "label": _("Employee"), + "options": "Employee", + "fieldname": "employee", + "fieldtype": "Link", + "width": 200 + }, + { + "label": _("Employee Name"), + "options": "Employee", + "fieldname": "employee_name", + "fieldtype": "Link", + "width": 160 + }, + { + "label": _("PF Account"), + "fieldname": "pf_account", + "fieldtype": "Data", + "width": 140 + }, + { + "label": _("PF Amount"), + "fieldname": "pf_amount", + "fieldtype": "Currency", + "width": 140 + }, + { + "label": _("Additional PF"), + "fieldname": "additional_pf", + "fieldtype": "Currency", + "width": 140 + }, + { + "label": _("PF Loan"), + "fieldname": "pf_loan", + "fieldtype": "Currency", + "width": 140 + }, + { + "label": _("Total"), + "fieldname": "total", + "fieldtype": "Currency", + "width": 140 + } + ] + + return columns + +def get_conditions(filters): + conditions = [""] + + if filters.get("department"): + conditions.append("sal.department = '%s' " % (filters["department"]) ) + + if filters.get("branch"): + conditions.append("sal.branch = '%s' " % (filters["branch"]) ) + + if filters.get("company"): + conditions.append("sal.company = '%s' " % (filters["company"]) ) + + if filters.get("period"): + conditions.append("month(sal.start_date) = '%s' " % (filters["period"])) + + if filters.get("mode_of_payment"): + conditions.append("sal.mode_of_payment = '%s' " % (filters["mode_of_payment"])) + + return " and ".join(conditions) + +def prepare_data(entry,component_type_dict): + data_list = {} + + employee_account_dict = frappe._dict(frappe.db.sql(""" select name, provident_fund_account from `tabEmployee`""")) + + for d in entry: + + component_type = component_type_dict.get(d.salary_component) + + if data_list.get(d.name): + data_list[d.name][component_type] = d.amount + else: + data_list.setdefault(d.name,{ + "employee": d.employee, + "employee_name": d.employee_name, + "pf_account": employee_account_dict.get(d.employee), + "component_type": d.amount + }) + + return data_list + +def get_data(filters): + data = [] + + conditions = get_conditions(filters) + + salary_slips = frappe.db.sql(""" select sal.name from `tabSalary Slip` sal + where docstatus = 1 %s + """ % (conditions), as_dict=1) + + component_type_dict = frappe._dict(frappe.db.sql(""" select name, component_type from `tabSalary Component` + where component_type in ('Provident Fund', 'Additional Provident Fund', 'Provident Fund Loan')""")) + + entry = frappe.db.sql(""" select sal.name, sal.employee, sal.employee_name, ded.salary_component, ded.amount + from `tabSalary Slip` sal, `tabSalary Detail` ded + where sal.name = ded.parent + and ded.parentfield = 'deductions' + and ded.parenttype = 'Salary Slip' + and sal.docstatus = 1 %s + and ded.salary_component in (%s) + """ % (conditions, ", ".join(['%s']*len(component_type_dict))), tuple(component_type_dict.keys()), as_dict=1) + + data_list = prepare_data(entry,component_type_dict) + + for d in salary_slips: + total = 0 + if data_list.get(d.name): + employee = { + "employee": data_list.get(d.name).get("employee"), + "employee_name": data_list.get(d.name).get("employee_name"), + "pf_account": data_list.get(d.name).get("pf_account") + } + + if data_list.get(d.name).get("Provident Fund"): + employee["pf_amount"] = data_list.get(d.name).get("Provident Fund") + total += data_list.get(d.name).get("Provident Fund") + + if data_list.get(d.name).get("Additional Provident Fund"): + employee["additional_pf"] = data_list.get(d.name).get("Additional Provident Fund") + total += data_list.get(d.name).get("Additional Provident Fund") + + if data_list.get(d.name).get("Provident Fund Loan"): + employee["pf_loan"] = data_list.get(d.name).get("Provident Fund Loan") + total += data_list.get(d.name).get("Provident Fund Loan") + + employee["total"] = total + + data.append(employee) + + return data \ No newline at end of file diff --git a/erpnext/setup/setup_wizard/operations/install_fixtures.py b/erpnext/setup/setup_wizard/operations/install_fixtures.py index 0d70d91f739..ad063cfc9d1 100644 --- a/erpnext/setup/setup_wizard/operations/install_fixtures.py +++ b/erpnext/setup/setup_wizard/operations/install_fixtures.py @@ -50,7 +50,7 @@ def install(country=None): 'is_group': 0, 'parent_item_group': _('All Item Groups') }, # salary component - {'doctype': 'Salary Component', 'salary_component': _('Income Tax'), 'description': _('Income Tax'), 'type': 'Deduction'}, + {'doctype': 'Salary Component', 'salary_component': _('Income Tax'), 'description': _('Income Tax'), 'type': 'Deduction', 'is_income_tax_component': 1}, {'doctype': 'Salary Component', 'salary_component': _('Basic'), 'description': _('Basic'), 'type': 'Earning'}, {'doctype': 'Salary Component', 'salary_component': _('Arrear'), 'description': _('Arrear'), 'type': 'Earning'}, {'doctype': 'Salary Component', 'salary_component': _('Leave Encashment'), 'description': _('Leave Encashment'), 'type': 'Earning'}, From 2bf8d60d7e3c44db7ce90aaa9184afe6b0c93d9b Mon Sep 17 00:00:00 2001 From: Kenneth Sequeira Date: Fri, 19 Jun 2020 22:25:55 +0530 Subject: [PATCH 424/608] fix: rogue s/codacy --- .../accounts/doctype/purchase_invoice/purchase_invoice_list.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js index e9849c20502..86c2e408c0b 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js @@ -24,5 +24,4 @@ frappe.listview_settings['Purchase Invoice'] = { return [__("Paid"), "green", "outstanding_amount,=,0"]; } } -}; -s \ No newline at end of file +}; \ No newline at end of file From 802c30021824f63c433f3cab88c24d0b05af025e Mon Sep 17 00:00:00 2001 From: Kenneth Sequeira Date: Fri, 19 Jun 2020 22:29:55 +0530 Subject: [PATCH 425/608] Revert "fix: rogue s/codacy" This reverts commit 2bf8d60d7e3c44db7ce90aaa9184afe6b0c93d9b. --- .../accounts/doctype/purchase_invoice/purchase_invoice_list.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js index 86c2e408c0b..e9849c20502 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js @@ -24,4 +24,5 @@ frappe.listview_settings['Purchase Invoice'] = { return [__("Paid"), "green", "outstanding_amount,=,0"]; } } -}; \ No newline at end of file +}; +s \ No newline at end of file From b7c8037e9a8f8a87afa5b6aff47546545552848e Mon Sep 17 00:00:00 2001 From: Kenneth Sequeira Date: Fri, 19 Jun 2020 22:33:51 +0530 Subject: [PATCH 426/608] fix codacy --- .../accounts/doctype/purchase_invoice/purchase_invoice_list.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js index e9849c20502..86c2e408c0b 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js @@ -24,5 +24,4 @@ frappe.listview_settings['Purchase Invoice'] = { return [__("Paid"), "green", "outstanding_amount,=,0"]; } } -}; -s \ No newline at end of file +}; \ No newline at end of file From 3fbe6e9e4b2489f8f749497597e4eb32430577da Mon Sep 17 00:00:00 2001 From: Afshan Date: Fri, 19 Jun 2020 22:51:52 +0530 Subject: [PATCH 427/608] fix: test case --- .../report/sales_analytics/test_analytics.py | 30 ------------------- 1 file changed, 30 deletions(-) diff --git a/erpnext/selling/report/sales_analytics/test_analytics.py b/erpnext/selling/report/sales_analytics/test_analytics.py index 7e8501dcc15..4d81a1e4dda 100644 --- a/erpnext/selling/report/sales_analytics/test_analytics.py +++ b/erpnext/selling/report/sales_analytics/test_analytics.py @@ -33,21 +33,6 @@ class TestAnalytics(unittest.TestCase): report = execute(filters) expected_data = [ - { - 'entity': 'Total', - 'apr_2017': 0.0, - 'may_2017': 0.0, - 'jun_2017': 2000.0, - 'jul_2017': 1000.0, - 'aug_2017': 0.0, - 'sep_2017': 1500.0, - 'oct_2017': 1000.0, - 'nov_2017': 0.0, - 'dec_2017': 0.0, - 'jan_2018': 0.0, - 'feb_2018': 2000.0, - 'mar_2018': 0.0 - }, { "entity": "_Test Customer 1", "entity_name": "_Test Customer 1", @@ -149,21 +134,6 @@ class TestAnalytics(unittest.TestCase): report = execute(filters) expected_data = [ - { - 'entity': 'Total', - 'apr_2017': 0.0, - 'may_2017': 0.0, - 'jun_2017': 20.0, - 'jul_2017': 10.0, - 'aug_2017': 0.0, - 'sep_2017': 15.0, - 'oct_2017': 10.0, - 'nov_2017': 0.0, - 'dec_2017': 0.0, - 'jan_2018': 0.0, - 'feb_2018': 20.0, - 'mar_2018': 0.0 - }, { "entity": "_Test Customer 1", "entity_name": "_Test Customer 1", From d39c97e281cadc862550c0640881f06d1bf62f58 Mon Sep 17 00:00:00 2001 From: Revant Nandgaonkar Date: Fri, 19 Jun 2020 23:56:53 +0530 Subject: [PATCH 428/608] fix: trigger docker build on release (#22353) --- .github/workflows/docker-release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-release.yml b/.github/workflows/docker-release.yml index 8f678583066..4b1147e79f9 100644 --- a/.github/workflows/docker-release.yml +++ b/.github/workflows/docker-release.yml @@ -1,7 +1,7 @@ name: Trigger Docker build on release on: release: - types: [created] + types: [released] jobs: curl: runs-on: ubuntu-latest From 53d5d16abb52790d60c0ef050a9c7a6685fbc5df Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 20 Jun 2020 11:55:56 +0530 Subject: [PATCH 429/608] fix: Move check from Stock Settings to Company Master --- .../purchase_invoice/purchase_invoice.py | 2 +- erpnext/setup/doctype/company/company.json | 373 +++++------------- .../purchase_receipt/purchase_receipt.py | 2 +- .../stock_settings/stock_settings.json | 131 ++---- 4 files changed, 130 insertions(+), 378 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 30c661a9721..32d61f955be 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -570,7 +570,7 @@ class PurchaseInvoice(BuyingController): else: amount = flt(item.base_net_amount + item.item_tax_amount, item.precision("base_net_amount")) - auto_accounting_for_non_stock_items = cint(frappe.db.get_single_value('Stock Settings', 'enable_perpetual_inventory_for_non_stock_items')) + auto_accounting_for_non_stock_items = cint(frappe.db.get_value('Company', self.company, 'enable_perpetual_inventory_for_non_stock_items')) service_received_but_not_billed_account = self.get_company_default("service_received_but_not_billed") if item.purchase_receipt and auto_accounting_for_non_stock_items: diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json index 0b91b700347..c25edc55057 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -67,6 +67,7 @@ "payment_terms", "auto_accounting_for_stock_settings", "enable_perpetual_inventory", + "enable_perpetual_inventory_for_non_stock_items", "default_inventory_account", "stock_adjustment_account", "column_break_32", @@ -107,9 +108,7 @@ { "fieldname": "details", "fieldtype": "Section Break", - "oldfieldtype": "Section Break", - "show_days": 1, - "show_seconds": 1 + "oldfieldtype": "Section Break" }, { "fieldname": "company_name", @@ -118,8 +117,6 @@ "oldfieldname": "company_name", "oldfieldtype": "Data", "reqd": 1, - "show_days": 1, - "show_seconds": 1, "unique": 1 }, { @@ -128,48 +125,36 @@ "label": "Abbr", "oldfieldname": "abbr", "oldfieldtype": "Data", - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "depends_on": "eval:!doc.__islocal && in_list(frappe.user_roles, \"System Manager\")", "fieldname": "change_abbr", "fieldtype": "Button", - "label": "Change Abbreviation", - "show_days": 1, - "show_seconds": 1 + "label": "Change Abbreviation" }, { "bold": 1, "default": "0", "fieldname": "is_group", "fieldtype": "Check", - "label": "Is Group", - "show_days": 1, - "show_seconds": 1 + "label": "Is Group" }, { "fieldname": "default_finance_book", "fieldtype": "Link", "label": "Default Finance Book", - "options": "Finance Book", - "show_days": 1, - "show_seconds": 1 + "options": "Finance Book" }, { "fieldname": "cb0", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "fieldname": "domain", "fieldtype": "Link", "label": "Domain", - "options": "Domain", - "show_days": 1, - "show_seconds": 1 + "options": "Domain" }, { "fieldname": "parent_company", @@ -177,32 +162,24 @@ "ignore_user_permissions": 1, "in_list_view": 1, "label": "Parent Company", - "options": "Company", - "show_days": 1, - "show_seconds": 1 + "options": "Company" }, { "fieldname": "company_logo", "fieldtype": "Attach Image", "hidden": 1, - "label": "Company Logo", - "show_days": 1, - "show_seconds": 1 + "label": "Company Logo" }, { "fieldname": "company_description", "fieldtype": "Text Editor", - "label": "Company Description", - "show_days": 1, - "show_seconds": 1 + "label": "Company Description" }, { "collapsible": 1, "fieldname": "sales_settings", "fieldtype": "Section Break", - "label": "Sales Settings", - "show_days": 1, - "show_seconds": 1 + "label": "Sales Settings" }, { "fieldname": "sales_monthly_history", @@ -210,9 +187,7 @@ "hidden": 1, "label": "Sales Monthly History", "no_copy": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "transactions_annual_history", @@ -220,23 +195,17 @@ "hidden": 1, "label": "Transactions Annual History", "no_copy": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "monthly_sales_target", "fieldtype": "Currency", "label": "Monthly Sales Target", - "options": "default_currency", - "show_days": 1, - "show_seconds": 1 + "options": "default_currency" }, { "fieldname": "column_break_goals", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "fieldname": "total_monthly_sales", @@ -244,16 +213,12 @@ "label": "Total Monthly Sales", "no_copy": 1, "options": "default_currency", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "charts_section", "fieldtype": "Section Break", - "label": "Default Values", - "show_days": 1, - "show_seconds": 1 + "label": "Default Values" }, { "fieldname": "default_currency", @@ -261,46 +226,34 @@ "ignore_user_permissions": 1, "label": "Default Currency", "options": "Currency", - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "fieldname": "default_letter_head", "fieldtype": "Link", "label": "Default Letter Head", - "options": "Letter Head", - "show_days": 1, - "show_seconds": 1 + "options": "Letter Head" }, { "fieldname": "default_holiday_list", "fieldtype": "Link", "label": "Default Holiday List", - "options": "Holiday List", - "show_days": 1, - "show_seconds": 1 + "options": "Holiday List" }, { "fieldname": "standard_working_hours", "fieldtype": "Float", - "label": "Standard Working Hours", - "show_days": 1, - "show_seconds": 1 + "label": "Standard Working Hours" }, { "fieldname": "default_warehouse_for_sales_return", "fieldtype": "Link", "label": "Default warehouse for Sales Return", - "options": "Warehouse", - "show_days": 1, - "show_seconds": 1 + "options": "Warehouse" }, { "fieldname": "column_break_10", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "fieldname": "country", @@ -308,26 +261,20 @@ "in_list_view": 1, "label": "Country", "options": "Country", - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "fieldname": "create_chart_of_accounts_based_on", "fieldtype": "Select", "label": "Create Chart Of Accounts Based On", - "options": "\nStandard Template\nExisting Company", - "show_days": 1, - "show_seconds": 1 + "options": "\nStandard Template\nExisting Company" }, { "depends_on": "eval:doc.create_chart_of_accounts_based_on===\"Standard Template\"", "fieldname": "chart_of_accounts", "fieldtype": "Select", "label": "Chart Of Accounts Template", - "no_copy": 1, - "show_days": 1, - "show_seconds": 1 + "no_copy": 1 }, { "depends_on": "eval:doc.create_chart_of_accounts_based_on===\"Existing Company\"", @@ -336,31 +283,23 @@ "ignore_user_permissions": 1, "label": "Existing Company ", "no_copy": 1, - "options": "Company", - "show_days": 1, - "show_seconds": 1 + "options": "Company" }, { "fieldname": "tax_id", "fieldtype": "Data", - "label": "Tax ID", - "show_days": 1, - "show_seconds": 1 + "label": "Tax ID" }, { "fieldname": "date_of_establishment", "fieldtype": "Date", - "label": "Date of Establishment", - "show_days": 1, - "show_seconds": 1 + "label": "Date of Establishment" }, { "fieldname": "default_settings", "fieldtype": "Section Break", "label": "Accounts Settings", - "oldfieldtype": "Section Break", - "show_days": 1, - "show_seconds": 1 + "oldfieldtype": "Section Break" }, { "depends_on": "eval:!doc.__islocal", @@ -371,9 +310,7 @@ "no_copy": 1, "oldfieldname": "default_bank_account", "oldfieldtype": "Link", - "options": "Account", - "show_days": 1, - "show_seconds": 1 + "options": "Account" }, { "depends_on": "eval:!doc.__islocal", @@ -382,9 +319,7 @@ "ignore_user_permissions": 1, "label": "Default Cash Account", "no_copy": 1, - "options": "Account", - "show_days": 1, - "show_seconds": 1 + "options": "Account" }, { "depends_on": "eval:!doc.__islocal", @@ -395,72 +330,54 @@ "no_copy": 1, "oldfieldname": "receivables_group", "oldfieldtype": "Link", - "options": "Account", - "show_days": 1, - "show_seconds": 1 + "options": "Account" }, { "fieldname": "round_off_account", "fieldtype": "Link", "label": "Round Off Account", - "options": "Account", - "show_days": 1, - "show_seconds": 1 + "options": "Account" }, { "fieldname": "round_off_cost_center", "fieldtype": "Link", "label": "Round Off Cost Center", - "options": "Cost Center", - "show_days": 1, - "show_seconds": 1 + "options": "Cost Center" }, { "fieldname": "write_off_account", "fieldtype": "Link", "label": "Write Off Account", - "options": "Account", - "show_days": 1, - "show_seconds": 1 + "options": "Account" }, { "fieldname": "discount_allowed_account", "fieldtype": "Link", "label": "Discount Allowed Account", - "options": "Account", - "show_days": 1, - "show_seconds": 1 + "options": "Account" }, { "fieldname": "discount_received_account", "fieldtype": "Link", "label": "Discount Received Account", - "options": "Account", - "show_days": 1, - "show_seconds": 1 + "options": "Account" }, { "fieldname": "exchange_gain_loss_account", "fieldtype": "Link", "label": "Exchange Gain / Loss Account", - "options": "Account", - "show_days": 1, - "show_seconds": 1 + "options": "Account" }, { "fieldname": "unrealized_exchange_gain_loss_account", "fieldtype": "Link", "label": "Unrealized Exchange Gain/Loss Account", - "options": "Account", - "show_days": 1, - "show_seconds": 1 + "options": "Account" }, { "fieldname": "column_break0", "fieldtype": "Column Break", "oldfieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1, "width": "50%" }, { @@ -468,9 +385,7 @@ "depends_on": "eval:doc.parent_company", "fieldname": "allow_account_creation_against_child_company", "fieldtype": "Check", - "label": "Allow Account Creation Against Child Company", - "show_days": 1, - "show_seconds": 1 + "label": "Allow Account Creation Against Child Company" }, { "depends_on": "eval:!doc.__islocal", @@ -481,18 +396,14 @@ "no_copy": 1, "oldfieldname": "payables_group", "oldfieldtype": "Link", - "options": "Account", - "show_days": 1, - "show_seconds": 1 + "options": "Account" }, { "fieldname": "default_employee_advance_account", "fieldtype": "Link", "label": "Default Employee Advance Account", "no_copy": 1, - "options": "Account", - "show_days": 1, - "show_seconds": 1 + "options": "Account" }, { "depends_on": "eval:!doc.__islocal", @@ -501,9 +412,7 @@ "ignore_user_permissions": 1, "label": "Default Cost of Goods Sold Account", "no_copy": 1, - "options": "Account", - "show_days": 1, - "show_seconds": 1 + "options": "Account" }, { "depends_on": "eval:!doc.__islocal", @@ -512,9 +421,7 @@ "ignore_user_permissions": 1, "label": "Default Income Account", "no_copy": 1, - "options": "Account", - "show_days": 1, - "show_seconds": 1 + "options": "Account" }, { "depends_on": "eval:!doc.__islocal", @@ -523,9 +430,7 @@ "ignore_user_permissions": 1, "label": "Default Deferred Revenue Account", "no_copy": 1, - "options": "Account", - "show_days": 1, - "show_seconds": 1 + "options": "Account" }, { "depends_on": "eval:!doc.__islocal", @@ -534,9 +439,7 @@ "ignore_user_permissions": 1, "label": "Default Deferred Expense Account", "no_copy": 1, - "options": "Account", - "show_days": 1, - "show_seconds": 1 + "options": "Account" }, { "depends_on": "eval:!doc.__islocal", @@ -545,9 +448,7 @@ "ignore_user_permissions": 1, "label": "Default Payroll Payable Account", "no_copy": 1, - "options": "Account", - "show_days": 1, - "show_seconds": 1 + "options": "Account" }, { "depends_on": "eval:!doc.__islocal", @@ -556,15 +457,11 @@ "ignore_user_permissions": 1, "label": "Default Expense Claim Payable Account", "no_copy": 1, - "options": "Account", - "show_days": 1, - "show_seconds": 1 + "options": "Account" }, { "fieldname": "section_break_22", - "fieldtype": "Section Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Section Break" }, { "depends_on": "eval:!doc.__islocal", @@ -573,15 +470,11 @@ "ignore_user_permissions": 1, "label": "Default Cost Center", "no_copy": 1, - "options": "Cost Center", - "show_days": 1, - "show_seconds": 1 + "options": "Cost Center" }, { "fieldname": "column_break_26", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "depends_on": "eval:!doc.__islocal", @@ -590,41 +483,31 @@ "label": "Credit Limit", "oldfieldname": "credit_limit", "oldfieldtype": "Currency", - "options": "default_currency", - "show_days": 1, - "show_seconds": 1 + "options": "default_currency" }, { "fieldname": "payment_terms", "fieldtype": "Link", "label": "Default Payment Terms Template", - "options": "Payment Terms Template", - "show_days": 1, - "show_seconds": 1 + "options": "Payment Terms Template" }, { "depends_on": "eval:!doc.__islocal", "fieldname": "auto_accounting_for_stock_settings", "fieldtype": "Section Break", - "label": "Stock Settings", - "show_days": 1, - "show_seconds": 1 + "label": "Stock Settings" }, { "default": "1", "fieldname": "enable_perpetual_inventory", "fieldtype": "Check", - "label": "Enable Perpetual Inventory", - "show_days": 1, - "show_seconds": 1 + "label": "Enable Perpetual Inventory" }, { "fieldname": "default_inventory_account", "fieldtype": "Link", "label": "Default Inventory Account", - "options": "Account", - "show_days": 1, - "show_seconds": 1 + "options": "Account" }, { "fieldname": "stock_adjustment_account", @@ -632,15 +515,11 @@ "ignore_user_permissions": 1, "label": "Stock Adjustment Account", "no_copy": 1, - "options": "Account", - "show_days": 1, - "show_seconds": 1 + "options": "Account" }, { "fieldname": "column_break_32", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "fieldname": "stock_received_but_not_billed", @@ -648,9 +527,7 @@ "ignore_user_permissions": 1, "label": "Stock Received But Not Billed", "no_copy": 1, - "options": "Account", - "show_days": 1, - "show_seconds": 1 + "options": "Account" }, { "fieldname": "expenses_included_in_valuation", @@ -658,144 +535,108 @@ "ignore_user_permissions": 1, "label": "Expenses Included In Valuation", "no_copy": 1, - "options": "Account", - "show_days": 1, - "show_seconds": 1 + "options": "Account" }, { "collapsible": 1, "fieldname": "fixed_asset_depreciation_settings", "fieldtype": "Section Break", - "label": "Fixed Asset Depreciation Settings", - "show_days": 1, - "show_seconds": 1 + "label": "Fixed Asset Depreciation Settings" }, { "fieldname": "accumulated_depreciation_account", "fieldtype": "Link", "label": "Accumulated Depreciation Account", "no_copy": 1, - "options": "Account", - "show_days": 1, - "show_seconds": 1 + "options": "Account" }, { "fieldname": "depreciation_expense_account", "fieldtype": "Link", "label": "Depreciation Expense Account", "no_copy": 1, - "options": "Account", - "show_days": 1, - "show_seconds": 1 + "options": "Account" }, { "fieldname": "series_for_depreciation_entry", "fieldtype": "Data", - "label": "Series for Asset Depreciation Entry (Journal Entry)", - "show_days": 1, - "show_seconds": 1 + "label": "Series for Asset Depreciation Entry (Journal Entry)" }, { "fieldname": "expenses_included_in_asset_valuation", "fieldtype": "Link", "label": "Expenses Included In Asset Valuation", - "options": "Account", - "show_days": 1, - "show_seconds": 1 + "options": "Account" }, { "fieldname": "column_break_40", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "fieldname": "disposal_account", "fieldtype": "Link", "label": "Gain/Loss Account on Asset Disposal", "no_copy": 1, - "options": "Account", - "show_days": 1, - "show_seconds": 1 + "options": "Account" }, { "fieldname": "depreciation_cost_center", "fieldtype": "Link", "label": "Asset Depreciation Cost Center", "no_copy": 1, - "options": "Cost Center", - "show_days": 1, - "show_seconds": 1 + "options": "Cost Center" }, { "fieldname": "capital_work_in_progress_account", "fieldtype": "Link", "label": "Capital Work In Progress Account", - "options": "Account", - "show_days": 1, - "show_seconds": 1 + "options": "Account" }, { "fieldname": "asset_received_but_not_billed", "fieldtype": "Link", "label": "Asset Received But Not Billed", - "options": "Account", - "show_days": 1, - "show_seconds": 1 + "options": "Account" }, { "collapsible": 1, "fieldname": "budget_detail", "fieldtype": "Section Break", - "label": "Budget Detail", - "show_days": 1, - "show_seconds": 1 + "label": "Budget Detail" }, { "fieldname": "exception_budget_approver_role", "fieldtype": "Link", "label": "Exception Budget Approver Role", - "options": "Role", - "show_days": 1, - "show_seconds": 1 + "options": "Role" }, { "collapsible": 1, "description": "For reference only.", "fieldname": "company_info", "fieldtype": "Section Break", - "label": "Company Info", - "show_days": 1, - "show_seconds": 1 + "label": "Company Info" }, { "fieldname": "date_of_incorporation", "fieldtype": "Date", - "label": "Date of Incorporation", - "show_days": 1, - "show_seconds": 1 + "label": "Date of Incorporation" }, { "fieldname": "address_html", - "fieldtype": "HTML", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "HTML" }, { "fieldname": "column_break1", "fieldtype": "Column Break", "oldfieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1, "width": "50%" }, { "depends_on": "eval:doc.date_of_incorporation", "fieldname": "date_of_commencement", "fieldtype": "Date", - "label": "Date of Commencement", - "show_days": 1, - "show_seconds": 1 + "label": "Date of Commencement" }, { "fieldname": "phone_no", @@ -803,9 +644,7 @@ "label": "Phone No", "oldfieldname": "phone_no", "oldfieldtype": "Data", - "options": "Phone", - "show_days": 1, - "show_seconds": 1 + "options": "Phone" }, { "fieldname": "fax", @@ -813,9 +652,7 @@ "label": "Fax", "oldfieldname": "fax", "oldfieldtype": "Data", - "options": "Phone", - "show_days": 1, - "show_seconds": 1 + "options": "Phone" }, { "fieldname": "email", @@ -823,25 +660,19 @@ "label": "Email", "oldfieldname": "email", "oldfieldtype": "Data", - "options": "Email", - "show_days": 1, - "show_seconds": 1 + "options": "Email" }, { "fieldname": "website", "fieldtype": "Data", "label": "Website", "oldfieldname": "website", - "oldfieldtype": "Data", - "show_days": 1, - "show_seconds": 1 + "oldfieldtype": "Data" }, { "fieldname": "registration_info", "fieldtype": "Section Break", "oldfieldtype": "Section Break", - "show_days": 1, - "show_seconds": 1, "width": "50%" }, { @@ -850,16 +681,12 @@ "fieldtype": "Code", "label": "Registration Details", "oldfieldname": "registration_details", - "oldfieldtype": "Code", - "show_days": 1, - "show_seconds": 1 + "oldfieldtype": "Code" }, { "fieldname": "delete_company_transactions", "fieldtype": "Button", - "label": "Delete Company Transactions", - "show_days": 1, - "show_seconds": 1 + "label": "Delete Company Transactions" }, { "fieldname": "lft", @@ -868,9 +695,7 @@ "label": "Lft", "print_hide": 1, "read_only": 1, - "search_index": 1, - "show_days": 1, - "show_seconds": 1 + "search_index": 1 }, { "fieldname": "rgt", @@ -879,9 +704,7 @@ "label": "Rgt", "print_hide": 1, "read_only": 1, - "search_index": 1, - "show_days": 1, - "show_seconds": 1 + "search_index": 1 }, { "fieldname": "old_parent", @@ -889,25 +712,19 @@ "hidden": 1, "label": "old_parent", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "default_selling_terms", "fieldtype": "Link", "label": "Default Selling Terms", - "options": "Terms and Conditions", - "show_days": 1, - "show_seconds": 1 + "options": "Terms and Conditions" }, { "fieldname": "default_buying_terms", "fieldtype": "Link", "label": "Default Buying Terms", - "options": "Terms and Conditions", - "show_days": 1, - "show_seconds": 1 + "options": "Terms and Conditions" }, { "fieldname": "service_received_but_not_billed", @@ -915,9 +732,13 @@ "ignore_user_permissions": 1, "label": "Service Received But Not Billed", "no_copy": 1, - "options": "Account", - "show_days": 1, - "show_seconds": 1 + "options": "Account" + }, + { + "default": "0", + "fieldname": "enable_perpetual_inventory_for_non_stock_items", + "fieldtype": "Check", + "label": "Enable Perpetual Inventory For Non Stock Items" } ], "icon": "fa fa-building", @@ -925,7 +746,7 @@ "image_field": "company_logo", "is_tree": 1, "links": [], - "modified": "2020-06-11 15:32:59.147735", + "modified": "2020-06-20 11:38:43.178970", "modified_by": "Administrator", "module": "Setup", "name": "Company", diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 94a6edd2c1c..15068ec5109 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -212,7 +212,7 @@ class PurchaseReceipt(BuyingController): landed_cost_entries = get_item_account_wise_additional_cost(self.name) expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation") service_received_but_not_billed_account = self.get_company_default("service_received_but_not_billed") - auto_accounting_for_non_stock_items = cint(frappe.db.get_single_value('Stock Settings', 'enable_perpetual_inventory_for_non_stock_items')) + auto_accounting_for_non_stock_items = cint(frappe.db.get_value('Company', self.company, 'enable_perpetual_inventory_for_non_stock_items')) gl_entries = [] warehouse_with_no_account = [] diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.json b/erpnext/stock/doctype/stock_settings/stock_settings.json index a7ed620dcaf..9c5d3d8340e 100644 --- a/erpnext/stock/doctype/stock_settings/stock_settings.json +++ b/erpnext/stock/doctype/stock_settings/stock_settings.json @@ -16,7 +16,6 @@ "action_if_quality_inspection_is_not_submitted", "show_barcode_field", "clean_description_html", - "enable_perpetual_inventory_for_non_stock_items", "section_break_7", "auto_insert_price_list_rate_if_missing", "allow_negative_stock", @@ -44,248 +43,180 @@ "fieldtype": "Select", "in_list_view": 1, "label": "Item Naming By", - "options": "Item Code\nNaming Series", - "show_days": 1, - "show_seconds": 1 + "options": "Item Code\nNaming Series" }, { "fieldname": "item_group", "fieldtype": "Link", "in_list_view": 1, "label": "Default Item Group", - "options": "Item Group", - "show_days": 1, - "show_seconds": 1 + "options": "Item Group" }, { "fieldname": "stock_uom", "fieldtype": "Link", "in_list_view": 1, "label": "Default Stock UOM", - "options": "UOM", - "show_days": 1, - "show_seconds": 1 + "options": "UOM" }, { "fieldname": "default_warehouse", "fieldtype": "Link", "label": "Default Warehouse", - "options": "Warehouse", - "show_days": 1, - "show_seconds": 1 + "options": "Warehouse" }, { "fieldname": "sample_retention_warehouse", "fieldtype": "Link", "label": "Sample Retention Warehouse", - "options": "Warehouse", - "show_days": 1, - "show_seconds": 1 + "options": "Warehouse" }, { "fieldname": "column_break_4", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "fieldname": "valuation_method", "fieldtype": "Select", "label": "Default Valuation Method", - "options": "FIFO\nMoving Average", - "show_days": 1, - "show_seconds": 1 + "options": "FIFO\nMoving Average" }, { "description": "Percentage you are allowed to receive or deliver more against the quantity ordered. For example: If you have ordered 100 units. and your Allowance is 10% then you are allowed to receive 110 units.", "fieldname": "over_delivery_receipt_allowance", "fieldtype": "Float", - "label": "Over Delivery/Receipt Allowance (%)", - "show_days": 1, - "show_seconds": 1 + "label": "Over Delivery/Receipt Allowance (%)" }, { "default": "Stop", "fieldname": "action_if_quality_inspection_is_not_submitted", "fieldtype": "Select", "label": "Action if Quality inspection is not submitted", - "options": "Stop\nWarn", - "show_days": 1, - "show_seconds": 1 + "options": "Stop\nWarn" }, { "default": "1", "fieldname": "show_barcode_field", "fieldtype": "Check", - "label": "Show Barcode Field", - "show_days": 1, - "show_seconds": 1 + "label": "Show Barcode Field" }, { "default": "1", "fieldname": "clean_description_html", "fieldtype": "Check", - "label": "Convert Item Description to Clean HTML", - "show_days": 1, - "show_seconds": 1 + "label": "Convert Item Description to Clean HTML" }, { "fieldname": "section_break_7", - "fieldtype": "Section Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Section Break" }, { "default": "0", "fieldname": "auto_insert_price_list_rate_if_missing", "fieldtype": "Check", - "label": "Auto insert Price List rate if missing", - "show_days": 1, - "show_seconds": 1 + "label": "Auto insert Price List rate if missing" }, { "default": "0", "fieldname": "allow_negative_stock", "fieldtype": "Check", - "label": "Allow Negative Stock", - "show_days": 1, - "show_seconds": 1 + "label": "Allow Negative Stock" }, { "fieldname": "column_break_10", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "default": "1", "fieldname": "automatically_set_serial_nos_based_on_fifo", "fieldtype": "Check", - "label": "Automatically Set Serial Nos based on FIFO", - "show_days": 1, - "show_seconds": 1 + "label": "Automatically Set Serial Nos based on FIFO" }, { "default": "1", "fieldname": "set_qty_in_transactions_based_on_serial_no_input", "fieldtype": "Check", - "label": "Set Qty in Transactions based on Serial No Input", - "show_days": 1, - "show_seconds": 1 + "label": "Set Qty in Transactions based on Serial No Input" }, { "fieldname": "auto_material_request", "fieldtype": "Section Break", - "label": "Auto Material Request", - "show_days": 1, - "show_seconds": 1 + "label": "Auto Material Request" }, { "default": "0", "fieldname": "auto_indent", "fieldtype": "Check", - "label": "Raise Material Request when stock reaches re-order level", - "show_days": 1, - "show_seconds": 1 + "label": "Raise Material Request when stock reaches re-order level" }, { "default": "0", "fieldname": "reorder_email_notify", "fieldtype": "Check", - "label": "Notify by Email on creation of automatic Material Request", - "show_days": 1, - "show_seconds": 1 + "label": "Notify by Email on creation of automatic Material Request" }, { "fieldname": "freeze_stock_entries", "fieldtype": "Section Break", - "label": "Freeze Stock Entries", - "show_days": 1, - "show_seconds": 1 + "label": "Freeze Stock Entries" }, { "fieldname": "stock_frozen_upto", "fieldtype": "Date", - "label": "Stock Frozen Upto", - "show_days": 1, - "show_seconds": 1 + "label": "Stock Frozen Upto" }, { "fieldname": "stock_frozen_upto_days", "fieldtype": "Int", - "label": "Freeze Stocks Older Than [Days]", - "show_days": 1, - "show_seconds": 1 + "label": "Freeze Stocks Older Than [Days]" }, { "fieldname": "stock_auth_role", "fieldtype": "Link", "label": "Role Allowed to edit frozen stock", - "options": "Role", - "show_days": 1, - "show_seconds": 1 + "options": "Role" }, { "fieldname": "batch_id_sb", "fieldtype": "Section Break", - "label": "Batch Identification", - "show_days": 1, - "show_seconds": 1 + "label": "Batch Identification" }, { "default": "0", "fieldname": "use_naming_series", "fieldtype": "Check", - "label": "Use Naming Series", - "show_days": 1, - "show_seconds": 1 + "label": "Use Naming Series" }, { "default": "BATCH-", "depends_on": "eval:doc.use_naming_series==1", "fieldname": "naming_series_prefix", "fieldtype": "Data", - "label": "Naming Series Prefix", - "show_days": 1, - "show_seconds": 1 + "label": "Naming Series Prefix" }, { "fieldname": "inter_warehouse_transfer_settings_section", "fieldtype": "Section Break", - "label": "Inter Warehouse Transfer Settings", - "show_days": 1, - "show_seconds": 1 + "label": "Inter Warehouse Transfer Settings" }, { "default": "0", "fieldname": "allow_from_dn", "fieldtype": "Check", - "label": "Allow Material Transfer From Delivery Note and Sales Invoice", - "show_days": 1, - "show_seconds": 1 + "label": "Allow Material Transfer From Delivery Note and Sales Invoice" }, { "default": "0", "fieldname": "allow_from_pr", "fieldtype": "Check", - "label": "Allow Material Transfer From Purchase Receipt and Purchase Invoice", - "show_days": 1, - "show_seconds": 1 - }, - { - "default": "0", - "fieldname": "enable_perpetual_inventory_for_non_stock_items", - "fieldtype": "Check", - "label": "Enable Perpetual Inventory For Non Stock Items", - "show_days": 1, - "show_seconds": 1 + "label": "Allow Material Transfer From Purchase Receipt and Purchase Invoice" } ], "icon": "icon-cog", "idx": 1, "issingle": 1, "links": [], - "modified": "2020-06-11 15:10:41.211638", + "modified": "2020-06-20 11:39:15.344112", "modified_by": "Administrator", "module": "Stock", "name": "Stock Settings", From bef80b7981b721f470a15b95bc5831d438b50132 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 20 Jun 2020 12:32:30 +0530 Subject: [PATCH 430/608] fix: Test --- .../account_balance/test_account_balance.py | 2 +- .../test_accounts_receivable.py | 22 +++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/erpnext/accounts/report/account_balance/test_account_balance.py b/erpnext/accounts/report/account_balance/test_account_balance.py index 5544fc46738..b6ced312d09 100644 --- a/erpnext/accounts/report/account_balance/test_account_balance.py +++ b/erpnext/accounts/report/account_balance/test_account_balance.py @@ -61,7 +61,7 @@ def make_sales_invoice(): debit_to = 'Debtors - _TC2', income_account = 'Sales - _TC2', expense_account = 'Cost of Goods Sold - _TC2', - cost_center = '_Test Company 2 - _TC2') + cost_center = 'Main - _TC2') diff --git a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py index f0274b44723..2ff5b531c51 100644 --- a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py @@ -63,7 +63,7 @@ def make_sales_invoice(): debit_to = 'Debtors - _TC2', income_account = 'Sales - _TC2', expense_account = 'Cost of Goods Sold - _TC2', - cost_center = '_Test Company 2 - _TC2', + cost_center = 'Main - _TC2', do_not_save=1) si.append('payment_schedule', dict(due_date=getdate(add_days(today(), 30)), invoice_portion=30.00, payment_amount=30)) @@ -83,14 +83,14 @@ def make_payment(docname): def make_credit_note(docname): create_sales_invoice(company="_Test Company 2", - customer = '_Test Customer 2', - currency = 'EUR', - qty = -1, - warehouse = 'Finished Goods - _TC2', - debit_to = 'Debtors - _TC2', - income_account = 'Sales - _TC2', - expense_account = 'Cost of Goods Sold - _TC2', - cost_center = '_Test Company 2 - _TC2', - is_return = 1, - return_against = docname) + customer = '_Test Customer 2', + currency = 'EUR', + qty = -1, + warehouse = 'Finished Goods - _TC2', + debit_to = 'Debtors - _TC2', + income_account = 'Sales - _TC2', + expense_account = 'Cost of Goods Sold - _TC2', + cost_center = 'Main - _TC2', + is_return = 1, + return_against = docname) From 84c69737421770163f96f712c72e1a2275b15267 Mon Sep 17 00:00:00 2001 From: Afshan Date: Sun, 21 Jun 2020 17:48:44 +0530 Subject: [PATCH 431/608] fix: adding json for checked add total row --- erpnext/selling/report/sales_analytics/sales_analytics.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.json b/erpnext/selling/report/sales_analytics/sales_analytics.json index bf9edd6cd49..de5c3a6b2a7 100644 --- a/erpnext/selling/report/sales_analytics/sales_analytics.json +++ b/erpnext/selling/report/sales_analytics/sales_analytics.json @@ -1,5 +1,5 @@ { - "add_total_row": 0, + "add_total_row": 1, "creation": "2018-09-21 12:46:29.451048", "disable_prepared_report": 0, "disabled": 0, @@ -7,7 +7,7 @@ "doctype": "Report", "idx": 0, "is_standard": "Yes", - "modified": "2020-04-30 19:49:02.303320", + "modified": "2020-06-19 17:41:03.132101", "modified_by": "Administrator", "module": "Selling", "name": "Sales Analytics", From ded3ab1cd778389ab348bcb5628379f129a8d02d Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 22 Jun 2020 09:40:15 +0530 Subject: [PATCH 432/608] fix: Test Cases --- .../purchase_invoice/purchase_invoice.py | 18 ++++++++++-------- erpnext/setup/doctype/company/company.py | 7 +++++++ .../purchase_receipt/purchase_receipt.py | 2 +- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 32d61f955be..0aa6475be80 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -571,16 +571,18 @@ class PurchaseInvoice(BuyingController): amount = flt(item.base_net_amount + item.item_tax_amount, item.precision("base_net_amount")) auto_accounting_for_non_stock_items = cint(frappe.db.get_value('Company', self.company, 'enable_perpetual_inventory_for_non_stock_items')) - service_received_but_not_billed_account = self.get_company_default("service_received_but_not_billed") - if item.purchase_receipt and auto_accounting_for_non_stock_items: - # Post reverse entry for Stock-Received-But-Not-Billed if it is booked in Purchase Receipt - expense_booked_in_pr = frappe.db.get_value('GL Entry', {'is_cancelled': 0, - 'voucher_type': 'Purchase Receipt', 'voucher_no': item.purchase_receipt, 'voucher_detail_no': item.pr_detail, - 'account':service_received_but_not_billed_account}, ['name']) + if auto_accounting_for_non_stock_items: + service_received_but_not_billed_account = self.get_company_default("service_received_but_not_billed") - if expense_booked_in_pr: - expense_account = service_received_but_not_billed_account + if item.purchase_receipt: + # Post reverse entry for Stock-Received-But-Not-Billed if it is booked in Purchase Receipt + expense_booked_in_pr = frappe.db.get_value('GL Entry', {'is_cancelled': 0, + 'voucher_type': 'Purchase Receipt', 'voucher_no': item.purchase_receipt, 'voucher_detail_no': item.pr_detail, + 'account':service_received_but_not_billed_account}, ['name']) + + if expense_booked_in_pr: + expense_account = service_received_but_not_billed_account gl_entries.append(self.get_gl_dict({ "account": expense_account, diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py index 8bcaa287072..47b41a97ad3 100644 --- a/erpnext/setup/doctype/company/company.py +++ b/erpnext/setup/doctype/company/company.py @@ -46,6 +46,7 @@ class Company(NestedSet): self.validate_currency() self.validate_coa_input() self.validate_perpetual_inventory() + self.validate_perpetual_inventory_for_non_stock_items() self.check_country_change() self.set_chart_of_accounts() self.validate_parent_company() @@ -182,6 +183,12 @@ class Company(NestedSet): frappe.msgprint(_("Set default inventory account for perpetual inventory"), alert=True, indicator='orange') + def validate_perpetual_inventory_for_non_stock_items(self): + if not self.get("__islocal"): + if cint(self.enable_perpetual_inventory_for_non_stock_items) == 1 and not self.service_received_but_not_billed: + frappe.throw(_("Set default {0} account for perpetual inventory for non stock items").format( + frappe.bold('Service Received But Not Billed'))) + def check_country_change(self): frappe.flags.country_change = False diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 15068ec5109..d0ba001d7e1 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -211,7 +211,6 @@ class PurchaseReceipt(BuyingController): stock_rbnb = self.get_company_default("stock_received_but_not_billed") landed_cost_entries = get_item_account_wise_additional_cost(self.name) expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation") - service_received_but_not_billed_account = self.get_company_default("service_received_but_not_billed") auto_accounting_for_non_stock_items = cint(frappe.db.get_value('Company', self.company, 'enable_perpetual_inventory_for_non_stock_items')) gl_entries = [] @@ -305,6 +304,7 @@ class PurchaseReceipt(BuyingController): warehouse_with_no_account.append(d.warehouse) elif d.item_code not in stock_items and flt(d.qty) and auto_accounting_for_non_stock_items: + service_received_but_not_billed_account = self.get_company_default("service_received_but_not_billed") credit_currency = get_account_currency(service_received_but_not_billed_account) gl_entries.append(self.get_gl_dict({ From d79e8e82cb198c3a191617c8933dbd3d9accafe5 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 22 Jun 2020 10:00:12 +0530 Subject: [PATCH 433/608] fix: Test cases --- erpnext/regional/report/datev/test_datev.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/erpnext/regional/report/datev/test_datev.py b/erpnext/regional/report/datev/test_datev.py index 3cc65fe9d3a..eed62a8690f 100644 --- a/erpnext/regional/report/datev/test_datev.py +++ b/erpnext/regional/report/datev/test_datev.py @@ -90,7 +90,7 @@ def make_customer_with_account(customer_name, company): if not frappe.db.exists("Customer", customer_name): customer = frappe.get_doc({ - "doctype": "Customer", + "doctype": "Customer", "customer_name": customer_name, "customer_type": "Company", "accounts": [{ @@ -155,17 +155,17 @@ class TestDatev(TestCase): setup_fiscal_year() warehouse = frappe.db.get_value("Item Default", { - "parent": item.name, + "parent": item.name, "company": self.company.name }, "default_warehouse") income_account = frappe.db.get_value("Account", { - "account_number": "4200", + "account_number": "4200", "company": self.company.name }, "name") tax_account = frappe.db.get_value("Account", { - "account_number": "3806", + "account_number": "3806", "company": self.company.name }, "name") @@ -186,9 +186,12 @@ class TestDatev(TestCase): "charge_type": "On Net Total", "account_head": tax_account, "description": "Umsatzsteuer 19 %", - "rate": 19 + "rate": 19, + "cost_center": self.company.cost_center }) + si.cost_center = self.company.cost_center + si.save() si.submit() @@ -196,7 +199,7 @@ class TestDatev(TestCase): def is_subset(get_data, allowed_keys): """ Validate that the dict contains only allowed keys. - + Params: get_data -- Function that returns a list of dicts. allowed_keys -- List of allowed keys From 43be163b32a0f201389ca9d8e4c4918d49730530 Mon Sep 17 00:00:00 2001 From: Alvaro Date: Mon, 22 Jun 2020 07:36:12 +0200 Subject: [PATCH 434/608] feat: Enabled translation on html files in LMS [Proposal] (#21582) * :fix: translation fix for html files in LMS * fix: typo in translation * Update content.html * Update content.html * Update profile.html Co-authored-by: Shivam Mishra Co-authored-by: Marica --- erpnext/www/lms/content.html | 22 +++++++++++----------- erpnext/www/lms/course.html | 6 +++--- erpnext/www/lms/index.html | 2 +- erpnext/www/lms/macros/card.html | 4 ++-- erpnext/www/lms/macros/hero.html | 10 +++++----- erpnext/www/lms/profile.html | 8 ++++---- erpnext/www/lms/program.html | 6 +++--- erpnext/www/lms/topic.html | 10 +++++----- 8 files changed, 34 insertions(+), 34 deletions(-) diff --git a/erpnext/www/lms/content.html b/erpnext/www/lms/content.html index cdc71412c48..dc9b6d80fb5 100644 --- a/erpnext/www/lms/content.html +++ b/erpnext/www/lms/content.html @@ -59,7 +59,7 @@ {% macro title() %}
    @@ -69,15 +69,15 @@ {% macro navigation() %} {% if previous %} - Previous + {{_('Previous')}} {% else %} - Back to Course + {{ _('Back to Course') }} {% endif %} {% if next %} - + {% else %} - + {% endif %} {% endmacro %} @@ -86,7 +86,7 @@ {{ title() }}
    {% if content.duration %} - {{ content.duration }} Mins + {{ content.duration }} {{_('Mins')}} {% endif %} {% if content.publish_date and content.duration%} @@ -94,7 +94,7 @@ {% endif %} {% if content.publish_date %} - Published on {{ content.publish_date.strftime('%d, %b %Y') }} + {{_('Published on')}} {{ content.publish_date.strftime('%d, %b %Y') }} {% endif %}
    @@ -109,13 +109,13 @@ {{ title() }}
    {% if content.author or content.publish_date %} - Published + {{_('Published')}} {% endif %} {% if content.author %} - by {{ content.author }} + {{_('by')}} {{ content.author }} {% endif %} {% if content.publish_date %} - on {{ content.publish_date.strftime('%d, %b %Y') }} + {{_('on')}} {{ content.publish_date.strftime('%d, %b %Y') }} {% endif %}
    @@ -205,4 +205,4 @@ {% endif %} -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/erpnext/www/lms/course.html b/erpnext/www/lms/course.html index f2fd9363e8c..0d70ed5cefd 100644 --- a/erpnext/www/lms/course.html +++ b/erpnext/www/lms/course.html @@ -72,11 +72,11 @@ {% if has_access %} diff --git a/erpnext/www/lms/index.html b/erpnext/www/lms/index.html index ffb4419f367..7ce3521273f 100644 --- a/erpnext/www/lms/index.html +++ b/erpnext/www/lms/index.html @@ -45,7 +45,7 @@

    {{ education_settings.description }}

    {% if frappe.session.user == 'Guest' %} - Sign Up + {{_('Sign Up')}} {% endif %}

    diff --git a/erpnext/www/lms/macros/card.html b/erpnext/www/lms/macros/card.html index 076061d41b3..dc8fc5c72c7 100644 --- a/erpnext/www/lms/macros/card.html +++ b/erpnext/www/lms/macros/card.html @@ -15,8 +15,8 @@
    {% if has_access or program.intro_video%} {% endif %} diff --git a/erpnext/www/lms/macros/hero.html b/erpnext/www/lms/macros/hero.html index 66bb861c467..94f239eb8ed 100644 --- a/erpnext/www/lms/macros/hero.html +++ b/erpnext/www/lms/macros/hero.html @@ -2,16 +2,16 @@

    {{ title }}

    {{ description or ''}}

    {% if frappe.session.user == 'Guest' %} - Sign Up + {{_('Sign Up')}} {% elif not has_access %} - + {% endif %}

    @@ -28,7 +28,7 @@ let btn = document.getElementById('enroll'); btn.disbaled = true; - btn.innerText = 'Enrolling...' + btn.innerText = __('Enrolling...') let opts = { method: 'erpnext.education.utils.enroll_in_program', @@ -44,7 +44,7 @@ window.location.reload() } }) - success_dialog.set_message('You have successfully enrolled for the program '); + success_dialog.set_message(__('You have successfully enrolled for the program ')); success_dialog.$message.show() success_dialog.show(); btn.disbaled = false; diff --git a/erpnext/www/lms/profile.html b/erpnext/www/lms/profile.html index 9508daedb71..5755dfe6d8e 100644 --- a/erpnext/www/lms/profile.html +++ b/erpnext/www/lms/profile.html @@ -30,7 +30,7 @@
    @@ -43,11 +43,11 @@

    {{ student.first_name }} {{ student.last_name or '' }}

    @@ -61,4 +61,4 @@
    -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/erpnext/www/lms/program.html b/erpnext/www/lms/program.html index 271b7813bb4..7ad618630a4 100644 --- a/erpnext/www/lms/program.html +++ b/erpnext/www/lms/program.html @@ -55,11 +55,11 @@ {% if has_access and progress[course.name] %} {% endif %} diff --git a/erpnext/www/lms/topic.html b/erpnext/www/lms/topic.html index 1f0d1876646..cd24616cd45 100644 --- a/erpnext/www/lms/topic.html +++ b/erpnext/www/lms/topic.html @@ -23,13 +23,13 @@ {% if has_access %} From 8ffb9f852f7b825e3acc6aa43c21f6b40fb50e41 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Mon, 22 Jun 2020 12:17:29 +0530 Subject: [PATCH 435/608] fix: patch check_is_income_tax_component (#22371) --- erpnext/patches.txt | 6 +++--- .../v13_0/check_is_income_tax_component.py | 21 ++++++++++++++++++- .../additional_salary/additional_salary.json | 2 +- .../employee_benefit_application.json | 2 +- .../employee_benefit_application_detail.json | 2 +- .../employee_benefit_claim.json | 2 +- .../employee_incentive.json | 2 +- .../employee_other_income.json | 2 +- .../employee_tax_exemption_category.json | 2 +- .../employee_tax_exemption_declaration.json | 2 +- ...ee_tax_exemption_declaration_category.json | 2 +- ...ployee_tax_exemption_proof_submission.json | 2 +- ...tax_exemption_proof_submission_detail.json | 2 +- .../employee_tax_exemption_sub_category.json | 2 +- .../income_tax_slab/income_tax_slab.json | 2 +- .../income_tax_slab_other_charges.json | 2 +- .../payroll_employee_detail.json | 2 +- .../doctype/payroll_entry/payroll_entry.json | 2 +- .../payroll_period/payroll_period.json | 2 +- .../payroll_period_date.json | 2 +- .../payroll_settings/payroll_settings.json | 2 +- .../retention_bonus/retention_bonus.json | 2 +- .../salary_component/salary_component.json | 2 +- .../doctype/salary_detail/salary_detail.json | 2 +- .../doctype/salary_slip/salary_slip.json | 2 +- .../salary_slip_timesheet.json | 2 +- .../salary_structure/salary_structure.json | 2 +- .../salary_structure_assignment.json | 2 +- .../taxable_salary_slab.json | 2 +- 29 files changed, 50 insertions(+), 31 deletions(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index b17cc474447..928c0ab9d86 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -702,6 +702,6 @@ erpnext.patches.v12_0.update_address_template_for_india erpnext.patches.v12_0.set_multi_uom_in_rfq erpnext.patches.v13_0.delete_old_sales_reports execute:frappe.delete_doc_if_exists("DocType", "Bank Reconciliation") -erpnext.patches.v13_0.move_doctype_reports_and_notification_from_hr_to_payroll -erpnext.patches.v13_0.move_payroll_setting_separately_from_hr_settings -erpnext.patches.v13_0.check_is_income_tax_component +erpnext.patches.v13_0.move_doctype_reports_and_notification_from_hr_to_payroll #22-06-2020 +erpnext.patches.v13_0.move_payroll_setting_separately_from_hr_settings #22-06-2020 +erpnext.patches.v13_0.check_is_income_tax_component #22-06-2020 diff --git a/erpnext/patches/v13_0/check_is_income_tax_component.py b/erpnext/patches/v13_0/check_is_income_tax_component.py index f69412c5383..9ad48e23b7f 100644 --- a/erpnext/patches/v13_0/check_is_income_tax_component.py +++ b/erpnext/patches/v13_0/check_is_income_tax_component.py @@ -4,9 +4,28 @@ from __future__ import unicode_literals import frappe, erpnext +from erpnext.regional.india.setup import setup def execute(): - frappe.reload_doc('Payroll', 'doctype', 'salary_structure') + + doctypes = ['salary_component', + 'Employee Tax Exemption Declaration', + 'Employee Tax Exemption Proof Submission', + 'Employee Tax Exemption Declaration Category', + 'Employee Tax Exemption Proof Submission Detail' + ] + + for doctype in doctypes: + frappe.reload_doc('Payroll', 'doctype', doctype) + + + reports = ['Professional Tax Deductions', 'Provident Fund Deductions'] + for report in reports: + frappe.reload_doc('Regional', 'Report', report) + frappe.reload_doc('Regional', 'Report', report) + + if erpnext.get_region() == "India": + setup(patch=True) if frappe.db.exists("Salary Component", "Income Tax"): frappe.db.set_value("Salary Component", "Income Tax", "is_income_tax_component", 1) diff --git a/erpnext/payroll/doctype/additional_salary/additional_salary.json b/erpnext/payroll/doctype/additional_salary/additional_salary.json index ad64289f0d2..69cb5da893e 100644 --- a/erpnext/payroll/doctype/additional_salary/additional_salary.json +++ b/erpnext/payroll/doctype/additional_salary/additional_salary.json @@ -163,7 +163,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-05-27 21:10:50.374063", + "modified": "2020-06-22 21:10:50.374063", "modified_by": "Administrator", "module": "Payroll", "name": "Additional Salary", diff --git a/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.json b/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.json index 275c855b687..b0c1bd6c3e5 100644 --- a/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.json +++ b/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.json @@ -119,7 +119,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-05-27 22:58:31.271922", + "modified": "2020-06-22 22:58:31.271922", "modified_by": "Administrator", "module": "Payroll", "name": "Employee Benefit Application", diff --git a/erpnext/payroll/doctype/employee_benefit_application_detail/employee_benefit_application_detail.json b/erpnext/payroll/doctype/employee_benefit_application_detail/employee_benefit_application_detail.json index f0415d2b108..fa6b4da2af3 100644 --- a/erpnext/payroll/doctype/employee_benefit_application_detail/employee_benefit_application_detail.json +++ b/erpnext/payroll/doctype/employee_benefit_application_detail/employee_benefit_application_detail.json @@ -45,7 +45,7 @@ ], "istable": 1, "links": [], - "modified": "2020-05-27 23:45:00.519134", + "modified": "2020-06-22 23:45:00.519134", "modified_by": "Administrator", "module": "Payroll", "name": "Employee Benefit Application Detail", diff --git a/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.json b/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.json index 7fea59fabf4..ae4c218615a 100644 --- a/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.json +++ b/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.json @@ -123,7 +123,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-05-27 23:01:50.791676", + "modified": "2020-06-22 23:01:50.791676", "modified_by": "Administrator", "module": "Payroll", "name": "Employee Benefit Claim", diff --git a/erpnext/payroll/doctype/employee_incentive/employee_incentive.json b/erpnext/payroll/doctype/employee_incentive/employee_incentive.json index 81ff86506a2..204c9a40b1d 100644 --- a/erpnext/payroll/doctype/employee_incentive/employee_incentive.json +++ b/erpnext/payroll/doctype/employee_incentive/employee_incentive.json @@ -74,7 +74,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-05-27 22:42:51.209630", + "modified": "2020-06-22 22:42:51.209630", "modified_by": "Administrator", "module": "Payroll", "name": "Employee Incentive", diff --git a/erpnext/payroll/doctype/employee_other_income/employee_other_income.json b/erpnext/payroll/doctype/employee_other_income/employee_other_income.json index c5a2a73e423..14f63e4fdd9 100644 --- a/erpnext/payroll/doctype/employee_other_income/employee_other_income.json +++ b/erpnext/payroll/doctype/employee_other_income/employee_other_income.json @@ -76,7 +76,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-05-27 22:55:17.604688", + "modified": "2020-06-22 22:55:17.604688", "modified_by": "Administrator", "module": "Payroll", "name": "Employee Other Income", diff --git a/erpnext/payroll/doctype/employee_tax_exemption_category/employee_tax_exemption_category.json b/erpnext/payroll/doctype/employee_tax_exemption_category/employee_tax_exemption_category.json index c09708279a0..f2556d7d962 100644 --- a/erpnext/payroll/doctype/employee_tax_exemption_category/employee_tax_exemption_category.json +++ b/erpnext/payroll/doctype/employee_tax_exemption_category/employee_tax_exemption_category.json @@ -26,7 +26,7 @@ } ], "links": [], - "modified": "2020-05-27 23:16:47.472910", + "modified": "2020-06-22 23:16:47.472910", "modified_by": "Administrator", "module": "Payroll", "name": "Employee Tax Exemption Category", diff --git a/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json b/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json index 5423365bdb9..de7c348bb2c 100644 --- a/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json +++ b/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.json @@ -107,7 +107,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-05-27 22:49:43.829892", + "modified": "2020-06-22 22:49:43.829892", "modified_by": "Administrator", "module": "Payroll", "name": "Employee Tax Exemption Declaration", diff --git a/erpnext/payroll/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.json b/erpnext/payroll/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.json index eddaec28970..8c2f9aa370a 100644 --- a/erpnext/payroll/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.json +++ b/erpnext/payroll/doctype/employee_tax_exemption_declaration_category/employee_tax_exemption_declaration_category.json @@ -48,7 +48,7 @@ ], "istable": 1, "links": [], - "modified": "2020-05-27 23:41:03.638739", + "modified": "2020-06-22 23:41:03.638739", "modified_by": "Administrator", "module": "Payroll", "name": "Employee Tax Exemption Declaration Category", diff --git a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.json b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.json index de8fa09a83b..b62b5aab0b4 100644 --- a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.json +++ b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.json @@ -130,7 +130,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-05-27 22:53:10.412321", + "modified": "2020-06-22 22:53:10.412321", "modified_by": "Administrator", "module": "Payroll", "name": "Employee Tax Exemption Proof Submission", diff --git a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.json b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.json index 4c53bd3f5d1..c1f532050ac 100644 --- a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.json +++ b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission_detail/employee_tax_exemption_proof_submission_detail.json @@ -53,7 +53,7 @@ ], "istable": 1, "links": [], - "modified": "2020-05-27 23:37:08.265600", + "modified": "2020-06-22 23:37:08.265600", "modified_by": "Administrator", "module": "Payroll", "name": "Employee Tax Exemption Proof Submission Detail", diff --git a/erpnext/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.json b/erpnext/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.json index b89d9c197f3..f8c4b8bcb0c 100644 --- a/erpnext/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.json +++ b/erpnext/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.json @@ -38,7 +38,7 @@ } ], "links": [], - "modified": "2020-05-27 23:18:08.254645", + "modified": "2020-06-22 23:18:08.254645", "modified_by": "Administrator", "module": "Payroll", "name": "Employee Tax Exemption Sub Category", diff --git a/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.json b/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.json index 72b43326c32..6337d5a6d3e 100644 --- a/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.json +++ b/erpnext/payroll/doctype/income_tax_slab/income_tax_slab.json @@ -94,7 +94,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-05-27 20:27:13.425084", + "modified": "2020-06-22 20:27:13.425084", "modified_by": "Administrator", "module": "Payroll", "name": "Income Tax Slab", diff --git a/erpnext/payroll/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.json b/erpnext/payroll/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.json index 2531c79828a..7f21204591a 100644 --- a/erpnext/payroll/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.json +++ b/erpnext/payroll/doctype/income_tax_slab_other_charges/income_tax_slab_other_charges.json @@ -62,7 +62,7 @@ ], "istable": 1, "links": [], - "modified": "2020-05-27 23:33:17.931912", + "modified": "2020-06-22 23:33:17.931912", "modified_by": "Administrator", "module": "Payroll", "name": "Income Tax Slab Other Charges", diff --git a/erpnext/payroll/doctype/payroll_employee_detail/payroll_employee_detail.json b/erpnext/payroll/doctype/payroll_employee_detail/payroll_employee_detail.json index b22e5de3243..bb68e1814a7 100644 --- a/erpnext/payroll/doctype/payroll_employee_detail/payroll_employee_detail.json +++ b/erpnext/payroll/doctype/payroll_employee_detail/payroll_employee_detail.json @@ -52,7 +52,7 @@ ], "istable": 1, "links": [], - "modified": "2020-05-27 23:25:13.779032", + "modified": "2020-06-22 23:25:13.779032", "modified_by": "Administrator", "module": "Payroll", "name": "Payroll Employee Detail", diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry.json b/erpnext/payroll/doctype/payroll_entry/payroll_entry.json index 4888be29871..31a899699d7 100644 --- a/erpnext/payroll/doctype/payroll_entry/payroll_entry.json +++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.json @@ -262,7 +262,7 @@ "icon": "fa fa-cog", "is_submittable": 1, "links": [], - "modified": "2020-05-27 20:06:06.953904", + "modified": "2020-06-22 20:06:06.953904", "modified_by": "Administrator", "module": "Payroll", "name": "Payroll Entry", diff --git a/erpnext/payroll/doctype/payroll_period/payroll_period.json b/erpnext/payroll/doctype/payroll_period/payroll_period.json index 345a2415a2c..c919b4fe13b 100644 --- a/erpnext/payroll/doctype/payroll_period/payroll_period.json +++ b/erpnext/payroll/doctype/payroll_period/payroll_period.json @@ -53,7 +53,7 @@ } ], "links": [], - "modified": "2020-05-27 20:12:32.684189", + "modified": "2020-06-22 20:12:32.684189", "modified_by": "Administrator", "module": "Payroll", "name": "Payroll Period", diff --git a/erpnext/payroll/doctype/payroll_period_date/payroll_period_date.json b/erpnext/payroll/doctype/payroll_period_date/payroll_period_date.json index d745fcd5325..4a2f383b379 100644 --- a/erpnext/payroll/doctype/payroll_period_date/payroll_period_date.json +++ b/erpnext/payroll/doctype/payroll_period_date/payroll_period_date.json @@ -26,7 +26,7 @@ ], "istable": 1, "links": [], - "modified": "2020-05-27 23:30:15.943356", + "modified": "2020-06-22 23:30:15.943356", "modified_by": "Administrator", "module": "Payroll", "name": "Payroll Period Date", diff --git a/erpnext/payroll/doctype/payroll_settings/payroll_settings.json b/erpnext/payroll/doctype/payroll_settings/payroll_settings.json index e3b8b3e7e04..e14b4785ba3 100644 --- a/erpnext/payroll/doctype/payroll_settings/payroll_settings.json +++ b/erpnext/payroll/doctype/payroll_settings/payroll_settings.json @@ -109,7 +109,7 @@ "icon": "fa fa-cog", "issingle": 1, "links": [], - "modified": "2020-06-05 12:35:34.861674", + "modified": "2020-06-22 12:35:34.861674", "modified_by": "Administrator", "module": "Payroll", "name": "Payroll Settings", diff --git a/erpnext/payroll/doctype/retention_bonus/retention_bonus.json b/erpnext/payroll/doctype/retention_bonus/retention_bonus.json index 53fe17f9f66..da884c2f289 100644 --- a/erpnext/payroll/doctype/retention_bonus/retention_bonus.json +++ b/erpnext/payroll/doctype/retention_bonus/retention_bonus.json @@ -93,7 +93,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-05-27 22:42:05.251951", + "modified": "2020-06-22 22:42:05.251951", "modified_by": "Administrator", "module": "Payroll", "name": "Retention Bonus", diff --git a/erpnext/payroll/doctype/salary_component/salary_component.json b/erpnext/payroll/doctype/salary_component/salary_component.json index f1e5cf090dd..225b0482937 100644 --- a/erpnext/payroll/doctype/salary_component/salary_component.json +++ b/erpnext/payroll/doctype/salary_component/salary_component.json @@ -245,7 +245,7 @@ ], "icon": "fa fa-flag", "links": [], - "modified": "2020-06-01 15:39:20.826565", + "modified": "2020-06-22 15:39:20.826565", "modified_by": "Administrator", "module": "Payroll", "name": "Salary Component", diff --git a/erpnext/payroll/doctype/salary_detail/salary_detail.json b/erpnext/payroll/doctype/salary_detail/salary_detail.json index b7d2bc12725..adb54f26c6d 100644 --- a/erpnext/payroll/doctype/salary_detail/salary_detail.json +++ b/erpnext/payroll/doctype/salary_detail/salary_detail.json @@ -211,7 +211,7 @@ ], "istable": 1, "links": [], - "modified": "2020-05-27 23:21:26.300951", + "modified": "2020-06-22 23:21:26.300951", "modified_by": "Administrator", "module": "Payroll", "name": "Salary Detail", diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.json b/erpnext/payroll/doctype/salary_slip/salary_slip.json index a6337deef58..663a3ef9ea2 100644 --- a/erpnext/payroll/doctype/salary_slip/salary_slip.json +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.json @@ -616,7 +616,7 @@ "idx": 9, "is_submittable": 1, "links": [], - "modified": "2020-06-05 14:42:43.921828", + "modified": "2020-06-22 14:42:43.921828", "modified_by": "Administrator", "module": "Payroll", "name": "Salary Slip", diff --git a/erpnext/payroll/doctype/salary_slip_timesheet/salary_slip_timesheet.json b/erpnext/payroll/doctype/salary_slip_timesheet/salary_slip_timesheet.json index 028c195a2fe..9930c53ec97 100644 --- a/erpnext/payroll/doctype/salary_slip_timesheet/salary_slip_timesheet.json +++ b/erpnext/payroll/doctype/salary_slip_timesheet/salary_slip_timesheet.json @@ -28,7 +28,7 @@ ], "istable": 1, "links": [], - "modified": "2020-05-27 23:27:43.463532", + "modified": "2020-06-22 23:27:43.463532", "modified_by": "Administrator", "module": "Payroll", "name": "Salary Slip Timesheet", diff --git a/erpnext/payroll/doctype/salary_structure/salary_structure.json b/erpnext/payroll/doctype/salary_structure/salary_structure.json index e710f6b72cb..5f94929f0b5 100644 --- a/erpnext/payroll/doctype/salary_structure/salary_structure.json +++ b/erpnext/payroll/doctype/salary_structure/salary_structure.json @@ -282,7 +282,7 @@ "idx": 1, "is_submittable": 1, "links": [], - "modified": "2020-06-05 17:07:26.129355", + "modified": "2020-06-22 17:07:26.129355", "modified_by": "Administrator", "module": "Payroll", "name": "Salary Structure", diff --git a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.json b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.json index 4f74a7fecf4..c84e034c727 100644 --- a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.json +++ b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.json @@ -124,7 +124,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-05-27 19:58:09.964692", + "modified": "2020-06-22 19:58:09.964692", "modified_by": "Administrator", "module": "Payroll", "name": "Salary Structure Assignment", diff --git a/erpnext/payroll/doctype/taxable_salary_slab/taxable_salary_slab.json b/erpnext/payroll/doctype/taxable_salary_slab/taxable_salary_slab.json index 277576eff97..ce9512f898f 100644 --- a/erpnext/payroll/doctype/taxable_salary_slab/taxable_salary_slab.json +++ b/erpnext/payroll/doctype/taxable_salary_slab/taxable_salary_slab.json @@ -51,7 +51,7 @@ ], "istable": 1, "links": [], - "modified": "2020-05-27 23:32:47.253106", + "modified": "2020-06-22 23:32:47.253106", "modified_by": "Administrator", "module": "Payroll", "name": "Taxable Salary Slab", From 62897d38784d91a4f4d0309f8d4c77b17c21b9e4 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 22 Jun 2020 14:54:01 +0530 Subject: [PATCH 436/608] fix: Unable to cancel employee advance --- erpnext/hr/doctype/employee_advance/employee_advance.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.py b/erpnext/hr/doctype/employee_advance/employee_advance.py index a49dfcfc2a2..76195812c80 100644 --- a/erpnext/hr/doctype/employee_advance/employee_advance.py +++ b/erpnext/hr/doctype/employee_advance/employee_advance.py @@ -22,6 +22,7 @@ class EmployeeAdvance(Document): self.validate_employee_advance_account() def on_cancel(self): + self.ignore_linked_doctypes = ('GL Entry') self.set_status() def set_status(self): From 0093b3b27cad0799a358aed1a756cebb5b1efc08 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Mon, 22 Jun 2020 17:21:01 +0530 Subject: [PATCH 437/608] fix: label (#22377) --- .../payroll/doctype/payroll_settings/payroll_settings.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/payroll/doctype/payroll_settings/payroll_settings.json b/erpnext/payroll/doctype/payroll_settings/payroll_settings.json index e14b4785ba3..c47caa1227b 100644 --- a/erpnext/payroll/doctype/payroll_settings/payroll_settings.json +++ b/erpnext/payroll/doctype/payroll_settings/payroll_settings.json @@ -63,7 +63,7 @@ "description": "The fraction of daily wages to be paid for half-day attendance", "fieldname": "daily_wages_fraction_for_half_day", "fieldtype": "Float", - "label": "Daily Wages Fraction for Half Day", + "label": "Fraction of Daily Salary for Half Day", "show_days": 1, "show_seconds": 1 }, @@ -109,7 +109,7 @@ "icon": "fa fa-cog", "issingle": 1, "links": [], - "modified": "2020-06-22 12:35:34.861674", + "modified": "2020-06-22 17:00:58.408030", "modified_by": "Administrator", "module": "Payroll", "name": "Payroll Settings", From 7672166ce3ef9de1ddf13bf926ff8ebce2c65a11 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Mon, 22 Jun 2020 17:22:05 +0530 Subject: [PATCH 438/608] fix: Wrong filter (#22375) --- .../report/salary_payments_via_ecs/salary_payments_via_ecs.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.py b/erpnext/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.py index afd5c13b32e..073bd91300e 100644 --- a/erpnext/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.py +++ b/erpnext/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.py @@ -86,6 +86,7 @@ def get_conditions(filters): if filters.get("period"): conditions.append("month(start_date) = '%s' " % (filters["period"])) + conditions.append("year(start_date) = '%s' " % (frappe.utils.getdate().year)) return " and ".join(conditions) From be4fc1a78eff5b2d6616565cef6a3f0634f4bbde Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Mon, 22 Jun 2020 18:03:11 +0530 Subject: [PATCH 439/608] fix(UX): Notify user of expense account change in Purchase Invoice (#22193) --- .../purchase_invoice/purchase_invoice.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index aa1d5b526c8..870718c22d5 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -238,6 +238,12 @@ class PurchaseInvoice(BuyingController): not frappe.db.get_value("Purchase Order Item", item.po_detail, "delivered_by_supplier")): if self.update_stock and (not item.from_warehouse): + if for_validate and item.expense_account and item.expense_account != warehouse_account[item.warehouse]["account"]: + frappe.msgprint(_('''Row {0}: Expense Head changed to {1} because account {2} + is not linked to warehouse {3} or it is not the default inventory account'''.format( + item.idx, frappe.bold(warehouse_account[item.warehouse]["account"]), + frappe.bold(item.expense_account), frappe.bold(item.warehouse)))) + item.expense_account = warehouse_account[item.warehouse]["account"] else: # check if 'Stock Received But Not Billed' account is credited in Purchase receipt or not @@ -247,10 +253,21 @@ class PurchaseInvoice(BuyingController): (item.purchase_receipt, stock_not_billed_account)) if negative_expense_booked_in_pr: + if for_validate and item.expense_account and item.expense_account != stock_not_billed_account: + frappe.msgprint(_('''Row {0}: Expense Head changed to {1} because + expense is booked against this account in Purchase Receipt {2}'''.format( + item.idx, frappe.bold(stock_not_billed_account), frappe.bold(item.purchase_receipt)))) + item.expense_account = stock_not_billed_account else: # If no purchase receipt present then book expense in 'Stock Received But Not Billed' # This is done in cases when Purchase Invoice is created before Purchase Receipt + if for_validate and item.expense_account and item.expense_account != stock_not_billed_account: + frappe.msgprint(_('''Row {0}: Expense Head changed to {1} as no Purchase + Receipt is created against Item {2}. This is done to handle accounting for cases + when Purchase Receipt is created after Purchase Invoice'''.format( + item.idx, frappe.bold(stock_not_billed_account), frappe.bold(item.item_code)))) + item.expense_account = stock_not_billed_account elif item.is_fixed_asset and not is_cwip_accounting_enabled(asset_category): @@ -1020,7 +1037,7 @@ class PurchaseInvoice(BuyingController): # calculate totals again after applying TDS self.calculate_taxes_and_totals() - + def set_status(self, update=False, status=None, update_modified=True): if self.is_new(): if self.get('amended_from'): From 71da90034d876b8bf37be03c1332840871855ded Mon Sep 17 00:00:00 2001 From: Anupam Kumar Date: Mon, 22 Jun 2020 18:27:14 +0530 Subject: [PATCH 440/608] fix: resetting lost reason in opportunity and quotation (#22378) --- erpnext/crm/doctype/opportunity/opportunity.js | 1 + erpnext/selling/doctype/quotation/quotation.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/erpnext/crm/doctype/opportunity/opportunity.js b/erpnext/crm/doctype/opportunity/opportunity.js index 0c9ba495c73..f1b81713496 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.js +++ b/erpnext/crm/doctype/opportunity/opportunity.js @@ -96,6 +96,7 @@ frappe.ui.form.on("Opportunity", { }); } else { frm.add_custom_button(__("Reopen"), function() { + frm.set_value("lost_reasons",[]) frm.set_value("status", "Open"); frm.save(); }); diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index 0e771c3025a..449a968a4f9 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -99,6 +99,8 @@ class Quotation(SellingController): self.update_lead() def on_cancel(self): + if self.lost_reasons: + self.lost_reasons = [] super(Quotation, self).on_cancel() #update enquiry status From 5dc175f06851973d4d4a64cfb7885e22bbe39dc1 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Mon, 22 Jun 2020 19:05:39 +0530 Subject: [PATCH 441/608] fix(Immutable Ledger): Item wise backdated stock entry posting (#22366) * fix(Immutable Ledger): Item wise backdated stock entry posting * fix: Remove fiscal year query * fix: Update message string --- erpnext/utilities/transaction_base.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/erpnext/utilities/transaction_base.py b/erpnext/utilities/transaction_base.py index 024aa6f31dd..2359648c95c 100644 --- a/erpnext/utilities/transaction_base.py +++ b/erpnext/utilities/transaction_base.py @@ -161,19 +161,19 @@ class TransactionBase(StatusUpdater): if not (self.get("update_stock") or self.get("is_pos")): return - fiscal_year = get_fiscal_year(self.get('posting_date'), as_dict=True).name + for item in self.get('items'): + last_transaction_time = frappe.db.sql(""" + select MAX(timestamp(posting_date, posting_time)) as posting_time + from `tabStock Ledger Entry` + where docstatus = 1 and item_code = %s """, (item.item_code))[0][0] - last_transaction_time = frappe.db.sql(""" - select MAX(timestamp(posting_date, posting_time)) as posting_time - from `tabStock Ledger Entry` - where docstatus = 1""")[0][0] + cur_doc_posting_datetime = "%s %s" % (self.posting_date, self.get("posting_time") or "00:00:00") - cur_doc_posting_datetime = "%s %s" % (self.posting_date, self.get("posting_time") or "00:00:00") - - if last_transaction_time and get_datetime(cur_doc_posting_datetime) < get_datetime(last_transaction_time): - frappe.throw(_("""Posting timestamp of current transaction - must be after last Stock transaction's timestamp which is {0}""").format(frappe.bold(last_transaction_time)), - title=_("Backdated Stock Entry")) + if last_transaction_time and get_datetime(cur_doc_posting_datetime) < get_datetime(last_transaction_time): + msg = _("Last Stock Transaction for item {0} was on {1}.").format(frappe.bold(item.item_code), frappe.bold(last_transaction_time)) + msg += "

    " + _("Stock Transactions for Item {0} cannot be posted before this time.").format(frappe.bold(item.item_code)) + msg += "

    " + _("Please remove this item and try to submit again or update the posting time.") + frappe.throw(msg, title=_("Backdated Stock Entry")) def delete_events(ref_type, ref_name): events = frappe.db.sql_list(""" SELECT From e3973a4730eff7becc183adede0b56d9315049a9 Mon Sep 17 00:00:00 2001 From: Poranut Chollavorn Date: Thu, 18 Jun 2020 14:41:26 +0000 Subject: [PATCH 442/608] fix(pricing_rule): apply_on logic dont get cleanup (cherry picked from commit acf399c4e0c74458c17866be7d72788aeecbe829) --- .../accounts/doctype/pricing_rule/pricing_rule.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py index 4d9053a55b3..ef1f4e6999f 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -80,9 +80,15 @@ class PricingRule(Document): for f in options: if not f: continue - f = frappe.scrub(f) - if f!=fieldname: - self.set(f, None) + scrubbed_f = frappe.scrub(f) + + if logic_field == 'apply_on': + apply_on_f = apply_on_dict.get(f, f) + else: + apply_on_f = scrubbed_f + + if scrubbed_f != fieldname: + self.set(apply_on_f, None) if self.mixed_conditions and self.get("same_item"): self.same_item = 0 From aaed3b988f4fe173685bfe140c9ee0e42c379468 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 22 Jun 2020 13:44:11 +0530 Subject: [PATCH 443/608] added validation and clear fields for Apply Rule On Other (cherry picked from commit ebbbc85fc0f9aa418163f8344d3a234ecbe08488) --- .../doctype/pricing_rule/pricing_rule.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py index ef1f4e6999f..ead300e3bb4 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -17,6 +17,8 @@ from six import string_types apply_on_dict = {"Item Code": "items", "Item Group": "item_groups", "Brand": "brands"} +other_fields = ["other_item_code", "other_item_group", "other_brand"] + class PricingRule(Document): def validate(self): self.validate_mandatory() @@ -47,6 +49,13 @@ class PricingRule(Document): if tocheck and not self.get(tocheck): throw(_("{0} is required").format(self.meta.get_label(tocheck)), frappe.MandatoryError) + if self.apply_rule_on_other: + o_field = 'other_' + frappe.scrub(self.apply_rule_on_other) + if not self.get(o_field) and o_field in other_fields: + frappe.throw(_("For the 'Apply Rule On Other' condition the field {0} is mandatory") + .format(frappe.bold(self.apply_rule_on_other))) + + if self.price_or_product_discount == 'Price' and not self.rate_or_discount: throw(_("Rate or Discount is required for the price discount."), frappe.MandatoryError) @@ -93,6 +102,14 @@ class PricingRule(Document): if self.mixed_conditions and self.get("same_item"): self.same_item = 0 + apply_rule_on_other = frappe.scrub(self.apply_rule_on_other or "") + + cleanup_other_fields = (other_fields if not apply_rule_on_other + else [o_field for o_field in other_fields if o_field != 'other_' + apply_rule_on_other]) + + for other_field in cleanup_other_fields: + self.set(other_field, None) + def validate_rate_or_discount(self): for field in ["Rate"]: if flt(self.get(frappe.scrub(field))) < 0: From 7cc1cf36cab04aa4b7c0fcdb7fd091952ea60699 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Tue, 23 Jun 2020 09:57:56 +0530 Subject: [PATCH 444/608] feat: More controlled deferred revenue booking (#21671) * feat: More controller deferred revenue booking * fix: Query for last gl entry * fix: Accounting Dimension for Deferred entries * fix: Deferred revenue booking against paid invoices * fix: Don not update outstanding on Deferred Entry submission * fix: Naming fixes * feat: Deferred revenue/expense booking based on months * fix: Test case for fix monthly deferred revenue booking * fix: Typo * fix: Patch to update settings * fix: Test case to book deferred expense via journal entry * fix: Update field for better UX * fix: Codacy * fix: Change select field to checkbox --- erpnext/accounts/deferred_revenue.py | 212 ++++++++++++++++-- .../accounts_settings/accounts_settings.json | 39 +++- .../doctype/journal_entry/journal_entry.json | 2 +- .../doctype/journal_entry/journal_entry.py | 26 ++- .../journal_entry_account.json | 7 + .../process_deferred_accounting.js | 12 + .../purchase_invoice/test_purchase_invoice.py | 66 +++++- .../sales_invoice/test_sales_invoice.py | 51 ++++- erpnext/patches.txt | 1 + .../patches/v13_0/update_deferred_settings.py | 11 + 10 files changed, 393 insertions(+), 34 deletions(-) create mode 100644 erpnext/patches/v13_0/update_deferred_settings.py diff --git a/erpnext/accounts/deferred_revenue.py b/erpnext/accounts/deferred_revenue.py index 448011016e7..d5ab1c17042 100644 --- a/erpnext/accounts/deferred_revenue.py +++ b/erpnext/accounts/deferred_revenue.py @@ -2,10 +2,11 @@ from __future__ import unicode_literals import frappe from frappe import _ -from frappe.utils import date_diff, add_months, today, getdate, add_days, flt, get_last_day, cint, get_link_to_form +from frappe.utils import date_diff, add_months, today, getdate, add_days, flt, get_last_day, get_first_day, cint, get_link_to_form, rounded from erpnext.accounts.utils import get_account_currency from frappe.email import sendmail_to_system_managers from frappe.utils.background_jobs import enqueue +from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions def validate_service_stop_date(doc): ''' Validates service_stop_date for Purchase Invoice and Sales Invoice ''' @@ -109,6 +110,18 @@ def get_booking_dates(doc, item, posting_date=None): order by posting_date desc limit 1 ''', (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True) + prev_gl_via_je = frappe.db.sql(''' + SELECT p.name, p.posting_date FROM `tabJournal Entry` p, `tabJournal Entry Account` c + WHERE p.name = c.parent and p.company=%s and c.account=%s + and c.reference_type=%s and c.reference_name=%s + and c.reference_detail_no=%s and c.docstatus < 2 order by posting_date desc limit 1 + ''', (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True) + + if prev_gl_via_je: + if (not prev_gl_entry) or (prev_gl_entry and + prev_gl_entry[0].posting_date < prev_gl_via_je[0].posting_date): + prev_gl_entry = prev_gl_via_je + if prev_gl_entry: start_date = getdate(add_days(prev_gl_entry[0].posting_date, 1)) else: @@ -130,14 +143,48 @@ def get_booking_dates(doc, item, posting_date=None): else: return None, None, None -def calculate_amount(doc, item, last_gl_entry, total_days, total_booking_days, account_currency): - if doc.doctype == "Sales Invoice": - total_credit_debit, total_credit_debit_currency = "debit", "debit_in_account_currency" - deferred_account = "deferred_revenue_account" - else: - total_credit_debit, total_credit_debit_currency = "credit", "credit_in_account_currency" - deferred_account = "deferred_expense_account" +def calculate_monthly_amount(doc, item, last_gl_entry, start_date, end_date, total_days, total_booking_days, account_currency): + amount, base_amount = 0, 0 + if not last_gl_entry: + total_months = (item.service_end_date.year - item.service_start_date.year) * 12 + \ + (item.service_end_date.month - item.service_start_date.month) + 1 + + prorate_factor = flt(date_diff(item.service_end_date, item.service_start_date)) \ + / flt(date_diff(get_last_day(item.service_end_date), get_first_day(item.service_start_date))) + + actual_months = rounded(total_months * prorate_factor, 1) + + already_booked_amount, already_booked_amount_in_account_currency = get_already_booked_amount(doc, item) + base_amount = flt(item.base_net_amount / actual_months, item.precision("base_net_amount")) + + if base_amount + already_booked_amount > item.base_net_amount: + base_amount = item.base_net_amount - already_booked_amount + + if account_currency==doc.company_currency: + amount = base_amount + else: + amount = flt(item.net_amount/actual_months, item.precision("net_amount")) + if amount + already_booked_amount_in_account_currency > item.net_amount: + amount = item.net_amount - already_booked_amount_in_account_currency + + if not (get_first_day(start_date) == start_date and get_last_day(end_date) == end_date): + partial_month = flt(date_diff(end_date, start_date)) \ + / flt(date_diff(get_last_day(end_date), get_first_day(start_date))) + + base_amount = rounded(partial_month, 1) * base_amount + amount = rounded(partial_month, 1) * amount + else: + already_booked_amount, already_booked_amount_in_account_currency = get_already_booked_amount(doc, item) + base_amount = flt(item.base_net_amount - already_booked_amount, item.precision("base_net_amount")) + if account_currency==doc.company_currency: + amount = base_amount + else: + amount = flt(item.net_amount - already_booked_amount_in_account_currency, item.precision("net_amount")) + + return amount, base_amount + +def calculate_amount(doc, item, last_gl_entry, total_days, total_booking_days, account_currency): amount, base_amount = 0, 0 if not last_gl_entry: base_amount = flt(item.base_net_amount*total_booking_days/flt(total_days), item.precision("base_net_amount")) @@ -146,27 +193,55 @@ def calculate_amount(doc, item, last_gl_entry, total_days, total_booking_days, a else: amount = flt(item.net_amount*total_booking_days/flt(total_days), item.precision("net_amount")) else: - gl_entries_details = frappe.db.sql(''' - select sum({0}) as total_credit, sum({1}) as total_credit_in_account_currency, voucher_detail_no - from `tabGL Entry` where company=%s and account=%s and voucher_type=%s and voucher_no=%s and voucher_detail_no=%s - group by voucher_detail_no - '''.format(total_credit_debit, total_credit_debit_currency), - (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True) - already_booked_amount = gl_entries_details[0].total_credit if gl_entries_details else 0 + already_booked_amount, already_booked_amount_in_account_currency = get_already_booked_amount(doc, item) + base_amount = flt(item.base_net_amount - already_booked_amount, item.precision("base_net_amount")) if account_currency==doc.company_currency: amount = base_amount else: - already_booked_amount_in_account_currency = gl_entries_details[0].total_credit_in_account_currency if gl_entries_details else 0 amount = flt(item.net_amount - already_booked_amount_in_account_currency, item.precision("net_amount")) return amount, base_amount +def get_already_booked_amount(doc, item): + if doc.doctype == "Sales Invoice": + total_credit_debit, total_credit_debit_currency = "debit", "debit_in_account_currency" + deferred_account = "deferred_revenue_account" + else: + total_credit_debit, total_credit_debit_currency = "credit", "credit_in_account_currency" + deferred_account = "deferred_expense_account" + + gl_entries_details = frappe.db.sql(''' + select sum({0}) as total_credit, sum({1}) as total_credit_in_account_currency, voucher_detail_no + from `tabGL Entry` where company=%s and account=%s and voucher_type=%s and voucher_no=%s and voucher_detail_no=%s + group by voucher_detail_no + '''.format(total_credit_debit, total_credit_debit_currency), + (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True) + + journal_entry_details = frappe.db.sql(''' + SELECT sum(c.{0}) as total_credit, sum(c.{1}) as total_credit_in_account_currency, reference_detail_no + FROM `tabJournal Entry` p , `tabJournal Entry Account` c WHERE p.name = c.parent and + p.company = %s and c.account=%s and c.reference_type=%s and c.reference_name=%s and c.reference_detail_no=%s + and p.docstatus < 2 group by reference_detail_no + '''.format(total_credit_debit, total_credit_debit_currency), + (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True) + + already_booked_amount = gl_entries_details[0].total_credit if gl_entries_details else 0 + already_booked_amount += journal_entry_details[0].total_credit if journal_entry_details else 0 + + if doc.currency == doc.company_currency: + already_booked_amount_in_account_currency = already_booked_amount + else: + already_booked_amount_in_account_currency = gl_entries_details[0].total_credit_in_account_currency if gl_entries_details else 0 + already_booked_amount_in_account_currency += journal_entry_details[0].total_credit_in_account_currency if journal_entry_details else 0 + + return already_booked_amount, already_booked_amount_in_account_currency + def book_deferred_income_or_expense(doc, deferred_process, posting_date=None): enable_check = "enable_deferred_revenue" \ if doc.doctype=="Sales Invoice" else "enable_deferred_expense" - def _book_deferred_revenue_or_expense(item): + def _book_deferred_revenue_or_expense(item, via_journal_entry, submit_journal_entry, book_deferred_entries_based_on): start_date, end_date, last_gl_entry = get_booking_dates(doc, item, posting_date=posting_date) if not (start_date and end_date): return @@ -181,23 +256,34 @@ def book_deferred_income_or_expense(doc, deferred_process, posting_date=None): total_days = date_diff(item.service_end_date, item.service_start_date) + 1 total_booking_days = date_diff(end_date, start_date) + 1 - amount, base_amount = calculate_amount(doc, item, last_gl_entry, - total_days, total_booking_days, account_currency) + if book_deferred_entries_based_on == 'Months': + amount, base_amount = calculate_monthly_amount(doc, item, last_gl_entry, + start_date, end_date, total_days, total_booking_days, account_currency) + else: + amount, base_amount = calculate_amount(doc, item, last_gl_entry, + total_days, total_booking_days, account_currency) - make_gl_entries(doc, credit_account, debit_account, against, - amount, base_amount, end_date, project, account_currency, item.cost_center, item, deferred_process) + if via_journal_entry: + book_revenue_via_journal_entry(doc, credit_account, debit_account, against, amount, + base_amount, end_date, project, account_currency, item.cost_center, item, deferred_process, submit_journal_entry) + else: + make_gl_entries(doc, credit_account, debit_account, against, + amount, base_amount, end_date, project, account_currency, item.cost_center, item, deferred_process) # Returned in case of any errors because it tries to submit the same record again and again in case of errors if frappe.flags.deferred_accounting_error: return if getdate(end_date) < getdate(posting_date) and not last_gl_entry: - _book_deferred_revenue_or_expense(item) + _book_deferred_revenue_or_expense(item, via_journal_entry, submit_journal_entry, book_deferred_entries_based_on) + via_journal_entry = cint(frappe.db.get_singles_value('Accounts Settings', 'book_deferred_entries_via_journal_entry')) + submit_journal_entry = cint(frappe.db.get_singles_value('Accounts Settings', 'submit_journal_entries')) + book_deferred_entries_based_on = frappe.db.get_singles_value('Accounts Settings', 'book_deferred_entries_based_on') for item in doc.get('items'): if item.get(enable_check): - _book_deferred_revenue_or_expense(item) + _book_deferred_revenue_or_expense(item, via_journal_entry, submit_journal_entry, book_deferred_entries_based_on) def process_deferred_accounting(posting_date=None): ''' Converts deferred income/expense into income/expense @@ -281,3 +367,83 @@ def send_mail(deferred_process): and submit manually after resolving errors """).format(get_link_to_form('Process Deferred Accounting', deferred_process)) sendmail_to_system_managers(title, content) + +def book_revenue_via_journal_entry(doc, credit_account, debit_account, against, + amount, base_amount, posting_date, project, account_currency, cost_center, item, + deferred_process=None, submit='No'): + + if amount == 0: return + + journal_entry = frappe.new_doc('Journal Entry') + journal_entry.posting_date = posting_date + journal_entry.company = doc.company + journal_entry.voucher_type = 'Deferred Revenue' if doc.doctype == 'Sales Invoice' \ + else 'Deferred Expense' + + debit_entry = { + 'account': credit_account, + 'credit': base_amount, + 'credit_in_account_currency': amount, + 'party_type': 'Customer' if doc.doctype == 'Sales Invoice' else 'Supplier', + 'party': against, + 'account_currency': account_currency, + 'reference_name': doc.name, + 'reference_type': doc.doctype, + 'reference_detail_no': item.name, + 'cost_center': cost_center, + 'project': project, + } + + credit_entry = { + 'account': debit_account, + 'debit': base_amount, + 'debit_in_account_currency': amount, + 'party_type': 'Customer' if doc.doctype == 'Sales Invoice' else 'Supplier', + 'party': against, + 'account_currency': account_currency, + 'reference_name': doc.name, + 'reference_type': doc.doctype, + 'reference_detail_no': item.name, + 'cost_center': cost_center, + 'project': project, + } + + for dimension in get_accounting_dimensions(): + debit_entry.update({ + dimension: item.get(dimension) + }) + + credit_entry.update({ + dimension: item.get(dimension) + }) + + journal_entry.append('accounts', debit_entry) + journal_entry.append('accounts', credit_entry) + + try: + journal_entry.save() + + if submit: + journal_entry.submit() + except: + frappe.db.rollback() + traceback = frappe.get_traceback() + frappe.log_error(message=traceback) + + frappe.flags.deferred_accounting_error = True + +def get_deferred_booking_accounts(doctype, voucher_detail_no, dr_or_cr): + + if doctype == 'Sales Invoice': + credit_account, debit_account = frappe.db.get_value('Sales Invoice Item', {'name': voucher_detail_no}, + ['income_account', 'deferred_revenue_account']) + else: + credit_account, debit_account = frappe.db.get_value('Purchase Invoice Item', {'name': voucher_detail_no}, + ['deferred_expense_account', 'expense_account']) + + if dr_or_cr == 'Debit': + return debit_account + else: + return credit_account + + diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json index 353ff77223f..e4b96ae67e9 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json @@ -22,7 +22,12 @@ "allow_cost_center_in_entry_of_bs_account", "add_taxes_from_item_tax_template", "automatically_fetch_payment_terms", + "deferred_accounting_settings_section", "automatically_process_deferred_accounting_entry", + "book_deferred_entries_based_on", + "column_break_18", + "book_deferred_entries_via_journal_entry", + "submit_journal_entries", "print_settings", "show_inclusive_tax_in_print", "column_break_12", @@ -189,13 +194,45 @@ "fieldname": "automatically_process_deferred_accounting_entry", "fieldtype": "Check", "label": "Automatically Process Deferred Accounting Entry" + }, + { + "fieldname": "deferred_accounting_settings_section", + "fieldtype": "Section Break", + "label": "Deferred Accounting Settings" + }, + { + "fieldname": "column_break_18", + "fieldtype": "Column Break" + }, + { + "default": "0", + "description": "If this is unchecked direct GL Entries will be created to book Deferred Revenue/Expense", + "fieldname": "book_deferred_entries_via_journal_entry", + "fieldtype": "Check", + "label": "Book Deferred Entries Via Journal Entry" + }, + { + "default": "0", + "depends_on": "eval:doc.book_deferred_entries_via_journal_entry", + "description": "If this is unchecked Journal Entries will be saved in a Draft state and will have to be submitted manually", + "fieldname": "submit_journal_entries", + "fieldtype": "Check", + "label": "Submit Journal Entries" + }, + { + "default": "Days", + "description": "If \"Months\" is selected then fixed amount will be booked as deferred revenue or expense for each month irrespective of number of days in a month. Will be prorated if deferred revenue or expense is not booked for an entire month.", + "fieldname": "book_deferred_entries_based_on", + "fieldtype": "Select", + "label": "Book Deferred Entries Based On", + "options": "Days\nMonths" } ], "icon": "icon-cog", "idx": 1, "issingle": 1, "links": [], - "modified": "2019-12-19 16:58:17.395595", + "modified": "2020-06-22 20:13:26.043092", "modified_by": "Administrator", "module": "Accounts", "name": "Accounts Settings", diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.json b/erpnext/accounts/doctype/journal_entry/journal_entry.json index af2aa65e6b2..4573c50134a 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.json +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.json @@ -83,7 +83,7 @@ "label": "Entry Type", "oldfieldname": "voucher_type", "oldfieldtype": "Select", - "options": "Journal Entry\nInter Company Journal Entry\nBank Entry\nCash Entry\nCredit Card Entry\nDebit Note\nCredit Note\nContra Entry\nExcise Entry\nWrite Off Entry\nOpening Entry\nDepreciation Entry\nExchange Rate Revaluation", + "options": "Journal Entry\nInter Company Journal Entry\nBank Entry\nCash Entry\nCredit Card Entry\nDebit Note\nCredit Note\nContra Entry\nExcise Entry\nWrite Off Entry\nOpening Entry\nDepreciation Entry\nExchange Rate Revaluation\nDeferred Revenue\nDeferred Expense", "reqd": 1, "search_index": 1 }, diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index caaf30f11fb..7360b399425 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -10,6 +10,7 @@ from erpnext.accounts.utils import get_balance_on, get_account_currency from erpnext.accounts.party import get_party_account from erpnext.hr.doctype.expense_claim.expense_claim import update_reimbursed_amount from erpnext.accounts.doctype.invoice_discounting.invoice_discounting import get_party_account_based_on_invoice_discounting +from erpnext.accounts.deferred_revenue import get_deferred_booking_accounts from six import string_types, iteritems @@ -265,7 +266,10 @@ class JournalEntry(AccountsController): # set totals if not d.reference_name in self.reference_totals: self.reference_totals[d.reference_name] = 0.0 - self.reference_totals[d.reference_name] += flt(d.get(dr_or_cr)) + + if self.voucher_type not in ('Deferred Revenue', 'Deferred Expense'): + self.reference_totals[d.reference_name] += flt(d.get(dr_or_cr)) + self.reference_types[d.reference_name] = d.reference_type self.reference_accounts[d.reference_name] = d.account @@ -277,10 +281,16 @@ class JournalEntry(AccountsController): # check if party and account match if d.reference_type in ("Sales Invoice", "Purchase Invoice"): - if d.reference_type == "Sales Invoice": - party_account = get_party_account_based_on_invoice_discounting(d.reference_name) or against_voucher[1] + if self.voucher_type in ('Deferred Revenue', 'Deferred Expense') and d.reference_detail_no: + debit_or_credit = 'Debit' if d.debit else 'Credit' + party_account = get_deferred_booking_accounts(d.reference_type, d.reference_detail_no, + debit_or_credit) else: - party_account = against_voucher[1] + if d.reference_type == "Sales Invoice": + party_account = get_party_account_based_on_invoice_discounting(d.reference_name) or against_voucher[1] + else: + party_account = against_voucher[1] + if (against_voucher[0] != d.party or party_account != d.account): frappe.throw(_("Row {0}: Party / Account does not match with {1} / {2} in {3} {4}") .format(d.idx, field_dict.get(d.reference_type)[0], field_dict.get(d.reference_type)[1], @@ -513,14 +523,20 @@ class JournalEntry(AccountsController): "against_voucher_type": d.reference_type, "against_voucher": d.reference_name, "remarks": remarks, + "voucher_detail_no": d.reference_detail_no, "cost_center": d.cost_center, "project": d.project, "finance_book": self.finance_book }, item=d) ) + if self.voucher_type in ('Deferred Revenue', 'Deferred Expense'): + update_outstanding = 'No' + else: + update_outstanding = 'Yes' + if gl_map: - make_gl_entries(gl_map, cancel=cancel, adv_adj=adv_adj) + make_gl_entries(gl_map, cancel=cancel, adv_adj=adv_adj, update_outstanding=update_outstanding) def get_balance(self): if not self.get('accounts'): diff --git a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json index ff3533a6792..ad0ecc4a0d8 100644 --- a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json +++ b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json @@ -33,6 +33,7 @@ "reference_type", "reference_name", "reference_due_date", + "reference_detail_no", "col_break3", "is_advance", "user_remark", @@ -268,6 +269,12 @@ "fieldtype": "Link", "label": "Bank Account", "options": "Bank Account" + }, + { + "fieldname": "reference_detail_no", + "fieldtype": "Data", + "hidden": 1, + "label": "Reference Detail No" } ], "idx": 1, diff --git a/erpnext/accounts/doctype/process_deferred_accounting/process_deferred_accounting.js b/erpnext/accounts/doctype/process_deferred_accounting/process_deferred_accounting.js index 975c60cf917..2800c195cee 100644 --- a/erpnext/accounts/doctype/process_deferred_accounting/process_deferred_accounting.js +++ b/erpnext/accounts/doctype/process_deferred_accounting/process_deferred_accounting.js @@ -10,6 +10,18 @@ frappe.ui.form.on('Process Deferred Accounting', { } }; }); + + if (frm.doc.company) { + frm.set_query("account", function() { + return { + filters: { + 'company': frm.doc.company, + 'root_type': 'Liability', + 'is_group': 0 + } + }; + }); + } }, validate: function() { diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 61700050614..b5955ca2581 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -7,14 +7,15 @@ import unittest import frappe, erpnext import frappe.model from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry -from frappe.utils import cint, flt, today, nowdate, add_days +from frappe.utils import cint, flt, today, nowdate, add_days, getdate import frappe.defaults from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory, \ test_records as pr_test_records, make_purchase_receipt, get_taxes from erpnext.controllers.accounts_controller import get_payment_terms from erpnext.exceptions import InvalidCurrency from erpnext.stock.doctype.stock_entry.test_stock_entry import get_qty_after_transaction -from erpnext.accounts.doctype.account.test_account import get_inventory_account +from erpnext.accounts.doctype.account.test_account import get_inventory_account, create_account +from erpnext.stock.doctype.item.test_item import create_item test_dependencies = ["Item", "Cost Center", "Payment Term", "Payment Terms Template"] test_ignore = ["Serial No"] @@ -866,6 +867,67 @@ class TestPurchaseInvoice(unittest.TestCase): for gle in gl_entries: self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center) + def test_deferred_expense_via_journal_entry(self): + deferred_account = create_account(account_name="Deferred Expense", + parent_account="Current Assets - _TC", company="_Test Company") + + acc_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') + acc_settings.book_deferred_entries_via_journal_entry = 1 + acc_settings.submit_journal_entries = 1 + acc_settings.save() + + item = create_item("_Test Item for Deferred Accounting") + item.enable_deferred_expense = 1 + item.deferred_expense_account = deferred_account + item.save() + + pi = make_purchase_invoice(item=item.name, qty=1, rate=100, do_not_save=True) + pi.set_posting_time = 1 + pi.posting_date = '2019-03-15' + pi.items[0].enable_deferred_expense = 1 + pi.items[0].service_start_date = "2019-01-10" + pi.items[0].service_end_date = "2019-03-15" + pi.items[0].deferred_expense_account = deferred_account + pi.save() + pi.submit() + + pda1 = frappe.get_doc(dict( + doctype='Process Deferred Accounting', + posting_date=nowdate(), + start_date="2019-01-01", + end_date="2019-03-31", + type="Expense", + company="_Test Company" + )) + + pda1.insert() + pda1.submit() + + expected_gle = [ + ["_Test Account Cost for Goods Sold - _TC", 0.0, 33.85, "2019-01-31"], + [deferred_account, 33.85, 0.0, "2019-01-31"], + ["_Test Account Cost for Goods Sold - _TC", 0.0, 43.08, "2019-02-28"], + [deferred_account, 43.08, 0.0, "2019-02-28"], + ["_Test Account Cost for Goods Sold - _TC", 0.0, 23.07, "2019-03-15"], + [deferred_account, 23.07, 0.0, "2019-03-15"] + ] + + gl_entries = gl_entries = frappe.db.sql("""select account, debit, credit, posting_date + from `tabGL Entry` + where voucher_type='Journal Entry' and voucher_detail_no=%s and posting_date <= %s + order by posting_date asc, account asc""", (pi.items[0].name, pi.posting_date), as_dict=1) + + for i, gle in enumerate(gl_entries): + self.assertEqual(expected_gle[i][0], gle.account) + self.assertEqual(expected_gle[i][1], gle.credit) + self.assertEqual(expected_gle[i][2], gle.debit) + self.assertEqual(getdate(expected_gle[i][3]), gle.posting_date) + + acc_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') + acc_settings.book_deferred_entries_via_journal_entry = 0 + acc_settings.submit_journal_entriessubmit_journal_entries = 0 + acc_settings.save() + def unlink_payment_on_cancel_of_invoice(enable=1): accounts_settings = frappe.get_doc("Accounts Settings") diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 6cdf9b57d1d..311cc12dd8d 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -1720,8 +1720,6 @@ class TestSalesInvoice(unittest.TestCase): si.save() si.submit() - from erpnext.accounts.deferred_revenue import convert_deferred_revenue_to_income - pda1 = frappe.get_doc(dict( doctype='Process Deferred Accounting', posting_date=nowdate(), @@ -1745,6 +1743,55 @@ class TestSalesInvoice(unittest.TestCase): check_gl_entries(self, si.name, expected_gle, "2019-01-30") + def test_fixed_deferred_revenue(self): + deferred_account = create_account(account_name="Deferred Revenue", + parent_account="Current Liabilities - _TC", company="_Test Company") + + acc_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') + acc_settings.book_deferred_entries_based_on = 'Months' + acc_settings.save() + + item = create_item("_Test Item for Deferred Accounting") + item.enable_deferred_revenue = 1 + item.deferred_revenue_account = deferred_account + item.no_of_months = 12 + item.save() + + si = create_sales_invoice(item=item.name, posting_date="2019-01-16", rate=50000, do_not_submit=True) + si.items[0].enable_deferred_revenue = 1 + si.items[0].service_start_date = "2019-01-16" + si.items[0].service_end_date = "2019-03-31" + si.items[0].deferred_revenue_account = deferred_account + si.save() + si.submit() + + pda1 = frappe.get_doc(dict( + doctype='Process Deferred Accounting', + posting_date='2019-03-31', + start_date="2019-01-01", + end_date="2019-03-31", + type="Income", + company="_Test Company" + )) + + pda1.insert() + pda1.submit() + + expected_gle = [ + [deferred_account, 10000.0, 0.0, "2019-01-31"], + ["Sales - _TC", 0.0, 10000.0, "2019-01-31"], + [deferred_account, 20000.0, 0.0, "2019-02-28"], + ["Sales - _TC", 0.0, 20000.0, "2019-02-28"], + [deferred_account, 20000.0, 0.0, "2019-03-31"], + ["Sales - _TC", 0.0, 20000.0, "2019-03-31"] + ] + + check_gl_entries(self, si.name, expected_gle, "2019-01-30") + + acc_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') + acc_settings.book_deferred_entries_based_on = 'Days' + acc_settings.save() + def test_inter_company_transaction(self): if not frappe.db.exists("Customer", "_Test Internal Customer"): diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 928c0ab9d86..17fbcc21909 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -699,6 +699,7 @@ erpnext.patches.v13_0.delete_old_purchase_reports erpnext.patches.v12_0.set_italian_import_supplier_invoice_permissions erpnext.patches.v13_0.update_sla_enhancements erpnext.patches.v12_0.update_address_template_for_india +erpnext.patches.v13_0.update_deferred_settings erpnext.patches.v12_0.set_multi_uom_in_rfq erpnext.patches.v13_0.delete_old_sales_reports execute:frappe.delete_doc_if_exists("DocType", "Bank Reconciliation") diff --git a/erpnext/patches/v13_0/update_deferred_settings.py b/erpnext/patches/v13_0/update_deferred_settings.py new file mode 100644 index 00000000000..a7d82077b76 --- /dev/null +++ b/erpnext/patches/v13_0/update_deferred_settings.py @@ -0,0 +1,11 @@ +# Copyright (c) 2019, Frappe and Contributors +# License: GNU General Public License v3. See license.txt +from __future__ import unicode_literals +import frappe + +def execute(): + accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') + accounts_settings.book_deferred_entries_based_on = 'Days' + accounts_settings.book_deferred_entries_via_journal_entry = 0 + accounts_settings.submit_journal_entries = 0 + accounts_settings.save() \ No newline at end of file From bd5c8573dec3a74fb1038a91bae97a5a858a718c Mon Sep 17 00:00:00 2001 From: Marica Date: Tue, 23 Jun 2020 10:32:51 +0530 Subject: [PATCH 445/608] fix: Unable to create batched Item (#22393) --- erpnext/stock/doctype/item/item.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index 3436a5d0136..a75ee67ec44 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -597,7 +597,7 @@ class Item(WebsiteGenerator): def stock_ledger_created(self): if not hasattr(self, '_stock_ledger_created'): self._stock_ledger_created = len(frappe.db.sql("""select name from `tabStock Ledger Entry` - where item_code = %s limit 1""", self.name)) + where item_code = %s and is_cancelled = 0 limit 1""", self.name)) return self._stock_ledger_created def validate_name_with_item_group(self): @@ -883,7 +883,12 @@ class Item(WebsiteGenerator): linked_doctypes += ["Sales Order Item", "Purchase Order Item", "Material Request Item"] for doctype in linked_doctypes: - if frappe.db.get_value(doctype, filters={"item_code": self.name, "docstatus": 1}) or \ + if doctype in ("Purchase Invoice Item", "Sales Invoice Item",): + # If Invoice has Stock impact, only then consider it. + if self.stock_ledger_created(): + return True + + elif frappe.db.get_value(doctype, filters={"item_code": self.name, "docstatus": 1}) or \ frappe.db.get_value("Production Order", filters={"production_item": self.name, "docstatus": 1}): return True From 3dee5273d6111ceb61f9a8285d8f58a16e5ec846 Mon Sep 17 00:00:00 2001 From: Marica Date: Tue, 23 Jun 2020 10:33:47 +0530 Subject: [PATCH 446/608] fix: Update Packed Items via Update Items in SO (#22392) --- erpnext/controllers/accounts_controller.py | 2 ++ .../product_bundle/test_product_bundle.py | 4 +-- .../doctype/sales_order/test_sales_order.py | 32 +++++++++++++------ 3 files changed, 27 insertions(+), 11 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index f54b593022c..ead503ed096 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -20,6 +20,7 @@ from erpnext.exceptions import InvalidCurrency from six import text_type from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions from erpnext.stock.get_item_details import get_item_warehouse +from erpnext.stock.doctype.packed_item.packed_item import make_packing_list force_item_fields = ("item_group", "brand", "stock_uom", "is_fixed_asset", "item_tax_rate", "pricing_rules") @@ -1301,6 +1302,7 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil parent.set_qty_as_per_stock_uom() parent.calculate_taxes_and_totals() if parent_doctype == "Sales Order": + make_packing_list(parent) parent.set_gross_profit() frappe.get_doc('Authorization Control').validate_approving_authority(parent.doctype, parent.company, parent.base_grand_total) diff --git a/erpnext/selling/doctype/product_bundle/test_product_bundle.py b/erpnext/selling/doctype/product_bundle/test_product_bundle.py index 85a2b209f64..7d1d372b111 100644 --- a/erpnext/selling/doctype/product_bundle/test_product_bundle.py +++ b/erpnext/selling/doctype/product_bundle/test_product_bundle.py @@ -7,7 +7,7 @@ from __future__ import unicode_literals import frappe test_records = frappe.get_test_records('Product Bundle') -def make_product_bundle(parent, items): +def make_product_bundle(parent, items, qty=None): if frappe.db.exists("Product Bundle", parent): return frappe.get_doc("Product Bundle", parent) @@ -17,7 +17,7 @@ def make_product_bundle(parent, items): }) for item in items: - product_bundle.append("items", {"item_code": item, "qty": 1}) + product_bundle.append("items", {"item_code": item, "qty": qty or 1}) product_bundle.insert() diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index 74e742fabbb..accaa9c3b3c 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -2,6 +2,7 @@ # License: GNU General Public License v3. See license.txt from __future__ import unicode_literals import frappe +import json from frappe.utils import flt, add_days, nowdate import frappe.permissions import unittest @@ -10,9 +11,10 @@ from erpnext.selling.doctype.sales_order.sales_order \ from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry from erpnext.selling.doctype.sales_order.sales_order import make_work_orders from erpnext.controllers.accounts_controller import update_child_qty_rate -import json from erpnext.selling.doctype.sales_order.sales_order import make_raw_material_request from erpnext.manufacturing.doctype.blanket_order.test_blanket_order import make_blanket_order +from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle +from erpnext.stock.doctype.item.test_item import make_item class TestSalesOrder(unittest.TestCase): def tearDown(self): @@ -417,6 +419,26 @@ class TestSalesOrder(unittest.TestCase): self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Sales Order', trans_item, so.name) frappe.set_user("Administrator") + def test_update_child_qty_rate_product_bundle(self): + # test Update Items with product bundle + if not frappe.db.exists("Item", "_Product Bundle Item"): + bundle_item = make_item("_Product Bundle Item", {"is_stock_item": 0}) + bundle_item.append("item_defaults", { + "company": "_Test Company", + "default_warehouse": "_Test Warehouse - _TC"}) + bundle_item.save(ignore_permissions=True) + + make_item("_Packed Item", {"is_stock_item": 1}) + make_product_bundle("_Product Bundle Item", ["_Packed Item"], 2) + + so = make_sales_order(item_code = "_Test Item", warehouse=None) + + added_item = json.dumps([{"item_code" : "_Product Bundle Item", "rate" : 200, 'qty' : 2}]) + update_child_qty_rate('Sales Order', added_item, so.name) + + so.reload() + self.assertEqual(so.packed_items[0].qty, 4) + def test_warehouse_user(self): frappe.permissions.add_user_permission("Warehouse", "_Test Warehouse 1 - _TC", "test@example.com") frappe.permissions.add_user_permission("Warehouse", "_Test Warehouse 2 - _TC1", "test2@example.com") @@ -457,8 +479,6 @@ class TestSalesOrder(unittest.TestCase): self.assertRaises(frappe.CancelledLinkError, dn.submit) def test_service_type_product_bundle(self): - from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle - from erpnext.stock.doctype.item.test_item import make_item make_item("_Test Service Product Bundle", {"is_stock_item": 0}) make_item("_Test Service Product Bundle Item 1", {"is_stock_item": 0}) make_item("_Test Service Product Bundle Item 2", {"is_stock_item": 0}) @@ -472,8 +492,6 @@ class TestSalesOrder(unittest.TestCase): self.assertTrue("_Test Service Product Bundle Item 2" in [d.item_code for d in so.packed_items]) def test_mix_type_product_bundle(self): - from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle - from erpnext.stock.doctype.item.test_item import make_item make_item("_Test Mix Product Bundle", {"is_stock_item": 0}) make_item("_Test Mix Product Bundle Item 1", {"is_stock_item": 1}) make_item("_Test Mix Product Bundle Item 2", {"is_stock_item": 0}) @@ -484,7 +502,6 @@ class TestSalesOrder(unittest.TestCase): self.assertRaises(WarehouseRequired, make_sales_order, item_code = "_Test Mix Product Bundle", warehouse="") def test_auto_insert_price(self): - from erpnext.stock.doctype.item.test_item import make_item make_item("_Test Item for Auto Price List", {"is_stock_item": 0}) frappe.db.set_value("Stock Settings", None, "auto_insert_price_list_rate_if_missing", 1) @@ -519,7 +536,6 @@ class TestSalesOrder(unittest.TestCase): from erpnext.buying.doctype.purchase_order.purchase_order import update_status make_stock_entry(target="_Test Warehouse - _TC", qty=10, rate=100) - from erpnext.stock.doctype.item.test_item import make_item po_item = make_item("_Test Item for Drop Shipping", {"is_stock_item": 1, "delivered_by_supplier": 1}) dn_item = make_item("_Test Regular Item", {"is_stock_item": 1}) @@ -714,7 +730,6 @@ class TestSalesOrder(unittest.TestCase): def test_serial_no_based_delivery(self): frappe.set_value("Stock Settings", None, "automatically_set_serial_nos_based_on_fifo", 1) - from erpnext.stock.doctype.item.test_item import make_item item = make_item("_Reserved_Serialized_Item", {"is_stock_item": 1, "maintain_stock": 1, "has_serial_no": 1, @@ -835,7 +850,6 @@ class TestSalesOrder(unittest.TestCase): self.assertRaises(frappe.LinkExistsError, so_doc.cancel) def test_request_for_raw_materials(self): - from erpnext.stock.doctype.item.test_item import make_item item = make_item("_Test Finished Item", {"is_stock_item": 1, "maintain_stock": 1, "valuation_rate": 500, From ca8894b0fc9ff1d41f8237bc6cb7be56065c0ee3 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 23 Jun 2020 10:42:32 +0530 Subject: [PATCH 447/608] fix: alternative item not working for subcontract (#22386) (cherry picked from commit 171699537c3e7d7632f45123c6d30012c8de7281) Co-authored-by: Rohit Waghchaure --- erpnext/stock/doctype/stock_entry/stock_entry.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 5fbd512bf4f..229cf027bd5 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -574,9 +574,7 @@ class StockEntry(StockController): {"parent": self.purchase_order, "item_code": se_item.subcontracted_item}, "bom") - allow_alternative_item = frappe.get_value("BOM", bom_no, "allow_alternative_item") - - if allow_alternative_item: + if se_item.allow_alternative_item: original_item_code = frappe.get_value("Item Alternative", {"alternative_item_code": item_code}, "item_code") required_qty = sum([flt(d.required_qty) for d in purchase_order.supplied_items \ @@ -743,7 +741,7 @@ class StockEntry(StockController): def get_item_details(self, args=None, for_update=False): item = frappe.db.sql("""select i.name, i.stock_uom, i.description, i.image, i.item_name, i.item_group, - i.has_batch_no, i.sample_quantity, i.has_serial_no, + i.has_batch_no, i.sample_quantity, i.has_serial_no, i.allow_alternative_item, id.expense_account, id.buying_cost_center from `tabItem` i LEFT JOIN `tabItem Default` id ON i.name=id.parent and id.company=%s where i.name=%s @@ -778,6 +776,9 @@ class StockEntry(StockController): 'expense_account' : item.expense_account }) + if self.purpose == 'Send to Subcontractor': + ret["allow_alternative_item"] = item.allow_alternative_item + # update uom if args.get("uom") and for_update: ret.update(get_uom_details(args.get('item_code'), args.get('uom'), args.get('qty'))) From 36560c8406488b0f9890d250460f7857f40c77a9 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Tue, 23 Jun 2020 10:44:33 +0530 Subject: [PATCH 448/608] fix: staffing Plan validation (#22379) --- erpnext/hr/doctype/job_offer/job_offer.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/erpnext/hr/doctype/job_offer/job_offer.py b/erpnext/hr/doctype/job_offer/job_offer.py index 32f1b89f49e..9a2c4c64ebb 100644 --- a/erpnext/hr/doctype/job_offer/job_offer.py +++ b/erpnext/hr/doctype/job_offer/job_offer.py @@ -20,10 +20,9 @@ class JobOffer(Document): staffing_plan = get_staffing_plan_detail(self.designation, self.company, self.offer_date) check_vacancies = frappe.get_single("HR Settings").check_vacancies if staffing_plan and check_vacancies: - vacancies = frappe.db.get_value("Staffing Plan Detail", filters={"name": staffing_plan.name}, fieldname=['vacancies']) - job_offers = len(self.get_job_offer(staffing_plan.from_date, staffing_plan.to_date)) - if vacancies - job_offers <= 0: - frappe.throw(_("There are no vacancies under staffing plan {0}").format(get_link_to_form("Staffing Plan", staffing_plan.parent))) + job_offers = self.get_job_offer(staffing_plan.from_date, staffing_plan.to_date) + if staffing_plan.vacancies - len(job_offers) <= 0: + frappe.throw(_("There are no vacancies under staffing plan {0}").format(frappe.bold(get_link_to_form("Staffing Plan", staffing_plan.parent)))) def on_change(self): update_job_applicant(self.status, self.job_applicant) @@ -42,18 +41,22 @@ def update_job_applicant(status, job_applicant): def get_staffing_plan_detail(designation, company, offer_date): detail = frappe.db.sql(""" - SELECT spd.name as name, + SELECT DISTINCT spd.parent, sp.from_date as from_date, sp.to_date as to_date, - sp.name as parent + sp.name, + sum(spd.vacancies) as vacancies, + spd.designation FROM `tabStaffing Plan Detail` spd, `tabStaffing Plan` sp WHERE sp.docstatus=1 AND spd.designation=%s AND sp.company=%s + AND spd.parent = sp.name AND %s between sp.from_date and sp.to_date """, (designation, company, offer_date), as_dict=1) - return detail[0] if detail else None + + return frappe._dict(detail[0]) if detail else None @frappe.whitelist() def make_employee(source_name, target_doc=None): From 101d15f2f8796a79252ee42e2573ac69dcbc7c5a Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Tue, 23 Jun 2020 10:45:54 +0530 Subject: [PATCH 449/608] fix: not working without from_amount and percentage_deduction (#22380) --- .../doctype/taxable_salary_slab/taxable_salary_slab.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/payroll/doctype/taxable_salary_slab/taxable_salary_slab.json b/erpnext/payroll/doctype/taxable_salary_slab/taxable_salary_slab.json index ce9512f898f..94eda4c043a 100644 --- a/erpnext/payroll/doctype/taxable_salary_slab/taxable_salary_slab.json +++ b/erpnext/payroll/doctype/taxable_salary_slab/taxable_salary_slab.json @@ -14,6 +14,7 @@ ], "fields": [ { + "default": "0", "fieldname": "from_amount", "fieldtype": "Currency", "in_list_view": 1, @@ -27,6 +28,7 @@ "label": "To Amount" }, { + "default": "0", "fieldname": "percent_deduction", "fieldtype": "Percent", "in_list_view": 1, @@ -51,7 +53,7 @@ ], "istable": 1, "links": [], - "modified": "2020-06-22 23:32:47.253106", + "modified": "2020-06-22 18:16:07.596493", "modified_by": "Administrator", "module": "Payroll", "name": "Taxable Salary Slab", From 5e16ca0326e3348775d143dc13058af335ed79cd Mon Sep 17 00:00:00 2001 From: Anoop Date: Tue, 23 Jun 2020 11:11:13 +0530 Subject: [PATCH 450/608] fix: Login page doesn't load for new sites (#22388) * fix: Login page doesn't load for new sites Login page broken as active domains are not set for new sites * Update footer_powered.html Co-authored-by: Nabin Hait --- erpnext/templates/includes/footer/footer_powered.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/templates/includes/footer/footer_powered.html b/erpnext/templates/includes/footer/footer_powered.html index 4274ba12cf5..82b2716a924 100644 --- a/erpnext/templates/includes/footer/footer_powered.html +++ b/erpnext/templates/includes/footer/footer_powered.html @@ -12,8 +12,9 @@ } %} {% set link = '' %} -{% set label = domains[0].domain %} +{% set label = '' %} {% if domains %} + {% set label = domains[0].domain %} {% set link = links[label] %} {% endif %} From d0d9b53361c57d362e7e975f16b48ccbfdc021a8 Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Tue, 23 Jun 2020 11:49:11 +0530 Subject: [PATCH 451/608] feat: added year filters --- .../salary_slip_deductions_report_filters.js | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/erpnext/public/js/salary_slip_deductions_report_filters.js b/erpnext/public/js/salary_slip_deductions_report_filters.js index 242037991aa..2b30e650753 100644 --- a/erpnext/public/js/salary_slip_deductions_report_filters.js +++ b/erpnext/public/js/salary_slip_deductions_report_filters.js @@ -11,8 +11,8 @@ erpnext.salary_slip_deductions_report_filters = { default: frappe.defaults.get_user_default("Company"), }, { - fieldname: "period", - label: __("Period"), + fieldname: "month", + label: __("Month"), fieldtype: "Select", reqd: 1 , options: [ @@ -31,6 +31,12 @@ erpnext.salary_slip_deductions_report_filters = { ], default: frappe.datetime.str_to_obj(frappe.datetime.get_today()).getMonth() + 1 }, + { + fieldname:"year", + label: __("Year"), + fieldtype: "Select", + reqd: 1 + }, { fieldname: "department", label: __("Department"), @@ -43,5 +49,18 @@ erpnext.salary_slip_deductions_report_filters = { fieldtype: "Link", options: "Branch", } - ] + ], + + "onload": function() { + return frappe.call({ + method: "erpnext.regional.report.provident_fund_deductions.provident_fund_deductions.get_years", + callback: function(r) { + var year_filter = frappe.query_report.get_filter('year'); + year_filter.df.options = r.message; + year_filter.df.default = r.message.split("\n")[0]; + year_filter.refresh(); + year_filter.set_input(year_filter.df.default); + } + }); + } } \ No newline at end of file From f9ca29cebd0a0963ec58b8971283d7e0d2868f2b Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Tue, 23 Jun 2020 11:49:47 +0530 Subject: [PATCH 452/608] Fix: if there is no component --- .../income_tax_deductions.py | 12 +++++++--- .../salary_payments_via_ecs.py | 8 ++++--- .../professional_tax_deductions.py | 5 +++- .../provident_fund_deductions.py | 24 +++++++++++++++---- 4 files changed, 37 insertions(+), 12 deletions(-) diff --git a/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.py b/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.py index 3bad5879bb3..8a79416edbf 100644 --- a/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.py +++ b/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.py @@ -6,8 +6,8 @@ import frappe, erpnext from frappe import _ def execute(filters=None): - columns = get_columns(filters) data = get_data(filters) + columns = get_columns(filters) if len(data) else [] return columns, data @@ -78,8 +78,11 @@ def get_conditions(filters): if filters.get("company"): conditions.append("sal.company = '%s' " % (filters["company"]) ) - if filters.get("period"): - conditions.append("month(sal.start_date) = '%s' " % (filters["period"])) + if filters.get("month"): + conditions.append("month(sal.start_date) = '%s' " % (filters["month"])) + + if filters.get("year"): + conditions.append("year(start_date) = '%s' " % (filters["year"])) return " and ".join(conditions) @@ -96,6 +99,9 @@ def get_data(filters): component_types = [comp_type[0] for comp_type in component_types] + if not len(component_types): + return [] + conditions = get_conditions(filters) entry = frappe.db.sql(""" select sal.employee, sal.employee_name, sal.posting_date, ded.salary_component, ded.amount,sal.gross_pay diff --git a/erpnext/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.py b/erpnext/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.py index 073bd91300e..d09745c37bb 100644 --- a/erpnext/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.py +++ b/erpnext/payroll/report/salary_payments_via_ecs/salary_payments_via_ecs.py @@ -84,9 +84,11 @@ def get_conditions(filters): if filters.get("company"): conditions.append("company = '%s' " % (filters["company"]) ) - if filters.get("period"): - conditions.append("month(start_date) = '%s' " % (filters["period"])) - conditions.append("year(start_date) = '%s' " % (frappe.utils.getdate().year)) + if filters.get("month"): + conditions.append("month(start_date) = '%s' " % (filters["month"])) + + if filters.get("year"): + conditions.append("year(start_date) = '%s' " % (filters["year"])) return " and ".join(conditions) diff --git a/erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.py b/erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.py index 900fe963b4b..acde68a942b 100644 --- a/erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.py +++ b/erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.py @@ -7,8 +7,8 @@ from frappe import _ from erpnext.regional.report.provident_fund_deductions.provident_fund_deductions import get_conditions def execute(filters=None): - columns = get_columns(filters) data = get_data(filters) + columns = get_columns(filters) if len(data) else [] return columns, data @@ -45,6 +45,9 @@ def get_data(filters): component_type_dict = frappe._dict(frappe.db.sql(""" select name, component_type from `tabSalary Component` where component_type = 'Professional Tax' """)) + if not len(component_type_dict): + return [] + conditions = get_conditions(filters) entry = frappe.db.sql(""" select sal.employee, sal.employee_name, ded.salary_component, ded.amount diff --git a/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.py b/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.py index 9f58957fed8..a79931ca324 100644 --- a/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.py +++ b/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.py @@ -6,8 +6,8 @@ import frappe from frappe import _ def execute(filters=None): - columns = get_columns(filters) data = get_data(filters) + columns = get_columns(filters) if len(data) else [] return columns, data @@ -71,10 +71,13 @@ def get_conditions(filters): conditions.append("sal.branch = '%s' " % (filters["branch"]) ) if filters.get("company"): - conditions.append("sal.company = '%s' " % (filters["company"]) ) + conditions.append("sal.company = '%s' " % (filters["company"])) - if filters.get("period"): - conditions.append("month(sal.start_date) = '%s' " % (filters["period"])) + if filters.get("month"): + conditions.append("month(sal.start_date) = '%s' " % (filters["month"])) + + if filters.get("year"): + conditions.append("year(start_date) = '%s' " % (filters["year"])) if filters.get("mode_of_payment"): conditions.append("sal.mode_of_payment = '%s' " % (filters["mode_of_payment"])) @@ -114,6 +117,9 @@ def get_data(filters): component_type_dict = frappe._dict(frappe.db.sql(""" select name, component_type from `tabSalary Component` where component_type in ('Provident Fund', 'Additional Provident Fund', 'Provident Fund Loan')""")) + if not len(component_type_dict): + return [] + entry = frappe.db.sql(""" select sal.name, sal.employee, sal.employee_name, ded.salary_component, ded.amount from `tabSalary Slip` sal, `tabSalary Detail` ded where sal.name = ded.parent @@ -150,4 +156,12 @@ def get_data(filters): data.append(employee) - return data \ No newline at end of file + return data + +@frappe.whitelist() +def get_years(): + year_list = frappe.db.sql_list("""select distinct YEAR(end_date) from `tabSalary Slip` ORDER BY YEAR(end_date) DESC""") + if not year_list: + year_list = [getdate().year] + + return "\n".join(str(year) for year in year_list) \ No newline at end of file From 4e37d25374766f314e154c334d84778d21ac767d Mon Sep 17 00:00:00 2001 From: Afshan Date: Tue, 23 Jun 2020 14:10:40 +0530 Subject: [PATCH 453/608] fix: removed condition that considered "Standard working hours" while creating "timesheet" as it was setting wrong time #20848 --- .../projects/doctype/timesheet/timesheet.js | 33 ++++--------------- 1 file changed, 7 insertions(+), 26 deletions(-) diff --git a/erpnext/projects/doctype/timesheet/timesheet.js b/erpnext/projects/doctype/timesheet/timesheet.js index defc18bf4e9..5de2930c1cd 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.js +++ b/erpnext/projects/doctype/timesheet/timesheet.js @@ -162,19 +162,11 @@ frappe.ui.form.on("Timesheet Detail", { to_time: function(frm, cdt, cdn) { var child = locals[cdt][cdn]; - var time_diff = (moment(child.to_time).diff(moment(child.from_time),"seconds")) / ( 60 * 60 * 24); - var std_working_hours = 0; if(frm._setting_hours) return; var hours = moment(child.to_time).diff(moment(child.from_time), "seconds") / 3600; - std_working_hours = time_diff * frappe.working_hours; - - if (std_working_hours < hours && std_working_hours > 0) { - frappe.model.set_value(cdt, cdn, "hours", std_working_hours); - } else { - frappe.model.set_value(cdt, cdn, "hours", hours); - } + frappe.model.set_value(cdt, cdn, "hours", hours); }, time_logs_add: function(frm) { @@ -236,23 +228,12 @@ var calculate_end_time = function(frm, cdt, cdn) { let d = moment(child.from_time); if(child.hours) { - var time_diff = (moment(child.to_time).diff(moment(child.from_time),"seconds")) / (60 * 60 * 24); - var std_working_hours = 0; - var hours = moment(child.to_time).diff(moment(child.from_time), "seconds") / 3600; - - std_working_hours = time_diff * frappe.working_hours; - - if (std_working_hours < hours && std_working_hours > 0) { - frappe.model.set_value(cdt, cdn, "hours", std_working_hours); - frappe.model.set_value(cdt, cdn, "to_time", d.add(hours, "hours").format(frappe.defaultDatetimeFormat)); - } else { - d.add(child.hours, "hours"); - frm._setting_hours = true; - frappe.model.set_value(cdt, cdn, "to_time", - d.format(frappe.defaultDatetimeFormat)).then(() => { - frm._setting_hours = false; - }); - } + d.add(child.hours, "hours"); + frm._setting_hours = true; + frappe.model.set_value(cdt, cdn, "to_time", + d.format(frappe.defaultDatetimeFormat)).then(() => { + frm._setting_hours = false; + }); } }; From ece9508eb5a02962a994fbaad0e989e323d3f56c Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Tue, 23 Jun 2020 13:47:28 +0530 Subject: [PATCH 454/608] fix: test_tax_for_payroll_period --- erpnext/payroll/doctype/salary_slip/test_salary_slip.py | 1 + .../provident_fund_deductions/provident_fund_deductions.py | 1 + 2 files changed, 2 insertions(+) diff --git a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py index f42b9ad11dc..be9a2d37284 100644 --- a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py +++ b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py @@ -271,6 +271,7 @@ class TestSalarySlip(unittest.TestCase): # as per assigned salary structure 40500 in monthly salary so 236000*5/100/12 frappe.db.sql("""delete from `tabPayroll Period`""") frappe.db.sql("""delete from `tabSalary Component`""") + frappe.db.sql("""delete from `tabAdditional Salary`""") payroll_period = create_payroll_period() diff --git a/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.py b/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.py index a79931ca324..084890e54fe 100644 --- a/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.py +++ b/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.py @@ -3,6 +3,7 @@ from __future__ import unicode_literals import frappe +from frappe.utils import getdate from frappe import _ def execute(filters=None): From ecbfda7d6adeaf0585d9f3186a47046ee9604e64 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Tue, 23 Jun 2020 14:33:49 +0530 Subject: [PATCH 455/608] fix: typo in template(#22401) --- .../doctype/salary_structure/condition_and_formula_help.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/payroll/doctype/salary_structure/condition_and_formula_help.html b/erpnext/payroll/doctype/salary_structure/condition_and_formula_help.html index e59d78d1b8b..d07a1ab551a 100644 --- a/erpnext/payroll/doctype/salary_structure/condition_and_formula_help.html +++ b/erpnext/payroll/doctype/salary_structure/condition_and_formula_help.html @@ -8,7 +8,7 @@ Variables from Employee:
    Employment Type = employment_type, Branch = branch etc.
  • - Variables Salary Slip:
    + Variables from Salary Slip:
    Payment Days = payment_days, Leave without pay = leave_without_pay etc.
  • From f5b7bd9dcbe389cd66373ff8feabd333196ecba7 Mon Sep 17 00:00:00 2001 From: Kenneth Sequeira Date: Tue, 23 Jun 2020 16:55:35 +0530 Subject: [PATCH 456/608] fix: add error prompt for wrong date range --- .../report/item_wise_sales_history/item_wise_sales_history.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py b/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py index bd59be663ad..1bc4657f295 100644 --- a/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py +++ b/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py @@ -9,6 +9,9 @@ from frappe.utils.nestedset import get_descendants_of def execute(filters=None): filters = frappe._dict(filters or {}) + if filters.from_date > filters.to_date: + frappe.throw(_('From Date cannot be greater than To Date')) + columns = get_columns(filters) data = get_data(filters) From 9c25101d9b13e793432f6ff260a54a44f22d8dc2 Mon Sep 17 00:00:00 2001 From: Rohan Date: Tue, 23 Jun 2020 17:39:37 +0530 Subject: [PATCH 457/608] feat: add ability for a contract to be authorised internally (#22095) * Added ability for a contract to be signed by a company user before being sent out * feat: contract signed by stays even after reload (#402) * format: use new JSON format Co-authored-by: Neil Lasrado Co-authored-by: nehasacher143 <45919049+nehasacher143@users.noreply.github.com> Co-authored-by: Marica --- erpnext/crm/doctype/contract/contract.json | 23 +- erpnext/crm/doctype/contract/contract.py | 3 + .../contract_template/contract_template.json | 374 ++++-------------- 3 files changed, 112 insertions(+), 288 deletions(-) diff --git a/erpnext/crm/doctype/contract/contract.json b/erpnext/crm/doctype/contract/contract.json index e04ad30ea72..0026e4a02eb 100755 --- a/erpnext/crm/doctype/contract/contract.json +++ b/erpnext/crm/doctype/contract/contract.json @@ -29,6 +29,9 @@ "requires_fulfilment", "fulfilment_deadline", "fulfilment_terms", + "authorised_by_section", + "signee_company", + "signed_by_company", "sb_references", "document_type", "cb_links", @@ -223,10 +226,28 @@ "options": "Contract", "print_hide": 1, "read_only": 1 + }, + { + "fieldname": "signee_company", + "fieldtype": "Signature", + "label": "Signee (Company)" + }, + { + "fieldname": "signed_by_company", + "fieldtype": "Link", + "label": "Signed By (Company)", + "options": "User", + "read_only": 1 + }, + { + "fieldname": "authorised_by_section", + "fieldtype": "Section Break", + "label": "Authorised By" } ], "is_submittable": 1, - "modified": "2019-09-30 00:56:41.559681", + "links": [], + "modified": "2020-03-30 06:56:07.257932", "modified_by": "Administrator", "module": "CRM", "name": "Contract", diff --git a/erpnext/crm/doctype/contract/contract.py b/erpnext/crm/doctype/contract/contract.py index 18444cc7e6e..c39397bf4b4 100644 --- a/erpnext/crm/doctype/contract/contract.py +++ b/erpnext/crm/doctype/contract/contract.py @@ -29,6 +29,9 @@ class Contract(Document): self.update_contract_status() self.update_fulfilment_status() + def before_submit(self): + self.signed_by_company = frappe.session.user + def before_update_after_submit(self): self.update_contract_status() self.update_fulfilment_status() diff --git a/erpnext/crm/doctype/contract_template/contract_template.json b/erpnext/crm/doctype/contract_template/contract_template.json index b883ce20c9b..ef9974f8636 100644 --- a/erpnext/crm/doctype/contract_template/contract_template.json +++ b/erpnext/crm/doctype/contract_template/contract_template.json @@ -1,306 +1,106 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "field:title", - "beta": 0, - "creation": "2018-04-16 06:44:48.791312", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "allow_rename": 1, + "autoname": "field:title", + "creation": "2018-04-16 06:44:48.791312", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "title", + "contract_terms", + "sb_fulfilment", + "requires_fulfilment", + "fulfilment_terms" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "title", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Title", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "title", + "fieldtype": "Data", + "label": "Title", + "unique": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sb_terms", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "contract_terms", + "fieldtype": "Text Editor", + "label": "Contract Terms and Conditions", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "contract_terms", - "fieldtype": "Text Editor", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Contract Terms and Conditions", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "sb_fulfilment", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sb_fulfilment", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "requires_fulfilment", + "fieldtype": "Check", + "label": "Requires Fulfilment" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "requires_fulfilment", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Requires Fulfilment", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.requires_fulfilment==1", - "fieldname": "fulfilment_terms", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Fulfilment Terms and Conditions", - "length": 0, - "no_copy": 0, - "options": "Contract Template Fulfilment Terms", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 + "depends_on": "eval:doc.requires_fulfilment==1", + "fieldname": "fulfilment_terms", + "fieldtype": "Table", + "label": "Fulfilment Terms and Conditions", + "options": "Contract Template Fulfilment Terms" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-04-17 07:36:05.217599", - "modified_by": "Administrator", - "module": "CRM", - "name": "Contract Template", - "name_case": "", - "owner": "Administrator", + ], + "links": [], + "modified": "2020-06-03 00:24:58.179816", + "modified_by": "Administrator", + "module": "CRM", + "name": "Contract Template", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, "write": 1 - }, + }, { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Sales Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Sales Manager", + "share": 1, "write": 1 - }, + }, { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Purchase Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Purchase Manager", + "share": 1, "write": 1 - }, + }, { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file From 7af7bb8311b7d3f376d3299fe3e542401c1af63d Mon Sep 17 00:00:00 2001 From: Marica Date: Tue, 23 Jun 2020 17:51:02 +0530 Subject: [PATCH 458/608] fix: Insert Supplier Group via List View (#22403) --- erpnext/setup/doctype/customer_group/customer_group.js | 5 +++-- erpnext/setup/doctype/item_group/item_group.js | 2 +- erpnext/setup/doctype/sales_person/sales_person.js | 4 ++-- erpnext/setup/doctype/supplier_group/supplier_group.js | 5 +++-- erpnext/setup/doctype/territory/territory.js | 2 +- 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/erpnext/setup/doctype/customer_group/customer_group.js b/erpnext/setup/doctype/customer_group/customer_group.js index c199a8e57f1..44a50191209 100644 --- a/erpnext/setup/doctype/customer_group/customer_group.js +++ b/erpnext/setup/doctype/customer_group/customer_group.js @@ -8,7 +8,7 @@ cur_frm.cscript.refresh = function(doc, cdt, cdn) { cur_frm.cscript.set_root_readonly = function(doc) { // read-only for root customer group - if(!doc.parent_customer_group) { + if(!doc.parent_customer_group && !doc.__islocal) { cur_frm.set_read_only(); cur_frm.set_intro(__("This is a root customer group and cannot be edited.")); } else { @@ -20,7 +20,8 @@ cur_frm.cscript.set_root_readonly = function(doc) { cur_frm.fields_dict['parent_customer_group'].get_query = function(doc,cdt,cdn) { return { filters: { - 'is_group': 1 + 'is_group': 1, + 'name': ['!=', cur_frm.doc.customer_group_name] } } } diff --git a/erpnext/setup/doctype/item_group/item_group.js b/erpnext/setup/doctype/item_group/item_group.js index df2223192bd..9892dc3dcc0 100644 --- a/erpnext/setup/doctype/item_group/item_group.js +++ b/erpnext/setup/doctype/item_group/item_group.js @@ -66,7 +66,7 @@ frappe.ui.form.on("Item Group", { set_root_readonly: function(frm) { // read-only for root item group frm.set_intro(""); - if(!frm.doc.parent_item_group) { + if(!frm.doc.parent_item_group && !frm.doc.__islocal) { frm.set_read_only(); frm.set_intro(__("This is a root item group and cannot be edited."), true); } diff --git a/erpnext/setup/doctype/sales_person/sales_person.js b/erpnext/setup/doctype/sales_person/sales_person.js index 89ca4a9dd74..8f7593d6eef 100644 --- a/erpnext/setup/doctype/sales_person/sales_person.js +++ b/erpnext/setup/doctype/sales_person/sales_person.js @@ -19,7 +19,7 @@ frappe.ui.form.on('Sales Person', { } } }; - + frm.make_methods = { 'Sales Order': () => frappe.new_doc("Sales Order") .then(() => frm.add_child("sales_team", {"sales_person": frm.doc.name})) @@ -33,7 +33,7 @@ cur_frm.cscript.refresh = function(doc, cdt, cdn) { cur_frm.cscript.set_root_readonly = function(doc) { // read-only for root - if(!doc.parent_sales_person) { + if(!doc.parent_sales_person && !doc.__islocal) { cur_frm.set_read_only(); cur_frm.set_intro(__("This is a root sales person and cannot be edited.")); } else { diff --git a/erpnext/setup/doctype/supplier_group/supplier_group.js b/erpnext/setup/doctype/supplier_group/supplier_group.js index ac5bda6e2c6..e75030d4414 100644 --- a/erpnext/setup/doctype/supplier_group/supplier_group.js +++ b/erpnext/setup/doctype/supplier_group/supplier_group.js @@ -8,7 +8,7 @@ cur_frm.cscript.refresh = function(doc) { cur_frm.cscript.set_root_readonly = function(doc) { // read-only for root customer group - if(!doc.parent_supplier_group) { + if(!doc.parent_supplier_group && !doc.__islocal) { cur_frm.set_read_only(); cur_frm.set_intro(__("This is a root supplier group and cannot be edited.")); } else { @@ -20,7 +20,8 @@ cur_frm.cscript.set_root_readonly = function(doc) { cur_frm.fields_dict['parent_supplier_group'].get_query = function() { return { filters: { - 'is_group': 1 + 'is_group': 1, + 'name': ['!=', cur_frm.doc.supplier_group_name] } }; }; diff --git a/erpnext/setup/doctype/territory/territory.js b/erpnext/setup/doctype/territory/territory.js index 1eb9958ce70..ceec47ae8c6 100644 --- a/erpnext/setup/doctype/territory/territory.js +++ b/erpnext/setup/doctype/territory/territory.js @@ -20,7 +20,7 @@ cur_frm.cscript.refresh = function(doc, cdt, cdn) { cur_frm.cscript.set_root_readonly = function(doc) { // read-only for root territory - if(!doc.parent_territory) { + if(!doc.parent_territory && !doc.__islocal) { cur_frm.set_read_only(); cur_frm.set_intro(__("This is a root territory and cannot be edited.")); } else { From a81c54f23a310eea2abc7ecd6092c78e7e8aeaa3 Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Wed, 24 Jun 2020 11:51:06 +0530 Subject: [PATCH 459/608] fix: 'Condition & Formula Help' appears twice --- erpnext/payroll/doctype/salary_structure/salary_structure.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/payroll/doctype/salary_structure/salary_structure.js b/erpnext/payroll/doctype/salary_structure/salary_structure.js index ca458f976f6..ad93a2fa4bf 100755 --- a/erpnext/payroll/doctype/salary_structure/salary_structure.js +++ b/erpnext/payroll/doctype/salary_structure/salary_structure.js @@ -35,7 +35,9 @@ frappe.ui.form.on('Salary Structure', { d.show() }); - frm.get_field("conditions_and_formula_variable_and_example").$wrapper.append(frm.doc.filters_html).append(help_button) + let help_button_wrapper = frm.get_field("conditions_and_formula_variable_and_example").$wrapper; + help_button_wrapper.empty(); + help_button_wrapper.append(frm.doc.filters_html).append(help_button) frm.toggle_reqd(['payroll_frequency'], !frm.doc.salary_slip_based_on_timesheet) From a07a548622a3b4e4a9dc4868f21f2bde906c450b Mon Sep 17 00:00:00 2001 From: Afshan Date: Wed, 24 Jun 2020 13:07:59 +0530 Subject: [PATCH 460/608] fix: removed "standard working hours" also fixed test cases --- .../doctype/timesheet/test_timesheet.py | 46 ------------------- .../projects/doctype/timesheet/timesheet.py | 12 ----- erpnext/setup/doctype/company/company.json | 8 +--- 3 files changed, 1 insertion(+), 65 deletions(-) diff --git a/erpnext/projects/doctype/timesheet/test_timesheet.py b/erpnext/projects/doctype/timesheet/test_timesheet.py index 03b67b10231..a5ce44dcf24 100644 --- a/erpnext/projects/doctype/timesheet/test_timesheet.py +++ b/erpnext/projects/doctype/timesheet/test_timesheet.py @@ -140,52 +140,6 @@ class TestTimesheet(unittest.TestCase): settings.ignore_employee_time_overlap = initial_setting settings.save() - def test_timesheet_std_working_hours(self): - emp = make_employee("test_employee_6@salary.com") - - company = frappe.get_doc('Company', "_Test Company") - company.standard_working_hours = 8 - company.save() - - timesheet = frappe.new_doc("Timesheet") - timesheet.employee = emp - timesheet.company = '_Test Company' - timesheet.append( - 'time_logs', - { - "activity_type": "_Test Activity Type", - "from_time": now_datetime(), - "to_time": now_datetime() + datetime.timedelta(days= 4) - } - ) - timesheet.save() - - ts = frappe.get_doc('Timesheet', timesheet.name) - self.assertEqual(ts.total_hours, 32) - ts.submit() - ts.cancel() - - company = frappe.get_doc('Company', "_Test Company") - company.standard_working_hours = 0 - company.save() - - timesheet = frappe.new_doc("Timesheet") - timesheet.employee = emp - timesheet.company = '_Test Company' - timesheet.append( - 'time_logs', - { - "activity_type": "_Test Activity Type", - "from_time": now_datetime(), - "to_time": now_datetime() + datetime.timedelta(days= 4) - } - ) - timesheet.save() - - ts = frappe.get_doc('Timesheet', timesheet.name) - self.assertEqual(ts.total_hours, 96) - ts.submit() - ts.cancel() def make_salary_structure_for_timesheet(employee): salary_structure_name = "Timesheet Salary Structure Test" diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py index e90821689bd..7fe22bec4b2 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.py +++ b/erpnext/projects/doctype/timesheet/timesheet.py @@ -24,7 +24,6 @@ class Timesheet(Document): self.set_status() self.validate_dates() self.validate_time_logs() - self.calculate_std_hours() self.update_cost() self.calculate_total_amounts() self.calculate_percentage_billed() @@ -91,17 +90,6 @@ class Timesheet(Document): self.start_date = getdate(start_date) self.end_date = getdate(end_date) - def calculate_std_hours(self): - std_working_hours = frappe.get_value("Company", self.company, 'standard_working_hours') - - for time in self.time_logs: - if time.from_time and time.to_time: - if flt(std_working_hours) and date_diff(time.to_time, time.from_time): - time.hours = flt(std_working_hours) * date_diff(time.to_time, time.from_time) - else: - if not time.hours: - time.hours = time_diff_in_hours(time.to_time, time.from_time) - def before_cancel(self): self.set_status() diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json index 020a93ff6ac..ceae634a846 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -22,7 +22,6 @@ "default_letter_head", "default_holiday_list", "default_finance_book", - "standard_working_hours", "default_selling_terms", "default_buying_terms", "default_warehouse_for_sales_return", @@ -238,11 +237,6 @@ "label": "Default Holiday List", "options": "Holiday List" }, - { - "fieldname": "standard_working_hours", - "fieldtype": "Float", - "label": "Standard Working Hours" - }, { "fieldname": "default_warehouse_for_sales_return", "fieldtype": "Link", @@ -730,7 +724,7 @@ "image_field": "company_logo", "is_tree": 1, "links": [], - "modified": "2020-03-21 18:09:53.534211", + "modified": "2020-06-24 12:45:31.462195", "modified_by": "Administrator", "module": "Setup", "name": "Company", From 020c6e9175fe4e743efb9424e1e20fb412052253 Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Wed, 24 Jun 2020 15:22:45 +0530 Subject: [PATCH 461/608] fix: Create salary slip button disappears --- .../doctype/payroll_entry/payroll_entry.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry.js b/erpnext/payroll/doctype/payroll_entry/payroll_entry.js index 1ae3553b9b9..0415ebf2fce 100644 --- a/erpnext/payroll/doctype/payroll_entry/payroll_entry.js +++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.js @@ -30,6 +30,7 @@ frappe.ui.form.on('Payroll Entry', { ).toggleClass('btn-primary', !(frm.doc.employees || []).length); } if ((frm.doc.employees || []).length) { + frm.page.clear_primary_action(); frm.page.set_primary_action(__('Create Salary Slips'), () => { frm.save('Submit').then(()=>{ frm.page.clear_primary_action(); @@ -49,13 +50,14 @@ frappe.ui.form.on('Payroll Entry', { return frappe.call({ doc: frm.doc, method: 'fill_employee_details', - callback: function(r) { - if (r.docs[0].employees){ - frm.save(); - frm.refresh(); - if(r.docs[0].validate_attendance){ - render_employee_attendance(frm, r.message); - } + }).then(r => { + if (r.docs[0].employees){ + frm.employees = r.docs[0].employees; + frm.dirty(); + frm.save(); + frm.refresh(); + if(r.docs[0].validate_attendance){ + render_employee_attendance(frm, r.message); } } }) From 3ab8d865dd3fa470cfd14f0368b3631ac554b331 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Wed, 24 Jun 2020 15:50:06 +0530 Subject: [PATCH 462/608] feat: added search to support page (#22376) * feat: added search to support page * style: add styles in style block --- erpnext/www/support/index.html | 45 +++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/erpnext/www/support/index.html b/erpnext/www/support/index.html index 93da503dbb0..12b4c2c0819 100644 --- a/erpnext/www/support/index.html +++ b/erpnext/www/support/index.html @@ -9,6 +9,33 @@

    {{ greeting_subtitle }}

    {% endif %} +
    + + +
    @@ -54,5 +81,21 @@ {% endif %} +{% endblock %} -{% endblock %} \ No newline at end of file +{%- block script -%} + +{%- endblock -%} + +{%- block style -%} + +{%- endblock -%} From d3ec1c1d89bc5474775c283eb719b94e390ab335 Mon Sep 17 00:00:00 2001 From: vishdha Date: Tue, 24 Mar 2020 11:31:41 +0530 Subject: [PATCH 463/608] feat: Taxjar Integration Added --- .../doctype/taxjar_settings/__init__.py | 0 .../taxjar_settings/taxjar_settings.js | 9 + .../taxjar_settings/taxjar_settings.json | 110 ++++++++ .../taxjar_settings/taxjar_settings.py | 10 + .../taxjar_settings/test_taxjar_settings.py | 10 + .../taxjar_integration.py | 251 ++++++++++++++++++ erpnext/hooks.py | 14 +- erpnext/patches.txt | 1 + .../v12_0/add_taxjar_integration_field.py | 12 + erpnext/regional/united_states/setup.py | 16 ++ .../doctype/quotation/test_quotation.py | 2 - requirements.txt | 4 +- 12 files changed, 434 insertions(+), 5 deletions(-) create mode 100644 erpnext/erpnext_integrations/doctype/taxjar_settings/__init__.py create mode 100644 erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.js create mode 100644 erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.json create mode 100644 erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.py create mode 100644 erpnext/erpnext_integrations/doctype/taxjar_settings/test_taxjar_settings.py create mode 100644 erpnext/erpnext_integrations/taxjar_integration.py create mode 100644 erpnext/patches/v12_0/add_taxjar_integration_field.py diff --git a/erpnext/erpnext_integrations/doctype/taxjar_settings/__init__.py b/erpnext/erpnext_integrations/doctype/taxjar_settings/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.js b/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.js new file mode 100644 index 00000000000..62d5709f51f --- /dev/null +++ b/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.js @@ -0,0 +1,9 @@ +// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('TaxJar Settings', { + is_sandbox: (frm) => { + frm.toggle_reqd("api_key", !frm.doc.is_sandbox); + frm.toggle_reqd("sandbox_api_key", frm.doc.is_sandbox); + } +}); diff --git a/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.json b/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.json new file mode 100644 index 00000000000..c0d60f7a317 --- /dev/null +++ b/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.json @@ -0,0 +1,110 @@ +{ + "actions": [], + "creation": "2017-06-15 08:21:24.624315", + "doctype": "DocType", + "document_type": "Setup", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "is_sandbox", + "taxjar_calculate_tax", + "taxjar_create_transactions", + "credentials", + "api_key", + "cb_keys", + "sandbox_api_key", + "configuration", + "tax_account_head", + "configuration_cb", + "shipping_account_head" + ], + "fields": [ + { + "fieldname": "credentials", + "fieldtype": "Section Break", + "label": "Credentials" + }, + { + "fieldname": "api_key", + "fieldtype": "Password", + "in_list_view": 1, + "label": "Live API Key", + "reqd": 1 + }, + { + "fieldname": "configuration", + "fieldtype": "Section Break", + "label": "Configuration" + }, + { + "fieldname": "tax_account_head", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Tax Account Head", + "options": "Account", + "reqd": 1 + }, + { + "fieldname": "shipping_account_head", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Shipping Account Head", + "options": "Account", + "reqd": 1 + }, + { + "default": "0", + "fieldname": "is_sandbox", + "fieldtype": "Check", + "label": "Sandbox Mode" + }, + { + "fieldname": "sandbox_api_key", + "fieldtype": "Password", + "label": "Sandbox API Key" + }, + { + "fieldname": "configuration_cb", + "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "taxjar_create_transactions", + "fieldtype": "Check", + "label": "Create TaxJar Transaction" + }, + { + "default": "0", + "fieldname": "taxjar_calculate_tax", + "fieldtype": "Check", + "label": "Enable Tax Calculation" + }, + { + "fieldname": "cb_keys", + "fieldtype": "Column Break" + } + ], + "issingle": 1, + "links": [], + "modified": "2020-04-30 04:38:03.311089", + "modified_by": "Administrator", + "module": "ERPNext Integrations", + "name": "TaxJar Settings", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.py b/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.py new file mode 100644 index 00000000000..7f5f0f0e7a8 --- /dev/null +++ b/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +# import frappe +from frappe.model.document import Document + +class TaxJarSettings(Document): + pass diff --git a/erpnext/erpnext_integrations/doctype/taxjar_settings/test_taxjar_settings.py b/erpnext/erpnext_integrations/doctype/taxjar_settings/test_taxjar_settings.py new file mode 100644 index 00000000000..7cdfd009561 --- /dev/null +++ b/erpnext/erpnext_integrations/doctype/taxjar_settings/test_taxjar_settings.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +# import frappe +import unittest + +class TestTaxJarSettings(unittest.TestCase): + pass diff --git a/erpnext/erpnext_integrations/taxjar_integration.py b/erpnext/erpnext_integrations/taxjar_integration.py new file mode 100644 index 00000000000..633692dd24b --- /dev/null +++ b/erpnext/erpnext_integrations/taxjar_integration.py @@ -0,0 +1,251 @@ +import traceback + +import pycountry +import taxjar + +import frappe +from erpnext import get_default_company +from frappe import _ +from frappe.contacts.doctype.address.address import get_company_address + +TAX_ACCOUNT_HEAD = frappe.db.get_single_value("TaxJar Settings", "tax_account_head") +SHIP_ACCOUNT_HEAD = frappe.db.get_single_value("TaxJar Settings", "shipping_account_head") +TAXJAR_CREATE_TRANSACTIONS = frappe.db.get_single_value("TaxJar Settings", "taxjar_create_transactions") +TAXJAR_CALCULATE_TAX = frappe.db.get_single_value("TaxJar Settings", "taxjar_calculate_tax") +SUPPORTED_COUNTRY_CODES = ["AT", "AU", "BE", "BG", "CA", "CY", "CZ", "DE", "DK", "EE", "ES", "FI", + "FR", "GB", "GR", "HR", "HU", "IE", "IT", "LT", "LU", "LV", "MT", "NL", "PL", "PT", "RO", + "SE", "SI", "SK", "US"] + + +def get_client(): + taxjar_settings = frappe.get_single("TaxJar Settings") + + if not taxjar_settings.is_sandbox: + api_key = taxjar_settings.api_key and taxjar_settings.get_password("api_key") + api_url = taxjar.DEFAULT_API_URL + else: + api_key = taxjar_settings.sandbox_api_key and taxjar_settings.get_password("sandbox_api_key") + api_url = taxjar.SANDBOX_API_URL + + if api_key and api_url: + return taxjar.Client(api_key=api_key, api_url=api_url) + + +def create_transaction(doc, method): + """Create an order transaction in TaxJar""" + + if not TAXJAR_CREATE_TRANSACTIONS: + return + + client = get_client() + + if not client: + return + + sales_tax = sum([tax.tax_amount for tax in doc.taxes if tax.account_head == TAX_ACCOUNT_HEAD]) + + if not sales_tax: + return + + tax_dict = get_tax_data(doc) + + if not tax_dict: + return + + tax_dict['transaction_id'] = doc.name + tax_dict['transaction_date'] = frappe.utils.today() + tax_dict['sales_tax'] = sales_tax + tax_dict['amount'] = doc.total + tax_dict['shipping'] + + try: + client.create_order(tax_dict) + except taxjar.exceptions.TaxJarResponseError as err: + frappe.throw(_(sanitize_error_response(err))) + except Exception as ex: + print(traceback.format_exc(ex)) + + +def delete_transaction(doc, method): + """Delete an existing TaxJar order transaction""" + + if not TAXJAR_CREATE_TRANSACTIONS: + return + + client = get_client() + + if not client: + return + + client.delete_order(doc.name) + + +def get_tax_data(doc): + from_address = get_company_address_details(doc) + from_shipping_state = from_address.get("state") + from_country_code = frappe.db.get_value("Country", from_address.country, "code") + from_country_code = from_country_code.upper() + + to_address = get_shipping_address_details(doc) + to_shipping_state = to_address.get("state") + to_country_code = frappe.db.get_value("Country", to_address.country, "code") + to_country_code = to_country_code.upper() + + if to_country_code not in SUPPORTED_COUNTRY_CODES: + return + + shipping = sum([tax.tax_amount for tax in doc.taxes if tax.account_head == SHIP_ACCOUNT_HEAD]) + + if to_shipping_state is not None: + to_shipping_state = get_iso_3166_2_state_code(to_address) + + tax_dict = { + 'from_country': from_country_code, + 'from_zip': from_address.pincode, + 'from_state': from_shipping_state, + 'from_city': from_address.city, + 'from_street': from_address.address_line1, + 'to_country': to_country_code, + 'to_zip': to_address.pincode, + 'to_city': to_address.city, + 'to_street': to_address.address_line1, + 'to_state': to_shipping_state, + 'shipping': shipping, + 'amount': doc.net_total + } + + return tax_dict + + +def set_sales_tax(doc, method): + if not TAXJAR_CALCULATE_TAX: + return + + if not doc.items: + return + + # if the party is exempt from sales tax, then set all tax account heads to zero + sales_tax_exempted = hasattr(doc, "exempt_from_sales_tax") and doc.exempt_from_sales_tax \ + or frappe.db.has_column("Customer", "exempt_from_sales_tax") and frappe.db.get_value("Customer", doc.customer, "exempt_from_sales_tax") + + if sales_tax_exempted: + for tax in doc.taxes: + if tax.account_head == TAX_ACCOUNT_HEAD: + tax.tax_amount = 0 + break + + doc.run_method("calculate_taxes_and_totals") + return + + tax_dict = get_tax_data(doc) + + if not tax_dict: + # Remove existing tax rows if address is changed from a taxable state/country + setattr(doc, "taxes", [tax for tax in doc.taxes if tax.account_head != TAX_ACCOUNT_HEAD]) + return + + tax_data = validate_tax_request(tax_dict) + + if tax_data is not None: + if not tax_data.amount_to_collect: + setattr(doc, "taxes", [tax for tax in doc.taxes if tax.account_head != TAX_ACCOUNT_HEAD]) + elif tax_data.amount_to_collect > 0: + # Loop through tax rows for existing Sales Tax entry + # If none are found, add a row with the tax amount + for tax in doc.taxes: + if tax.account_head == TAX_ACCOUNT_HEAD: + tax.tax_amount = tax_data.amount_to_collect + + doc.run_method("calculate_taxes_and_totals") + break + else: + doc.append("taxes", { + "charge_type": "Actual", + "description": "Sales Tax", + "account_head": TAX_ACCOUNT_HEAD, + "tax_amount": tax_data.amount_to_collect + }) + + doc.run_method("calculate_taxes_and_totals") + + +def validate_tax_request(tax_dict): + """Return the sales tax that should be collected for a given order.""" + + client = get_client() + + if not client: + return + + try: + tax_data = client.tax_for_order(tax_dict) + except taxjar.exceptions.TaxJarResponseError as err: + frappe.throw(_(sanitize_error_response(err))) + else: + return tax_data + + +def get_company_address_details(doc): + """Return default company address details""" + + company_address = get_company_address(get_default_company()).company_address + + if not company_address: + frappe.throw(_("Please set a default company address")) + + company_address = frappe.get_doc("Address", company_address) + return company_address + + +def get_shipping_address_details(doc): + """Return customer shipping address details""" + + if doc.shipping_address_name: + shipping_address = frappe.get_doc("Address", doc.shipping_address_name) + else: + shipping_address = get_company_address_details(doc) + + return shipping_address + + +def get_iso_3166_2_state_code(address): + country_code = frappe.db.get_value("Country", address.get("country"), "code") + + error_message = _("""{0} is not a valid state! Check for typos or enter the ISO code for your state.""").format(address.get("state")) + state = address.get("state").upper().strip() + + # The max length for ISO state codes is 3, excluding the country code + if len(state) <= 3: + # PyCountry returns state code as {country_code}-{state-code} (e.g. US-FL) + address_state = (country_code + "-" + state).upper() + + states = pycountry.subdivisions.get(country_code=country_code.upper()) + states = [pystate.code for pystate in states] + + if address_state in states: + return state + + frappe.throw(_(error_message)) + else: + try: + lookup_state = pycountry.subdivisions.lookup(state) + except LookupError: + frappe.throw(_(error_message)) + else: + return lookup_state.code.split('-')[1] + + +def sanitize_error_response(response): + response = response.full_response.get("detail") + response = response.replace("_", " ") + + sanitized_responses = { + "to zip": "Zipcode", + "to city": "City", + "to state": "State", + "to country": "Country" + } + + for k, v in sanitized_responses.items(): + response = response.replace(k, v) + + return response diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 2a695896ed0..835d92ef5c1 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -234,8 +234,15 @@ doc_events = { "validate": "erpnext.portal.doctype.products_settings.products_settings.home_page_is_products" }, "Sales Invoice": { - "on_submit": ["erpnext.regional.create_transaction_log", "erpnext.regional.italy.utils.sales_invoice_on_submit"], - "on_cancel": "erpnext.regional.italy.utils.sales_invoice_on_cancel", + "on_submit": [ + "erpnext.regional.create_transaction_log", + "erpnext.regional.italy.utils.sales_invoice_on_submit", + "erpnext.erpnext_integrations.taxjar_integration.create_transaction" + ], + "on_cancel": [ + "erpnext.regional.italy.utils.sales_invoice_on_cancel", + "erpnext.erpnext_integrations.taxjar_integration.delete_transaction" + ], "on_trash": "erpnext.regional.check_deletion_permission" }, "Purchase Invoice": { @@ -261,6 +268,9 @@ doc_events = { }, "Email Unsubscribe": { "after_insert": "erpnext.crm.doctype.email_campaign.email_campaign.unsubscribe_recipient" + }, + ('Quotation', 'Sales Order', 'Sales Invoice'): { + 'validate': ["erpnext.erpnext_integrations.taxjar_integration.set_sales_tax"] } } diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 17fbcc21909..c7a7abf8196 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -706,3 +706,4 @@ execute:frappe.delete_doc_if_exists("DocType", "Bank Reconciliation") erpnext.patches.v13_0.move_doctype_reports_and_notification_from_hr_to_payroll #22-06-2020 erpnext.patches.v13_0.move_payroll_setting_separately_from_hr_settings #22-06-2020 erpnext.patches.v13_0.check_is_income_tax_component #22-06-2020 +erpnext.patches.v12_0.add_taxjar_integration_field diff --git a/erpnext/patches/v12_0/add_taxjar_integration_field.py b/erpnext/patches/v12_0/add_taxjar_integration_field.py new file mode 100644 index 00000000000..4c823e13bdf --- /dev/null +++ b/erpnext/patches/v12_0/add_taxjar_integration_field.py @@ -0,0 +1,12 @@ +from __future__ import unicode_literals + +import frappe +from erpnext.regional.united_states.setup import make_custom_fields + + +def execute(): + company = frappe.get_all('Company', filters={'country': 'United States'}) + if not company: + return + + make_custom_fields() diff --git a/erpnext/regional/united_states/setup.py b/erpnext/regional/united_states/setup.py index cae28bee8bc..2b0ecafebc5 100644 --- a/erpnext/regional/united_states/setup.py +++ b/erpnext/regional/united_states/setup.py @@ -14,6 +14,22 @@ def make_custom_fields(update=True): 'Supplier': [ dict(fieldname='irs_1099', fieldtype='Check', insert_after='tax_id', label='Is IRS 1099 reporting required for supplier?') + ], + 'Sales Order': [ + dict(fieldname='exempt_from_sales_tax', fieldtype='Check', insert_after='taxes_and_charges', + label='Is customer exempted from sales tax?') + ], + 'Sales Invoice': [ + dict(fieldname='exempt_from_sales_tax', fieldtype='Check', insert_after='taxes_section', + label='Is customer exempted from sales tax?') + ], + 'Customer': [ + dict(fieldname='exempt_from_sales_tax', fieldtype='Check', insert_after='represents_company', + label='Is customer exempted from sales tax?') + ], + 'Quotation': [ + dict(fieldname='exempt_from_sales_tax', fieldtype='Check', insert_after='taxes_and_charges', + label='Is customer exempted from sales tax?') ] } create_custom_fields(custom_fields, update=update) diff --git a/erpnext/selling/doctype/quotation/test_quotation.py b/erpnext/selling/doctype/quotation/test_quotation.py index ee6b429ccae..b4c3d79f31c 100644 --- a/erpnext/selling/doctype/quotation/test_quotation.py +++ b/erpnext/selling/doctype/quotation/test_quotation.py @@ -280,5 +280,3 @@ def make_quotation(**args): qo.submit() return qo - - diff --git a/requirements.txt b/requirements.txt index 9da537e4933..cfd0ab8e075 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,8 +4,10 @@ gocardless-pro==1.11.0 googlemaps==3.1.1 pandas==0.24.2 plaid-python==3.4.0 +pycountry==19.8.18 PyGithub==1.44.1 python-stdnum==1.12 +taxjar==1.9.0 +tweepy==3.8.0 Unidecode==1.1.1 WooCommerce==2.1.1 -tweepy==3.8.0 \ No newline at end of file From 10c7faf090b6cbf91ef94e02b654382a90ed6cb2 Mon Sep 17 00:00:00 2001 From: Anupam Kumar Date: Wed, 24 Jun 2020 17:34:52 +0530 Subject: [PATCH 464/608] updating education report modified time (#22418) Co-authored-by: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> --- .../absent_student_report.json | 33 ++++++++--------- .../assessment_plan_status.json | 33 ++++++++--------- .../course_wise_assessment_report.json | 36 ++++++++++--------- .../final_assessment_grades.json | 35 +++++++++--------- .../student_and_guardian_contact_details.json | 35 +++++++++--------- .../student_batch_wise_attendance.json | 33 ++++++++--------- .../student_fee_collection.json | 35 +++++++++--------- .../student_monthly_attendance_sheet.json | 33 ++++++++--------- 8 files changed, 141 insertions(+), 132 deletions(-) diff --git a/erpnext/education/report/absent_student_report/absent_student_report.json b/erpnext/education/report/absent_student_report/absent_student_report.json index 0d5eebabf8c..92ad860cc65 100644 --- a/erpnext/education/report/absent_student_report/absent_student_report.json +++ b/erpnext/education/report/absent_student_report/absent_student_report.json @@ -1,20 +1,21 @@ { - "add_total_row": 0, - "apply_user_permissions": 1, - "creation": "2013-05-13 14:04:03", - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 3, - "is_standard": "Yes", - "modified": "2017-11-10 19:42:36.457449", - "modified_by": "Administrator", - "module": "Education", - "name": "Absent Student Report", - "owner": "Administrator", - "ref_doctype": "Student Attendance", - "report_name": "Absent Student Report", - "report_type": "Script Report", + "add_total_row": 0, + "creation": "2013-05-13 14:04:03", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 3, + "is_standard": "Yes", + "modified": "2020-06-24 17:16:40.251116", + "modified_by": "Administrator", + "module": "Education", + "name": "Absent Student Report", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Student Attendance", + "report_name": "Absent Student Report", + "report_type": "Script Report", "roles": [ { "role": "Academics User" diff --git a/erpnext/education/report/assessment_plan_status/assessment_plan_status.json b/erpnext/education/report/assessment_plan_status/assessment_plan_status.json index 3000bec1f8d..cbca648d572 100644 --- a/erpnext/education/report/assessment_plan_status/assessment_plan_status.json +++ b/erpnext/education/report/assessment_plan_status/assessment_plan_status.json @@ -1,20 +1,21 @@ { - "add_total_row": 0, - "apply_user_permissions": 1, - "creation": "2017-11-09 15:07:30.404428", - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 0, - "is_standard": "Yes", - "modified": "2017-11-28 18:35:44.903665", - "modified_by": "Administrator", - "module": "Education", - "name": "Assessment Plan Status", - "owner": "Administrator", - "ref_doctype": "Assessment Plan", - "report_name": "Assessment Plan Status", - "report_type": "Script Report", + "add_total_row": 0, + "creation": "2017-11-09 15:07:30.404428", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "modified": "2020-06-24 17:16:02.027410", + "modified_by": "Administrator", + "module": "Education", + "name": "Assessment Plan Status", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Assessment Plan", + "report_name": "Assessment Plan Status", + "report_type": "Script Report", "roles": [ { "role": "Academics User" diff --git a/erpnext/education/report/course_wise_assessment_report/course_wise_assessment_report.json b/erpnext/education/report/course_wise_assessment_report/course_wise_assessment_report.json index 61976b4508b..416db9d00f3 100644 --- a/erpnext/education/report/course_wise_assessment_report/course_wise_assessment_report.json +++ b/erpnext/education/report/course_wise_assessment_report/course_wise_assessment_report.json @@ -1,24 +1,26 @@ { - "add_total_row": 0, - "apply_user_permissions": 1, - "creation": "2017-05-05 14:46:13.776133", - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 0, - "is_standard": "Yes", - "modified": "2018-02-08 15:11:24.904628", - "modified_by": "Administrator", - "module": "Education", - "name": "Course wise Assessment Report", - "owner": "Administrator", - "ref_doctype": "Assessment Result", - "report_name": "Course wise Assessment Report", - "report_type": "Script Report", + "add_total_row": 0, + "creation": "2017-05-05 14:46:13.776133", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "modified": "2020-06-24 17:15:15.477530", + "modified_by": "Administrator", + "module": "Education", + "name": "Course wise Assessment Report", + "owner": "Administrator", + "prepared_report": 0, + "query": "", + "ref_doctype": "Assessment Result", + "report_name": "Course wise Assessment Report", + "report_type": "Script Report", "roles": [ { "role": "Instructor" - }, + }, { "role": "Education Manager" } diff --git a/erpnext/education/report/final_assessment_grades/final_assessment_grades.json b/erpnext/education/report/final_assessment_grades/final_assessment_grades.json index 4d444b46ce9..6a234947688 100644 --- a/erpnext/education/report/final_assessment_grades/final_assessment_grades.json +++ b/erpnext/education/report/final_assessment_grades/final_assessment_grades.json @@ -1,24 +1,25 @@ { - "add_total_row": 0, - "apply_user_permissions": 1, - "creation": "2018-01-22 17:04:43.412054", - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 0, - "is_standard": "Yes", - "modified": "2019-02-08 15:11:35.339434", - "modified_by": "Administrator", - "module": "Education", - "name": "Final Assessment Grades", - "owner": "Administrator", - "ref_doctype": "Assessment Result", - "report_name": "Final Assessment Grades", - "report_type": "Script Report", + "add_total_row": 0, + "creation": "2018-01-22 17:04:43.412054", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "modified": "2020-06-24 17:13:35.373756", + "modified_by": "Administrator", + "module": "Education", + "name": "Final Assessment Grades", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Assessment Result", + "report_name": "Final Assessment Grades", + "report_type": "Script Report", "roles": [ { "role": "Instructor" - }, + }, { "role": "Education Manager" } diff --git a/erpnext/education/report/student_and_guardian_contact_details/student_and_guardian_contact_details.json b/erpnext/education/report/student_and_guardian_contact_details/student_and_guardian_contact_details.json index fe7d1586c86..fa9be656810 100644 --- a/erpnext/education/report/student_and_guardian_contact_details/student_and_guardian_contact_details.json +++ b/erpnext/education/report/student_and_guardian_contact_details/student_and_guardian_contact_details.json @@ -1,24 +1,25 @@ { - "add_total_row": 0, - "apply_user_permissions": 1, - "creation": "2017-03-27 17:47:16.831433", - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 0, - "is_standard": "Yes", - "modified": "2017-11-10 19:42:30.300729", - "modified_by": "Administrator", - "module": "Education", - "name": "Student and Guardian Contact Details", - "owner": "Administrator", - "ref_doctype": "Program Enrollment", - "report_name": "Student and Guardian Contact Details", - "report_type": "Script Report", + "add_total_row": 0, + "creation": "2017-03-27 17:47:16.831433", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 0, + "is_standard": "Yes", + "modified": "2020-06-24 17:16:50.639488", + "modified_by": "Administrator", + "module": "Education", + "name": "Student and Guardian Contact Details", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Program Enrollment", + "report_name": "Student and Guardian Contact Details", + "report_type": "Script Report", "roles": [ { "role": "Instructor" - }, + }, { "role": "Academics User" } diff --git a/erpnext/education/report/student_batch_wise_attendance/student_batch_wise_attendance.json b/erpnext/education/report/student_batch_wise_attendance/student_batch_wise_attendance.json index eb547b71025..8baf8f9fe0e 100644 --- a/erpnext/education/report/student_batch_wise_attendance/student_batch_wise_attendance.json +++ b/erpnext/education/report/student_batch_wise_attendance/student_batch_wise_attendance.json @@ -1,20 +1,21 @@ { - "add_total_row": 0, - "apply_user_permissions": 1, - "creation": "2016-11-28 22:07:03.859124", - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 2, - "is_standard": "Yes", - "modified": "2017-11-10 19:41:12.328346", - "modified_by": "Administrator", - "module": "Education", - "name": "Student Batch-Wise Attendance", - "owner": "Administrator", - "ref_doctype": "Student Attendance", - "report_name": "Student Batch-Wise Attendance", - "report_type": "Script Report", + "add_total_row": 0, + "creation": "2016-11-28 22:07:03.859124", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 2, + "is_standard": "Yes", + "modified": "2020-06-24 17:16:59.823709", + "modified_by": "Administrator", + "module": "Education", + "name": "Student Batch-Wise Attendance", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Student Attendance", + "report_name": "Student Batch-Wise Attendance", + "report_type": "Script Report", "roles": [ { "role": "Academics User" diff --git a/erpnext/education/report/student_fee_collection/student_fee_collection.json b/erpnext/education/report/student_fee_collection/student_fee_collection.json index eb945cfffb8..8deb865ebcd 100644 --- a/erpnext/education/report/student_fee_collection/student_fee_collection.json +++ b/erpnext/education/report/student_fee_collection/student_fee_collection.json @@ -1,21 +1,22 @@ { - "add_total_row": 0, - "creation": "2016-06-22 02:58:41.024538", - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 3, - "is_standard": "Yes", - "modified": "2018-12-17 16:46:46.176620", - "modified_by": "Administrator", - "module": "Education", - "name": "Student Fee Collection", - "owner": "Administrator", - "prepared_report": 0, - "query": "SELECT\n student as \"Student:Link/Student:200\",\n student_name as \"Student Name::200\",\n sum(grand_total) - sum(outstanding_amount) as \"Paid Amount:Currency:150\",\n sum(outstanding_amount) as \"Outstanding Amount:Currency:150\",\n sum(grand_total) as \"Grand Total:Currency:150\"\nFROM\n `tabFees` \nGROUP BY\n student", - "ref_doctype": "Fees", - "report_name": "Student Fee Collection", - "report_type": "Query Report", + "add_total_row": 0, + "creation": "2016-06-22 02:58:41.024538", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 3, + "is_standard": "Yes", + "modified": "2020-06-24 17:14:39.452551", + "modified_by": "Administrator", + "module": "Education", + "name": "Student Fee Collection", + "owner": "Administrator", + "prepared_report": 0, + "query": "SELECT\n student as \"Student:Link/Student:200\",\n student_name as \"Student Name::200\",\n sum(grand_total) - sum(outstanding_amount) as \"Paid Amount:Currency:150\",\n sum(outstanding_amount) as \"Outstanding Amount:Currency:150\",\n sum(grand_total) as \"Grand Total:Currency:150\"\nFROM\n `tabFees` \nGROUP BY\n student", + "ref_doctype": "Fees", + "report_name": "Student Fee Collection", + "report_type": "Query Report", "roles": [ { "role": "Academics User" diff --git a/erpnext/education/report/student_monthly_attendance_sheet/student_monthly_attendance_sheet.json b/erpnext/education/report/student_monthly_attendance_sheet/student_monthly_attendance_sheet.json index e10f190e1c7..1423d4fee1a 100644 --- a/erpnext/education/report/student_monthly_attendance_sheet/student_monthly_attendance_sheet.json +++ b/erpnext/education/report/student_monthly_attendance_sheet/student_monthly_attendance_sheet.json @@ -1,20 +1,21 @@ { - "add_total_row": 0, - "apply_user_permissions": 1, - "creation": "2013-05-13 14:04:03", - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 3, - "is_standard": "Yes", - "modified": "2017-11-10 19:42:43.376658", - "modified_by": "Administrator", - "module": "Education", - "name": "Student Monthly Attendance Sheet", - "owner": "Administrator", - "ref_doctype": "Student Attendance", - "report_name": "Student Monthly Attendance Sheet", - "report_type": "Script Report", + "add_total_row": 0, + "creation": "2013-05-13 14:04:03", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 3, + "is_standard": "Yes", + "modified": "2020-06-24 17:16:13.307053", + "modified_by": "Administrator", + "module": "Education", + "name": "Student Monthly Attendance Sheet", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Student Attendance", + "report_name": "Student Monthly Attendance Sheet", + "report_type": "Script Report", "roles": [ { "role": "Academics User" From 2abd66da9dd1b258c33e14521c373a9f46b7c07c Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 24 Jun 2020 17:49:50 +0530 Subject: [PATCH 465/608] fix: Update journal entry account timesstamp --- .../doctype/journal_entry_account/journal_entry_account.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json index ad0ecc4a0d8..774159d6919 100644 --- a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json +++ b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json @@ -280,7 +280,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2020-06-18 14:06:54.833738", + "modified": "2020-06-24 14:06:54.833738", "modified_by": "Administrator", "module": "Accounts", "name": "Journal Entry Account", From 6fd1068af3a6c9b8fc3ec67709b1ce42e7b02ba6 Mon Sep 17 00:00:00 2001 From: John Clarke Date: Wed, 24 Jun 2020 07:31:21 -0600 Subject: [PATCH 466/608] Update stock_ageing.py --- erpnext/stock/report/stock_ageing/stock_ageing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.py b/erpnext/stock/report/stock_ageing/stock_ageing.py index 723ed5c1c46..d5878cb6624 100644 --- a/erpnext/stock/report/stock_ageing/stock_ageing.py +++ b/erpnext/stock/report/stock_ageing/stock_ageing.py @@ -187,7 +187,7 @@ def get_fifo_queue(filters, sle=None): transferred_item_details[(d.voucher_no, d.name)].append(fifo_queue.pop(0)) else: # all from current batch - batch[0] -= qty_to_pop + batch[0] = flt(batch[0]) - qty_to_pop transferred_item_details[(d.voucher_no, d.name)].append([qty_to_pop, batch[1]]) qty_to_pop = 0 From da0385cd3982abe986018d62ff926abb4341f242 Mon Sep 17 00:00:00 2001 From: Michelle Alva <50285544+michellealva@users.noreply.github.com> Date: Thu, 25 Jun 2020 08:24:01 +0530 Subject: [PATCH 467/608] fix: change error messages in RFQ Before: 1. Row {0}: For supplier {0} Email Address is required to send email 2. Email sent to supplier 3. Request for Quotation is disabled to access from portal, for more check portal settings. 4. Supplier Quotation created. After: 1. Row {0}: For Supplier {0} Email Address is Required to Send Email 2. Email sent to Supplier 3. The Access to Request for Quotation From Portal is Disabled. To Allow Access, Enable it in Portal Settings. 4. Supplier Quotation Created. --- .../request_for_quotation/request_for_quotation.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py index dfdb487f9e0..56af4d90279 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py @@ -51,7 +51,7 @@ class RequestforQuotation(BuyingController): def validate_email_id(self, args): if not args.email_id: - frappe.throw(_("Row {0}: For supplier {0} Email Address is required to send email").format(args.idx, args.supplier)) + frappe.throw(_("Row {0}: For Supplier {0}, Email Address is Required to Send Email").format(args.idx, args.supplier)) def on_submit(self): frappe.db.set(self, 'status', 'Submitted') @@ -154,7 +154,7 @@ class RequestforQuotation(BuyingController): sender=sender,attachments = attachments, send_email=True, doctype=self.doctype, name=self.name)["name"] - frappe.msgprint(_("Email sent to supplier {0}").format(data.supplier)) + frappe.msgprint(_("Email Sent to Supplier {0}").format(data.supplier)) def get_attachments(self): attachments = [d.name for d in get_attachments(self.doctype, self.name)] @@ -193,7 +193,7 @@ def send_supplier_emails(rfq_name): def check_portal_enabled(reference_doctype): if not frappe.db.get_value('Portal Menu Item', {'reference_doctype': reference_doctype}, 'enabled'): - frappe.throw(_("Request for Quotation is disabled to access from portal, for more check portal settings.")) + frappe.throw(_("The Access to Request for Quotation From Portal is Disabled. To Allow Access, Enable it in Portal Settings.")) def get_list_context(context=None): from erpnext.controllers.website_list_for_contact import get_list_context @@ -259,7 +259,7 @@ def create_supplier_quotation(doc): sq_doc.flags.ignore_permissions = True sq_doc.run_method("set_missing_values") sq_doc.save() - frappe.msgprint(_("Supplier Quotation {0} created").format(sq_doc.name)) + frappe.msgprint(_("Supplier Quotation {0} Created").format(sq_doc.name)) return sq_doc.name except Exception: return None From 53346217d2e23836c010c2d25e69cafa716e1623 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 25 Jun 2020 11:12:09 +0530 Subject: [PATCH 468/608] Revert "feat: added search to support page (#22376)" (#22431) This reverts commit 3ab8d865dd3fa470cfd14f0368b3631ac554b331. --- erpnext/www/support/index.html | 45 +--------------------------------- 1 file changed, 1 insertion(+), 44 deletions(-) diff --git a/erpnext/www/support/index.html b/erpnext/www/support/index.html index 12b4c2c0819..93da503dbb0 100644 --- a/erpnext/www/support/index.html +++ b/erpnext/www/support/index.html @@ -9,33 +9,6 @@

    {{ greeting_subtitle }}

    {% endif %} -
    - - -
    @@ -81,21 +54,5 @@ {% endif %} -{% endblock %} -{%- block script -%} - -{%- endblock -%} - -{%- block style -%} - -{%- endblock -%} +{% endblock %} \ No newline at end of file From acdaf2b0015f8bfd94193aa6f9b6cc2db95412d4 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Thu, 25 Jun 2020 12:43:25 +0530 Subject: [PATCH 469/608] fiix: cannot cancel asset and asset movement --- erpnext/assets/doctype/asset/asset.py | 16 ------------- .../doctype/asset_movement/asset_movement.py | 24 ------------------- 2 files changed, 40 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 2ecabe60f03..0b5a2ce4e10 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -35,9 +35,6 @@ class Asset(AccountsController): if not self.booked_fixed_asset and self.validate_make_gl_entry(): self.make_gl_entries() - def before_cancel(self): - self.cancel_auto_gen_movement() - def on_cancel(self): self.validate_cancellation() self.delete_depreciation_entries() @@ -134,19 +131,6 @@ class Asset(AccountsController): Please do not book expense of multiple assets against one single Asset.") .format(frappe.bold("equal"), "
    "), title=_("Invalid Gross Purchase Amount")) - def cancel_auto_gen_movement(self): - movements = frappe.db.sql( - """SELECT asm.name, asm.docstatus - FROM `tabAsset Movement` asm, `tabAsset Movement Item` asm_item - WHERE asm_item.parent=asm.name and asm_item.asset=%s and asm.docstatus=1""", self.name, as_dict=1) - if len(movements) > 1: - frappe.throw(_('Asset has multiple Asset Movement Entries which has to be \ - cancelled manually to cancel this asset.')) - if movements: - movement = frappe.get_doc('Asset Movement', movements[0].get('name')) - movement.flags.ignore_validate = True - movement.cancel() - def make_asset_movement(self): reference_doctype = 'Purchase Receipt' if self.purchase_receipt else 'Purchase Invoice' reference_docname = self.purchase_receipt or self.purchase_invoice diff --git a/erpnext/assets/doctype/asset_movement/asset_movement.py b/erpnext/assets/doctype/asset_movement/asset_movement.py index 3da355e2b99..b2de250b168 100644 --- a/erpnext/assets/doctype/asset_movement/asset_movement.py +++ b/erpnext/assets/doctype/asset_movement/asset_movement.py @@ -87,33 +87,9 @@ class AssetMovement(Document): def on_submit(self): self.set_latest_location_in_asset() - - def before_cancel(self): - self.validate_last_movement() def on_cancel(self): self.set_latest_location_in_asset() - - def validate_last_movement(self): - for d in self.assets: - auto_gen_movement_entry = frappe.db.sql( - """ - SELECT asm.name - FROM `tabAsset Movement Item` asm_item, `tabAsset Movement` asm - WHERE - asm.docstatus=1 and - asm_item.parent=asm.name and - asm_item.asset=%s and - asm.company=%s and - asm_item.source_location is NULL and - asm.purpose=%s - ORDER BY - asm.transaction_date asc - """, (d.asset, self.company, 'Receipt'), as_dict=1) - - if auto_gen_movement_entry and auto_gen_movement_entry[0].get('name') == self.name: - frappe.throw(_('{0} will be cancelled automatically on asset cancellation as it was \ - auto generated for Asset {1}').format(self.name, d.asset)) def set_latest_location_in_asset(self): current_location, current_employee = '', '' From 7b3700b3ece495af116b79094d46e2fa81211d38 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 25 Jun 2020 13:29:14 +0530 Subject: [PATCH 470/608] fix: handle custom statuses for the pause SLA configuration (#22349) * fix: handle hold time for custom statuses * fix: set total hold time only if hold configurations are enabled Co-authored-by: Himanshu --- erpnext/support/doctype/issue/issue.py | 67 ++++++++++++++------------ 1 file changed, 36 insertions(+), 31 deletions(-) diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index 883e603fd31..87168e151e6 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -79,47 +79,52 @@ class Issue(Document): def handle_hold_time(self, status): if self.service_level_agreement: - # set response and resolution variance as None as the issue is on Hold for status as Replied + # set response and resolution variance as None as the issue is on Hold pause_sla_on = frappe.db.get_all("Pause SLA On Status", fields=["status"], filters={"parent": self.service_level_agreement}) hold_statuses = [entry.status for entry in pause_sla_on] update_values = {} - if self.status in hold_statuses and status not in hold_statuses: - update_values['on_hold_since'] = frappe.flags.current_time or now_datetime() - if not self.first_responded_on: - update_values['response_by'] = None - update_values['response_by_variance'] = 0 - update_values['resolution_by'] = None - update_values['resolution_by_variance'] = 0 + if hold_statuses: + if self.status in hold_statuses and status not in hold_statuses: + update_values['on_hold_since'] = frappe.flags.current_time or now_datetime() + if not self.first_responded_on: + update_values['response_by'] = None + update_values['response_by_variance'] = 0 + update_values['resolution_by'] = None + update_values['resolution_by_variance'] = 0 - # calculate hold time when status is changed from Replied to any other status - if self.status not in hold_statuses and status in hold_statuses: - hold_time = self.total_hold_time if self.total_hold_time else 0 - now_time = frappe.flags.current_time or now_datetime() - update_values['total_hold_time'] = hold_time + time_diff_in_seconds(now_time, self.on_hold_since) + # calculate hold time when status is changed from any hold status to any non-hold status + if self.status not in hold_statuses and status in hold_statuses: + hold_time = self.total_hold_time if self.total_hold_time else 0 + now_time = frappe.flags.current_time or now_datetime() + last_hold_time = 0 + if self.on_hold_since: + # last_hold_time will be added to the sla variables + last_hold_time = time_diff_in_seconds(now_time, self.on_hold_since) + update_values['total_hold_time'] = hold_time + last_hold_time - # re-calculate SLA variables after issue changes from Replied to Open - # add hold time to SLA variables - if self.status == "Open" and status in hold_statuses: - start_date_time = get_datetime(self.service_level_agreement_creation) - priority = get_priority(self) - now_time = frappe.flags.current_time or now_datetime() - hold_time = time_diff_in_seconds(now_time, self.on_hold_since) + # re-calculate SLA variables after issue changes from any hold status to any non-hold status + # add hold time to SLA variables + start_date_time = get_datetime(self.service_level_agreement_creation) + priority = get_priority(self) + now_time = frappe.flags.current_time or now_datetime() - if not self.first_responded_on: - response_by = get_expected_time_for(parameter="response", service_level=priority, start_date_time=start_date_time) - update_values['response_by'] = add_to_date(response_by, seconds=round(hold_time)) - response_by_variance = round(time_diff_in_hours(self.response_by, now_time)) - update_values['response_by_variance'] = response_by_variance + (hold_time // 3600) + if not self.first_responded_on: + response_by = get_expected_time_for(parameter="response", service_level=priority, start_date_time=start_date_time) + response_by = add_to_date(response_by, seconds=round(last_hold_time)) + response_by_variance = round(time_diff_in_hours(response_by, now_time)) + update_values['response_by'] = response_by + update_values['response_by_variance'] = response_by_variance + (last_hold_time // 3600) - resolution_by = get_expected_time_for(parameter="resolution", service_level=priority, start_date_time=start_date_time) - update_values['resolution_by'] = add_to_date(resolution_by, seconds=round(hold_time)) - resolution_by_variance = round(time_diff_in_hours(self.resolution_by, now_time)) - update_values['resolution_by_variance'] = resolution_by_variance + (hold_time // 3600) - update_values['on_hold_since'] = None + resolution_by = get_expected_time_for(parameter="resolution", service_level=priority, start_date_time=start_date_time) + resolution_by = add_to_date(resolution_by, seconds=round(last_hold_time)) + resolution_by_variance = round(time_diff_in_hours(resolution_by, now_time)) + update_values['resolution_by'] = resolution_by + update_values['resolution_by_variance'] = resolution_by_variance + (last_hold_time // 3600) + update_values['on_hold_since'] = None - self.db_set(update_values) + self.db_set(update_values) def update_agreement_status(self): if self.service_level_agreement and self.agreement_fulfilled == "Ongoing": From e7c91917aa458e00d2ebac7fee1d5902e3326948 Mon Sep 17 00:00:00 2001 From: Rohan Bansal Date: Thu, 25 Jun 2020 13:32:58 +0530 Subject: [PATCH 471/608] fix: setup status indicators for Job Offer and Job Applicant --- .../doctype/job_applicant/job_applicant_list.js | 15 +++++++++++++++ erpnext/hr/doctype/job_offer/job_offer.json | 3 +-- erpnext/hr/doctype/job_offer/job_offer_list.js | 15 +++++++++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 erpnext/hr/doctype/job_applicant/job_applicant_list.js create mode 100644 erpnext/hr/doctype/job_offer/job_offer_list.js diff --git a/erpnext/hr/doctype/job_applicant/job_applicant_list.js b/erpnext/hr/doctype/job_applicant/job_applicant_list.js new file mode 100644 index 00000000000..3b9141ba79c --- /dev/null +++ b/erpnext/hr/doctype/job_applicant/job_applicant_list.js @@ -0,0 +1,15 @@ +// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +// MIT License. See license.txt + +frappe.listview_settings['Job Applicant'] = { + add_fields: ["company", "designation", "job_applicant", "status"], + get_indicator: function (doc) { + if (doc.status == "Accepted") { + return [__(doc.status), "green", "status,=," + doc.status]; + } else if (["Open", "Replied"].includes(doc.status)) { + return [__(doc.status), "orange", "status,=," + doc.status]; + } else if (["Hold", "Rejected"].includes(doc.status)) { + return [__(doc.status), "red", "status,=," + doc.status]; + } + } +}; diff --git a/erpnext/hr/doctype/job_offer/job_offer.json b/erpnext/hr/doctype/job_offer/job_offer.json index ccbfdc53830..c0b7f69e1bb 100644 --- a/erpnext/hr/doctype/job_offer/job_offer.json +++ b/erpnext/hr/doctype/job_offer/job_offer.json @@ -30,7 +30,6 @@ { "fieldname": "job_applicant", "fieldtype": "Link", - "in_list_view": 1, "label": "Job Applicant", "options": "Job Applicant", "print_hide": 1, @@ -161,7 +160,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2019-12-31 02:40:33.650728", + "modified": "2020-06-25 00:56:24.756395", "modified_by": "Administrator", "module": "HR", "name": "Job Offer", diff --git a/erpnext/hr/doctype/job_offer/job_offer_list.js b/erpnext/hr/doctype/job_offer/job_offer_list.js new file mode 100644 index 00000000000..4fa5be7cc84 --- /dev/null +++ b/erpnext/hr/doctype/job_offer/job_offer_list.js @@ -0,0 +1,15 @@ +// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +// MIT License. See license.txt + +frappe.listview_settings['Job Offer'] = { + add_fields: ["company", "designation", "job_applicant", "status"], + get_indicator: function (doc) { + if (doc.status == "Accepted") { + return [__(doc.status), "green", "status,=," + doc.status]; + } else if (doc.status == "Awaiting Response") { + return [__(doc.status), "orange", "status,=," + doc.status]; + } else if (doc.status == "Rejected") { + return [__(doc.status), "red", "status,=," + doc.status]; + } + } +}; From ecff8f958226c0746f0a72ec689e4c895a3afc4e Mon Sep 17 00:00:00 2001 From: Sun Howwrongbum Date: Thu, 25 Jun 2020 14:25:53 +0530 Subject: [PATCH 472/608] fix: UnboundLocalError when setting product_info (#22436) Co-authored-by: Marica --- erpnext/portal/product_configurator/utils.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/portal/product_configurator/utils.py b/erpnext/portal/product_configurator/utils.py index 6b6b8c579be..f8af30a1c30 100644 --- a/erpnext/portal/product_configurator/utils.py +++ b/erpnext/portal/product_configurator/utils.py @@ -239,13 +239,12 @@ def get_next_attribute_and_values(item_code, selected_attributes): if exact_match: data = get_product_info_for_website(exact_match[0]) product_info = data.product_info + product_info["allow_items_not_in_stock"] = cint(data.cart_settings.allow_items_not_in_stock) if not data.cart_settings.show_price: product_info = None else: product_info = None - product_info["allow_items_not_in_stock"] = cint(data.cart_settings.allow_items_not_in_stock) - return { 'next_attribute': next_attribute, 'valid_options_for_attributes': valid_options_for_attributes, From 8a55ec1580f514847311505445b59b21b8d3d8d4 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Thu, 25 Jun 2020 15:34:01 +0530 Subject: [PATCH 473/608] fix: Add view ledger button for cancelled docs (#22432) * fix: Add view ledger button for cancelled docs * fix: Add moddified to date in Stock Ledger --- .../invoice_discounting/invoice_discounting.js | 7 ++++--- .../accounts/doctype/journal_entry/journal_entry.js | 7 ++++--- .../accounts/doctype/payment_entry/payment_entry.js | 11 ++++++----- .../period_closing_voucher/period_closing_voucher.js | 5 +++-- erpnext/education/doctype/fees/fees.js | 7 ++++--- erpnext/hr/doctype/expense_claim/expense_claim.js | 7 +++++-- erpnext/loan_management/loan_common.js | 7 +++++-- erpnext/public/js/controllers/stock_controller.js | 10 ++++++---- 8 files changed, 37 insertions(+), 24 deletions(-) diff --git a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.js b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.js index 0fab8b70f43..db4f7c423f9 100644 --- a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.js +++ b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.js @@ -188,14 +188,15 @@ frappe.ui.form.on('Invoice Discounting', { }, show_general_ledger: (frm) => { - if(frm.doc.docstatus===1) { + if(frm.doc.docstatus > 0) { cur_frm.add_custom_button(__('Accounting Ledger'), function() { frappe.route_options = { voucher_no: frm.doc.name, from_date: frm.doc.posting_date, - to_date: frm.doc.posting_date, + to_date: moment(frm.doc.modified).format('YYYY-MM-DD'), company: frm.doc.company, - group_by: "Group by Voucher (Consolidated)" + group_by: "Group by Voucher (Consolidated)", + show_cancelled_entries: frm.doc.docstatus === 2 }; frappe.set_route("query-report", "General Ledger"); }, __("View")); diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js index 5685f839fe8..a09face7918 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.js +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js @@ -13,15 +13,16 @@ frappe.ui.form.on("Journal Entry", { refresh: function(frm) { erpnext.toggle_naming_series(); - if(frm.doc.docstatus==1) { + if(frm.doc.docstatus > 0) { frm.add_custom_button(__('Ledger'), function() { frappe.route_options = { "voucher_no": frm.doc.name, "from_date": frm.doc.posting_date, - "to_date": frm.doc.posting_date, + "to_date": moment(frm.doc.modified).format('YYYY-MM-DD'), "company": frm.doc.company, "finance_book": frm.doc.finance_book, - "group_by_voucher": 0 + "group_by": '', + "show_cancelled_entries": frm.doc.docstatus === 2 }; frappe.set_route("query-report", "General Ledger"); }, __('View')); diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index a378a51cdf7..42c9fdeba46 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -172,8 +172,8 @@ frappe.ui.form.on('Payment Entry', { frm.toggle_display("base_paid_amount", frm.doc.paid_from_account_currency != company_currency); frm.toggle_display("base_received_amount", ( - frm.doc.paid_to_account_currency != company_currency - && frm.doc.paid_from_account_currency != frm.doc.paid_to_account_currency + frm.doc.paid_to_account_currency != company_currency + && frm.doc.paid_from_account_currency != frm.doc.paid_to_account_currency && frm.doc.base_paid_amount != frm.doc.base_received_amount )); @@ -234,14 +234,15 @@ frappe.ui.form.on('Payment Entry', { }, show_general_ledger: function(frm) { - if(frm.doc.docstatus==1) { + if(frm.doc.docstatus > 0) { frm.add_custom_button(__('Ledger'), function() { frappe.route_options = { "voucher_no": frm.doc.name, "from_date": frm.doc.posting_date, - "to_date": frm.doc.posting_date, + "to_date": moment(frm.doc.modified).format('YYYY-MM-DD'), "company": frm.doc.company, - group_by: "" + "group_by": "", + "show_cancelled_entries": frm.doc.docstatus === 2 }; frappe.set_route("query-report", "General Ledger"); }, "fa fa-table"); diff --git a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.js b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.js index 87e02fef1b1..e923d4ed5e6 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.js +++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.js @@ -25,9 +25,10 @@ frappe.ui.form.on('Period Closing Voucher', { frappe.route_options = { "voucher_no": frm.doc.name, "from_date": frm.doc.posting_date, - "to_date": frm.doc.posting_date, + "to_date": moment(frm.doc.modified).format('YYYY-MM-DD'), "company": frm.doc.company, - group_by_voucher: 0 + "group_by": "", + "show_cancelled_entries": frm.doc.docstatus === 2 }; frappe.set_route("query-report", "General Ledger"); }, "fa fa-table"); diff --git a/erpnext/education/doctype/fees/fees.js b/erpnext/education/doctype/fees/fees.js index 17ef44954b1..867866fbf14 100644 --- a/erpnext/education/doctype/fees/fees.js +++ b/erpnext/education/doctype/fees/fees.js @@ -55,14 +55,15 @@ frappe.ui.form.on("Fees", { frm.set_df_property('posting_date', 'read_only', 1); frm.set_df_property('posting_time', 'read_only', 1); } - if(frm.doc.docstatus===1) { + if(frm.doc.docstatus > 0) { frm.add_custom_button(__('Accounting Ledger'), function() { frappe.route_options = { voucher_no: frm.doc.name, from_date: frm.doc.posting_date, - to_date: frm.doc.posting_date, + to_date: moment(frm.doc.modified).format('YYYY-MM-DD'), company: frm.doc.company, - group_by_voucher: false + group_by: '', + show_cancelled_entries: frm.doc.docstatus === 2 }; frappe.set_route("query-report", "General Ledger"); }, __("View")); diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.js b/erpnext/hr/doctype/expense_claim/expense_claim.js index 6bb9af98263..fa63ec2834a 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.js +++ b/erpnext/hr/doctype/expense_claim/expense_claim.js @@ -213,12 +213,15 @@ frappe.ui.form.on("Expense Claim", { refresh: function(frm) { frm.trigger("toggle_fields"); - if(frm.doc.docstatus === 1 && frm.doc.approval_status !== "Rejected") { + if(frm.doc.docstatus > 0 && frm.doc.approval_status !== "Rejected") { frm.add_custom_button(__('Accounting Ledger'), function() { frappe.route_options = { voucher_no: frm.doc.name, company: frm.doc.company, - group_by_voucher: false + from_date: frm.doc.posting_date, + to_date: moment(frm.doc.modified).format('YYYY-MM-DD'), + group_by: '', + show_cancelled_entries: frm.doc.docstatus === 2 }; frappe.set_route("query-report", "General Ledger"); }, __("View")); diff --git a/erpnext/loan_management/loan_common.js b/erpnext/loan_management/loan_common.js index 3a47a88cbe7..d9dd415296e 100644 --- a/erpnext/loan_management/loan_common.js +++ b/erpnext/loan_management/loan_common.js @@ -9,12 +9,15 @@ frappe.ui.form.on(cur_frm.doctype, { } if (['Loan Disbursement', 'Loan Repayment', 'Loan Interest Accrual'].includes(frm.doc.doctype) - && frm.doc.docstatus == 1) { + && frm.doc.docstatus > 0) { frm.add_custom_button(__("Accounting Ledger"), function() { frappe.route_options = { voucher_no: frm.doc.name, - company: frm.doc.company + company: frm.doc.company, + from_date: frm.doc.posting_date, + to_date: moment(frm.doc.modified).format('YYYY-MM-DD'), + show_cancelled_entries: frm.doc.docstatus === 2 }; frappe.set_route("query-report", "General Ledger"); diff --git a/erpnext/public/js/controllers/stock_controller.js b/erpnext/public/js/controllers/stock_controller.js index 2ce49e766b9..87b21b78de8 100644 --- a/erpnext/public/js/controllers/stock_controller.js +++ b/erpnext/public/js/controllers/stock_controller.js @@ -55,8 +55,9 @@ erpnext.stock.StockController = frappe.ui.form.Controller.extend({ frappe.route_options = { voucher_no: me.frm.doc.name, from_date: me.frm.doc.posting_date, - to_date: me.frm.doc.posting_date, - company: me.frm.doc.company + to_date: moment(me.frm.doc.modified).format('YYYY-MM-DD'), + company: me.frm.doc.company, + show_cancelled_entries: me.frm.doc.docstatus === 2 }; frappe.set_route("query-report", "Stock Ledger"); }, __("View")); @@ -71,9 +72,10 @@ erpnext.stock.StockController = frappe.ui.form.Controller.extend({ frappe.route_options = { voucher_no: me.frm.doc.name, from_date: me.frm.doc.posting_date, - to_date: me.frm.doc.posting_date, + to_date: moment(me.frm.doc.modified).format('YYYY-MM-DD'), company: me.frm.doc.company, - group_by: "Group by Voucher (Consolidated)" + group_by: "Group by Voucher (Consolidated)", + show_cancelled_entries: me.frm.doc.docstatus === 2 }; frappe.set_route("query-report", "General Ledger"); }, __("View")); From 40847aeac3c5a138dc422d4996979f8a04c0e74a Mon Sep 17 00:00:00 2001 From: Saqib Date: Thu, 25 Jun 2020 15:43:31 +0530 Subject: [PATCH 474/608] fix: cannot cancel assets with repair pending (#22440) * fix: cannot cancel assets with repair pending * fix: message * Update asset.py Co-authored-by: Nabin Hait --- erpnext/assets/doctype/asset/asset.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 2ecabe60f03..9ae5a87e6bc 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -408,6 +408,8 @@ class Asset(AccountsController): row.expected_value_after_useful_life = asset_value_after_full_schedule def validate_cancellation(self): + if self.status in ("In Maintenance", "Out of Order"): + frappe.throw(_("There are active maintenance or repairs against the asset. You must complete all of them before cancelling the asset.")) if self.status not in ("Submitted", "Partially Depreciated", "Fully Depreciated"): frappe.throw(_("Asset cannot be cancelled, as it is already {0}").format(self.status)) From f311f52f6ecd61a34e640a246abd4a33711c988a Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 25 Jun 2020 16:02:18 +0530 Subject: [PATCH 475/608] fix: Disable Renaming in Serial No --- erpnext/stock/doctype/serial_no/serial_no.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/serial_no/serial_no.json b/erpnext/stock/doctype/serial_no/serial_no.json index d9f8b627545..2be14c8006a 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.json +++ b/erpnext/stock/doctype/serial_no/serial_no.json @@ -1,7 +1,6 @@ { "actions": [], "allow_import": 1, - "allow_rename": 1, "autoname": "field:serial_no", "creation": "2013-05-16 10:59:15", "description": "Distinct unit of an Item", @@ -427,7 +426,7 @@ "icon": "fa fa-barcode", "idx": 1, "links": [], - "modified": "2020-05-21 19:29:58.517772", + "modified": "2020-06-25 15:53:50.900855", "modified_by": "Administrator", "module": "Stock", "name": "Serial No", From 7f2a3e8c7dd1e91fa9b5cea0ee86e75401827d56 Mon Sep 17 00:00:00 2001 From: Wolfram Schmidt Date: Thu, 25 Jun 2020 13:56:32 +0200 Subject: [PATCH 476/608] Update customs_tariff_number.json Main information of tariff numbers are the number. A description is more or less double information as this is mostly the number. Thus a description should not be mandetory. --- .../doctype/customs_tariff_number/customs_tariff_number.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/customs_tariff_number/customs_tariff_number.json b/erpnext/stock/doctype/customs_tariff_number/customs_tariff_number.json index 07992b8231a..90385a245d5 100644 --- a/erpnext/stock/doctype/customs_tariff_number/customs_tariff_number.json +++ b/erpnext/stock/doctype/customs_tariff_number/customs_tariff_number.json @@ -71,7 +71,7 @@ "read_only": 0, "remember_last_selected_value": 0, "report_hide": 0, - "reqd": 1, + "reqd": 0, "search_index": 0, "set_only_once": 0, "translatable": 0, @@ -143,4 +143,4 @@ "track_changes": 1, "track_seen": 0, "track_views": 0 -} \ No newline at end of file +} From 5374e7ae1ffe9c32ae50bb227e61b6c073c0e789 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 25 Jun 2020 19:41:42 +0530 Subject: [PATCH 477/608] fix: add company filter in email digest --- .../setup/doctype/email_digest/email_digest.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/erpnext/setup/doctype/email_digest/email_digest.py b/erpnext/setup/doctype/email_digest/email_digest.py index 7c0be3bff63..b30bd7814b6 100644 --- a/erpnext/setup/doctype/email_digest/email_digest.py +++ b/erpnext/setup/doctype/email_digest/email_digest.py @@ -405,8 +405,8 @@ class EmailDigest(Document): value, count = frappe.db.sql("""select ifnull((sum(grand_total)) - (sum(grand_total*per_billed/100)),0), count(*) from `tabSales Order` - where (transaction_date <= %(to_date)s) and billing_status != "Fully Billed" - and status not in ('Closed','Cancelled', 'Completed') """, {"to_date": self.future_to_date})[0] + where (transaction_date <= %(to_date)s) and billing_status != "Fully Billed" and company = %(company)s + and status not in ('Closed','Cancelled', 'Completed') """, {"to_date": self.future_to_date, "company": self.company})[0] label = get_link_to_report('Sales Order', label=self.meta.get_label("sales_orders_to_bill"), report_type="Report Builder", @@ -430,8 +430,8 @@ class EmailDigest(Document): value, count = frappe.db.sql("""select ifnull((sum(grand_total)) - (sum(grand_total*per_delivered/100)),0), count(*) from `tabSales Order` - where (transaction_date <= %(to_date)s) and delivery_status != "Fully Delivered" - and status not in ('Closed','Cancelled', 'Completed') """, {"to_date": self.future_to_date})[0] + where (transaction_date <= %(to_date)s) and delivery_status != "Fully Delivered" and company = %(company)s + and status not in ('Closed','Cancelled', 'Completed') """, {"to_date": self.future_to_date, "company": self.company})[0] label = get_link_to_report('Sales Order', label=self.meta.get_label("sales_orders_to_deliver"), report_type="Report Builder", @@ -455,8 +455,8 @@ class EmailDigest(Document): value, count = frappe.db.sql("""select ifnull((sum(grand_total))-(sum(grand_total*per_received/100)),0), count(*) from `tabPurchase Order` - where (transaction_date <= %(to_date)s) and per_received < 100 - and status not in ('Closed','Cancelled', 'Completed') """, {"to_date": self.future_to_date})[0] + where (transaction_date <= %(to_date)s) and per_received < 100 and company = %(company)s + and status not in ('Closed','Cancelled', 'Completed') """, {"to_date": self.future_to_date, "company": self.company})[0] label = get_link_to_report('Purchase Order', label=self.meta.get_label("purchase_orders_to_receive"), report_type="Report Builder", @@ -480,8 +480,8 @@ class EmailDigest(Document): value, count = frappe.db.sql("""select ifnull((sum(grand_total)) - (sum(grand_total*per_billed/100)),0), count(*) from `tabPurchase Order` - where (transaction_date <= %(to_date)s) and per_billed < 100 - and status not in ('Closed','Cancelled', 'Completed') """, {"to_date": self.future_to_date})[0] + where (transaction_date <= %(to_date)s) and per_billed < 100 and company = %(company)s + and status not in ('Closed','Cancelled', 'Completed') """, {"to_date": self.future_to_date, "company": self.company})[0] label = get_link_to_report('Purchase Order', label=self.meta.get_label("purchase_orders_to_bill"), report_type="Report Builder", From c8f590bc8f4b589b7f0991ccc0bccc37c357375b Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 25 Jun 2020 21:30:45 +0530 Subject: [PATCH 478/608] fix: Handling Empty tables in Production Plan --- .../production_plan/production_plan.py | 33 ++++++++++++------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index 560286e68af..016ab14d5fd 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -98,11 +98,17 @@ class ProductionPlan(Document): elif self.get_items_from == "Material Request": self.get_mr_items() + def get_so_mr_list(self, field, table): + """Returns a list of Sales Orders or Material Requests from the respective tables""" + so_mr_list = [d.get(field) for d in self.get(table) if d.get(field)] + return so_mr_list + def get_so_items(self): - so_list = [d.sales_order for d in self.sales_orders if d.sales_order] - if not so_list: - msgprint(_("Please enter Sales Orders in the above table")) - return [] + # Check for empty table or empty rows + if not self.get("sales_orders") or not self.get_so_mr_list("sales_order", "sales_orders"): + frappe.throw(_("Please fill the Sales Orders table"), title=_("Sales Orders Required")) + + so_list = self.get_so_mr_list("sales_order", "sales_orders") item_condition = "" if self.item_code: @@ -134,10 +140,11 @@ class ProductionPlan(Document): self.calculate_total_planned_qty() def get_mr_items(self): - mr_list = [d.material_request for d in self.material_requests if d.material_request] - if not mr_list: - msgprint(_("Please enter Material Requests in the above table")) - return [] + # Check for empty table or empty rows + if not self.get("material_requests") or not self.get_so_mr_list("material_request", "material_requests"): + frappe.throw(_("Please fill the Material Requests table"), title=_("Material Requests Required")) + + mr_list = self.get_so_mr_list("material_request", "material_requests") item_condition = "" if self.item_code: @@ -628,16 +635,18 @@ def get_items_for_material_requests(doc, warehouses=None): if warehouse_list: warehouses = list(set(warehouse_list)) - + if doc.get("for_warehouse") and doc.get("for_warehouse") in warehouses: warehouses.remove(doc.get("for_warehouse")) warehouse_list = None doc['mr_items'] = [] - po_items = doc.get('po_items') if doc.get('po_items') else doc.get('items') - if not po_items: - frappe.throw(_("Items are required to pull the raw materials which is associated with it.")) + + po_items = doc.get('po_items') + if not po_items or not [row.get('item_code') for row in po_items if row.get('item_code')]: + frappe.throw(_("Items to Manufacture are required to pull the Raw Materials associated with it."), + title=_("Items Required")) company = doc.get('company') ignore_existing_ordered_qty = doc.get('ignore_existing_ordered_qty') From 978722621fb804abc31d791c4176a90dc024cdac Mon Sep 17 00:00:00 2001 From: Michelle Alva <50285544+michellealva@users.noreply.github.com> Date: Thu, 25 Jun 2020 22:35:33 +0530 Subject: [PATCH 479/608] fix: grammatical fix on error message Before: Fiscal Year: 2020 does not exists After: Fiscal Year 2020 Does Not Exist --- erpnext/controllers/trends.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/trends.py b/erpnext/controllers/trends.py index 092baa4018f..9b4b0eb9173 100644 --- a/erpnext/controllers/trends.py +++ b/erpnext/controllers/trends.py @@ -33,7 +33,7 @@ def validate_filters(filters): frappe.throw(_("{0} is mandatory").format(f)) if not frappe.db.exists("Fiscal Year", filters.get("fiscal_year")): - frappe.throw(_("Fiscal Year: {0} does not exists").format(filters.get("fiscal_year"))) + frappe.throw(_("Fiscal Year {0} Does Not Exist").format(filters.get("fiscal_year"))) if filters.get("based_on") == filters.get("group_by"): frappe.throw(_("'Based On' and 'Group By' can not be same")) From 27d27c924dde64d846f2200b7f4570e819058d7c Mon Sep 17 00:00:00 2001 From: Anupam K Date: Fri, 26 Jun 2020 12:46:40 +0530 Subject: [PATCH 480/608] enable show_configure_button when shopping cart is enabled --- .../shopping_cart_settings/shopping_cart_settings.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.js b/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.js index e1510f53354..45889872dcf 100644 --- a/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.js +++ b/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.js @@ -13,6 +13,12 @@ $.extend(cur_frm.cscript, { }, enable_checkout: function(){ toggle_mandatory(cur_frm) + }, + enabled: function() { + if(cur_frm.doc.enabled == 1) { + cur_frm.doc.show_configure_button = 1; + cur_frm.refresh_field('show_configure_button'); + } } }); From 0ff5af222a5842e93f4b9e1bdbc31011abbe105d Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 26 Jun 2020 12:46:42 +0530 Subject: [PATCH 481/608] fix: Send email to credit controllers to increase credit limit --- erpnext/selling/doctype/customer/customer.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index 682dfede72f..d70c64fce49 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -388,8 +388,7 @@ def check_credit_limit(customer, company, ignore_outstanding_sales_order=False, credit_controller_users = get_users_with_role(credit_controller_role or "Sales Master Manager") # form a list of emails and names to show to the user - credit_controller_users_list = [user for user in credit_controller_users if frappe.db.exists("Employee", {"prefered_email": user})] - credit_controller_users = [get_formatted_email(user).replace("<", "(").replace(">", ")") for user in credit_controller_users_list] + credit_controller_users = [get_formatted_email(user).replace("<", "(").replace(">", ")") for user in credit_controller_users] if not credit_controller_users: frappe.throw(_("Please contact your administrator to extend the credit limits for {0}.".format(customer))) @@ -409,7 +408,7 @@ def check_credit_limit(customer, company, ignore_outstanding_sales_order=False, 'customer': customer, 'customer_outstanding': customer_outstanding, 'credit_limit': credit_limit, - 'credit_controller_users_list': credit_controller_users_list + 'credit_controller_users_list': credit_controller_users } } ) From a97715e4642a1145750262400fba1e5a78bd5c53 Mon Sep 17 00:00:00 2001 From: Wolfram Schmidt Date: Fri, 26 Jun 2020 10:24:48 +0200 Subject: [PATCH 482/608] Update customs_tariff_number.json --- .../doctype/customs_tariff_number/customs_tariff_number.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/customs_tariff_number/customs_tariff_number.json b/erpnext/stock/doctype/customs_tariff_number/customs_tariff_number.json index 90385a245d5..7eeb14760f8 100644 --- a/erpnext/stock/doctype/customs_tariff_number/customs_tariff_number.json +++ b/erpnext/stock/doctype/customs_tariff_number/customs_tariff_number.json @@ -88,7 +88,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-09-12 09:30:03.951743", + "modified": "2020-06-26 09:30:03.951743", "modified_by": "Administrator", "module": "Stock", "name": "Customs Tariff Number", From 0f056ec51d73aa6f2e6914921bd8a7aa8548c81a Mon Sep 17 00:00:00 2001 From: marination Date: Fri, 26 Jun 2020 14:12:11 +0530 Subject: [PATCH 483/608] fix: Sales Order to MR test failing --- .../manufacturing/doctype/production_plan/production_plan.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index 016ab14d5fd..c8892376b7a 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -643,7 +643,8 @@ def get_items_for_material_requests(doc, warehouses=None): doc['mr_items'] = [] - po_items = doc.get('po_items') + po_items = doc.get('po_items') if doc.get('po_items') else doc.get('items') + # Check for empty table or empty rows if not po_items or not [row.get('item_code') for row in po_items if row.get('item_code')]: frappe.throw(_("Items to Manufacture are required to pull the Raw Materials associated with it."), title=_("Items Required")) From 13e8c3217247dca8d17e1330b512d35a9a94d956 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Fri, 26 Jun 2020 15:07:32 +0530 Subject: [PATCH 484/608] Update shopping_cart_settings.js --- .../doctype/shopping_cart_settings/shopping_cart_settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.js b/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.js index 45889872dcf..ffc5daba62f 100644 --- a/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.js +++ b/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.js @@ -15,7 +15,7 @@ $.extend(cur_frm.cscript, { toggle_mandatory(cur_frm) }, enabled: function() { - if(cur_frm.doc.enabled == 1) { + if (cur_frm.doc.enabled === 1) { cur_frm.doc.show_configure_button = 1; cur_frm.refresh_field('show_configure_button'); } From 0086a3727fbc6351ffa109d04eb6a6ee6f46d662 Mon Sep 17 00:00:00 2001 From: britlog Date: Tue, 9 Jun 2020 22:50:49 +0200 Subject: [PATCH 485/608] fix: product_info --- erpnext/shopping_cart/product_info.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/shopping_cart/product_info.py b/erpnext/shopping_cart/product_info.py index 7c08f5b5b24..29617a87485 100644 --- a/erpnext/shopping_cart/product_info.py +++ b/erpnext/shopping_cart/product_info.py @@ -55,7 +55,7 @@ def get_product_info_for_website(item_code, skip_quotation_creation=False): def set_product_info_for_website(item): """set product price uom for website""" - product_info = get_product_info_for_website(item.item_code, skip_quotation_creation=True) + product_info = get_product_info_for_website(item.item_code, skip_quotation_creation=True).get("product_info") if product_info: item.update(product_info) From e9cec2ff760beb986f532a5b3b384a7fa597233d Mon Sep 17 00:00:00 2001 From: michellealva Date: Fri, 26 Jun 2020 19:29:25 +0530 Subject: [PATCH 486/608] fix: remove redundant label in salary slip --- erpnext/payroll/doctype/salary_slip/salary_slip.json | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.json b/erpnext/payroll/doctype/salary_slip/salary_slip.json index 663a3ef9ea2..88931c2a4b8 100644 --- a/erpnext/payroll/doctype/salary_slip/salary_slip.json +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.json @@ -381,7 +381,6 @@ { "fieldname": "earning", "fieldtype": "Column Break", - "label": "Earning", "oldfieldtype": "Column Break", "show_days": 1, "show_seconds": 1, @@ -400,7 +399,6 @@ { "fieldname": "deduction", "fieldtype": "Column Break", - "label": "Deduction", "oldfieldtype": "Column Break", "show_days": 1, "show_seconds": 1, @@ -616,7 +614,7 @@ "idx": 9, "is_submittable": 1, "links": [], - "modified": "2020-06-22 14:42:43.921828", + "modified": "2020-06-25 14:42:43.921828", "modified_by": "Administrator", "module": "Payroll", "name": "Salary Slip", From 08f842d12ee2472fc7a33472b4f888f73d331bb8 Mon Sep 17 00:00:00 2001 From: michellealva Date: Fri, 26 Jun 2020 19:56:00 +0530 Subject: [PATCH 487/608] fix: apply filters in all transactions --- erpnext/public/js/controllers/transaction.js | 21 ++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index ca897dd4b17..421d44b5e76 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -159,6 +159,27 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ }; }); } + if (this.frm.fields_dict["items"].grid.get_field("cost_center")) { + this.frm.set_query("cost_center", "items", function(doc) { + return { + filters: { + "company": doc.company, + "is_group": 0 + } + }; + }); + } + + if (this.frm.fields_dict["items"].grid.get_field("expense_account")) { + this.frm.set_query("expense_account", "items", function(doc) { + return { + filters: { + "company": doc.company + } + }; + }); + } + if(frappe.meta.get_docfield(this.frm.doc.doctype, "pricing_rules")) { this.frm.set_indicator_formatter('pricing_rule', function(doc) { From 2f37c447dd15d7488ec6e905968750655244717b Mon Sep 17 00:00:00 2001 From: Michelle Alva <50285544+michellealva@users.noreply.github.com> Date: Sat, 27 Jun 2020 09:27:45 +0530 Subject: [PATCH 488/608] fix: change button label in issue Changed the label of the button from "Make" to "Create" so that it is consistent with other DocTypes. Before: --- erpnext/support/doctype/issue/issue.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/support/doctype/issue/issue.js b/erpnext/support/doctype/issue/issue.js index e7e5bd312bc..9e15757ce08 100644 --- a/erpnext/support/doctype/issue/issue.js +++ b/erpnext/support/doctype/issue/issue.js @@ -79,7 +79,7 @@ frappe.ui.form.on("Issue", { method: "erpnext.support.doctype.issue.issue.make_task", frm: frm }); - }, __("Make")); + }, __("Create")); } else { if (frm.doc.service_level_agreement) { @@ -232,4 +232,4 @@ function get_status(variance) { } else { return {"diff_display": "Failed", "indicator": "red"}; } -} \ No newline at end of file +} From 589364856c23aa97f7b235c01d96ab7d6e4b208e Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 27 Jun 2020 18:44:36 +0530 Subject: [PATCH 489/608] fix: Syntax Error --- .../doctype/loan_security_shortfall/loan_security_shortfall.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py b/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py index 99cb0b3d620..ffd96737a54 100644 --- a/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py +++ b/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py @@ -20,7 +20,7 @@ def update_shortfall_status(loan, security_value): if security_value >= loan_security_shortfall.shortfall_amount: frappe.db.set_value("Loan Security Shortfall", loan_security_shortfall.name, { - "status", "Completed", + "status": "Completed", "shortfall_value": loan_security_shortfall.shortfall_amount}) else: frappe.db.set_value("Loan Security Shortfall", loan_security_shortfall.name, From d5736711ce04131364b079a29b10f6ee585d3dd7 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Sun, 28 Jun 2020 11:12:20 +0530 Subject: [PATCH 490/608] fix: indentation --- erpnext/public/js/controllers/transaction.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 421d44b5e76..905f6fb80f5 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -161,7 +161,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ } if (this.frm.fields_dict["items"].grid.get_field("cost_center")) { this.frm.set_query("cost_center", "items", function(doc) { - return { + return { filters: { "company": doc.company, "is_group": 0 @@ -170,9 +170,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ }); } - if (this.frm.fields_dict["items"].grid.get_field("expense_account")) { + if (this.frm.fields_dict["items"].grid.get_field("expense_account")) { this.frm.set_query("expense_account", "items", function(doc) { - return { + return { filters: { "company": doc.company } @@ -180,7 +180,6 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ }); } - if(frappe.meta.get_docfield(this.frm.doc.doctype, "pricing_rules")) { this.frm.set_indicator_formatter('pricing_rule', function(doc) { return (doc.rule_applied) ? "green" : "red"; From 04cf2e029f98874b4eb8043ccbdce08f649f36d6 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Sun, 28 Jun 2020 17:08:12 +0530 Subject: [PATCH 491/608] fix: letter head not found in opening invoice creation tool --- .../opening_invoice_creation_tool.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py index 54464e71c4e..a53417eedf9 100644 --- a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py +++ b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py @@ -68,6 +68,9 @@ class OpeningInvoiceCreationTool(Document): if not self.company: frappe.throw(_("Please select the Company")) + company_details = frappe.get_cached_value('Company', self.company, + ["default_currency", "default_letter_head"], as_dict=1) or {} + for row in self.invoices: if not row.qty: row.qty = 1.0 @@ -99,6 +102,12 @@ class OpeningInvoiceCreationTool(Document): if not args: continue + if company_details: + args.update({ + "currency": company_details.get("default_currency"), + "letter_head": company_details.get("default_letter_head") + }) + doc = frappe.get_doc(args).insert() doc.submit() names.append(doc.name) @@ -172,8 +181,7 @@ class OpeningInvoiceCreationTool(Document): "due_date": row.due_date, "posting_date": row.posting_date, frappe.scrub(party_type): row.party, - "doctype": "Sales Invoice" if self.invoice_type == "Sales" else "Purchase Invoice", - "currency": frappe.get_cached_value('Company', self.company, "default_currency") + "doctype": "Sales Invoice" if self.invoice_type == "Sales" else "Purchase Invoice" }) accounting_dimension = get_accounting_dimensions() From eca2c1cd936a982fd842df927e67aa31e881076b Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 28 Jun 2020 18:53:55 +0530 Subject: [PATCH 492/608] fix: patch --- erpnext/patches/v12_0/create_irs_1099_field_united_states.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/patches/v12_0/create_irs_1099_field_united_states.py b/erpnext/patches/v12_0/create_irs_1099_field_united_states.py index 82c8f5c4147..4fb66b07a47 100644 --- a/erpnext/patches/v12_0/create_irs_1099_field_united_states.py +++ b/erpnext/patches/v12_0/create_irs_1099_field_united_states.py @@ -5,6 +5,7 @@ from erpnext.regional.united_states.setup import make_custom_fields def execute(): frappe.reload_doc('accounts', 'doctype', 'allowed_to_transact_with', force=True) + frappe.reload_doc('accounts', 'doctype', 'pricing_rule_detail', force=True) company = frappe.get_all('Company', filters = {'country': 'United States'}) if not company: From 50e6e47e66238c3c10be53c0c633fa357130ebf0 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 28 Jun 2020 21:32:08 +0530 Subject: [PATCH 493/608] fix: Patch --- erpnext/patches/v12_0/create_irs_1099_field_united_states.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/patches/v12_0/create_irs_1099_field_united_states.py b/erpnext/patches/v12_0/create_irs_1099_field_united_states.py index 4fb66b07a47..43bd0ccdd7c 100644 --- a/erpnext/patches/v12_0/create_irs_1099_field_united_states.py +++ b/erpnext/patches/v12_0/create_irs_1099_field_united_states.py @@ -6,6 +6,7 @@ def execute(): frappe.reload_doc('accounts', 'doctype', 'allowed_to_transact_with', force=True) frappe.reload_doc('accounts', 'doctype', 'pricing_rule_detail', force=True) + frappe.reload_doc('crm', 'doctype', 'lost_reason_detail', force=True) company = frappe.get_all('Company', filters = {'country': 'United States'}) if not company: From 73edba0e10d0a33fdd4186a83673e9d2295d578f Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Mon, 29 Jun 2020 08:54:53 +0530 Subject: [PATCH 494/608] fix: handle nonetype issue for packed items (#22493) --- .../pending_so_items_for_purchase_request.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/selling/report/pending_so_items_for_purchase_request/pending_so_items_for_purchase_request.py b/erpnext/selling/report/pending_so_items_for_purchase_request/pending_so_items_for_purchase_request.py index 14d80315823..e89c45182fd 100644 --- a/erpnext/selling/report/pending_so_items_for_purchase_request/pending_so_items_for_purchase_request.py +++ b/erpnext/selling/report/pending_so_items_for_purchase_request/pending_so_items_for_purchase_request.py @@ -158,7 +158,7 @@ def get_data(): } pending_so.append(so_record) else: - for item in bundled_item_map.get((so.name, so.item_code)): + for item in bundled_item_map.get((so.name, so.item_code), []): material_requests_against_so = materials_request_dict.get((so.name, item.item_code)) or {} if flt(item.qty) > flt(material_requests_against_so.get('qty')): so_record = { From cc42aa175069c3ce3fd70bd36270c3d6bba2ec1f Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 29 Jun 2020 10:33:24 +0530 Subject: [PATCH 495/608] fix: cancel asset movement on asset cancellation --- erpnext/assets/doctype/asset/asset.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 0b5a2ce4e10..2cf510c3fa2 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -37,6 +37,7 @@ class Asset(AccountsController): def on_cancel(self): self.validate_cancellation() + self.cancel_movement_entries() self.delete_depreciation_entries() self.set_status() self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry') @@ -395,6 +396,16 @@ class Asset(AccountsController): if self.status not in ("Submitted", "Partially Depreciated", "Fully Depreciated"): frappe.throw(_("Asset cannot be cancelled, as it is already {0}").format(self.status)) + def cancel_movement_entries(self): + movements = frappe.db.sql( + """SELECT asm.name, asm.docstatus + FROM `tabAsset Movement` asm, `tabAsset Movement Item` asm_item + WHERE asm_item.parent=asm.name and asm_item.asset=%s and asm.docstatus=1""", self.name, as_dict=1) + + for movement in movements: + movement = frappe.get_doc('Asset Movement', movement.get('name')) + movement.cancel() + def delete_depreciation_entries(self): for d in self.get("schedules"): if d.journal_entry: From ba5f571b0d243a8d44f1d189ec42db187ca91d9d Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Mon, 29 Jun 2020 17:35:27 +0530 Subject: [PATCH 496/608] fix: take parent cost center for child if no cost center at child (#22496) --- erpnext/hr/doctype/expense_claim/expense_claim.js | 8 +++++--- erpnext/hr/doctype/expense_claim/expense_claim.py | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.js b/erpnext/hr/doctype/expense_claim/expense_claim.js index fa63ec2834a..8f8dafdd7ff 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.js +++ b/erpnext/hr/doctype/expense_claim/expense_claim.js @@ -300,6 +300,11 @@ frappe.ui.form.on("Expense Claim", { cost_center: function(frm) { frm.events.set_child_cost_center(frm); }, + + validate: function(frm) { + frm.events.set_child_cost_center(frm); + }, + set_child_cost_center: function(frm){ (frm.doc.expenses || []).forEach(function(d) { if (!d.cost_center){ @@ -349,9 +354,6 @@ frappe.ui.form.on("Expense Claim", { }); frappe.ui.form.on("Expense Claim Detail", { - expenses_add: function(frm, cdt, cdn) { - frm.events.set_child_cost_center(frm); - }, amount: function(frm, cdt, cdn) { var child = locals[cdt][cdn]; frappe.model.set_value(cdt, cdn, 'sanctioned_amount', child.amount); diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.py b/erpnext/hr/doctype/expense_claim/expense_claim.py index ea469b82c98..5563c245f4a 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.py +++ b/erpnext/hr/doctype/expense_claim/expense_claim.py @@ -129,7 +129,7 @@ class ExpenseClaim(AccountsController): "debit": data.sanctioned_amount, "debit_in_account_currency": data.sanctioned_amount, "against": self.employee, - "cost_center": data.cost_center + "cost_center": data.cost_center or self.cost_center }, item=data) ) From 8732b8caf618037b402ebcbe3097402e15c65494 Mon Sep 17 00:00:00 2001 From: Marica Date: Mon, 29 Jun 2020 17:38:42 +0530 Subject: [PATCH 497/608] fix: Stock Onboarding typo and reorder (#22499) --- .../stock/doctype/stock_settings/stock_settings.js | 2 +- erpnext/stock/module_onboarding/stock/stock.json | 11 +++++------ .../create_a_supplier/create_a_supplier.json | 2 +- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.js b/erpnext/stock/doctype/stock_settings/stock_settings.js index d5049ac6ed0..48624e0f25e 100644 --- a/erpnext/stock/doctype/stock_settings/stock_settings.js +++ b/erpnext/stock/doctype/stock_settings/stock_settings.js @@ -20,7 +20,7 @@ frappe.tour['Stock Settings'] = [ { fieldname: "item_naming_by", title: __("Item Naming By"), - description: __("By default, the Item Name is set as per the Item Code entered. If you want Items to be named by a") + "Naming Series" + __(" choose the 'Naming Series' option."), + description: __("By default, the Item Name is set as per the Item Code entered. If you want Items to be named by a ") + "Naming Series" + __(" choose the 'Naming Series' option."), }, { fieldname: "default_warehouse", diff --git a/erpnext/stock/module_onboarding/stock/stock.json b/erpnext/stock/module_onboarding/stock/stock.json index de24575a140..10f05d45201 100644 --- a/erpnext/stock/module_onboarding/stock/stock.json +++ b/erpnext/stock/module_onboarding/stock/stock.json @@ -19,7 +19,7 @@ "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/stock", "idx": 0, "is_complete": 0, - "modified": "2020-05-19 19:03:23.602423", + "modified": "2020-06-29 16:41:09.440378", "modified_by": "Administrator", "module": "Stock", "name": "Stock", @@ -31,15 +31,15 @@ { "step": "Create a Product" }, + { + "step": "Create a Supplier" + }, { "step": "Introduction to Stock Entry" }, { "step": "Create a Stock Entry" }, - { - "step": "Create a Supplier" - }, { "step": "Create a Purchase Receipt" }, @@ -49,6 +49,5 @@ ], "subtitle": "Inventory, Warehouses, Analysis and more.", "success_message": "The Stock Module is all set up!", - "title": "Let's Setup the Stock Module.", - "user_can_dismiss": 1 + "title": "Let's Set Up the Stock Module." } \ No newline at end of file diff --git a/erpnext/stock/onboarding_step/create_a_supplier/create_a_supplier.json b/erpnext/stock/onboarding_step/create_a_supplier/create_a_supplier.json index 7a64224bd43..4e753f4d84e 100644 --- a/erpnext/stock/onboarding_step/create_a_supplier/create_a_supplier.json +++ b/erpnext/stock/onboarding_step/create_a_supplier/create_a_supplier.json @@ -8,7 +8,7 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-14 22:09:10.043554", + "modified": "2020-06-29 16:36:53.948242", "modified_by": "Administrator", "name": "Create a Supplier", "owner": "Administrator", From 3e93a2f67fc98c56da4b2948f6dbfdf1e4a7b55a Mon Sep 17 00:00:00 2001 From: Marica Date: Mon, 29 Jun 2020 17:39:26 +0530 Subject: [PATCH 498/608] fix: Set Root as Parent if no parent in new tree view node (#22497) Co-authored-by: Nabin Hait --- erpnext/setup/doctype/customer_group/customer_group.py | 5 ++++- erpnext/setup/doctype/sales_person/sales_person.py | 5 ++++- erpnext/setup/doctype/territory/territory.py | 4 +++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/erpnext/setup/doctype/customer_group/customer_group.py b/erpnext/setup/doctype/customer_group/customer_group.py index f62613ea1bb..68e1ccb6356 100644 --- a/erpnext/setup/doctype/customer_group/customer_group.py +++ b/erpnext/setup/doctype/customer_group/customer_group.py @@ -6,9 +6,12 @@ import frappe from frappe import _ -from frappe.utils.nestedset import NestedSet +from frappe.utils.nestedset import NestedSet, get_root_of class CustomerGroup(NestedSet): nsm_parent_field = 'parent_customer_group' + def validate(self): + if not self.parent_customer_group: + self.parent_customer_group = get_root_of("Customer Group") def on_update(self): self.validate_name_with_customer() diff --git a/erpnext/setup/doctype/sales_person/sales_person.py b/erpnext/setup/doctype/sales_person/sales_person.py index 3379534cf82..19c2e5b9543 100644 --- a/erpnext/setup/doctype/sales_person/sales_person.py +++ b/erpnext/setup/doctype/sales_person/sales_person.py @@ -5,13 +5,16 @@ from __future__ import unicode_literals import frappe from frappe import _ from frappe.utils import flt -from frappe.utils.nestedset import NestedSet +from frappe.utils.nestedset import NestedSet, get_root_of from erpnext import get_default_currency class SalesPerson(NestedSet): nsm_parent_field = 'parent_sales_person' def validate(self): + if not self.parent_sales_person: + self.parent_sales_person = get_root_of("Sales Person") + for d in self.get('targets') or []: if not flt(d.target_qty) and not flt(d.target_amount): frappe.throw(_("Either target qty or target amount is mandatory.")) diff --git a/erpnext/setup/doctype/territory/territory.py b/erpnext/setup/doctype/territory/territory.py index 808b5386abe..05e8f666cfb 100644 --- a/erpnext/setup/doctype/territory/territory.py +++ b/erpnext/setup/doctype/territory/territory.py @@ -6,12 +6,14 @@ import frappe from frappe.utils import flt from frappe import _ -from frappe.utils.nestedset import NestedSet +from frappe.utils.nestedset import NestedSet, get_root_of class Territory(NestedSet): nsm_parent_field = 'parent_territory' def validate(self): + if not self.parent_territory: + self.parent_territory = get_root_of("Territory") for d in self.get('targets') or []: if not flt(d.target_qty) and not flt(d.target_amount): From 343651fc393055ed07e6ac6b56203248e0818058 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Mon, 29 Jun 2020 18:23:22 +0530 Subject: [PATCH 499/608] fix: job offer validation fix (#22504) --- erpnext/hr/doctype/job_offer/job_offer.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/hr/doctype/job_offer/job_offer.py b/erpnext/hr/doctype/job_offer/job_offer.py index 9a2c4c64ebb..f9ee44a4de5 100644 --- a/erpnext/hr/doctype/job_offer/job_offer.py +++ b/erpnext/hr/doctype/job_offer/job_offer.py @@ -32,7 +32,8 @@ class JobOffer(Document): return frappe.get_all("Job Offer", filters={ "offer_date": ['between', (from_date, to_date)], "designation": self.designation, - "company": self.company + "company": self.company, + "docstatus": 1 }, fields=['name']) def update_job_applicant(status, job_applicant): From 510436931b99ab5471c3498df760d6c90635e67a Mon Sep 17 00:00:00 2001 From: Marica Date: Mon, 29 Jun 2020 19:52:23 +0530 Subject: [PATCH 500/608] fix: Added Missing Trends Reports to Buying/Selling Desk (#22502) * fix: Added Missing Trends Reports to Buying/Selling Desk * fix: Move Reports to Other Reports --- erpnext/buying/desk_page/buying/buying.json | 4 ++-- erpnext/selling/desk_page/selling/selling.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/buying/desk_page/buying/buying.json b/erpnext/buying/desk_page/buying/buying.json index bddb9573ad5..565d39c3c83 100644 --- a/erpnext/buying/desk_page/buying/buying.json +++ b/erpnext/buying/desk_page/buying/buying.json @@ -33,7 +33,7 @@ { "hidden": 0, "label": "Other Reports", - "links": "[\n {\n \"is_query_report\": true,\n \"label\": \"Items To Be Requested\",\n \"name\": \"Items To Be Requested\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Item-wise Purchase History\",\n \"name\": \"Item-wise Purchase History\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Subcontracted Raw Materials To Be Transferred\",\n \"name\": \"Subcontracted Raw Materials To Be Transferred\",\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Subcontracted Item To Be Received\",\n \"name\": \"Subcontracted Item To Be Received\",\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Quoted Item Comparison\",\n \"name\": \"Quoted Item Comparison\",\n \"onboard\": 1,\n \"reference_doctype\": \"Supplier Quotation\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Material Requests for which Supplier Quotations are not created\",\n \"name\": \"Material Requests for which Supplier Quotations are not created\",\n \"reference_doctype\": \"Material Request\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Supplier Addresses And Contacts\",\n \"name\": \"Address And Contacts\",\n \"reference_doctype\": \"Address\",\n \"route_options\": {\n \"party_type\": \"Supplier\"\n },\n \"type\": \"report\"\n }\n]" + "links": "[\n {\n \"is_query_report\": true,\n \"label\": \"Items To Be Requested\",\n \"name\": \"Items To Be Requested\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Item-wise Purchase History\",\n \"name\": \"Item-wise Purchase History\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Purchase Receipt Trends\",\n \"name\": \"Purchase Receipt Trends\",\n \"reference_doctype\": \"Purchase Receipt\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Purchase Invoice Trends\",\n \"name\": \"Purchase Invoice Trends\",\n \"reference_doctype\": \"Purchase Invoice\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Subcontracted Raw Materials To Be Transferred\",\n \"name\": \"Subcontracted Raw Materials To Be Transferred\",\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Subcontracted Item To Be Received\",\n \"name\": \"Subcontracted Item To Be Received\",\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Quoted Item Comparison\",\n \"name\": \"Quoted Item Comparison\",\n \"onboard\": 1,\n \"reference_doctype\": \"Supplier Quotation\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Material Requests for which Supplier Quotations are not created\",\n \"name\": \"Material Requests for which Supplier Quotations are not created\",\n \"reference_doctype\": \"Material Request\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Supplier Addresses And Contacts\",\n \"name\": \"Address And Contacts\",\n \"reference_doctype\": \"Address\",\n \"route_options\": {\n \"party_type\": \"Supplier\"\n },\n \"type\": \"report\"\n }\n]" }, { "hidden": 0, @@ -60,7 +60,7 @@ "idx": 0, "is_standard": 1, "label": "Buying", - "modified": "2020-05-28 13:32:49.960574", + "modified": "2020-06-29 19:30:24.983050", "modified_by": "Administrator", "module": "Buying", "name": "Buying", diff --git a/erpnext/selling/desk_page/selling/selling.json b/erpnext/selling/desk_page/selling/selling.json index 60b15326e83..225238233a3 100644 --- a/erpnext/selling/desk_page/selling/selling.json +++ b/erpnext/selling/desk_page/selling/selling.json @@ -23,7 +23,7 @@ { "hidden": 0, "label": "Other Reports", - "links": "[\n {\n \"dependencies\": [\n \"Lead\"\n ],\n \"doctype\": \"Lead\",\n \"is_query_report\": true,\n \"label\": \"Lead Details\",\n \"name\": \"Lead Details\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Address\"\n ],\n \"doctype\": \"Address\",\n \"is_query_report\": true,\n \"label\": \"Customer Addresses And Contacts\",\n \"name\": \"Address And Contacts\",\n \"route_options\": {\n \"party_type\": \"Customer\"\n },\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"doctype\": \"Item\",\n \"is_query_report\": true,\n \"label\": \"Available Stock for Packing Items\",\n \"name\": \"Available Stock for Packing Items\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Pending SO Items For Purchase Request\",\n \"name\": \"Pending SO Items For Purchase Request\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Customer Credit Balance\",\n \"name\": \"Customer Credit Balance\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Customers Without Any Sales Transactions\",\n \"name\": \"Customers Without Any Sales Transactions\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Sales Partners Commission\",\n \"name\": \"Sales Partners Commission\",\n \"type\": \"report\"\n }\n]" + "links": "[\n {\n \"dependencies\": [\n \"Lead\"\n ],\n \"doctype\": \"Lead\",\n \"is_query_report\": true,\n \"label\": \"Lead Details\",\n \"name\": \"Lead Details\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Address\"\n ],\n \"doctype\": \"Address\",\n \"is_query_report\": true,\n \"label\": \"Customer Addresses And Contacts\",\n \"name\": \"Address And Contacts\",\n \"route_options\": {\n \"party_type\": \"Customer\"\n },\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Item\"\n ],\n \"doctype\": \"Item\",\n \"is_query_report\": true,\n \"label\": \"Available Stock for Packing Items\",\n \"name\": \"Available Stock for Packing Items\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Order\"\n ],\n \"doctype\": \"Sales Order\",\n \"is_query_report\": true,\n \"label\": \"Pending SO Items For Purchase Request\",\n \"name\": \"Pending SO Items For Purchase Request\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Delivery Note\"\n ],\n \"doctype\": \"Delivery Note\",\n \"is_query_report\": true,\n \"label\": \"Delivery Note Trends\",\n \"name\": \"Delivery Note Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Sales Invoice Trends\",\n \"name\": \"Sales Invoice Trends\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Customer Credit Balance\",\n \"name\": \"Customer Credit Balance\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Customers Without Any Sales Transactions\",\n \"name\": \"Customers Without Any Sales Transactions\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Sales Partners Commission\",\n \"name\": \"Sales Partners Commission\",\n \"type\": \"report\"\n }\n]" } ], "category": "Modules", @@ -44,7 +44,7 @@ "idx": 0, "is_standard": 1, "label": "Selling", - "modified": "2020-06-19 13:23:24.861706", + "modified": "2020-06-29 19:26:35.139097", "modified_by": "Administrator", "module": "Selling", "name": "Selling", From c2523e845340addec214ea36e42992ba89d9b210 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Mon, 29 Jun 2020 19:54:23 +0530 Subject: [PATCH 501/608] fixes: Payroll module pre relese test fixes (#22500) * fix: salary payment based on payments * fix: bank remittance report * fix: Payroll period onboarding * fix: provident-fund-deductions report * fix: Considered unmarked days * fix: onboarding paYroll * feat: quick entry in payroll entry --- .../payroll/doctype/payroll_period/payroll_period.json | 3 ++- erpnext/payroll/doctype/salary_slip/salary_slip.py | 8 +++----- erpnext/payroll/module_onboarding/payroll/payroll.json | 2 +- .../create_payroll_period/create_payroll_period.json | 2 +- .../payroll_settings/payroll_settings.json | 10 +++++----- .../payroll/report/bank_remittance/bank_remittance.py | 5 ++++- .../salary_payments_based_on_payment_mode.py | 2 ++ .../provident_fund_deductions.py | 2 +- 8 files changed, 19 insertions(+), 15 deletions(-) diff --git a/erpnext/payroll/doctype/payroll_period/payroll_period.json b/erpnext/payroll/doctype/payroll_period/payroll_period.json index c919b4fe13b..0e0948475cf 100644 --- a/erpnext/payroll/doctype/payroll_period/payroll_period.json +++ b/erpnext/payroll/doctype/payroll_period/payroll_period.json @@ -53,7 +53,7 @@ } ], "links": [], - "modified": "2020-06-22 20:12:32.684189", + "modified": "2020-06-29 17:17:12.689089", "modified_by": "Administrator", "module": "Payroll", "name": "Payroll Period", @@ -96,6 +96,7 @@ "write": 1 } ], + "quick_entry": 1, "sort_field": "modified", "sort_order": "DESC", "track_changes": 1 diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.py b/erpnext/payroll/doctype/salary_slip/salary_slip.py index 2da19b03978..1e2983e4218 100644 --- a/erpnext/payroll/doctype/salary_slip/salary_slip.py +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py @@ -207,7 +207,7 @@ class SalarySlip(TransactionBase): frappe.throw(_("There are more holidays than working days this month.")) if not payroll_based_on: - frappe.throw(_("Please set Payroll based on in HR settings")) + frappe.throw(_("Please set Payroll based on in Payroll settings")) if payroll_based_on == "Attendance": actual_lwp, absent = self.calculate_lwp_and_absent_days_based_on_attendance(holidays) @@ -244,15 +244,13 @@ class SalarySlip(TransactionBase): for holiday in holidays: if not frappe.db.exists("Attendance", {"employee": self.employee, "attendance_date": holiday, "docstatus": 1 }): self.payment_days += 1 - - else: self.payment_days = 0 def get_unmarked_days(self): marked_days = frappe.get_all("Attendance", filters = { - "attendance_date": ["between", ['2020-05-1',"2020-05-30"]], - "employee": 'HR-EMP-00003', + "attendance_date": ["between", [self.start_date, self.end_date]], + "employee": self.employee, "docstatus": 1 }, fields = ["COUNT(*) as marked_days"])[0].marked_days diff --git a/erpnext/payroll/module_onboarding/payroll/payroll.json b/erpnext/payroll/module_onboarding/payroll/payroll.json index a4ea53640eb..7ed786faeea 100644 --- a/erpnext/payroll/module_onboarding/payroll/payroll.json +++ b/erpnext/payroll/module_onboarding/payroll/payroll.json @@ -13,7 +13,7 @@ "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/human-resources/payroll-entry", "idx": 0, "is_complete": 0, - "modified": "2020-06-04 16:35:30.650792", + "modified": "2020-06-29 17:00:25.113341", "modified_by": "Administrator", "module": "Payroll", "name": "Payroll", diff --git a/erpnext/payroll/onboarding_step/create_payroll_period/create_payroll_period.json b/erpnext/payroll/onboarding_step/create_payroll_period/create_payroll_period.json index 4bae67546c7..b1a7cc27344 100644 --- a/erpnext/payroll/onboarding_step/create_payroll_period/create_payroll_period.json +++ b/erpnext/payroll/onboarding_step/create_payroll_period/create_payroll_period.json @@ -8,7 +8,7 @@ "is_mandatory": 1, "is_single": 0, "is_skipped": 0, - "modified": "2020-06-01 11:53:54.553947", + "modified": "2020-06-29 11:53:54.553947", "modified_by": "Administrator", "name": "Create Payroll Period", "owner": "Administrator", diff --git a/erpnext/payroll/onboarding_step/payroll_settings/payroll_settings.json b/erpnext/payroll/onboarding_step/payroll_settings/payroll_settings.json index 946b8c8707a..a7cf7bf9884 100644 --- a/erpnext/payroll/onboarding_step/payroll_settings/payroll_settings.json +++ b/erpnext/payroll/onboarding_step/payroll_settings/payroll_settings.json @@ -1,19 +1,19 @@ { - "action": "Go to Page", + "action": "Update Settings", "creation": "2020-06-04 16:34:29.664917", "docstatus": 0, "doctype": "Onboarding Step", "idx": 0, "is_complete": 0, "is_mandatory": 0, - "is_single": 0, + "is_single": 1, "is_skipped": 0, - "modified": "2020-06-04 16:34:29.664917", + "modified": "2020-06-29 16:34:29.664917", "modified_by": "Administrator", "name": "Payroll Settings", "owner": "Administrator", - "path": "#Form/Payroll Settings", + "reference_document": "Payroll Settings", "show_full_form": 0, "title": "Payroll Settings", - "validate_action": 1 + "validate_action": 0 } \ No newline at end of file diff --git a/erpnext/payroll/report/bank_remittance/bank_remittance.py b/erpnext/payroll/report/bank_remittance/bank_remittance.py index a35d8e550ec..4b052bf5c4b 100644 --- a/erpnext/payroll/report/bank_remittance/bank_remittance.py +++ b/erpnext/payroll/report/bank_remittance/bank_remittance.py @@ -152,6 +152,9 @@ def set_company_account(payment_accounts, payroll_entries): company_accounts_map[acc.account] = acc for entry in payroll_entries: - entry["company_account"] = company_accounts_map[entry.payment_account]['bank_account_no'] + company_account = '' + if entry.payment_account in company_accounts_map: + company_account = company_accounts_map[entry.payment_account]['bank_account_no'] + entry["company_account"] = company_account return payroll_entries diff --git a/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.py b/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.py index 7f0c2e2e000..a0dab63654e 100644 --- a/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.py +++ b/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.py @@ -100,6 +100,8 @@ def get_data(filters, mode_of_payments): total_row = get_total_based_on_mode_of_payment(data, mode_of_payments) total_deductions = gross_pay - total_row.get("total") + report_summary = [] + if data: data.append(total_row) data.append({}) diff --git a/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.py b/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.py index 084890e54fe..597072c53a1 100644 --- a/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.py +++ b/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.py @@ -101,7 +101,7 @@ def prepare_data(entry,component_type_dict): "employee": d.employee, "employee_name": d.employee_name, "pf_account": employee_account_dict.get(d.employee), - "component_type": d.amount + component_type: d.amount }) return data_list From d1bf6e027734a57b1ca6b73b9db84dabc744cbd5 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 29 Jun 2020 21:14:03 +0530 Subject: [PATCH 502/608] fix: Do not ignore validate methods on GLL entry submit --- erpnext/accounts/general_ledger.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index bfe35ab0068..da9123b92a3 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -140,10 +140,8 @@ def make_entry(args, adv_adj, update_outstanding): gle = frappe.new_doc("GL Entry") gle.update(args) gle.flags.ignore_permissions = 1 - gle.validate() gle.db_insert() gle.run_method("on_update_with_args", adv_adj, update_outstanding) - gle.flags.ignore_validate = True gle.submit() # check against budget From 8230e41a4202924b24521dc51543b2412d9f56db Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 29 Jun 2020 21:20:05 +0530 Subject: [PATCH 503/608] fix: Insert instead of DB insert --- erpnext/accounts/general_ledger.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index da9123b92a3..a245d63f52b 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -140,7 +140,7 @@ def make_entry(args, adv_adj, update_outstanding): gle = frappe.new_doc("GL Entry") gle.update(args) gle.flags.ignore_permissions = 1 - gle.db_insert() + gle.insert() gle.run_method("on_update_with_args", adv_adj, update_outstanding) gle.submit() From 06f11aba0bb79689dda2d30be78db9a2e5ae8e58 Mon Sep 17 00:00:00 2001 From: Afshan Date: Mon, 29 Jun 2020 21:39:35 +0530 Subject: [PATCH 504/608] feat: Autofill Supplier pop-up when only 1 Supplier in RFQ --- .../doctype/request_for_quotation/request_for_quotation.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js index 455bd68ecff..4a937f7f0d3 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js @@ -166,7 +166,8 @@ frappe.ui.form.on("Request for Quotation",{ { "fieldtype": "Select", "label": __("Supplier"), "fieldname": "supplier", "options": doc.suppliers.map(d => d.supplier), - "reqd": 1 }, + "reqd": 1, + "default": doc.suppliers.length === 1 ? doc.suppliers[0].supplier_name : "" }, { "fieldtype": "Button", "label": __('Create Supplier Quotation'), "fieldname": "make_supplier_quotation", "cssClass": "btn-primary" }, ] From 7056cd3782161f0bfb1ba5c0519e87d40c09ce8d Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Tue, 30 Jun 2020 08:20:03 +0530 Subject: [PATCH 505/608] fix: manufacturing dashboard grammar (#22510) * fix: manufacturing dashboard grammar * Update manufacturing.json --- erpnext/manufacturing/dashboard_fixtures.py | 8 ++++---- .../module_onboarding/manufacturing/manufacturing.json | 7 +++---- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/erpnext/manufacturing/dashboard_fixtures.py b/erpnext/manufacturing/dashboard_fixtures.py index 64e4bc6ed06..0e9a21c0268 100644 --- a/erpnext/manufacturing/dashboard_fixtures.py +++ b/erpnext/manufacturing/dashboard_fixtures.py @@ -192,7 +192,7 @@ def get_number_cards(): ]), "function": "Count", "is_public": 1, - "label": _("Monthly Total Work Order"), + "label": _("Monthly Total Work Orders"), "show_percentage_stats": 1, "stats_time_interval": "Weekly" }, @@ -207,7 +207,7 @@ def get_number_cards(): ]), "function": "Count", "is_public": 1, - "label": _("Monthly Completed Work Order"), + "label": _("Monthly Completed Work Orders"), "show_percentage_stats": 1, "stats_time_interval": "Weekly" }, @@ -221,7 +221,7 @@ def get_number_cards(): ]), "function": "Count", "is_public": 1, - "label": _("Ongoing Job Card"), + "label": _("Ongoing Job Cards"), "show_percentage_stats": 1, "stats_time_interval": "Weekly" }, @@ -235,7 +235,7 @@ def get_number_cards(): ]), "function": "Count", "is_public": 1, - "label": _("Monthly Quality Inspection"), + "label": _("Monthly Quality Inspections"), "show_percentage_stats": 1, "stats_time_interval": "Weekly" }] \ No newline at end of file diff --git a/erpnext/manufacturing/module_onboarding/manufacturing/manufacturing.json b/erpnext/manufacturing/module_onboarding/manufacturing/manufacturing.json index 952d1f0e071..a36b63a1d95 100644 --- a/erpnext/manufacturing/module_onboarding/manufacturing/manufacturing.json +++ b/erpnext/manufacturing/module_onboarding/manufacturing/manufacturing.json @@ -19,7 +19,7 @@ "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/manufacturing", "idx": 0, "is_complete": 0, - "modified": "2020-05-19 12:51:42.744570", + "modified": "2020-06-29 20:25:36.899106", "modified_by": "Administrator", "module": "Manufacturing", "name": "Manufacturing", @@ -52,6 +52,5 @@ ], "subtitle": "Products, Raw Materials, BOM, Work Order and more.", "success_message": "Manufacturing module is all setup!", - "title": "Let's Setup Manufacturing Module", - "user_can_dismiss": 1 -} \ No newline at end of file + "title": "Let's Set Up the Manufacturing Module" +} From fdbd10f1939a47511d2288dbaf4aad138f81eec8 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 30 Jun 2020 09:25:17 +0530 Subject: [PATCH 506/608] fix: Dashboard label in Projects and Assets module (#22517) --- erpnext/assets/desk_page/assets/assets.json | 2 +- .../desk_page/manufacturing/manufacturing.json | 14 +++++++------- erpnext/projects/desk_page/projects/projects.json | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/erpnext/assets/desk_page/assets/assets.json b/erpnext/assets/desk_page/assets/assets.json index 94939fdd2a2..449a5facb08 100644 --- a/erpnext/assets/desk_page/assets/assets.json +++ b/erpnext/assets/desk_page/assets/assets.json @@ -58,7 +58,7 @@ "type": "Report" }, { - "label": "Assets Dashboard", + "label": "Dashboard", "link_to": "Asset", "type": "Dashboard" } diff --git a/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json b/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json index 763f533a94b..8d11294164f 100644 --- a/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json +++ b/erpnext/manufacturing/desk_page/manufacturing/manufacturing.json @@ -93,12 +93,6 @@ "stats_filter": "{ \n \"status\": [\"not in\", [\"Completed\"]]\n}", "type": "DocType" }, - { - "label": "Dashboard", - "link_to": "Manufacturing", - "restrict_to_domain": "Manufacturing", - "type": "Dashboard" - }, { "label": "Forecasting", "link_to": "Exponential Smoothing Forecasting", @@ -119,6 +113,12 @@ "label": "Production Planning Report", "link_to": "Production Planning Report", "type": "Report" - } + }, + { + "label": "Dashboard", + "link_to": "Manufacturing", + "restrict_to_domain": "Manufacturing", + "type": "Dashboard" + } ] } \ No newline at end of file diff --git a/erpnext/projects/desk_page/projects/projects.json b/erpnext/projects/desk_page/projects/projects.json index d91fe5304a3..e24cf3081cb 100644 --- a/erpnext/projects/desk_page/projects/projects.json +++ b/erpnext/projects/desk_page/projects/projects.json @@ -68,7 +68,7 @@ "type": "Report" }, { - "label": "Project Dashboard", + "label": "Dashboard", "link_to": "Project", "type": "Dashboard" } From 8f2cb0beb6f2774a21b95573a86aee6fbd95d956 Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Tue, 30 Jun 2020 12:11:03 +0530 Subject: [PATCH 507/608] fix: set half day date None if half day is unchecked --- erpnext/hr/doctype/leave_application/leave_application.js | 2 ++ erpnext/hr/doctype/leave_application/leave_application.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/erpnext/hr/doctype/leave_application/leave_application.js b/erpnext/hr/doctype/leave_application/leave_application.js index fb1f2c00b13..9413120bc5f 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.js +++ b/erpnext/hr/doctype/leave_application/leave_application.js @@ -40,6 +40,8 @@ frappe.ui.form.on("Leave Application", { validate: function(frm) { if (frm.doc.from_date == frm.doc.to_date && frm.doc.half_day == 1){ frm.doc.half_day_date = frm.doc.from_date; + }else if (frm.doc.half_day == 0){ + frm.doc.half_day_date = undefined; } frm.toggle_reqd("half_day_date", frm.doc.half_day == 1); }, diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index 0423824c0e9..3f25f583833 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -293,6 +293,8 @@ class LeaveApplication(Document): def set_half_day_date(self): if self.from_date == self.to_date and self.half_day == 1: self.half_day_date = self.from_date + elif self.half_day == 0: + self.half_day_date = None def notify_employee(self): employee = frappe.get_doc("Employee", self.employee) From ad4eb649a28e2598cd8dddda9b1410807fb485d5 Mon Sep 17 00:00:00 2001 From: Sahil Khan Date: Tue, 30 Jun 2020 17:54:41 +0530 Subject: [PATCH 508/608] chore: set develop version as 13.x.x --- erpnext/hooks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 835d92ef5c1..70217dc8991 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -13,7 +13,7 @@ source_link = "https://github.com/frappe/erpnext" app_logo_url = '/assets/erpnext/images/erp-icon.svg' -develop_version = '12.x.x-develop' +develop_version = '13.x.x-develop' app_include_js = "assets/js/erpnext.min.js" app_include_css = "assets/css/erpnext.css" From ec84afa86d54b30c6946ef6bd2e71189ca9bc04b Mon Sep 17 00:00:00 2001 From: Michelle Alva <50285544+michellealva@users.noreply.github.com> Date: Tue, 30 Jun 2020 18:04:20 +0530 Subject: [PATCH 509/608] fix: Refactor dashboard links in leave policy (#22519) * fix: refactor dashboard links in leave policy * fx: code fix * fix: add labels to links * fix: code change --- .../leave_policy/leave_policy_dashboard.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/erpnext/hr/doctype/leave_policy/leave_policy_dashboard.py b/erpnext/hr/doctype/leave_policy/leave_policy_dashboard.py index 48a204596c3..ff5dc2ff3e0 100644 --- a/erpnext/hr/doctype/leave_policy/leave_policy_dashboard.py +++ b/erpnext/hr/doctype/leave_policy/leave_policy_dashboard.py @@ -1,4 +1,5 @@ from __future__ import unicode_literals +from frappe import _ def get_data(): return { @@ -8,13 +9,17 @@ def get_data(): }, 'transactions': [ { - 'items': ['Employee'] - }, - { - 'items': ['Employee Grade'] + 'label': _('Employees'), + 'items': ['Employee', 'Employee Grade'] }, { + 'label': _('Leaves'), 'items': ['Leave Allocation'] }, ] - } \ No newline at end of file + } + + + + + \ No newline at end of file From aa1210921b944532986c6ecac918fca748fa19a3 Mon Sep 17 00:00:00 2001 From: "Chinmay D. Pai" Date: Wed, 1 Jul 2020 21:14:32 +0530 Subject: [PATCH 510/608] fix: whitelist all query functions for search widget Signed-off-by: Chinmay D. Pai --- erpnext/controllers/queries.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index f6a8d27d440..f373a43689e 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -10,7 +10,8 @@ from collections import defaultdict from erpnext.stock.get_item_details import _get_item_tax_template from frappe.utils import unique - # searches for active employees +# searches for active employees +@frappe.whitelist() def employee_query(doctype, txt, searchfield, start, page_len, filters): conditions = [] fields = get_fields("Employee", ["name", "employee_name"]) @@ -40,6 +41,7 @@ def employee_query(doctype, txt, searchfield, start, page_len, filters): # searches for leads which are not converted +@frappe.whitelist() def lead_query(doctype, txt, searchfield, start, page_len, filters): fields = get_fields("Lead", ["name", "lead_name", "company_name"]) @@ -69,6 +71,7 @@ def lead_query(doctype, txt, searchfield, start, page_len, filters): # searches for customer +@frappe.whitelist() def customer_query(doctype, txt, searchfield, start, page_len, filters): conditions = [] cust_master_name = frappe.defaults.get_user_default("cust_master_name") @@ -106,6 +109,7 @@ def customer_query(doctype, txt, searchfield, start, page_len, filters): # searches for supplier +@frappe.whitelist() def supplier_query(doctype, txt, searchfield, start, page_len, filters): supp_master_name = frappe.defaults.get_user_default("supp_master_name") if supp_master_name == "Supplier Name": @@ -137,6 +141,7 @@ def supplier_query(doctype, txt, searchfield, start, page_len, filters): }) +@frappe.whitelist() def tax_account_query(doctype, txt, searchfield, start, page_len, filters): company_currency = erpnext.get_company_currency(filters.get('company')) @@ -162,6 +167,7 @@ def tax_account_query(doctype, txt, searchfield, start, page_len, filters): return tax_accounts +@frappe.whitelist() def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=False): conditions = [] @@ -224,6 +230,7 @@ def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=Fals }, as_dict=as_dict) +@frappe.whitelist() def bom(doctype, txt, searchfield, start, page_len, filters): conditions = [] fields = get_fields("BOM", ["name", "item"]) @@ -250,6 +257,7 @@ def bom(doctype, txt, searchfield, start, page_len, filters): }) +@frappe.whitelist() def get_project_name(doctype, txt, searchfield, start, page_len, filters): cond = '' if filters.get('customer'): @@ -276,6 +284,7 @@ def get_project_name(doctype, txt, searchfield, start, page_len, filters): }) +@frappe.whitelist() def get_delivery_notes_to_be_billed(doctype, txt, searchfield, start, page_len, filters, as_dict): fields = get_fields("Delivery Note", ["name", "customer", "posting_date"]) @@ -305,6 +314,7 @@ def get_delivery_notes_to_be_billed(doctype, txt, searchfield, start, page_len, }, {"txt": ("%%%s%%" % txt)}, as_dict=as_dict) +@frappe.whitelist() def get_batch_no(doctype, txt, searchfield, start, page_len, filters): cond = "" if filters.get("posting_date"): @@ -362,6 +372,7 @@ def get_batch_no(doctype, txt, searchfield, start, page_len, filters): limit %(start)s, %(page_len)s""".format(cond, match_conditions=get_match_cond(doctype)), args) +@frappe.whitelist() def get_account_list(doctype, txt, searchfield, start, page_len, filters): filter_list = [] @@ -385,6 +396,7 @@ def get_account_list(doctype, txt, searchfield, start, page_len, filters): limit_start=start, limit_page_length=page_len, as_list=True) +@frappe.whitelist() def get_blanket_orders(doctype, txt, searchfield, start, page_len, filters): return frappe.db.sql("""select distinct bo.name, bo.blanket_order_type, bo.to_date from `tabBlanket Order` bo, `tabBlanket Order Item` boi From ed0bb20e6f86844c8d7d507264e9885c4eed9e97 Mon Sep 17 00:00:00 2001 From: "Chinmay D. Pai" Date: Thu, 2 Jul 2020 12:35:41 +0530 Subject: [PATCH 511/608] chore: add query functions to whitelist Signed-off-by: Chinmay D. Pai --- erpnext/accounts/doctype/journal_entry/journal_entry.py | 1 + erpnext/accounts/doctype/payment_order/payment_order.py | 4 +++- erpnext/accounts/doctype/pos_profile/pos_profile.py | 1 + erpnext/accounts/doctype/pricing_rule/pricing_rule.py | 3 ++- .../accounts/page/bank_reconciliation/bank_reconciliation.py | 5 ++++- .../doctype/request_for_quotation/request_for_quotation.py | 1 + erpnext/manufacturing/doctype/bom/bom.py | 3 ++- erpnext/manufacturing/doctype/work_order/work_order.py | 1 + .../report/bom_variance_report/bom_variance_report.py | 2 +- erpnext/payroll/doctype/payroll_entry/payroll_entry.py | 1 + erpnext/projects/doctype/project/project.py | 1 + erpnext/projects/doctype/task/task.py | 1 + .../doctype/restaurant_order_entry/restaurant_order_entry.py | 3 ++- erpnext/selling/doctype/customer/customer.py | 1 + erpnext/selling/doctype/product_bundle/product_bundle.py | 5 +++-- erpnext/selling/page/point_of_sale/point_of_sale.py | 3 ++- erpnext/stock/doctype/item_alternative/item_alternative.py | 3 ++- erpnext/stock/doctype/material_request/material_request.py | 3 ++- erpnext/stock/doctype/packing_slip/packing_slip.py | 1 + .../stock/doctype/quality_inspection/quality_inspection.py | 3 ++- 20 files changed, 34 insertions(+), 12 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 7360b399425..cfdae936a48 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -840,6 +840,7 @@ def get_opening_accounts(company): return [{"account": a, "balance": get_balance_on(a)} for a in accounts] +@frappe.whitelist() def get_against_jv(doctype, txt, searchfield, start, page_len, filters): return frappe.db.sql("""select jv.name, jv.posting_date, jv.user_remark from `tabJournal Entry` jv, `tabJournal Entry Account` jv_detail diff --git a/erpnext/accounts/doctype/payment_order/payment_order.py b/erpnext/accounts/doctype/payment_order/payment_order.py index 7ecdc41d034..4702e58cef1 100644 --- a/erpnext/accounts/doctype/payment_order/payment_order.py +++ b/erpnext/accounts/doctype/payment_order/payment_order.py @@ -26,6 +26,7 @@ class PaymentOrder(Document): for d in self.references: frappe.db.set_value(self.payment_order_type, d.get(frappe.scrub(self.payment_order_type)), ref_field, status) +@frappe.whitelist() def get_mop_query(doctype, txt, searchfield, start, page_len, filters): return frappe.db.sql(""" select mode_of_payment from `tabPayment Order Reference` where parent = %(parent)s and mode_of_payment like %(txt)s @@ -36,6 +37,7 @@ def get_mop_query(doctype, txt, searchfield, start, page_len, filters): 'txt': "%%%s%%" % txt }) +@frappe.whitelist() def get_supplier_query(doctype, txt, searchfield, start, page_len, filters): return frappe.db.sql(""" select supplier from `tabPayment Order Reference` where parent = %(parent)s and supplier like %(txt)s and @@ -86,4 +88,4 @@ def make_journal_entry(doc, supplier, mode_of_payment=None): je.flags.ignore_mandatory = True je.save() - frappe.msgprint(_("{0} {1} created").format(je.doctype, je.name)) \ No newline at end of file + frappe.msgprint(_("{0} {1} created").format(je.doctype, je.name)) diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.py b/erpnext/accounts/doctype/pos_profile/pos_profile.py index 4f17e9f9954..f1869671ae9 100644 --- a/erpnext/accounts/doctype/pos_profile/pos_profile.py +++ b/erpnext/accounts/doctype/pos_profile/pos_profile.py @@ -115,6 +115,7 @@ def get_item_groups(pos_profile): def get_series(): return frappe.get_meta("Sales Invoice").get_field("naming_series").options or "" +@frappe.whitelist() def pos_profile_query(doctype, txt, searchfield, start, page_len, filters): user = frappe.session['user'] company = filters.get('company') or frappe.defaults.get_user_default('company') diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py index ead300e3bb4..d4d83af1ede 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -432,6 +432,7 @@ def make_pricing_rule(doctype, docname): return doc +@frappe.whitelist() def get_item_uoms(doctype, txt, searchfield, start, page_len, filters): items = [filters.get('value')] if filters.get('apply_on') != 'Item Code': @@ -442,4 +443,4 @@ def get_item_uoms(doctype, txt, searchfield, start, page_len, filters): return frappe.get_all('UOM Conversion Detail', filters = {'parent': ('in', items), 'uom': ("like", "{0}%".format(txt))}, - fields = ["distinct uom"], as_list=1) \ No newline at end of file + fields = ["distinct uom"], as_list=1) diff --git a/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.py b/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.py index 69f9907a8d8..7df090bf62e 100644 --- a/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.py +++ b/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.py @@ -21,7 +21,7 @@ def reconcile(bank_transaction, payment_doctype, payment_name): if payment_doctype == "Payment Entry" and payment_entry.unallocated_amount > transaction.unallocated_amount: frappe.throw(_("The unallocated amount of Payment Entry {0} \ is greater than the Bank Transaction's unallocated amount").format(payment_name)) - + if transaction.unallocated_amount == 0: frappe.throw(_("This bank transaction is already fully reconciled")) @@ -289,6 +289,7 @@ def get_matching_transactions_payments(description_matching): else: return [] +@frappe.whitelist() def payment_entry_query(doctype, txt, searchfield, start, page_len, filters): account = frappe.db.get_value("Bank Account", filters.get("bank_account"), "account") if not account: @@ -317,6 +318,7 @@ def payment_entry_query(doctype, txt, searchfield, start, page_len, filters): } ) +@frappe.whitelist() def journal_entry_query(doctype, txt, searchfield, start, page_len, filters): account = frappe.db.get_value("Bank Account", filters.get("bank_account"), "account") @@ -352,6 +354,7 @@ def journal_entry_query(doctype, txt, searchfield, start, page_len, filters): } ) +@frappe.whitelist() def sales_invoices_query(doctype, txt, searchfield, start, page_len, filters): return frappe.db.sql(""" SELECT diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py index 56af4d90279..4b852300e5f 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py @@ -206,6 +206,7 @@ def get_list_context(context=None): }) return list_context +@frappe.whitelist() def get_supplier_contacts(doctype, txt, searchfield, start, page_len, filters): return frappe.db.sql("""select `tabContact`.name from `tabContact`, `tabDynamic Link` where `tabDynamic Link`.link_doctype = 'Supplier' and (`tabDynamic Link`.link_name=%(name)s diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 7d31a1cd15e..256c95792f2 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -910,6 +910,7 @@ def get_bom_diff(bom1, bom2): return out +@frappe.whitelist() def item_query(doctype, txt, searchfield, start, page_len, filters): meta = frappe.get_meta("Item", cached=True) searchfields = meta.get_search_fields() @@ -989,4 +990,4 @@ def make_variant_bom(source_name, bom_no, item, variant_items, target_doc=None): }, }, target_doc, postprocess) - return doc \ No newline at end of file + return doc diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index e2233a3e2f6..f962a1157b3 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -631,6 +631,7 @@ class WorkOrder(Document): bom.set_bom_material_details() return bom +@frappe.whitelist() def get_bom_operations(doctype, txt, searchfield, start, page_len, filters): if txt: filters['operation'] = ('like', '%%%s%%' % txt) diff --git a/erpnext/manufacturing/report/bom_variance_report/bom_variance_report.py b/erpnext/manufacturing/report/bom_variance_report/bom_variance_report.py index c5627e0c087..e3e440ebc62 100644 --- a/erpnext/manufacturing/report/bom_variance_report/bom_variance_report.py +++ b/erpnext/manufacturing/report/bom_variance_report/bom_variance_report.py @@ -19,7 +19,7 @@ def get_columns(filters): "options": "Work Order", "width": 120 }] - + if not filters.get('bom_no'): columns.extend([ { diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py index e6bb708e418..ad9b6d86c89 100644 --- a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py +++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py @@ -539,6 +539,7 @@ def submit_salary_slips_for_employees(payroll_entry, salary_slips, publish_progr if not_submitted_ss: frappe.msgprint(_("Could not submit some Salary Slips")) +@frappe.whitelist() def get_payroll_entries_for_jv(doctype, txt, searchfield, start, page_len, filters): return frappe.db.sql(""" select name from `tabPayroll Entry` diff --git a/erpnext/projects/doctype/project/project.py b/erpnext/projects/doctype/project/project.py index afdb5b7a01d..32ea05b42ad 100644 --- a/erpnext/projects/doctype/project/project.py +++ b/erpnext/projects/doctype/project/project.py @@ -238,6 +238,7 @@ def get_list_context(context=None): "row_template": "templates/includes/projects/project_row.html" } +@frappe.whitelist() def get_users_for_project(doctype, txt, searchfield, start, page_len, filters): conditions = [] return frappe.db.sql("""select name, concat_ws(' ', first_name, middle_name, last_name) diff --git a/erpnext/projects/doctype/task/task.py b/erpnext/projects/doctype/task/task.py index 1cb2c50cbf8..845cdba8bfa 100755 --- a/erpnext/projects/doctype/task/task.py +++ b/erpnext/projects/doctype/task/task.py @@ -188,6 +188,7 @@ def check_if_child_exists(name): return child_tasks +@frappe.whitelist() def get_project(doctype, txt, searchfield, start, page_len, filters): from erpnext.controllers.queries import get_match_cond return frappe.db.sql(""" select name from `tabProject` diff --git a/erpnext/restaurant/doctype/restaurant_order_entry/restaurant_order_entry.py b/erpnext/restaurant/doctype/restaurant_order_entry/restaurant_order_entry.py index a748f9a0075..357deaac007 100644 --- a/erpnext/restaurant/doctype/restaurant_order_entry/restaurant_order_entry.py +++ b/erpnext/restaurant/doctype/restaurant_order_entry/restaurant_order_entry.py @@ -65,6 +65,7 @@ def make_invoice(table, customer, mode_of_payment): return invoice.name +@frappe.whitelist() def item_query_restaurant(doctype='Item', txt='', searchfield='name', start=0, page_len=20, filters=None, as_dict=False): '''Return items that are selected in active menu of the restaurant''' restaurant, menu = get_restaurant_and_menu_name(filters['table']) @@ -84,4 +85,4 @@ def get_restaurant_and_menu_name(table): if not menu: frappe.throw(_('Please set an active menu for Restaurant {0}').format(restaurant)) - return restaurant, menu \ No newline at end of file + return restaurant, menu diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index d70c64fce49..26bfea0a0e6 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -542,6 +542,7 @@ def make_address(args, is_primary_address=1): return address +@frappe.whitelist() def get_customer_primary_contact(doctype, txt, searchfield, start, page_len, filters): customer = filters.get('customer') return frappe.db.sql(""" diff --git a/erpnext/selling/doctype/product_bundle/product_bundle.py b/erpnext/selling/doctype/product_bundle/product_bundle.py index c8a71677f93..e04228b66ad 100644 --- a/erpnext/selling/doctype/product_bundle/product_bundle.py +++ b/erpnext/selling/doctype/product_bundle/product_bundle.py @@ -22,12 +22,13 @@ class ProductBundle(Document): """Validates, main Item is not a stock item""" if frappe.db.get_value("Item", self.new_item_code, "is_stock_item"): frappe.throw(_("Parent Item {0} must not be a Stock Item").format(self.new_item_code)) - + def validate_child_items(self): for item in self.items: if frappe.db.exists("Product Bundle", item.item_code): frappe.throw(_("Child Item should not be a Product Bundle. Please remove item `{0}` and save").format(item.item_code)) - + +@frappe.whitelist() def get_new_item_code(doctype, txt, searchfield, start, page_len, filters): from erpnext.controllers.queries import get_match_cond diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.py b/erpnext/selling/page/point_of_sale/point_of_sale.py index dfa0f7f2dbf..1ae1fde588d 100644 --- a/erpnext/selling/page/point_of_sale/point_of_sale.py +++ b/erpnext/selling/page/point_of_sale/point_of_sale.py @@ -167,6 +167,7 @@ def get_item_group_condition(pos_profile): return cond % tuple(item_groups) +@frappe.whitelist() def item_group_query(doctype, txt, searchfield, start, page_len, filters): item_groups = [] cond = "1=1" @@ -187,4 +188,4 @@ def item_group_query(doctype, txt, searchfield, start, page_len, filters): @frappe.whitelist() def get_pos_fields(): return frappe.get_all("POS Field", fields=["label", "fieldname", - "fieldtype", "default_value", "reqd", "read_only", "options"]) \ No newline at end of file + "fieldtype", "default_value", "reqd", "read_only", "options"]) diff --git a/erpnext/stock/doctype/item_alternative/item_alternative.py b/erpnext/stock/doctype/item_alternative/item_alternative.py index da0c3b7f1e4..522dfc67a9c 100644 --- a/erpnext/stock/doctype/item_alternative/item_alternative.py +++ b/erpnext/stock/doctype/item_alternative/item_alternative.py @@ -42,6 +42,7 @@ class ItemAlternative(Document): 'alternative_item_code': self.alternative_item_code, 'name': ('!=', self.name)}): frappe.throw(_("Already record exists for the item {0}").format(self.item_code)) +@frappe.whitelist() def get_alternative_items(doctype, txt, searchfield, start, page_len, filters): return frappe.db.sql(""" (select alternative_item_code from `tabItem Alternative` where item_code = %(item_code)s and alternative_item_code like %(txt)s) @@ -52,4 +53,4 @@ def get_alternative_items(doctype, txt, searchfield, start, page_len, filters): """.format(start, page_len), { "item_code": filters.get('item_code'), "txt": '%' + txt + '%' - }) \ No newline at end of file + }) diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py index 97606f4e3a0..25f1ed95058 100644 --- a/erpnext/stock/doctype/material_request/material_request.py +++ b/erpnext/stock/doctype/material_request/material_request.py @@ -402,6 +402,7 @@ def get_material_requests_based_on_supplier(doctype, txt, searchfield, start, pa return material_requests +@frappe.whitelist() def get_default_supplier_query(doctype, txt, searchfield, start, page_len, filters): doc = frappe.get_doc("Material Request", filters.get("doc")) item_list = [] @@ -567,4 +568,4 @@ def create_pick_list(source_name, target_doc=None): doc.set_item_locations() - return doc \ No newline at end of file + return doc diff --git a/erpnext/stock/doctype/packing_slip/packing_slip.py b/erpnext/stock/doctype/packing_slip/packing_slip.py index 7a5ae317c2b..4f831d7a858 100644 --- a/erpnext/stock/doctype/packing_slip/packing_slip.py +++ b/erpnext/stock/doctype/packing_slip/packing_slip.py @@ -175,6 +175,7 @@ class PackingSlip(Document): self.update_item_details() +@frappe.whitelist() def item_details(doctype, txt, searchfield, start, page_len, filters): from erpnext.controllers.queries import get_match_cond return frappe.db.sql("""select name, item_name, description from `tabItem` diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.py b/erpnext/stock/doctype/quality_inspection/quality_inspection.py index 37ab807cb7b..7c54fd03f91 100644 --- a/erpnext/stock/doctype/quality_inspection/quality_inspection.py +++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.py @@ -58,6 +58,7 @@ class QualityInspection(Document): .format(parent_doc=self.reference_type, child_doc=doctype), (quality_inspection, self.modified, self.reference_name, self.item_code)) +@frappe.whitelist() def item_query(doctype, txt, searchfield, start, page_len, filters): if filters.get("from"): from frappe.desk.reportview import get_match_cond @@ -118,4 +119,4 @@ def make_quality_inspection(source_name, target_doc=None): } }, target_doc, postprocess) - return doc \ No newline at end of file + return doc From 49d04f449e06f6a4eb430eab642d1021f21d3af7 Mon Sep 17 00:00:00 2001 From: Khushal Trivedi Date: Thu, 2 Jul 2020 14:51:20 +0530 Subject: [PATCH 512/608] fix: no else condition required since we can decalre it by default (#22537) * fix: removing unnecessary else condition on customer form * fix: removing unnecessary else condition on customer form * fix: no else condition required since we can decalre it by default Co-authored-by: Khushal Trivedi --- erpnext/selling/doctype/customer/customer.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index d70c64fce49..e5c7c3c1fc1 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -342,11 +342,10 @@ def get_loyalty_programs(doc): @frappe.whitelist() def get_customer_list(doctype, txt, searchfield, start, page_len, filters=None): from erpnext.controllers.queries import get_fields + fields = ["name", "customer_name", "customer_group", "territory"] if frappe.db.get_default("cust_master_name") == "Customer Name": fields = ["name", "customer_group", "territory"] - else: - fields = ["name", "customer_name", "customer_group", "territory"] fields = get_fields("Customer", fields) @@ -552,4 +551,4 @@ def get_customer_primary_contact(doctype, txt, searchfield, start, page_len, fil """, { 'customer': customer, 'txt': '%%%s%%' % txt - }) + }) \ No newline at end of file From bfffd9b01b1aeb05ea62edf72bdcfe0e418af722 Mon Sep 17 00:00:00 2001 From: Afshan <33727827+AfshanKhan@users.noreply.github.com> Date: Thu, 2 Jul 2020 15:11:45 +0530 Subject: [PATCH 513/608] made "Subscription Section", "Auto Repeat" and Hub Publishing" collapsible by default (#22521) Co-authored-by: Marica --- .../doctype/sales_invoice/sales_invoice.json | 399 +++++++++++++++++- .../doctype/sales_order/sales_order.json | 287 ++++++++++++- erpnext/stock/doctype/item/item.json | 5 +- 3 files changed, 655 insertions(+), 36 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index 63c34ed2056..02b42065449 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -189,6 +189,8 @@ { "fieldname": "customer_section", "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1, "options": "fa fa-user" }, { @@ -197,6 +199,8 @@ "fieldname": "title", "fieldtype": "Data", "hidden": 1, + "hide_days": 1, + "hide_seconds": 1, "label": "Title", "no_copy": 1, "print_hide": 1 @@ -205,6 +209,8 @@ "bold": 1, "fieldname": "naming_series", "fieldtype": "Select", + "hide_days": 1, + "hide_seconds": 1, "label": "Series", "no_copy": 1, "oldfieldname": "naming_series", @@ -218,6 +224,8 @@ "bold": 1, "fieldname": "customer", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "in_standard_filter": 1, "label": "Customer", "oldfieldname": "customer", @@ -232,6 +240,8 @@ "fetch_from": "customer.customer_name", "fieldname": "customer_name", "fieldtype": "Data", + "hide_days": 1, + "hide_seconds": 1, "in_global_search": 1, "label": "Customer Name", "oldfieldname": "customer_name", @@ -241,6 +251,8 @@ { "fieldname": "tax_id", "fieldtype": "Data", + "hide_days": 1, + "hide_seconds": 1, "label": "Tax Id", "print_hide": 1, "read_only": 1 @@ -248,6 +260,8 @@ { "fieldname": "project", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "in_global_search": 1, "label": "Project", "oldfieldname": "project_name", @@ -259,6 +273,8 @@ "default": "0", "fieldname": "is_pos", "fieldtype": "Check", + "hide_days": 1, + "hide_seconds": 1, "label": "Include Payment (POS)", "oldfieldname": "is_pos", "oldfieldtype": "Check", @@ -268,6 +284,8 @@ "depends_on": "is_pos", "fieldname": "pos_profile", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "POS Profile", "options": "POS Profile", "print_hide": 1 @@ -276,6 +294,8 @@ "fieldname": "offline_pos_name", "fieldtype": "Data", "hidden": 1, + "hide_days": 1, + "hide_seconds": 1, "label": "Offline POS Name", "print_hide": 1, "read_only": 1 @@ -284,6 +304,8 @@ "default": "0", "fieldname": "is_return", "fieldtype": "Check", + "hide_days": 1, + "hide_seconds": 1, "label": "Is Return (Credit Note)", "no_copy": 1, "print_hide": 1 @@ -291,11 +313,15 @@ { "fieldname": "column_break1", "fieldtype": "Column Break", + "hide_days": 1, + "hide_seconds": 1, "oldfieldtype": "Column Break" }, { "fieldname": "company", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "in_standard_filter": 1, "label": "Company", "oldfieldname": "company", @@ -308,6 +334,8 @@ { "fieldname": "cost_center", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "Cost Center", "options": "Cost Center" }, @@ -316,6 +344,8 @@ "default": "Today", "fieldname": "posting_date", "fieldtype": "Date", + "hide_days": 1, + "hide_seconds": 1, "label": "Date", "no_copy": 1, "oldfieldname": "posting_date", @@ -326,6 +356,8 @@ { "fieldname": "posting_time", "fieldtype": "Time", + "hide_days": 1, + "hide_seconds": 1, "label": "Posting Time", "no_copy": 1, "oldfieldname": "posting_time", @@ -337,12 +369,16 @@ "depends_on": "eval:doc.docstatus==0", "fieldname": "set_posting_time", "fieldtype": "Check", + "hide_days": 1, + "hide_seconds": 1, "label": "Edit Posting Date and Time", "print_hide": 1 }, { "fieldname": "due_date", "fieldtype": "Date", + "hide_days": 1, + "hide_seconds": 1, "label": "Payment Due Date", "no_copy": 1, "oldfieldname": "due_date", @@ -351,6 +387,8 @@ { "fieldname": "amended_from", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "ignore_user_permissions": 1, "label": "Amended From", "no_copy": 1, @@ -364,12 +402,16 @@ "depends_on": "return_against", "fieldname": "returns", "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1, "label": "Returns" }, { "depends_on": "return_against", "fieldname": "return_against", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "Return Against Sales Invoice", "no_copy": 1, "options": "Sales Invoice", @@ -379,13 +421,17 @@ }, { "fieldname": "column_break_21", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "hide_days": 1, + "hide_seconds": 1 }, { "default": "0", "depends_on": "eval: doc.is_return && doc.return_against", "fieldname": "update_billed_amount_in_sales_order", "fieldtype": "Check", + "hide_days": 1, + "hide_seconds": 1, "label": "Update Billed Amount in Sales Order" }, { @@ -393,35 +439,47 @@ "collapsible_depends_on": "po_no", "fieldname": "customer_po_details", "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1, "label": "Customer PO Details" }, { "allow_on_submit": 1, "fieldname": "po_no", "fieldtype": "Small Text", + "hide_days": 1, + "hide_seconds": 1, "label": "Customer's Purchase Order", "no_copy": 1, "print_hide": 1 }, { "fieldname": "column_break_23", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "hide_days": 1, + "hide_seconds": 1 }, { "allow_on_submit": 1, "fieldname": "po_date", "fieldtype": "Date", + "hide_days": 1, + "hide_seconds": 1, "label": "Customer's Purchase Order Date" }, { "collapsible": 1, "fieldname": "address_and_contact", "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1, "label": "Address and Contact" }, { "fieldname": "customer_address", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "Customer Address", "options": "Address", "print_hide": 1 @@ -429,12 +487,16 @@ { "fieldname": "address_display", "fieldtype": "Small Text", + "hide_days": 1, + "hide_seconds": 1, "label": "Address", "read_only": 1 }, { "fieldname": "contact_person", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "in_global_search": 1, "label": "Contact Person", "options": "Contact", @@ -443,6 +505,8 @@ { "fieldname": "contact_display", "fieldtype": "Small Text", + "hide_days": 1, + "hide_seconds": 1, "label": "Contact", "read_only": 1 }, @@ -450,6 +514,8 @@ "fieldname": "contact_mobile", "fieldtype": "Small Text", "hidden": 1, + "hide_days": 1, + "hide_seconds": 1, "label": "Mobile No", "read_only": 1 }, @@ -457,6 +523,8 @@ "fieldname": "contact_email", "fieldtype": "Data", "hidden": 1, + "hide_days": 1, + "hide_seconds": 1, "label": "Contact Email", "options": "Email", "print_hide": 1, @@ -465,17 +533,23 @@ { "fieldname": "territory", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "Territory", "options": "Territory", "print_hide": 1 }, { "fieldname": "col_break4", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "hide_days": 1, + "hide_seconds": 1 }, { "fieldname": "shipping_address_name", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "Shipping Address Name", "options": "Address", "print_hide": 1 @@ -483,6 +557,8 @@ { "fieldname": "shipping_address", "fieldtype": "Small Text", + "hide_days": 1, + "hide_seconds": 1, "label": "Shipping Address", "print_hide": 1, "read_only": 1 @@ -490,6 +566,8 @@ { "fieldname": "company_address", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "Company Address Name", "options": "Address", "print_hide": 1 @@ -498,6 +576,8 @@ "fieldname": "company_address_display", "fieldtype": "Small Text", "hidden": 1, + "hide_days": 1, + "hide_seconds": 1, "label": "Company Address", "print_hide": 1, "read_only": 1 @@ -507,11 +587,15 @@ "depends_on": "customer", "fieldname": "currency_and_price_list", "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1, "label": "Currency and Price List" }, { "fieldname": "currency", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "Currency", "oldfieldname": "currency", "oldfieldtype": "Select", @@ -523,6 +607,8 @@ "description": "Rate at which Customer Currency is converted to customer's base currency", "fieldname": "conversion_rate", "fieldtype": "Float", + "hide_days": 1, + "hide_seconds": 1, "label": "Exchange Rate", "oldfieldname": "conversion_rate", "oldfieldtype": "Currency", @@ -533,11 +619,15 @@ { "fieldname": "column_break2", "fieldtype": "Column Break", + "hide_days": 1, + "hide_seconds": 1, "width": "50%" }, { "fieldname": "selling_price_list", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "Price List", "oldfieldname": "price_list_name", "oldfieldtype": "Select", @@ -548,6 +638,8 @@ { "fieldname": "price_list_currency", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "Price List Currency", "options": "Currency", "print_hide": 1, @@ -558,6 +650,8 @@ "description": "Rate at which Price list currency is converted to customer's base currency", "fieldname": "plc_conversion_rate", "fieldtype": "Float", + "hide_days": 1, + "hide_seconds": 1, "label": "Price List Exchange Rate", "precision": "9", "print_hide": 1, @@ -567,6 +661,8 @@ "default": "0", "fieldname": "ignore_pricing_rule", "fieldtype": "Check", + "hide_days": 1, + "hide_seconds": 1, "label": "Ignore Pricing Rule", "no_copy": 1, "permlevel": 1, @@ -574,12 +670,16 @@ }, { "fieldname": "sec_warehouse", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1 }, { "depends_on": "update_stock", "fieldname": "set_warehouse", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "Set Source Warehouse", "options": "Warehouse", "print_hide": 1 @@ -587,6 +687,8 @@ { "fieldname": "items_section", "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1, "oldfieldtype": "Section Break", "options": "fa fa-shopping-cart" }, @@ -594,6 +696,8 @@ "default": "0", "fieldname": "update_stock", "fieldtype": "Check", + "hide_days": 1, + "hide_seconds": 1, "label": "Update Stock", "oldfieldname": "update_stock", "oldfieldtype": "Check", @@ -602,12 +706,16 @@ { "fieldname": "scan_barcode", "fieldtype": "Data", + "hide_days": 1, + "hide_seconds": 1, "label": "Scan Barcode" }, { "allow_bulk_edit": 1, "fieldname": "items", "fieldtype": "Table", + "hide_days": 1, + "hide_seconds": 1, "label": "Items", "oldfieldname": "entries", "oldfieldtype": "Table", @@ -617,11 +725,15 @@ { "fieldname": "pricing_rule_details", "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1, "label": "Pricing Rules" }, { "fieldname": "pricing_rules", "fieldtype": "Table", + "hide_days": 1, + "hide_seconds": 1, "label": "Pricing Rule Detail", "options": "Pricing Rule Detail", "read_only": 1 @@ -629,6 +741,8 @@ { "fieldname": "packing_list", "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1, "label": "Packing List", "options": "fa fa-suitcase", "print_hide": 1 @@ -636,6 +750,8 @@ { "fieldname": "packed_items", "fieldtype": "Table", + "hide_days": 1, + "hide_seconds": 1, "label": "Packed Items", "options": "Packed Item", "print_hide": 1 @@ -643,6 +759,8 @@ { "fieldname": "product_bundle_help", "fieldtype": "HTML", + "hide_days": 1, + "hide_seconds": 1, "label": "Product Bundle Help", "print_hide": 1 }, @@ -651,11 +769,15 @@ "collapsible_depends_on": "eval:doc.total_billing_amount > 0", "fieldname": "time_sheet_list", "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1, "label": "Time Sheet List" }, { "fieldname": "timesheets", "fieldtype": "Table", + "hide_days": 1, + "hide_seconds": 1, "label": "Time Sheets", "options": "Sales Invoice Timesheet", "print_hide": 1 @@ -664,23 +786,31 @@ "default": "0", "fieldname": "total_billing_amount", "fieldtype": "Currency", + "hide_days": 1, + "hide_seconds": 1, "label": "Total Billing Amount", "print_hide": 1, "read_only": 1 }, { "fieldname": "section_break_30", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1 }, { "fieldname": "total_qty", "fieldtype": "Float", + "hide_days": 1, + "hide_seconds": 1, "label": "Total Quantity", "read_only": 1 }, { "fieldname": "base_total", "fieldtype": "Currency", + "hide_days": 1, + "hide_seconds": 1, "label": "Total (Company Currency)", "options": "Company:company:default_currency", "print_hide": 1, @@ -689,6 +819,8 @@ { "fieldname": "base_net_total", "fieldtype": "Currency", + "hide_days": 1, + "hide_seconds": 1, "label": "Net Total (Company Currency)", "oldfieldname": "net_total", "oldfieldtype": "Currency", @@ -699,11 +831,15 @@ }, { "fieldname": "column_break_32", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "hide_days": 1, + "hide_seconds": 1 }, { "fieldname": "total", "fieldtype": "Currency", + "hide_days": 1, + "hide_seconds": 1, "label": "Total", "options": "currency", "read_only": 1 @@ -711,6 +847,8 @@ { "fieldname": "net_total", "fieldtype": "Currency", + "hide_days": 1, + "hide_seconds": 1, "label": "Net Total", "options": "currency", "print_hide": 1, @@ -719,6 +857,8 @@ { "fieldname": "total_net_weight", "fieldtype": "Float", + "hide_days": 1, + "hide_seconds": 1, "label": "Total Net Weight", "print_hide": 1, "read_only": 1 @@ -726,12 +866,16 @@ { "fieldname": "taxes_section", "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1, "oldfieldtype": "Section Break", "options": "fa fa-money" }, { "fieldname": "taxes_and_charges", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "Sales Taxes and Charges Template", "oldfieldname": "charge", "oldfieldtype": "Link", @@ -740,11 +884,15 @@ }, { "fieldname": "column_break_38", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "hide_days": 1, + "hide_seconds": 1 }, { "fieldname": "shipping_rule", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "Shipping Rule", "oldfieldtype": "Button", "options": "Shipping Rule", @@ -753,17 +901,23 @@ { "fieldname": "tax_category", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "Tax Category", "options": "Tax Category", "print_hide": 1 }, { "fieldname": "section_break_40", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1 }, { "fieldname": "taxes", "fieldtype": "Table", + "hide_days": 1, + "hide_seconds": 1, "label": "Sales Taxes and Charges", "oldfieldname": "other_charges", "oldfieldtype": "Table", @@ -773,11 +927,15 @@ "collapsible": 1, "fieldname": "sec_tax_breakup", "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1, "label": "Tax Breakup" }, { "fieldname": "other_charges_calculation", "fieldtype": "Long Text", + "hide_days": 1, + "hide_seconds": 1, "label": "Taxes and Charges Calculation", "no_copy": 1, "oldfieldtype": "HTML", @@ -786,11 +944,15 @@ }, { "fieldname": "section_break_43", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1 }, { "fieldname": "base_total_taxes_and_charges", "fieldtype": "Currency", + "hide_days": 1, + "hide_seconds": 1, "label": "Total Taxes and Charges (Company Currency)", "oldfieldname": "other_charges_total", "oldfieldtype": "Currency", @@ -800,11 +962,15 @@ }, { "fieldname": "column_break_47", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "hide_days": 1, + "hide_seconds": 1 }, { "fieldname": "total_taxes_and_charges", "fieldtype": "Currency", + "hide_days": 1, + "hide_seconds": 1, "label": "Total Taxes and Charges", "options": "currency", "print_hide": 1, @@ -814,12 +980,16 @@ "collapsible": 1, "fieldname": "loyalty_points_redemption", "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1, "label": "Loyalty Points Redemption" }, { "depends_on": "redeem_loyalty_points", "fieldname": "loyalty_points", "fieldtype": "Int", + "hide_days": 1, + "hide_seconds": 1, "label": "Loyalty Points", "no_copy": 1, "print_hide": 1 @@ -828,6 +998,8 @@ "depends_on": "redeem_loyalty_points", "fieldname": "loyalty_amount", "fieldtype": "Currency", + "hide_days": 1, + "hide_seconds": 1, "label": "Loyalty Amount", "no_copy": 1, "options": "Company:company:default_currency", @@ -838,18 +1010,24 @@ "default": "0", "fieldname": "redeem_loyalty_points", "fieldtype": "Check", + "hide_days": 1, + "hide_seconds": 1, "label": "Redeem Loyalty Points", "no_copy": 1, "print_hide": 1 }, { "fieldname": "column_break_77", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "hide_days": 1, + "hide_seconds": 1 }, { "fetch_from": "customer.loyalty_program", "fieldname": "loyalty_program", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "Loyalty Program", "no_copy": 1, "options": "Loyalty Program", @@ -860,6 +1038,8 @@ "depends_on": "redeem_loyalty_points", "fieldname": "loyalty_redemption_account", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "Redemption Account", "no_copy": 1, "options": "Account" @@ -868,6 +1048,8 @@ "depends_on": "redeem_loyalty_points", "fieldname": "loyalty_redemption_cost_center", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "Redemption Cost Center", "no_copy": 1, "options": "Cost Center" @@ -877,12 +1059,16 @@ "collapsible_depends_on": "discount_amount", "fieldname": "section_break_49", "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1, "label": "Additional Discount" }, { "default": "Grand Total", "fieldname": "apply_discount_on", "fieldtype": "Select", + "hide_days": 1, + "hide_seconds": 1, "label": "Apply Additional Discount On", "options": "\nGrand Total\nNet Total", "print_hide": 1 @@ -890,6 +1076,8 @@ { "fieldname": "base_discount_amount", "fieldtype": "Currency", + "hide_days": 1, + "hide_seconds": 1, "label": "Additional Discount Amount (Company Currency)", "options": "Company:company:default_currency", "print_hide": 1, @@ -897,17 +1085,23 @@ }, { "fieldname": "column_break_51", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "hide_days": 1, + "hide_seconds": 1 }, { "fieldname": "additional_discount_percentage", "fieldtype": "Float", + "hide_days": 1, + "hide_seconds": 1, "label": "Additional Discount Percentage", "print_hide": 1 }, { "fieldname": "discount_amount", "fieldtype": "Currency", + "hide_days": 1, + "hide_seconds": 1, "label": "Additional Discount Amount", "options": "currency", "print_hide": 1 @@ -915,6 +1109,8 @@ { "fieldname": "totals", "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1, "oldfieldtype": "Section Break", "options": "fa fa-money", "print_hide": 1 @@ -922,6 +1118,8 @@ { "fieldname": "base_grand_total", "fieldtype": "Currency", + "hide_days": 1, + "hide_seconds": 1, "label": "Grand Total (Company Currency)", "oldfieldname": "grand_total", "oldfieldtype": "Currency", @@ -933,6 +1131,8 @@ { "fieldname": "base_rounding_adjustment", "fieldtype": "Currency", + "hide_days": 1, + "hide_seconds": 1, "label": "Rounding Adjustment (Company Currency)", "no_copy": 1, "options": "Company:company:default_currency", @@ -942,6 +1142,8 @@ { "fieldname": "base_rounded_total", "fieldtype": "Currency", + "hide_days": 1, + "hide_seconds": 1, "label": "Rounded Total (Company Currency)", "oldfieldname": "rounded_total", "oldfieldtype": "Currency", @@ -953,6 +1155,8 @@ "description": "In Words will be visible once you save the Sales Invoice.", "fieldname": "base_in_words", "fieldtype": "Data", + "hide_days": 1, + "hide_seconds": 1, "label": "In Words (Company Currency)", "oldfieldname": "in_words", "oldfieldtype": "Data", @@ -962,6 +1166,8 @@ { "fieldname": "column_break5", "fieldtype": "Column Break", + "hide_days": 1, + "hide_seconds": 1, "oldfieldtype": "Column Break", "print_hide": 1, "width": "50%" @@ -970,6 +1176,8 @@ "bold": 1, "fieldname": "grand_total", "fieldtype": "Currency", + "hide_days": 1, + "hide_seconds": 1, "in_list_view": 1, "label": "Grand Total", "oldfieldname": "grand_total_export", @@ -981,6 +1189,8 @@ { "fieldname": "rounding_adjustment", "fieldtype": "Currency", + "hide_days": 1, + "hide_seconds": 1, "label": "Rounding Adjustment", "no_copy": 1, "options": "currency", @@ -991,6 +1201,8 @@ "bold": 1, "fieldname": "rounded_total", "fieldtype": "Currency", + "hide_days": 1, + "hide_seconds": 1, "label": "Rounded Total", "oldfieldname": "rounded_total_export", "oldfieldtype": "Currency", @@ -1000,6 +1212,8 @@ { "fieldname": "in_words", "fieldtype": "Data", + "hide_days": 1, + "hide_seconds": 1, "label": "In Words", "oldfieldname": "in_words_export", "oldfieldtype": "Data", @@ -1009,6 +1223,8 @@ { "fieldname": "total_advance", "fieldtype": "Currency", + "hide_days": 1, + "hide_seconds": 1, "label": "Total Advance", "oldfieldname": "total_advance", "oldfieldtype": "Currency", @@ -1019,6 +1235,8 @@ { "fieldname": "outstanding_amount", "fieldtype": "Currency", + "hide_days": 1, + "hide_seconds": 1, "label": "Outstanding Amount", "no_copy": 1, "oldfieldname": "outstanding_amount", @@ -1032,6 +1250,8 @@ "collapsible_depends_on": "advances", "fieldname": "advances_section", "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1, "label": "Advance Payments", "oldfieldtype": "Section Break", "options": "fa fa-money", @@ -1041,18 +1261,24 @@ "default": "0", "fieldname": "allocate_advances_automatically", "fieldtype": "Check", + "hide_days": 1, + "hide_seconds": 1, "label": "Allocate Advances Automatically (FIFO)" }, { "depends_on": "eval:!doc.allocate_advances_automatically", "fieldname": "get_advances", "fieldtype": "Button", + "hide_days": 1, + "hide_seconds": 1, "label": "Get Advances Received", "options": "set_advances" }, { "fieldname": "advances", "fieldtype": "Table", + "hide_days": 1, + "hide_seconds": 1, "label": "Advances", "oldfieldname": "advance_adjustment_details", "oldfieldtype": "Table", @@ -1064,12 +1290,16 @@ "collapsible_depends_on": "eval:(!doc.is_pos && !doc.is_return)", "fieldname": "payment_schedule_section", "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1, "label": "Payment Terms" }, { "depends_on": "eval:(!doc.is_pos && !doc.is_return)", "fieldname": "payment_terms_template", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "Payment Terms Template", "no_copy": 1, "options": "Payment Terms Template", @@ -1079,6 +1309,8 @@ "depends_on": "eval:(!doc.is_pos && !doc.is_return)", "fieldname": "payment_schedule", "fieldtype": "Table", + "hide_days": 1, + "hide_seconds": 1, "label": "Payment Schedule", "no_copy": 1, "options": "Payment Schedule", @@ -1088,6 +1320,8 @@ "depends_on": "eval:doc.is_pos===1||(doc.advances && doc.advances.length>0)", "fieldname": "payments_section", "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1, "label": "Payments", "options": "fa fa-money" }, @@ -1096,6 +1330,8 @@ "fieldname": "cash_bank_account", "fieldtype": "Link", "hidden": 1, + "hide_days": 1, + "hide_seconds": 1, "label": "Cash/Bank Account", "oldfieldname": "cash_bank_account", "oldfieldtype": "Link", @@ -1106,17 +1342,23 @@ "depends_on": "eval:doc.is_pos===1", "fieldname": "payments", "fieldtype": "Table", + "hide_days": 1, + "hide_seconds": 1, "label": "Sales Invoice Payment", "options": "Sales Invoice Payment", "print_hide": 1 }, { "fieldname": "section_break_84", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1 }, { "fieldname": "base_paid_amount", "fieldtype": "Currency", + "hide_days": 1, + "hide_seconds": 1, "label": "Paid Amount (Company Currency)", "no_copy": 1, "options": "Company:company:default_currency", @@ -1125,12 +1367,16 @@ }, { "fieldname": "column_break_86", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "hide_days": 1, + "hide_seconds": 1 }, { "depends_on": "eval: doc.is_pos || doc.redeem_loyalty_points", "fieldname": "paid_amount", "fieldtype": "Currency", + "hide_days": 1, + "hide_seconds": 1, "label": "Paid Amount", "no_copy": 1, "oldfieldname": "paid_amount", @@ -1141,12 +1387,16 @@ }, { "fieldname": "section_break_88", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1 }, { "depends_on": "is_pos", "fieldname": "base_change_amount", "fieldtype": "Currency", + "hide_days": 1, + "hide_seconds": 1, "label": "Base Change Amount (Company Currency)", "no_copy": 1, "options": "Company:company:default_currency", @@ -1155,12 +1405,16 @@ }, { "fieldname": "column_break_90", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "hide_days": 1, + "hide_seconds": 1 }, { "depends_on": "is_pos", "fieldname": "change_amount", "fieldtype": "Currency", + "hide_days": 1, + "hide_seconds": 1, "label": "Change Amount", "no_copy": 1, "options": "currency", @@ -1170,6 +1424,8 @@ "depends_on": "is_pos", "fieldname": "account_for_change_amount", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "Account for Change Amount", "options": "Account", "print_hide": 1 @@ -1180,12 +1436,16 @@ "depends_on": "grand_total", "fieldname": "column_break4", "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1, "label": "Write Off", "width": "50%" }, { "fieldname": "write_off_amount", "fieldtype": "Currency", + "hide_days": 1, + "hide_seconds": 1, "label": "Write Off Amount", "no_copy": 1, "options": "currency", @@ -1194,6 +1454,8 @@ { "fieldname": "base_write_off_amount", "fieldtype": "Currency", + "hide_days": 1, + "hide_seconds": 1, "label": "Write Off Amount (Company Currency)", "no_copy": 1, "options": "Company:company:default_currency", @@ -1205,16 +1467,22 @@ "depends_on": "is_pos", "fieldname": "write_off_outstanding_amount_automatically", "fieldtype": "Check", + "hide_days": 1, + "hide_seconds": 1, "label": "Write Off Outstanding Amount", "print_hide": 1 }, { "fieldname": "column_break_74", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "hide_days": 1, + "hide_seconds": 1 }, { "fieldname": "write_off_account", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "Write Off Account", "options": "Account", "print_hide": 1 @@ -1222,6 +1490,8 @@ { "fieldname": "write_off_cost_center", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "Write Off Cost Center", "options": "Cost Center", "print_hide": 1 @@ -1231,12 +1501,16 @@ "collapsible_depends_on": "terms", "fieldname": "terms_section_break", "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1, "label": "Terms and Conditions", "oldfieldtype": "Section Break" }, { "fieldname": "tc_name", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "Terms", "oldfieldname": "tc_name", "oldfieldtype": "Link", @@ -1246,6 +1520,8 @@ { "fieldname": "terms", "fieldtype": "Text Editor", + "hide_days": 1, + "hide_seconds": 1, "label": "Terms and Conditions Details", "oldfieldname": "terms", "oldfieldtype": "Text Editor" @@ -1254,12 +1530,16 @@ "collapsible": 1, "fieldname": "edit_printing_settings", "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1, "label": "Printing Settings" }, { "allow_on_submit": 1, "fieldname": "letter_head", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "Letter Head", "oldfieldname": "letter_head", "oldfieldtype": "Select", @@ -1271,24 +1551,32 @@ "default": "0", "fieldname": "group_same_items", "fieldtype": "Check", + "hide_days": 1, + "hide_seconds": 1, "label": "Group same items", "print_hide": 1 }, { "fieldname": "language", "fieldtype": "Data", + "hide_days": 1, + "hide_seconds": 1, "label": "Print Language", "print_hide": 1, "read_only": 1 }, { "fieldname": "column_break_84", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "hide_days": 1, + "hide_seconds": 1 }, { "allow_on_submit": 1, "fieldname": "select_print_heading", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "Print Heading", "no_copy": 1, "oldfieldname": "select_print_heading", @@ -1302,11 +1590,15 @@ "depends_on": "customer", "fieldname": "more_information", "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1, "label": "More Information" }, { "fieldname": "inter_company_invoice_reference", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "Inter Company Invoice Reference", "options": "Purchase Invoice", "read_only": 1 @@ -1315,6 +1607,8 @@ "fieldname": "customer_group", "fieldtype": "Link", "hidden": 1, + "hide_days": 1, + "hide_seconds": 1, "label": "Customer Group", "options": "Customer Group", "print_hide": 1 @@ -1322,6 +1616,8 @@ { "fieldname": "campaign", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "Campaign", "oldfieldname": "campaign", "oldfieldtype": "Link", @@ -1332,6 +1628,8 @@ "default": "0", "fieldname": "is_discounted", "fieldtype": "Check", + "hide_days": 1, + "hide_seconds": 1, "label": "Is Discounted", "no_copy": 1, "read_only": 1 @@ -1339,12 +1637,16 @@ { "fieldname": "col_break23", "fieldtype": "Column Break", + "hide_days": 1, + "hide_seconds": 1, "width": "50%" }, { "default": "Draft", "fieldname": "status", "fieldtype": "Select", + "hide_days": 1, + "hide_seconds": 1, "in_standard_filter": 1, "label": "Status", "no_copy": 1, @@ -1355,6 +1657,8 @@ { "fieldname": "source", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "Source", "oldfieldname": "source", "oldfieldtype": "Select", @@ -1365,6 +1669,8 @@ "collapsible": 1, "fieldname": "more_info", "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1, "label": "Accounting Details", "oldfieldtype": "Section Break", "options": "fa fa-file-text", @@ -1373,6 +1679,8 @@ { "fieldname": "debit_to", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "Debit To", "oldfieldname": "debit_to", "oldfieldtype": "Link", @@ -1385,6 +1693,8 @@ "fieldname": "party_account_currency", "fieldtype": "Link", "hidden": 1, + "hide_days": 1, + "hide_seconds": 1, "label": "Party Account Currency", "no_copy": 1, "options": "Currency", @@ -1395,6 +1705,8 @@ "default": "No", "fieldname": "is_opening", "fieldtype": "Select", + "hide_days": 1, + "hide_seconds": 1, "label": "Is Opening Entry", "oldfieldname": "is_opening", "oldfieldtype": "Select", @@ -1404,6 +1716,8 @@ { "fieldname": "c_form_applicable", "fieldtype": "Select", + "hide_days": 1, + "hide_seconds": 1, "label": "C-Form Applicable", "no_copy": 1, "options": "No\nYes", @@ -1412,6 +1726,8 @@ { "fieldname": "c_form_no", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "C-Form No", "no_copy": 1, "options": "C-Form", @@ -1421,12 +1737,16 @@ { "fieldname": "column_break8", "fieldtype": "Column Break", + "hide_days": 1, + "hide_seconds": 1, "oldfieldtype": "Column Break", "print_hide": 1 }, { "fieldname": "remarks", "fieldtype": "Small Text", + "hide_days": 1, + "hide_seconds": 1, "label": "Remarks", "no_copy": 1, "oldfieldname": "remarks", @@ -1438,6 +1758,8 @@ "collapsible_depends_on": "sales_partner", "fieldname": "sales_team_section_break", "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1, "label": "Commission", "oldfieldtype": "Section Break", "options": "fa fa-group", @@ -1446,6 +1768,8 @@ { "fieldname": "sales_partner", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "Sales Partner", "oldfieldname": "sales_partner", "oldfieldtype": "Link", @@ -1455,6 +1779,8 @@ { "fieldname": "column_break10", "fieldtype": "Column Break", + "hide_days": 1, + "hide_seconds": 1, "oldfieldtype": "Column Break", "print_hide": 1, "width": "50%" @@ -1462,6 +1788,8 @@ { "fieldname": "commission_rate", "fieldtype": "Float", + "hide_days": 1, + "hide_seconds": 1, "label": "Commission Rate (%)", "oldfieldname": "commission_rate", "oldfieldtype": "Currency", @@ -1470,6 +1798,8 @@ { "fieldname": "total_commission", "fieldtype": "Currency", + "hide_days": 1, + "hide_seconds": 1, "label": "Total Commission", "oldfieldname": "total_commission", "oldfieldtype": "Currency", @@ -1481,6 +1811,8 @@ "collapsible_depends_on": "sales_team", "fieldname": "section_break2", "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1, "label": "Sales Team", "print_hide": 1 }, @@ -1488,6 +1820,8 @@ "allow_on_submit": 1, "fieldname": "sales_team", "fieldtype": "Table", + "hide_days": 1, + "hide_seconds": 1, "label": "Sales Team1", "oldfieldname": "sales_team", "oldfieldtype": "Table", @@ -1495,14 +1829,19 @@ "print_hide": 1 }, { + "collapsible": 1, "fieldname": "subscription_section", "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1, "label": "Subscription Section" }, { "allow_on_submit": 1, "fieldname": "from_date", "fieldtype": "Date", + "hide_days": 1, + "hide_seconds": 1, "label": "From Date", "no_copy": 1, "print_hide": 1 @@ -1511,18 +1850,24 @@ "allow_on_submit": 1, "fieldname": "to_date", "fieldtype": "Date", + "hide_days": 1, + "hide_seconds": 1, "label": "To Date", "no_copy": 1, "print_hide": 1 }, { "fieldname": "column_break_140", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "hide_days": 1, + "hide_seconds": 1 }, { "allow_on_submit": 1, "fieldname": "auto_repeat", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "Auto Repeat", "no_copy": 1, "options": "Auto Repeat", @@ -1534,12 +1879,16 @@ "depends_on": "eval: doc.auto_repeat", "fieldname": "update_auto_repeat_reference", "fieldtype": "Button", + "hide_days": 1, + "hide_seconds": 1, "label": "Update Auto Repeat Reference" }, { "fieldname": "against_income_account", "fieldtype": "Small Text", "hidden": 1, + "hide_days": 1, + "hide_seconds": 1, "label": "Against Income Account", "no_copy": 1, "oldfieldname": "against_income_account", @@ -1551,6 +1900,8 @@ "fieldname": "pos_total_qty", "fieldtype": "Float", "hidden": 1, + "hide_days": 1, + "hide_seconds": 1, "label": "Total Qty", "print_hide": 1, "print_hide_if_no_value": 1, @@ -1560,17 +1911,23 @@ "collapsible": 1, "fieldname": "accounting_dimensions_section", "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1, "label": "Accounting Dimensions" }, { "fieldname": "dimension_col_break", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "hide_days": 1, + "hide_seconds": 1 }, { "default": "0", "fetch_from": "customer.is_internal_customer", "fieldname": "is_internal_customer", "fieldtype": "Check", + "hide_days": 1, + "hide_seconds": 1, "label": "Is Internal Customer", "read_only": 1 } @@ -1579,7 +1936,7 @@ "idx": 181, "is_submittable": 1, "links": [], - "modified": "2020-05-19 17:00:57.208696", + "modified": "2020-06-30 12:00:03.890180", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice", diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json index b57c4f30981..cd4e1d07926 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.json +++ b/erpnext/selling/doctype/sales_order/sales_order.json @@ -143,11 +143,15 @@ { "fieldname": "customer_section", "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1, "options": "fa fa-user" }, { "fieldname": "column_break0", "fieldtype": "Column Break", + "hide_days": 1, + "hide_seconds": 1, "oldfieldtype": "Column Break", "width": "50%" }, @@ -157,6 +161,8 @@ "fieldname": "title", "fieldtype": "Data", "hidden": 1, + "hide_days": 1, + "hide_seconds": 1, "label": "Title", "no_copy": 1, "print_hide": 1 @@ -164,6 +170,8 @@ { "fieldname": "naming_series", "fieldtype": "Select", + "hide_days": 1, + "hide_seconds": 1, "label": "Series", "no_copy": 1, "oldfieldname": "naming_series", @@ -177,6 +185,8 @@ "bold": 1, "fieldname": "customer", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "in_global_search": 1, "in_standard_filter": 1, "label": "Customer", @@ -192,6 +202,8 @@ "fetch_from": "customer.customer_name", "fieldname": "customer_name", "fieldtype": "Data", + "hide_days": 1, + "hide_seconds": 1, "in_global_search": 1, "label": "Customer Name", "read_only": 1 @@ -200,6 +212,8 @@ "default": "Sales", "fieldname": "order_type", "fieldtype": "Select", + "hide_days": 1, + "hide_seconds": 1, "label": "Order Type", "oldfieldname": "order_type", "oldfieldtype": "Select", @@ -210,6 +224,8 @@ { "fieldname": "column_break1", "fieldtype": "Column Break", + "hide_days": 1, + "hide_seconds": 1, "oldfieldtype": "Column Break", "width": "50%" }, @@ -217,6 +233,8 @@ "fieldname": "amended_from", "fieldtype": "Link", "hidden": 1, + "hide_days": 1, + "hide_seconds": 1, "ignore_user_permissions": 1, "label": "Amended From", "no_copy": 1, @@ -230,6 +248,8 @@ { "fieldname": "company", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "in_standard_filter": 1, "label": "Company", "oldfieldname": "company", @@ -244,6 +264,8 @@ "default": "Today", "fieldname": "transaction_date", "fieldtype": "Date", + "hide_days": 1, + "hide_seconds": 1, "in_standard_filter": 1, "label": "Date", "no_copy": 1, @@ -258,6 +280,8 @@ "depends_on": "eval:!doc.skip_delivery_note", "fieldname": "delivery_date", "fieldtype": "Date", + "hide_days": 1, + "hide_seconds": 1, "in_list_view": 1, "label": "Delivery Date", "no_copy": 1 @@ -266,6 +290,8 @@ "allow_on_submit": 1, "fieldname": "po_no", "fieldtype": "Data", + "hide_days": 1, + "hide_seconds": 1, "label": "Customer's Purchase Order", "oldfieldname": "po_no", "oldfieldtype": "Data", @@ -276,6 +302,8 @@ "depends_on": "eval:doc.po_no", "fieldname": "po_date", "fieldtype": "Date", + "hide_days": 1, + "hide_seconds": 1, "label": "Customer's Purchase Order Date", "oldfieldname": "po_date", "oldfieldtype": "Date", @@ -285,6 +313,8 @@ "fetch_from": "customer.tax_id", "fieldname": "tax_id", "fieldtype": "Data", + "hide_days": 1, + "hide_seconds": 1, "label": "Tax Id", "read_only": 1, "width": "100px" @@ -294,6 +324,8 @@ "depends_on": "customer", "fieldname": "contact_info", "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1, "label": "Address and Contact", "options": "fa fa-bullhorn" }, @@ -301,6 +333,8 @@ "allow_on_submit": 1, "fieldname": "customer_address", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "Customer Address", "options": "Address", "print_hide": 1 @@ -309,12 +343,16 @@ "allow_on_submit": 1, "fieldname": "address_display", "fieldtype": "Small Text", + "hide_days": 1, + "hide_seconds": 1, "label": "Address", "read_only": 1 }, { "fieldname": "contact_person", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "Contact Person", "options": "Contact", "print_hide": 1 @@ -322,6 +360,8 @@ { "fieldname": "contact_display", "fieldtype": "Small Text", + "hide_days": 1, + "hide_seconds": 1, "in_global_search": 1, "label": "Contact", "read_only": 1 @@ -329,6 +369,8 @@ { "fieldname": "contact_mobile", "fieldtype": "Small Text", + "hide_days": 1, + "hide_seconds": 1, "label": "Mobile No", "read_only": 1 }, @@ -336,6 +378,8 @@ "fieldname": "contact_email", "fieldtype": "Data", "hidden": 1, + "hide_days": 1, + "hide_seconds": 1, "label": "Contact Email", "options": "Email", "print_hide": 1, @@ -344,24 +388,32 @@ { "fieldname": "company_address_display", "fieldtype": "Small Text", + "hide_days": 1, + "hide_seconds": 1, "label": "Company Address", "read_only": 1 }, { "fieldname": "company_address", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "Company Address Name", "options": "Address" }, { "fieldname": "col_break46", "fieldtype": "Column Break", + "hide_days": 1, + "hide_seconds": 1, "width": "50%" }, { "allow_on_submit": 1, "fieldname": "shipping_address_name", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "Shipping Address Name", "options": "Address", "print_hide": 1 @@ -370,6 +422,8 @@ "allow_on_submit": 1, "fieldname": "shipping_address", "fieldtype": "Small Text", + "hide_days": 1, + "hide_seconds": 1, "label": "Shipping Address", "print_hide": 1, "read_only": 1 @@ -378,6 +432,8 @@ "fieldname": "customer_group", "fieldtype": "Link", "hidden": 1, + "hide_days": 1, + "hide_seconds": 1, "label": "Customer Group", "options": "Customer Group", "print_hide": 1 @@ -385,6 +441,8 @@ { "fieldname": "territory", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "Territory", "options": "Territory", "print_hide": 1 @@ -393,6 +451,8 @@ "collapsible": 1, "fieldname": "currency_and_price_list", "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1, "label": "Currency and Price List", "options": "fa fa-tag", "print_hide": 1 @@ -400,6 +460,8 @@ { "fieldname": "currency", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "Currency", "oldfieldname": "currency", "oldfieldtype": "Select", @@ -412,6 +474,8 @@ "description": "Rate at which customer's currency is converted to company's base currency", "fieldname": "conversion_rate", "fieldtype": "Float", + "hide_days": 1, + "hide_seconds": 1, "label": "Exchange Rate", "oldfieldname": "conversion_rate", "oldfieldtype": "Currency", @@ -423,11 +487,15 @@ { "fieldname": "column_break2", "fieldtype": "Column Break", + "hide_days": 1, + "hide_seconds": 1, "width": "50%" }, { "fieldname": "selling_price_list", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "Price List", "oldfieldname": "price_list_name", "oldfieldtype": "Select", @@ -439,6 +507,8 @@ { "fieldname": "price_list_currency", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "Price List Currency", "options": "Currency", "print_hide": 1, @@ -449,6 +519,8 @@ "description": "Rate at which Price list currency is converted to company's base currency", "fieldname": "plc_conversion_rate", "fieldtype": "Float", + "hide_days": 1, + "hide_seconds": 1, "label": "Price List Exchange Rate", "precision": "9", "print_hide": 1, @@ -458,6 +530,8 @@ "default": "0", "fieldname": "ignore_pricing_rule", "fieldtype": "Check", + "hide_days": 1, + "hide_seconds": 1, "label": "Ignore Pricing Rule", "no_copy": 1, "permlevel": 1, @@ -465,11 +539,15 @@ }, { "fieldname": "sec_warehouse", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1 }, { "fieldname": "set_warehouse", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "Set Source Warehouse", "options": "Warehouse", "print_hide": 1 @@ -477,18 +555,24 @@ { "fieldname": "items_section", "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1, "oldfieldtype": "Section Break", "options": "fa fa-shopping-cart" }, { "fieldname": "scan_barcode", "fieldtype": "Data", + "hide_days": 1, + "hide_seconds": 1, "label": "Scan Barcode" }, { "allow_bulk_edit": 1, "fieldname": "items", "fieldtype": "Table", + "hide_days": 1, + "hide_seconds": 1, "label": "Items", "oldfieldname": "sales_order_details", "oldfieldtype": "Table", @@ -498,32 +582,44 @@ { "fieldname": "pricing_rule_details", "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1, "label": "Pricing Rules" }, { "fieldname": "pricing_rules", "fieldtype": "Table", + "hide_days": 1, + "hide_seconds": 1, "label": "Pricing Rule Detail", "options": "Pricing Rule Detail", "read_only": 1 }, { "fieldname": "section_break_31", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1 }, { "fieldname": "column_break_33a", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "hide_days": 1, + "hide_seconds": 1 }, { "fieldname": "total_qty", "fieldtype": "Float", + "hide_days": 1, + "hide_seconds": 1, "label": "Total Quantity", "read_only": 1 }, { "fieldname": "base_total", "fieldtype": "Currency", + "hide_days": 1, + "hide_seconds": 1, "label": "Total (Company Currency)", "options": "Company:company:default_currency", "print_hide": 1, @@ -532,6 +628,8 @@ { "fieldname": "base_net_total", "fieldtype": "Currency", + "hide_days": 1, + "hide_seconds": 1, "label": "Net Total (Company Currency)", "oldfieldname": "net_total", "oldfieldtype": "Currency", @@ -542,11 +640,15 @@ }, { "fieldname": "column_break_33", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "hide_days": 1, + "hide_seconds": 1 }, { "fieldname": "total", "fieldtype": "Currency", + "hide_days": 1, + "hide_seconds": 1, "label": "Total", "options": "currency", "read_only": 1 @@ -554,6 +656,8 @@ { "fieldname": "net_total", "fieldtype": "Currency", + "hide_days": 1, + "hide_seconds": 1, "label": "Net Total", "options": "currency", "print_hide": 1, @@ -562,6 +666,8 @@ { "fieldname": "total_net_weight", "fieldtype": "Float", + "hide_days": 1, + "hide_seconds": 1, "label": "Total Net Weight", "print_hide": 1, "read_only": 1 @@ -569,6 +675,8 @@ { "fieldname": "taxes_section", "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1, "label": "Taxes and Charges", "oldfieldtype": "Section Break", "options": "fa fa-money" @@ -576,17 +684,23 @@ { "fieldname": "tax_category", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "Tax Category", "options": "Tax Category", "print_hide": 1 }, { "fieldname": "column_break_38", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "hide_days": 1, + "hide_seconds": 1 }, { "fieldname": "shipping_rule", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "Shipping Rule", "oldfieldtype": "Button", "options": "Shipping Rule", @@ -594,11 +708,15 @@ }, { "fieldname": "section_break_40", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1 }, { "fieldname": "taxes_and_charges", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "Sales Taxes and Charges Template", "oldfieldname": "charge", "oldfieldtype": "Link", @@ -608,6 +726,8 @@ { "fieldname": "taxes", "fieldtype": "Table", + "hide_days": 1, + "hide_seconds": 1, "label": "Sales Taxes and Charges", "oldfieldname": "other_charges", "oldfieldtype": "Table", @@ -617,11 +737,15 @@ "collapsible": 1, "fieldname": "sec_tax_breakup", "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1, "label": "Tax Breakup" }, { "fieldname": "other_charges_calculation", "fieldtype": "Long Text", + "hide_days": 1, + "hide_seconds": 1, "label": "Taxes and Charges Calculation", "no_copy": 1, "oldfieldtype": "HTML", @@ -630,11 +754,15 @@ }, { "fieldname": "section_break_43", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1 }, { "fieldname": "base_total_taxes_and_charges", "fieldtype": "Currency", + "hide_days": 1, + "hide_seconds": 1, "label": "Total Taxes and Charges (Company Currency)", "oldfieldname": "other_charges_total", "oldfieldtype": "Currency", @@ -645,11 +773,15 @@ }, { "fieldname": "column_break_46", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "hide_days": 1, + "hide_seconds": 1 }, { "fieldname": "total_taxes_and_charges", "fieldtype": "Currency", + "hide_days": 1, + "hide_seconds": 1, "label": "Total Taxes and Charges", "options": "currency", "print_hide": 1, @@ -659,6 +791,8 @@ "fieldname": "loyalty_points_redemption", "fieldtype": "Section Break", "hidden": 1, + "hide_days": 1, + "hide_seconds": 1, "label": "Loyalty Points Redemption", "print_hide": 1 }, @@ -666,6 +800,8 @@ "fieldname": "loyalty_points", "fieldtype": "Int", "hidden": 1, + "hide_days": 1, + "hide_seconds": 1, "label": "Loyalty Points", "read_only": 1 }, @@ -673,6 +809,8 @@ "fieldname": "loyalty_amount", "fieldtype": "Currency", "hidden": 1, + "hide_days": 1, + "hide_seconds": 1, "label": "Loyalty Amount", "print_hide": 1, "read_only": 1 @@ -682,11 +820,15 @@ "collapsible_depends_on": "discount_amount", "fieldname": "section_break_48", "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1, "label": "Additional Discount and Coupon Code" }, { "fieldname": "coupon_code", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "Coupon Code", "options": "Coupon Code" }, @@ -694,6 +836,8 @@ "default": "Grand Total", "fieldname": "apply_discount_on", "fieldtype": "Select", + "hide_days": 1, + "hide_seconds": 1, "label": "Apply Additional Discount On", "options": "\nGrand Total\nNet Total", "print_hide": 1 @@ -701,6 +845,8 @@ { "fieldname": "base_discount_amount", "fieldtype": "Currency", + "hide_days": 1, + "hide_seconds": 1, "label": "Additional Discount Amount (Company Currency)", "options": "Company:company:default_currency", "print_hide": 1, @@ -708,17 +854,23 @@ }, { "fieldname": "column_break_50", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "hide_days": 1, + "hide_seconds": 1 }, { "fieldname": "additional_discount_percentage", "fieldtype": "Float", + "hide_days": 1, + "hide_seconds": 1, "label": "Additional Discount Percentage", "print_hide": 1 }, { "fieldname": "discount_amount", "fieldtype": "Currency", + "hide_days": 1, + "hide_seconds": 1, "label": "Additional Discount Amount", "options": "currency", "print_hide": 1 @@ -726,6 +878,8 @@ { "fieldname": "totals", "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1, "oldfieldtype": "Section Break", "options": "fa fa-money", "print_hide": 1 @@ -733,6 +887,8 @@ { "fieldname": "base_grand_total", "fieldtype": "Currency", + "hide_days": 1, + "hide_seconds": 1, "label": "Grand Total (Company Currency)", "oldfieldname": "grand_total", "oldfieldtype": "Currency", @@ -744,6 +900,8 @@ { "fieldname": "base_rounding_adjustment", "fieldtype": "Currency", + "hide_days": 1, + "hide_seconds": 1, "label": "Rounding Adjustment (Company Currency)", "no_copy": 1, "options": "Company:company:default_currency", @@ -753,6 +911,8 @@ { "fieldname": "base_rounded_total", "fieldtype": "Currency", + "hide_days": 1, + "hide_seconds": 1, "label": "Rounded Total (Company Currency)", "oldfieldname": "rounded_total", "oldfieldtype": "Currency", @@ -765,6 +925,8 @@ "description": "In Words will be visible once you save the Sales Order.", "fieldname": "base_in_words", "fieldtype": "Data", + "hide_days": 1, + "hide_seconds": 1, "label": "In Words (Company Currency)", "oldfieldname": "in_words", "oldfieldtype": "Data", @@ -775,6 +937,8 @@ { "fieldname": "column_break3", "fieldtype": "Column Break", + "hide_days": 1, + "hide_seconds": 1, "oldfieldtype": "Column Break", "print_hide": 1, "width": "50%" @@ -782,6 +946,8 @@ { "fieldname": "grand_total", "fieldtype": "Currency", + "hide_days": 1, + "hide_seconds": 1, "in_list_view": 1, "label": "Grand Total", "oldfieldname": "grand_total_export", @@ -793,6 +959,8 @@ { "fieldname": "rounding_adjustment", "fieldtype": "Currency", + "hide_days": 1, + "hide_seconds": 1, "label": "Rounding Adjustment", "no_copy": 1, "options": "currency", @@ -803,6 +971,8 @@ "bold": 1, "fieldname": "rounded_total", "fieldtype": "Currency", + "hide_days": 1, + "hide_seconds": 1, "label": "Rounded Total", "oldfieldname": "rounded_total_export", "oldfieldtype": "Currency", @@ -813,6 +983,8 @@ { "fieldname": "in_words", "fieldtype": "Data", + "hide_days": 1, + "hide_seconds": 1, "label": "In Words", "oldfieldname": "in_words_export", "oldfieldtype": "Data", @@ -823,6 +995,8 @@ { "fieldname": "advance_paid", "fieldtype": "Currency", + "hide_days": 1, + "hide_seconds": 1, "label": "Advance Paid", "no_copy": 1, "options": "party_account_currency", @@ -834,6 +1008,8 @@ "collapsible_depends_on": "packed_items", "fieldname": "packing_list", "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1, "label": "Packing List", "oldfieldtype": "Section Break", "options": "fa fa-suitcase", @@ -842,6 +1018,8 @@ { "fieldname": "packed_items", "fieldtype": "Table", + "hide_days": 1, + "hide_seconds": 1, "label": "Packed Items", "options": "Packed Item", "print_hide": 1, @@ -850,11 +1028,15 @@ { "fieldname": "payment_schedule_section", "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1, "label": "Payment Terms" }, { "fieldname": "payment_terms_template", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "Payment Terms Template", "options": "Payment Terms Template", "print_hide": 1 @@ -862,6 +1044,8 @@ { "fieldname": "payment_schedule", "fieldtype": "Table", + "hide_days": 1, + "hide_seconds": 1, "label": "Payment Schedule", "no_copy": 1, "options": "Payment Schedule", @@ -872,6 +1056,8 @@ "collapsible_depends_on": "terms", "fieldname": "terms_section_break", "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1, "label": "Terms and Conditions", "oldfieldtype": "Section Break", "options": "fa fa-legal" @@ -879,6 +1065,8 @@ { "fieldname": "tc_name", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "Terms", "oldfieldname": "tc_name", "oldfieldtype": "Link", @@ -888,6 +1076,8 @@ { "fieldname": "terms", "fieldtype": "Text Editor", + "hide_days": 1, + "hide_seconds": 1, "label": "Terms and Conditions Details", "oldfieldname": "terms", "oldfieldtype": "Text Editor" @@ -897,6 +1087,8 @@ "collapsible_depends_on": "project", "fieldname": "more_info", "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1, "label": "More Information", "oldfieldtype": "Section Break", "options": "fa fa-file-text", @@ -905,6 +1097,8 @@ { "fieldname": "inter_company_order_reference", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "Inter Company Order Reference", "options": "Purchase Order" }, @@ -912,6 +1106,8 @@ "description": "Track this Sales Order against any Project", "fieldname": "project", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "Project", "oldfieldname": "project", "oldfieldtype": "Link", @@ -921,6 +1117,8 @@ "fieldname": "party_account_currency", "fieldtype": "Link", "hidden": 1, + "hide_days": 1, + "hide_seconds": 1, "label": "Party Account Currency", "no_copy": 1, "options": "Currency", @@ -929,11 +1127,15 @@ }, { "fieldname": "column_break_77", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "hide_days": 1, + "hide_seconds": 1 }, { "fieldname": "source", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "Source", "oldfieldname": "source", "oldfieldtype": "Select", @@ -943,6 +1145,8 @@ { "fieldname": "campaign", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "Campaign", "oldfieldname": "campaign", "oldfieldtype": "Link", @@ -953,11 +1157,15 @@ "collapsible": 1, "fieldname": "printing_details", "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1, "label": "Print Settings" }, { "fieldname": "language", "fieldtype": "Data", + "hide_days": 1, + "hide_seconds": 1, "label": "Print Language", "print_hide": 1, "read_only": 1 @@ -966,6 +1174,8 @@ "allow_on_submit": 1, "fieldname": "letter_head", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "Letter Head", "oldfieldname": "letter_head", "oldfieldtype": "Select", @@ -975,6 +1185,8 @@ { "fieldname": "column_break4", "fieldtype": "Column Break", + "hide_days": 1, + "hide_seconds": 1, "oldfieldtype": "Column Break", "print_hide": 1, "width": "50%" @@ -983,6 +1195,8 @@ "allow_on_submit": 1, "fieldname": "select_print_heading", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "Print Heading", "no_copy": 1, "oldfieldname": "select_print_heading", @@ -996,6 +1210,8 @@ "default": "0", "fieldname": "group_same_items", "fieldtype": "Check", + "hide_days": 1, + "hide_seconds": 1, "label": "Group same items", "print_hide": 1 }, @@ -1003,6 +1219,8 @@ "collapsible": 1, "fieldname": "section_break_78", "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1, "label": "Billing and Delivery Status", "oldfieldtype": "Column Break", "print_hide": 1, @@ -1012,6 +1230,8 @@ "default": "Draft", "fieldname": "status", "fieldtype": "Select", + "hide_days": 1, + "hide_seconds": 1, "in_list_view": 1, "label": "Status", "no_copy": 1, @@ -1028,6 +1248,8 @@ "fieldname": "delivery_status", "fieldtype": "Select", "hidden": 1, + "hide_days": 1, + "hide_seconds": 1, "in_standard_filter": 1, "label": "Delivery Status", "no_copy": 1, @@ -1039,6 +1261,8 @@ "description": "% of materials delivered against this Sales Order", "fieldname": "per_delivered", "fieldtype": "Percent", + "hide_days": 1, + "hide_seconds": 1, "in_list_view": 1, "label": "% Delivered", "no_copy": 1, @@ -1050,13 +1274,17 @@ }, { "fieldname": "column_break_81", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "hide_days": 1, + "hide_seconds": 1 }, { "depends_on": "eval:!doc.__islocal", "description": "% of materials billed against this Sales Order", "fieldname": "per_billed", "fieldtype": "Percent", + "hide_days": 1, + "hide_seconds": 1, "in_list_view": 1, "label": "% Amount Billed", "no_copy": 1, @@ -1070,6 +1298,8 @@ "fieldname": "billing_status", "fieldtype": "Select", "hidden": 1, + "hide_days": 1, + "hide_seconds": 1, "in_standard_filter": 1, "label": "Billing Status", "no_copy": 1, @@ -1081,6 +1311,8 @@ "collapsible_depends_on": "commission_rate", "fieldname": "sales_team_section_break", "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1, "label": "Commission", "oldfieldtype": "Section Break", "options": "fa fa-group", @@ -1089,6 +1321,8 @@ { "fieldname": "sales_partner", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "Sales Partner", "oldfieldname": "sales_partner", "oldfieldtype": "Link", @@ -1099,12 +1333,16 @@ { "fieldname": "column_break7", "fieldtype": "Column Break", + "hide_days": 1, + "hide_seconds": 1, "print_hide": 1, "width": "50%" }, { "fieldname": "commission_rate", "fieldtype": "Float", + "hide_days": 1, + "hide_seconds": 1, "label": "Commission Rate", "oldfieldname": "commission_rate", "oldfieldtype": "Currency", @@ -1114,6 +1352,8 @@ { "fieldname": "total_commission", "fieldtype": "Currency", + "hide_days": 1, + "hide_seconds": 1, "label": "Total Commission", "oldfieldname": "total_commission", "oldfieldtype": "Currency", @@ -1125,6 +1365,8 @@ "collapsible_depends_on": "sales_team", "fieldname": "section_break1", "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1, "label": "Sales Team", "print_hide": 1 }, @@ -1132,6 +1374,8 @@ "allow_on_submit": 1, "fieldname": "sales_team", "fieldtype": "Table", + "hide_days": 1, + "hide_seconds": 1, "label": "Sales Team", "oldfieldname": "sales_team", "oldfieldtype": "Table", @@ -1140,8 +1384,11 @@ }, { "allow_on_submit": 1, + "collapsible": 1, "fieldname": "subscription_section", "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1, "label": "Auto Repeat Section", "no_copy": 1, "print_hide": 1, @@ -1151,6 +1398,8 @@ "allow_on_submit": 1, "fieldname": "from_date", "fieldtype": "Date", + "hide_days": 1, + "hide_seconds": 1, "label": "From Date", "no_copy": 1 }, @@ -1158,16 +1407,22 @@ "allow_on_submit": 1, "fieldname": "to_date", "fieldtype": "Date", + "hide_days": 1, + "hide_seconds": 1, "label": "To Date", "no_copy": 1 }, { "fieldname": "column_break_108", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "hide_days": 1, + "hide_seconds": 1 }, { "fieldname": "auto_repeat", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "Auto Repeat", "options": "Auto Repeat" }, @@ -1176,11 +1431,15 @@ "depends_on": "eval: doc.auto_repeat", "fieldname": "update_auto_repeat_reference", "fieldtype": "Button", + "hide_days": 1, + "hide_seconds": 1, "label": "Update Auto Repeat Reference" }, { "fieldname": "contact_phone", "fieldtype": "Data", + "hide_days": 1, + "hide_seconds": 1, "label": "Phone", "read_only": 1 }, @@ -1189,6 +1448,8 @@ "fieldname": "skip_delivery_note", "fieldtype": "Check", "hidden": 1, + "hide_days": 1, + "hide_seconds": 1, "label": "Skip Delivery Note", "print_hide": 1 } @@ -1197,7 +1458,7 @@ "idx": 105, "is_submittable": 1, "links": [], - "modified": "2020-05-19 21:39:19.486684", + "modified": "2020-06-30 11:56:42.301317", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order", diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json index c371999a277..963c87a0af5 100644 --- a/erpnext/stock/doctype/item/item.json +++ b/erpnext/stock/doctype/item/item.json @@ -987,6 +987,7 @@ "read_only": 1 }, { + "collapsible": 1, "depends_on": "eval:(!doc.is_item_from_hub)", "fieldname": "hub_publishing_sb", "fieldtype": "Section Break", @@ -1060,7 +1061,7 @@ "image_field": "image", "links": [], "max_attachments": 1, - "modified": "2020-04-08 15:56:06.195722", + "modified": "2020-06-30 12:01:07.534447", "modified_by": "Administrator", "module": "Stock", "name": "Item", @@ -1122,4 +1123,4 @@ "sort_order": "DESC", "title_field": "item_name", "track_changes": 1 -} +} \ No newline at end of file From 3c004ad79880f023917017ec715da9b0e07b0b7b Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 2 Jul 2020 21:18:29 +0530 Subject: [PATCH 514/608] fix(GST): Do not add tax amount in grand total for reverse charge invoices --- .../purchase_invoice/purchase_invoice.py | 6 ++ .../purchase_invoice/regional/india.js | 30 ++++++++++ erpnext/accounts/general_ledger.py | 1 + erpnext/hooks.py | 5 +- erpnext/regional/india/utils.py | 59 +++++++++++++------ 5 files changed, 80 insertions(+), 21 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 3cd57d403a2..c701a7c9dd4 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -438,6 +438,8 @@ class PurchaseInvoice(BuyingController): self.make_tax_gl_entries(gl_entries) + gl_entries = make_regional_gl_entries(gl_entries, self) + gl_entries = merge_similar_entries(gl_entries) self.make_payment_gl_entries(gl_entries) @@ -1097,6 +1099,10 @@ def get_list_context(context=None): }) return list_context +@erpnext.allow_regional +def make_regional_gl_entries(gl_entries, doc): + return gl_entries + @frappe.whitelist() def make_debit_note(source_name, target_doc=None): from erpnext.controllers.sales_and_purchase_return import make_return_doc diff --git a/erpnext/accounts/doctype/purchase_invoice/regional/india.js b/erpnext/accounts/doctype/purchase_invoice/regional/india.js index 81488a2c52a..83cb03a77c6 100644 --- a/erpnext/accounts/doctype/purchase_invoice/regional/india.js +++ b/erpnext/accounts/doctype/purchase_invoice/regional/india.js @@ -1,3 +1,33 @@ {% include "erpnext/regional/india/taxes.js" %} erpnext.setup_auto_gst_taxation('Purchase Invoice'); + + +frappe.ui.form.on('Purchase Taxes and Charges', { + taxes_add: function(frm) { + if (frm.doc.reverse_charge === 'Y') { + frappe.call({ + 'method': 'erpnext.regional.india.utils.get_gst_accounts', + 'args': { + company: frm.doc.company + }, + 'callback': function(r) { + let accounts = r.message; + let account_list = accounts['cgst_account'] + accounts['sgst_account'] + + accounts['igst_account'] + + let gst_tax = 0; + + $.each(frm.doc.taxes || [], function(i, row) { + if (account_list.includes(row.account_head)) { + gst_tax += row.base_tax_amount_after_discount_amount; + } + }); + + frm.doc.taxes_and_charges_added -= flt(gst_tax); + frm.refresh_field('taxes_and_charges_added'); + } + }) + } + } +}); diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index bfe35ab0068..a1f8c037b30 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -20,6 +20,7 @@ def make_gl_entries(gl_map, cancel=False, adv_adj=False, merge_entries=True, upd if not cancel: validate_accounting_period(gl_map) gl_map = process_gl_map(gl_map, merge_entries) + print(gl_map, "$$$$$$$$$") if gl_map and len(gl_map) > 1: save_entries(gl_map, adv_adj, update_outstanding) else: diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 835d92ef5c1..89c5b7409df 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -246,7 +246,7 @@ doc_events = { "on_trash": "erpnext.regional.check_deletion_permission" }, "Purchase Invoice": { - "on_submit": "erpnext.regional.india.utils.make_reverse_charge_entries" + "validate": "erpnext.regional.india.utils.update_grand_total_for_rcm" }, "Payment Entry": { "on_submit": ["erpnext.regional.create_transaction_log", "erpnext.accounts.doctype.payment_request.payment_request.update_payment_req_status"], @@ -374,7 +374,8 @@ regional_overrides = { 'erpnext.controllers.taxes_and_totals.get_itemised_tax_breakup_data': 'erpnext.regional.india.utils.get_itemised_tax_breakup_data', 'erpnext.accounts.party.get_regional_address_details': 'erpnext.regional.india.utils.get_regional_address_details', 'erpnext.hr.utils.calculate_annual_eligible_hra_exemption': 'erpnext.regional.india.utils.calculate_annual_eligible_hra_exemption', - 'erpnext.hr.utils.calculate_hra_exemption_for_period': 'erpnext.regional.india.utils.calculate_hra_exemption_for_period' + 'erpnext.hr.utils.calculate_hra_exemption_for_period': 'erpnext.regional.india.utils.calculate_hra_exemption_for_period', + 'erpnext.accounts.doctype.purchase_invoice.purchase_invoice.make_regional_gl_entries': 'erpnext.regional.india.utils.make_regional_gl_entries' }, 'United Arab Emirates': { 'erpnext.controllers.taxes_and_totals.update_itemised_tax_data': 'erpnext.regional.united_arab_emirates.utils.update_itemised_tax_data' diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index 05ffa87f144..c6df1366d1a 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -1,7 +1,7 @@ from __future__ import unicode_literals import frappe, re, json from frappe import _ -from frappe.utils import cstr, flt, date_diff, nowdate +from frappe.utils import cstr, flt, date_diff, nowdate, round_based_on_smallest_currency_fraction, money_in_words from erpnext.regional.india import states, state_numbers from erpnext.controllers.taxes_and_totals import get_itemised_tax, get_itemised_taxable_amount from erpnext.controllers.accounts_controller import get_taxes_and_charges @@ -644,6 +644,7 @@ def validate_state_code(state_code, address): else: return int(state_code) +@frappe.whitelist() def get_gst_accounts(company, account_wise=False): gst_accounts = frappe._dict() gst_settings_accounts = frappe.get_all("GST Account", @@ -662,14 +663,49 @@ def get_gst_accounts(company, account_wise=False): return gst_accounts -def make_reverse_charge_entries(doc, method): +def update_grand_total_for_rcm(doc, method): + if doc.reverse_charge == 'Y': + gst_accounts = get_gst_accounts(doc.company) + gst_account_list = gst_accounts.get('cgst_account') + gst_accounts.get('sgst_account') \ + + gst_accounts.get('igst_account') + + gst_tax = 0 + for tax in doc.get('taxes'): + if tax.category not in ("Total", "Valuation and Total"): + continue + + if flt(tax.base_tax_amount_after_discount_amount) and tax.account_head in gst_account_list: + gst_tax += tax.base_tax_amount_after_discount_amount + + doc.taxes_and_charges_added -= gst_tax + doc.total_taxes_and_charges -= gst_tax + + update_totals(gst_tax, doc) + +def update_totals(gst_tax, doc): + doc.grand_total -= gst_tax + + if doc.meta.get_field("rounded_total"): + if doc.is_rounded_total_disabled(): + doc.outstanding_amount = doc.grand_total + else: + doc.rounded_total = round_based_on_smallest_currency_fraction(doc.grand_total, + doc.currency, doc.precision("rounded_total")) + + doc.rounding_adjustment += flt(doc.rounded_total - doc.grand_total, + doc.precision("rounding_adjustment")) + + doc.outstanding_amount = doc.base_rounded_total + + doc.in_words = money_in_words(doc.grand_total, doc.currency) + +def make_regional_gl_entries(gl_entries, doc): country = frappe.get_cached_value('Company', doc.company, 'country') if country != 'India': return if doc.reverse_charge == 'Y': - gl_entries = [] gst_accounts = get_gst_accounts(doc.company) gst_account_list = gst_accounts.get('cgst_account') + gst_accounts.get('sgst_account') \ + gst_accounts.get('igst_account') @@ -694,19 +730,4 @@ def make_reverse_charge_entries(doc, method): }, account_currency, item=tax) ) - gl_entries.append(doc.get_gl_dict( - { - "account": doc.credit_to if doc.doctype == 'Purchase Invoice' else doc.debit_to, - "cost_center": doc.cost_center, - "posting_date": doc.posting_date, - "party_type": 'Supplier', - "party": doc.supplier, - "against": tax.account_head, - "debit": tax.base_tax_amount_after_discount_amount, - "debit_in_account_currency": tax.base_tax_amount_after_discount_amount \ - if account_currency==doc.company_currency \ - else tax.tax_amount_after_discount_amount - }, account_currency, item=doc) - ) - - make_gl_entries(gl_entries) \ No newline at end of file + return gl_entries \ No newline at end of file From 30d9101cbf3a2fce1addfee0c5d45ece1e2616e6 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 2 Jul 2020 23:39:12 +0530 Subject: [PATCH 515/608] fix: Remove loan security field from loan --- erpnext/loan_management/doctype/loan/loan.js | 12 -- .../loan_management/doctype/loan/loan.json | 32 +--- erpnext/loan_management/doctype/loan/loan.py | 40 ++--- .../loan_management/doctype/loan/test_loan.py | 144 ++++++++++-------- .../loan_application/loan_application.py | 13 +- .../test_loan_disbursement.py | 18 +-- .../test_loan_interest_accrual.py | 17 +-- .../loan_security_pledge.json | 14 +- 8 files changed, 130 insertions(+), 160 deletions(-) diff --git a/erpnext/loan_management/doctype/loan/loan.js b/erpnext/loan_management/doctype/loan/loan.js index 9cd8b2e90a9..ffef60b6b0a 100644 --- a/erpnext/loan_management/doctype/loan/loan.js +++ b/erpnext/loan_management/doctype/loan/loan.js @@ -45,15 +45,6 @@ frappe.ui.form.on('Loan', { }); }) - frm.set_query('loan_security_pledge', function(doc, cdt, cdn) { - return { - filters: { - applicant: frm.doc.applicant, - docstatus: 1, - loan_application: frm.doc.loan_application || '' - } - }; - }); }, refresh: function (frm) { @@ -86,9 +77,6 @@ frappe.ui.form.on('Loan', { frm.toggle_display("repayment_periods", s1 - frm.doc.is_term_loan); }, - is_secured_loan: function(frm) { - frm.toggle_reqd("loan_security_pledge", frm.doc.is_secured_loan); - }, make_loan_disbursement: function (frm) { frappe.call({ diff --git a/erpnext/loan_management/doctype/loan/loan.json b/erpnext/loan_management/doctype/loan/loan.json index b04e82274e9..192beee7e3d 100644 --- a/erpnext/loan_management/doctype/loan/loan.json +++ b/erpnext/loan_management/doctype/loan/loan.json @@ -25,15 +25,12 @@ "disbursement_date", "disbursed_amount", "column_break_11", + "maximum_loan_amount", "is_term_loan", "repayment_method", "repayment_periods", "monthly_repayment_amount", "repayment_start_date", - "loan_security_details_section", - "loan_security_pledge", - "column_break_25", - "maximum_loan_value", "account_info", "mode_of_payment", "payment_account", @@ -292,13 +289,8 @@ "default": "0", "fieldname": "is_secured_loan", "fieldtype": "Check", - "label": "Is Secured Loan" - }, - { - "depends_on": "is_secured_loan", - "fieldname": "loan_security_details_section", - "fieldtype": "Section Break", - "label": "Loan Security Details" + "label": "Is Secured Loan", + "read_only": 1 }, { "default": "0", @@ -324,12 +316,6 @@ "options": "Company:company:default_currency", "read_only": 1 }, - { - "fieldname": "loan_security_pledge", - "fieldtype": "Link", - "label": "Loan Security Pledge", - "options": "Loan Security Pledge" - }, { "fieldname": "disbursed_amount", "fieldtype": "Currency", @@ -338,21 +324,17 @@ "read_only": 1 }, { - "fetch_from": "loan_security_pledge.maximum_loan_value", - "fieldname": "maximum_loan_value", + "fetch_from": "loan_application.maximum_loan_amount", + "fieldname": "maximum_loan_amount", "fieldtype": "Currency", - "label": "Maximum Loan Value", + "label": "Maximum Loan Amount", "options": "Company:company:default_currency", "read_only": 1 - }, - { - "fieldname": "column_break_25", - "fieldtype": "Column Break" } ], "is_submittable": 1, "links": [], - "modified": "2020-04-13 13:16:10.192624", + "modified": "2020-07-02 20:46:40.128142", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan", diff --git a/erpnext/loan_management/doctype/loan/loan.py b/erpnext/loan_management/doctype/loan/loan.py index 4e805d4a27d..e20b484fc0e 100644 --- a/erpnext/loan_management/doctype/loan/loan.py +++ b/erpnext/loan_management/doctype/loan/loan.py @@ -13,11 +13,9 @@ from erpnext.controllers.accounts_controller import AccountsController class Loan(AccountsController): def validate(self): self.set_loan_amount() - + self.validate_loan_amount() self.set_missing_fields() self.validate_accounts() - self.validate_loan_security_pledge() - self.validate_loan_amount() self.check_sanctioned_amount_limit() self.validate_repay_from_salary() @@ -56,21 +54,6 @@ class Loan(AccountsController): if self.repayment_method == "Repay Over Number of Periods": self.monthly_repayment_amount = get_monthly_repayment_amount(self.repayment_method, self.loan_amount, self.rate_of_interest, self.repayment_periods) - def validate_loan_security_pledge(self): - - if self.is_secured_loan and not self.loan_security_pledge: - frappe.throw(_("Loan Security Pledge is mandatory for secured loan")) - - if self.loan_security_pledge: - loan_security_details = frappe.db.get_value("Loan Security Pledge", self.loan_security_pledge, - ['loan', 'company'], as_dict=1) - - if loan_security_details.loan: - frappe.throw(_("Loan Security Pledge already pledged against loan {0}").format(loan_security_details.loan)) - - if loan_security_details.company != self.company: - frappe.throw(_("Loan Security Pledge Company and Loan Company must be same")) - def check_sanctioned_amount_limit(self): total_loan_amount = get_total_loan_amount(self.applicant_type, self.applicant, self.company) sanctioned_amount_limit = get_sanctioned_amount_limit(self.applicant_type, self.applicant, self.company) @@ -129,22 +112,29 @@ class Loan(AccountsController): self.total_payment = self.loan_amount def set_loan_amount(self): + if self.loan_application and not self.loan_amount: + self.loan_amount = frappe.db.get_value('Loan Application', self.loan_application, 'loan_amount') - if not self.loan_amount and self.is_secured_loan and self.loan_security_pledge: - self.loan_amount = self.maximum_loan_value def validate_loan_amount(self): - if self.is_secured_loan and self.loan_amount > self.maximum_loan_value: - msg = _("Loan amount cannot be greater than {0}").format(self.maximum_loan_value) + if self.maximum_loan_amount and self.loan_amount > self.maximum_loan_amount: + msg = _("Loan amount cannot be greater than {0}").format(self.maximum_loan_amount) frappe.throw(msg) if not self.loan_amount: frappe.throw(_("Loan amount is mandatory")) def link_loan_security_pledge(self): - frappe.db.sql("""UPDATE `tabLoan Security Pledge` SET - loan = %s, status = 'Pledged', pledge_time = %s - where name = %s """, (self.name, now_datetime(), self.loan_security_pledge)) + if self.is_secured_loan: + loan_security_pledge = frappe.db.get_value('Loan Security Pledge', {'loan_application': self.loan_application}, + 'name') + + if loan_security_pledge: + frappe.db.set_value('Loan Security Pledge', loan_security_pledge, { + 'loan': self.name, + 'status': 'Pledged', + 'pledge_time': now_datetime() + }) def unlink_loan_security_pledge(self): frappe.db.sql("""UPDATE `tabLoan Security Pledge` SET diff --git a/erpnext/loan_management/doctype/loan/test_loan.py b/erpnext/loan_management/doctype/loan/test_loan.py index 3f37a26418b..1f771c2090c 100644 --- a/erpnext/loan_management/doctype/loan/test_loan.py +++ b/erpnext/loan_management/doctype/loan/test_loan.py @@ -16,6 +16,7 @@ from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual from erpnext.loan_management.doctype.process_loan_security_shortfall.process_loan_security_shortfall import create_process_loan_security_shortfall from erpnext.loan_management.doctype.loan.loan import create_loan_security_unpledge from erpnext.loan_management.doctype.loan_security_unpledge.loan_security_unpledge import get_pledged_security_qty +from erpnext.loan_management.doctype.loan_application.loan_application import create_pledge class TestLoan(unittest.TestCase): def setUp(self): @@ -72,31 +73,28 @@ class TestLoan(unittest.TestCase): self.assertEquals(loan.total_payment, 302712) def test_loan_with_security(self): - pledges = [] - pledges.append({ + + pledge = [{ "loan_security": "Test Security 1", "qty": 4000.00, - "haircut": 50, - "loan_security_price": 500.00 - }) + }] - loan_security_pledge = create_loan_security_pledge(self.applicant2, pledges) - - loan = create_loan_with_security(self.applicant2, "Stock Loan", "Repay Over Number of Periods", 12, loan_security_pledge.name) + loan_application = create_loan_application('_Test Company', self.applicant2, 'Stock Loan', pledge, "Repay Over Number of Periods", 12) + create_pledge(loan_application) + loan = create_loan_with_security(self.applicant2, "Stock Loan", "Repay Over Number of Periods", 12, loan_application) self.assertEquals(loan.loan_amount, 1000000) def test_loan_disbursement(self): - pledges = [] - pledges.append({ + pledge = [{ "loan_security": "Test Security 1", - "qty": 4000.00, - "haircut": 50 - }) + "qty": 4000.00 + }] - loan_security_pledge = create_loan_security_pledge(self.applicant2, pledges) + loan_application = create_loan_application('_Test Company', self.applicant2, 'Stock Loan', pledge, "Repay Over Number of Periods", 12) + create_pledge(loan_application) - loan = create_loan_with_security(self.applicant2, "Stock Loan", "Repay Over Number of Periods", 12, loan_security_pledge.name) + loan = create_loan_with_security(self.applicant2, "Stock Loan", "Repay Over Number of Periods", 12, loan_application) self.assertEquals(loan.loan_amount, 1000000) loan.submit() @@ -121,18 +119,15 @@ class TestLoan(unittest.TestCase): self.assertTrue(gl_entries2) def test_regular_loan_repayment(self): - pledges = [] - pledges.append({ + pledge = [{ "loan_security": "Test Security 1", - "qty": 4000.00, - "haircut": 50 - }) + "qty": 4000.00 + }] - loan_security_pledge = create_loan_security_pledge(self.applicant2, pledges) - - loan = create_demand_loan(self.applicant2, "Demand Loan", loan_security_pledge.name, - posting_date=get_first_day(nowdate())) + loan_application = create_loan_application('_Test Company', self.applicant2, 'Demand Loan', pledge) + create_pledge(loan_application) + loan = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date=get_first_day(nowdate())) loan.submit() self.assertEquals(loan.loan_amount, 1000000) @@ -166,16 +161,15 @@ class TestLoan(unittest.TestCase): penalty_amount - amounts[0], 2)) def test_loan_closure_repayment(self): - pledges = [] - pledges.append({ + pledge = [{ "loan_security": "Test Security 1", - "qty": 4000.00, - "haircut": 50 - }) + "qty": 4000.00 + }] - loan_security_pledge = create_loan_security_pledge(self.applicant2, pledges) - loan = create_demand_loan(self.applicant2, "Demand Loan", loan_security_pledge.name, - posting_date=get_first_day(nowdate())) + loan_application = create_loan_application('_Test Company', self.applicant2, 'Demand Loan', pledge) + create_pledge(loan_application) + + loan = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date=get_first_day(nowdate())) loan.submit() self.assertEquals(loan.loan_amount, 1000000) @@ -214,23 +208,20 @@ class TestLoan(unittest.TestCase): self.assertEquals(loan.status, "Loan Closure Requested") def test_loan_repayment_for_term_loan(self): - pledges = [] - pledges.append({ + pledges = [{ "loan_security": "Test Security 2", - "qty": 4000.00, - "haircut": 50 - }) - - pledges.append({ + "qty": 4000.00 + }, + { "loan_security": "Test Security 1", - "qty": 2000.00, - "haircut": 50 - }) + "qty": 2000.00 + }] - loan_security_pledge = create_loan_security_pledge(self.applicant2, pledges) + loan_application = create_loan_application('_Test Company', self.applicant2, 'Stock Loan', pledges) + create_pledge(loan_application) - loan = create_loan_with_security(self.applicant2, "Stock Loan", "Repay Over Number of Periods", 12, - loan_security_pledge.name, posting_date=add_months(nowdate(), -1)) + loan = create_loan_with_security(self.applicant2, "Stock Loan", "Repay Over Number of Periods", 12, loan_application, + posting_date=add_months(nowdate(), -1)) loan.submit() @@ -250,16 +241,16 @@ class TestLoan(unittest.TestCase): self.assertEquals(amounts[1], 78303.00) def test_security_shortfall(self): - pledges = [] - pledges.append({ + pledges = [{ "loan_security": "Test Security 2", "qty": 8000.00, "haircut": 50, - }) + }] - loan_security_pledge = create_loan_security_pledge(self.applicant2, pledges) + loan_application = create_loan_application('_Test Company', self.applicant2, 'Stock Loan', pledges) + create_pledge(loan_application) - loan = create_loan_with_security(self.applicant2, "Stock Loan", "Repay Over Number of Periods", 12, loan_security_pledge.name) + loan = create_loan_with_security(self.applicant2, "Stock Loan", "Repay Over Number of Periods", 12, loan_application) loan.submit() make_loan_disbursement_entry(loan.name, loan.loan_amount) @@ -279,16 +270,15 @@ class TestLoan(unittest.TestCase): where loan_security='Test Security 2'""") def test_loan_security_unpledge(self): - pledges = [] - pledges.append({ + pledge = [{ "loan_security": "Test Security 1", - "qty": 4000.00, - "haircut": 50 - }) + "qty": 4000.00 + }] - loan_security_pledge = create_loan_security_pledge(self.applicant2, pledges) - loan = create_demand_loan(self.applicant2, "Demand Loan", loan_security_pledge.name, - posting_date=get_first_day(nowdate())) + loan_application = create_loan_application('_Test Company', self.applicant2, 'Demand Loan', pledge) + create_pledge(loan_application) + + loan = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date=get_first_day(nowdate())) loan.submit() self.assertEquals(loan.loan_amount, 1000000) @@ -446,12 +436,13 @@ def create_loan_security(): "haircut": 50.00, }).insert(ignore_permissions=True) -def create_loan_security_pledge(applicant, pledges): +def create_loan_security_pledge(applicant, pledges, loan_application): lsp = frappe.new_doc("Loan Security Pledge") lsp.applicant_type = 'Customer' lsp.applicant = applicant lsp.company = "_Test Company" + lsp.loan_application = loan_application for pledge in pledges: lsp.append('securities', { @@ -510,6 +501,31 @@ def create_repayment_entry(loan, applicant, posting_date, payment_type, paid_amo return lr +def create_loan_application(company, applicant, loan_type, proposed_pledges, repayment_method=None, + repayment_periods=None, posting_date=None): + loan_application = frappe.new_doc('Loan Application') + loan_application.applicant_type = 'Customer' + loan_application.company = company + loan_application.applicant = applicant + loan_application.loan_type = loan_type + loan_application.posting_date = posting_date or nowdate() + loan_application.is_secured_loan = 1 + + if repayment_method: + loan_application.repayment_method = repayment_method + loan_application.repayment_periods = repayment_periods + + for pledge in proposed_pledges: + loan_application.append('proposed_pledges', pledge) + + loan_application.save() + loan_application.submit() + + loan_application.status = 'Approved' + loan_application.save() + + return loan_application.name + def create_loan(applicant, loan_type, loan_amount, repayment_method, repayment_periods, repayment_start_date=None, posting_date=None): @@ -531,14 +547,13 @@ def create_loan(applicant, loan_type, loan_amount, repayment_method, repayment_p loan.save() return loan -def create_loan_with_security(applicant, loan_type, repayment_method, repayment_periods, loan_security_pledge, - posting_date=None, repayment_start_date=None): - +def create_loan_with_security(applicant, loan_type, repayment_method, repayment_periods, loan_application, posting_date=None, repayment_start_date=None): loan = frappe.get_doc({ "doctype": "Loan", "company": "_Test Company", "applicant_type": "Customer", "posting_date": posting_date or nowdate(), + "loan_application": loan_application, "applicant": applicant, "loan_type": loan_type, "is_term_loan": 1, @@ -547,7 +562,6 @@ def create_loan_with_security(applicant, loan_type, repayment_method, repayment_ "repayment_periods": repayment_periods, "repayment_start_date": repayment_start_date or nowdate(), "mode_of_payment": frappe.db.get_value('Mode of Payment', {'type': 'Cash'}, 'name'), - "loan_security_pledge": loan_security_pledge, "payment_account": 'Payment Account - _TC', "loan_account": 'Loan Account - _TC', "interest_income_account": 'Interest Income Account - _TC', @@ -558,19 +572,19 @@ def create_loan_with_security(applicant, loan_type, repayment_method, repayment_ return loan -def create_demand_loan(applicant, loan_type, loan_security_pledge, posting_date=None): +def create_demand_loan(applicant, loan_type, loan_application, posting_date=None): loan = frappe.get_doc({ "doctype": "Loan", "company": "_Test Company", "applicant_type": "Customer", "posting_date": posting_date or nowdate(), + 'loan_application': loan_application, "applicant": applicant, "loan_type": loan_type, "is_term_loan": 0, "is_secured_loan": 1, "mode_of_payment": frappe.db.get_value('Mode of Payment', {'type': 'Cash'}, 'name'), - "loan_security_pledge": loan_security_pledge, "payment_account": 'Payment Account - _TC', "loan_account": 'Loan Account - _TC', "interest_income_account": 'Interest Income Account - _TC', diff --git a/erpnext/loan_management/doctype/loan_application/loan_application.py b/erpnext/loan_management/doctype/loan_application/loan_application.py index d3b816464f1..f051755f67c 100644 --- a/erpnext/loan_management/doctype/loan_application/loan_application.py +++ b/erpnext/loan_management/doctype/loan_application/loan_application.py @@ -103,10 +103,13 @@ class LoanApplication(Document): if self.is_secured_loan and not self.proposed_pledges: frappe.throw(_("Proposed Pledges are mandatory for secured Loans")) - if not self.loan_amount and self.is_secured_loan and self.proposed_pledges: - self.loan_amount = 0 + if self.is_secured_loan and self.proposed_pledges: + self.maximum_loan_amount = 0 for security in self.proposed_pledges: - self.loan_amount += security.post_haircut_amount + self.maximum_loan_amount += security.post_haircut_amount + + if not self.loan_amount and self.is_secured_loan and self.proposed_pledges: + self.loan_amount = self.maximum_loan_amount @frappe.whitelist() def create_loan(source_name, target_doc=None, submit=0): @@ -116,7 +119,6 @@ def create_loan(source_name, target_doc=None, submit=0): filters = {'name': source_doc.loan_type} )[0] - loan_security_pledge = frappe.db.get_value("Loan Security Pledge", {"loan_application": source_name}, 'name') target_doc.mode_of_payment = account_details.mode_of_payment target_doc.payment_account = account_details.payment_account @@ -124,9 +126,6 @@ def create_loan(source_name, target_doc=None, submit=0): target_doc.interest_income_account = account_details.interest_income_account target_doc.penalty_income_account = account_details.penalty_income_account - if loan_security_pledge: - target_doc.is_secured_loan = 1 - target_doc.loan_security_pledge = loan_security_pledge doclist = get_mapped_doc("Loan Application", source_name, { "Loan Application": { diff --git a/erpnext/loan_management/doctype/loan_disbursement/test_loan_disbursement.py b/erpnext/loan_management/doctype/loan_disbursement/test_loan_disbursement.py index 0c1578ffef5..2cb26376126 100644 --- a/erpnext/loan_management/doctype/loan_disbursement/test_loan_disbursement.py +++ b/erpnext/loan_management/doctype/loan_disbursement/test_loan_disbursement.py @@ -5,11 +5,12 @@ from __future__ import unicode_literals import frappe import unittest from frappe.utils import (nowdate, add_days, get_datetime, get_first_day, get_last_day, date_diff, flt, add_to_date) -from erpnext.loan_management.doctype.loan.test_loan import (create_loan_type, create_loan_security_pledge, create_repayment_entry, +from erpnext.loan_management.doctype.loan.test_loan import (create_loan_type, create_loan_security_pledge, create_repayment_entry, create_loan_application, make_loan_disbursement_entry, create_loan_accounts, create_loan_security_type, create_loan_security, create_demand_loan, create_loan_security_price) from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual_for_demand_loans from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import days_in_year from erpnext.selling.doctype.customer.test_customer import get_customer_dict +from erpnext.loan_management.doctype.loan_application.loan_application import create_pledge class TestLoanDisbursement(unittest.TestCase): @@ -31,18 +32,15 @@ class TestLoanDisbursement(unittest.TestCase): self.applicant = frappe.db.get_value("Customer", {'name': '_Test Loan Customer'}, 'name') def test_loan_topup(self): - pledges = [] - pledges.append({ + pledge = [{ "loan_security": "Test Security 1", - "qty": 4000.00, - "haircut": 50, - "loan_security_price": 500.00 - }) + "qty": 4000.00 + }] - loan_security_pledge = create_loan_security_pledge(self.applicant, pledges) + loan_application = create_loan_application('_Test Company', self.applicant, 'Demand Loan', pledge) + create_pledge(loan_application) - loan = create_demand_loan(self.applicant, "Demand Loan", loan_security_pledge.name, - posting_date=get_first_day(nowdate())) + loan = create_demand_loan(self.applicant, "Demand Loan", loan_application, posting_date=get_first_day(nowdate())) loan.submit() diff --git a/erpnext/loan_management/doctype/loan_interest_accrual/test_loan_interest_accrual.py b/erpnext/loan_management/doctype/loan_interest_accrual/test_loan_interest_accrual.py index 2afed08e185..4b85b218696 100644 --- a/erpnext/loan_management/doctype/loan_interest_accrual/test_loan_interest_accrual.py +++ b/erpnext/loan_management/doctype/loan_interest_accrual/test_loan_interest_accrual.py @@ -6,10 +6,11 @@ import frappe import unittest from frappe.utils import (nowdate, add_days, get_datetime, get_first_day, get_last_day, date_diff, flt, add_to_date) from erpnext.loan_management.doctype.loan.test_loan import (create_loan_type, create_loan_security_pledge, create_loan_security_price, - make_loan_disbursement_entry, create_loan_accounts, create_loan_security_type, create_loan_security, create_demand_loan) + make_loan_disbursement_entry, create_loan_accounts, create_loan_security_type, create_loan_security, create_demand_loan, create_loan_application) from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual_for_demand_loans from erpnext.loan_management.doctype.loan_interest_accrual.loan_interest_accrual import days_in_year from erpnext.selling.doctype.customer.test_customer import get_customer_dict +from erpnext.loan_management.doctype.loan_application.loan_application import create_pledge class TestLoanInterestAccrual(unittest.TestCase): def setUp(self): @@ -29,17 +30,15 @@ class TestLoanInterestAccrual(unittest.TestCase): self.applicant = frappe.db.get_value("Customer", {'name': '_Test Loan Customer'}, 'name') def test_loan_interest_accural(self): - pledges = [] - pledges.append({ + pledge = [{ "loan_security": "Test Security 1", - "qty": 4000.00, - "haircut": 50, - "loan_security_price": 500.00 - }) + "qty": 4000.00 + }] - loan_security_pledge = create_loan_security_pledge(self.applicant, pledges) + loan_application = create_loan_application('_Test Company', self.applicant, 'Demand Loan', pledge) + create_pledge(loan_application) - loan = create_demand_loan(self.applicant, "Demand Loan", loan_security_pledge.name, + loan = create_demand_loan(self.applicant, "Demand Loan", loan_application, posting_date=get_first_day(nowdate())) loan.submit() diff --git a/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.json b/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.json index 1553844704d..4572e992998 100644 --- a/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.json +++ b/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.json @@ -1,4 +1,5 @@ { + "actions": [], "autoname": "LS-.{applicant}.-.#####", "creation": "2019-08-29 18:48:51.371674", "doctype": "DocType", @@ -6,10 +7,10 @@ "engine": "InnoDB", "field_order": [ "loan_details_section", - "loan_application", - "loan", "applicant_type", "applicant", + "loan", + "loan_application", "column_break_3", "company", "pledge_time", @@ -55,15 +56,13 @@ "fieldname": "loan", "fieldtype": "Link", "label": "Loan", - "options": "Loan", - "read_only": 1 + "options": "Loan" }, { "fieldname": "loan_application", "fieldtype": "Link", "label": "Loan Application", - "options": "Loan Application", - "read_only": 1 + "options": "Loan Application" }, { "fieldname": "total_security_value", @@ -133,7 +132,8 @@ } ], "is_submittable": 1, - "modified": "2019-10-10 13:22:53.297519", + "links": [], + "modified": "2020-07-02 23:38:24.002382", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan Security Pledge", From b60abb01585be9148f92214e35009071020e14cd Mon Sep 17 00:00:00 2001 From: Andy Zhu Date: Fri, 3 Jul 2020 14:13:15 +1200 Subject: [PATCH 516/608] fix: fetch item price in sales invoice When create standalone sales invoice, the item price will not be created fetch. By adding "posting_date" to dict: item_price_args, it will be fetch on building the condition of SQL. --- erpnext/stock/get_item_details.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 0ed3b276e39..0af018bbad8 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -636,6 +636,10 @@ def get_item_price(args, item_code, ignore_party=False): if args.get('transaction_date'): conditions += """ and %(transaction_date)s between ifnull(valid_from, '2000-01-01') and ifnull(valid_upto, '2500-12-31')""" + + if args.get('posting_date'): + conditions += """ and %(posting_date)s between + ifnull(valid_from, '2000-01-01') and ifnull(valid_upto, '2500-12-31')""" return frappe.db.sql(""" select name, price_list_rate, uom from `tabItem Price` {conditions} @@ -657,6 +661,7 @@ def get_price_list_rate_for(args, item_code): "supplier": args.get('supplier'), "uom": args.get('uom'), "transaction_date": args.get('transaction_date'), + "posting_date": args.get('posting_date'), } item_price_data = 0 From 9ed0384bea9d34b2eded39c336965281c4d127ea Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 3 Jul 2020 14:54:41 +0530 Subject: [PATCH 517/608] fix: Code cleanup --- .../purchase_invoice/regional/india.js | 30 ------------------- 1 file changed, 30 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/regional/india.js b/erpnext/accounts/doctype/purchase_invoice/regional/india.js index 83cb03a77c6..81488a2c52a 100644 --- a/erpnext/accounts/doctype/purchase_invoice/regional/india.js +++ b/erpnext/accounts/doctype/purchase_invoice/regional/india.js @@ -1,33 +1,3 @@ {% include "erpnext/regional/india/taxes.js" %} erpnext.setup_auto_gst_taxation('Purchase Invoice'); - - -frappe.ui.form.on('Purchase Taxes and Charges', { - taxes_add: function(frm) { - if (frm.doc.reverse_charge === 'Y') { - frappe.call({ - 'method': 'erpnext.regional.india.utils.get_gst_accounts', - 'args': { - company: frm.doc.company - }, - 'callback': function(r) { - let accounts = r.message; - let account_list = accounts['cgst_account'] + accounts['sgst_account'] - + accounts['igst_account'] - - let gst_tax = 0; - - $.each(frm.doc.taxes || [], function(i, row) { - if (account_list.includes(row.account_head)) { - gst_tax += row.base_tax_amount_after_discount_amount; - } - }); - - frm.doc.taxes_and_charges_added -= flt(gst_tax); - frm.refresh_field('taxes_and_charges_added'); - } - }) - } - } -}); From 41c0c9f46bc8765171d3268d6b8b2afa8d765e54 Mon Sep 17 00:00:00 2001 From: mohammadahmad1990 Date: Thu, 18 Jun 2020 11:18:44 +0500 Subject: [PATCH 518/608] fix: Item Tax Template --- erpnext/controllers/queries.py | 3 ++- erpnext/public/js/controllers/transaction.js | 3 ++- erpnext/stock/doctype/item_tax/item_tax.json | 15 +++++++++++---- erpnext/stock/get_item_details.py | 2 +- 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index f6a8d27d440..ee8364a40e3 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -570,7 +570,8 @@ def get_tax_template(doctype, txt, searchfield, start, page_len, filters): args = { 'item_code': filters.get('item_code'), 'posting_date': filters.get('valid_from'), - 'tax_category': filters.get('tax_category') + 'tax_category': filters.get('tax_category'), + 'company': filters.get('company') } taxes = _get_item_tax_template(args, taxes, for_validate=True) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 905f6fb80f5..3c56a636bd1 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1835,7 +1835,8 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ if (doc.tax_category) filters['tax_category'] = doc.tax_category; - + if (doc.company) + filters['company'] = doc.company; return { query: "erpnext.controllers.queries.get_tax_template", filters: filters diff --git a/erpnext/stock/doctype/item_tax/item_tax.json b/erpnext/stock/doctype/item_tax/item_tax.json index a93e4636add..cf3c0e9c347 100644 --- a/erpnext/stock/doctype/item_tax/item_tax.json +++ b/erpnext/stock/doctype/item_tax/item_tax.json @@ -1,5 +1,4 @@ { - "actions": [], "creation": "2013-02-22 01:28:01", "doctype": "DocType", "editable_grid": 1, @@ -7,7 +6,8 @@ "field_order": [ "item_tax_template", "tax_category", - "valid_from" + "valid_from", + "company" ], "fields": [ { @@ -34,12 +34,19 @@ "fieldtype": "Date", "in_list_view": 1, "label": "Valid From" + }, + { + "fetch_from": "item_tax_template.company", + "fieldname": "company", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Company", + "options": "Company" } ], "idx": 1, "istable": 1, - "links": [], - "modified": "2019-12-28 21:54:40.807849", + "modified": "2020-06-18 02:30:44.610171", "modified_by": "Administrator", "module": "Stock", "name": "Item Tax", diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 0ed3b276e39..08ee6300ffe 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -442,7 +442,7 @@ def _get_item_tax_template(args, taxes, out={}, for_validate=False): taxes_with_no_validity = [] for tax in taxes: - if tax.valid_from: + if tax.valid_from and tax.company == args['company']: # In purchase Invoice first preference will be given to supplier invoice date # if supplier date is not present then posting date validation_date = args.get('transaction_date') or args.get('bill_date') or args.get('posting_date') From e39649bc9e30c39a99b2642a19dc571dcfbe70cd Mon Sep 17 00:00:00 2001 From: mohammadahmad1990 Date: Thu, 18 Jun 2020 11:42:53 +0500 Subject: [PATCH 519/608] fix: Item Tax Template with no validity --- erpnext/stock/get_item_details.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 08ee6300ffe..67333f4aa9b 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -450,7 +450,8 @@ def _get_item_tax_template(args, taxes, out={}, for_validate=False): if getdate(tax.valid_from) <= getdate(validation_date): taxes_with_validity.append(tax) else: - taxes_with_no_validity.append(tax) + if tax.company == args['company']: + taxes_with_no_validity.append(tax) if taxes_with_validity: taxes = sorted(taxes_with_validity, key = lambda i: i.valid_from, reverse=True) From 728bf0e6dd282ddff629edcd37879b5608beda08 Mon Sep 17 00:00:00 2001 From: mohammadahmad1990 Date: Thu, 18 Jun 2020 12:21:42 +0500 Subject: [PATCH 520/608] fix: Item Tax Template --- erpnext/controllers/taxes_and_totals.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index 4e568e2795a..a9eb9963bfe 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -53,7 +53,8 @@ class calculate_taxes_and_totals(object): 'tax_category': self.doc.get('tax_category'), 'posting_date': self.doc.get('posting_date'), 'bill_date': self.doc.get('bill_date'), - 'transaction_date': self.doc.get('transaction_date') + 'transaction_date': self.doc.get('transaction_date'), + 'company': self.doc.get('company') } item_group = item_doc.item_group From 015910b4c458a5ad89be796ead759e76eaed2794 Mon Sep 17 00:00:00 2001 From: mohammadahmad1990 Date: Thu, 18 Jun 2020 23:15:22 +0500 Subject: [PATCH 521/608] Company Field Added in Item Tax Template --- .../item_tax_template/item_tax_template.js | 12 + .../item_tax_template/item_tax_template.json | 225 ++++++------------ 2 files changed, 83 insertions(+), 154 deletions(-) diff --git a/erpnext/accounts/doctype/item_tax_template/item_tax_template.js b/erpnext/accounts/doctype/item_tax_template/item_tax_template.js index 42abdb809ba..e30ef61a232 100644 --- a/erpnext/accounts/doctype/item_tax_template/item_tax_template.js +++ b/erpnext/accounts/doctype/item_tax_template/item_tax_template.js @@ -6,6 +6,18 @@ frappe.ui.form.on('Item Tax Template', { frm.set_query("tax_type", "taxes", function(doc) { return { filters: [ + ['Account', 'company', '=', frm.doc.company], + ['Account', 'is_group', '=', 0], + ['Account', 'account_type', 'in', ['Tax', 'Chargeable', 'Income Account', 'Expense Account', 'Expenses Included In Valuation']] + ] + } + }); + }, + company: function (frm) { + frm.set_query("tax_type", "taxes", function(doc) { + return { + filters: [ + ['Account', 'company', '=', frm.doc.company], ['Account', 'is_group', '=', 0], ['Account', 'account_type', 'in', ['Tax', 'Chargeable', 'Income Account', 'Expense Account', 'Expenses Included In Valuation']] ] diff --git a/erpnext/accounts/doctype/item_tax_template/item_tax_template.json b/erpnext/accounts/doctype/item_tax_template/item_tax_template.json index f713cfc0bac..856c371ecfa 100644 --- a/erpnext/accounts/doctype/item_tax_template/item_tax_template.json +++ b/erpnext/accounts/doctype/item_tax_template/item_tax_template.json @@ -1,168 +1,85 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 1, - "autoname": "field:title", - "beta": 0, - "creation": "2018-11-22 22:45:00.370913", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Setup", - "editable_grid": 1, - "engine": "InnoDB", + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:title", + "creation": "2018-11-22 22:45:00.370913", + "doctype": "DocType", + "document_type": "Setup", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "title", + "company", + "taxes" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "title", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 1, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Title", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "fieldname": "title", + "fieldtype": "Data", + "in_filter": 1, + "in_list_view": 1, + "label": "Title", + "no_copy": 1, + "reqd": 1, "unique": 1 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "taxes", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Tax Rates", - "length": 0, - "no_copy": 0, - "options": "Item Tax Template Detail", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "taxes", + "fieldtype": "Table", + "label": "Tax Rates", + "options": "Item Tax Template Detail", + "reqd": 1 + }, + { + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "reqd": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-12-21 23:51:16.328340", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Item Tax Template", - "name_case": "", - "owner": "Administrator", + ], + "modified": "2020-06-18 20:27:42.615842", + "modified_by": "ahmad@havenir.com", + "module": "Accounts", + "name": "Item Tax Template", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, "write": 1 - }, + }, { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts Manager", + "share": 1, "write": 1 - }, + }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts User", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 0 + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts User", + "share": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "search_fields": "", - "show_name_in_global_search": 1, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + ], + "show_name_in_global_search": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file From ad3d44d62272c87d275ae26adc4b5338ace698c8 Mon Sep 17 00:00:00 2001 From: mohammadahmad1990 Date: Thu, 18 Jun 2020 23:16:16 +0500 Subject: [PATCH 522/608] fix: Item Tax Template check on company field --- erpnext/stock/doctype/item_tax/item_tax.json | 5 +++-- erpnext/stock/get_item_details.py | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/doctype/item_tax/item_tax.json b/erpnext/stock/doctype/item_tax/item_tax.json index cf3c0e9c347..8ff6f69fdb0 100644 --- a/erpnext/stock/doctype/item_tax/item_tax.json +++ b/erpnext/stock/doctype/item_tax/item_tax.json @@ -41,12 +41,13 @@ "fieldtype": "Link", "in_list_view": 1, "label": "Company", - "options": "Company" + "options": "Company", + "read_only": 1 } ], "idx": 1, "istable": 1, - "modified": "2020-06-18 02:30:44.610171", + "modified": "2020-06-18 22:53:44.546967", "modified_by": "Administrator", "module": "Stock", "name": "Item Tax", diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 67333f4aa9b..143a8ebef85 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -413,7 +413,7 @@ def get_item_tax_info(company, tax_category, item_codes): continue out[item_code] = {} item = frappe.get_cached_doc("Item", item_code) - get_item_tax_template({"tax_category": tax_category}, item, out[item_code]) + get_item_tax_template({"company": company, "tax_category": tax_category}, item, out[item_code]) out[item_code]["item_tax_rate"] = get_item_tax_map(company, out[item_code].get("item_tax_template"), as_json=True) return out From 261ecba3d64afd0b746c72933255c2c5427fef4b Mon Sep 17 00:00:00 2001 From: mohammadahmad1990 Date: Thu, 25 Jun 2020 01:56:53 +0500 Subject: [PATCH 523/608] Company field removed from Item Taxes Table --- erpnext/stock/doctype/item_tax/item_tax.json | 14 ++------------ erpnext/stock/get_item_details.py | 5 +++-- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/erpnext/stock/doctype/item_tax/item_tax.json b/erpnext/stock/doctype/item_tax/item_tax.json index 8ff6f69fdb0..ae36efc7e3f 100644 --- a/erpnext/stock/doctype/item_tax/item_tax.json +++ b/erpnext/stock/doctype/item_tax/item_tax.json @@ -6,8 +6,7 @@ "field_order": [ "item_tax_template", "tax_category", - "valid_from", - "company" + "valid_from" ], "fields": [ { @@ -34,20 +33,11 @@ "fieldtype": "Date", "in_list_view": 1, "label": "Valid From" - }, - { - "fetch_from": "item_tax_template.company", - "fieldname": "company", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Company", - "options": "Company", - "read_only": 1 } ], "idx": 1, "istable": 1, - "modified": "2020-06-18 22:53:44.546967", + "modified": "2020-06-25 01:40:28.859752", "modified_by": "Administrator", "module": "Stock", "name": "Item Tax", diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 143a8ebef85..e6a545f6ecf 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -442,7 +442,8 @@ def _get_item_tax_template(args, taxes, out={}, for_validate=False): taxes_with_no_validity = [] for tax in taxes: - if tax.valid_from and tax.company == args['company']: + tax_company = frappe.get_value("Item Tax Template", tax.item_tax_template, 'company') + if tax.valid_from and tax_company == args['company']: # In purchase Invoice first preference will be given to supplier invoice date # if supplier date is not present then posting date validation_date = args.get('transaction_date') or args.get('bill_date') or args.get('posting_date') @@ -450,7 +451,7 @@ def _get_item_tax_template(args, taxes, out={}, for_validate=False): if getdate(tax.valid_from) <= getdate(validation_date): taxes_with_validity.append(tax) else: - if tax.company == args['company']: + if tax_company == args['company']: taxes_with_no_validity.append(tax) if taxes_with_validity: From f933cf80dffb4f650ea453ea697a36d7d98eb343 Mon Sep 17 00:00:00 2001 From: mohammadahmad1990 Date: Thu, 25 Jun 2020 02:12:35 +0500 Subject: [PATCH 524/608] Patch for Updating item Tax Template Company --- .../patches/v12_0/update_item_tax_template_company.py | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 erpnext/patches/v12_0/update_item_tax_template_company.py diff --git a/erpnext/patches/v12_0/update_item_tax_template_company.py b/erpnext/patches/v12_0/update_item_tax_template_company.py new file mode 100644 index 00000000000..54ce78ed222 --- /dev/null +++ b/erpnext/patches/v12_0/update_item_tax_template_company.py @@ -0,0 +1,11 @@ +from __future__ import unicode_literals +import frappe + +def execute(): + item_tax_template_list = frappe.get_list('Item Tax Template') + for template in item_tax_template_list: + doc = frappe.get_doc('Item Tax Template', template.name) + for tax in doc.taxes: + doc.company = frappe.get_value('Account', tax.tax_type, 'company') + break + doc.save() \ No newline at end of file From b2725577b204d1d32da38f584a752150c0003a11 Mon Sep 17 00:00:00 2001 From: mohammadahmad1990 Date: Thu, 25 Jun 2020 02:20:42 +0500 Subject: [PATCH 525/608] Patch file updated --- erpnext/patches.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index c7a7abf8196..7fe5c61c4fb 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -707,3 +707,5 @@ erpnext.patches.v13_0.move_doctype_reports_and_notification_from_hr_to_payroll # erpnext.patches.v13_0.move_payroll_setting_separately_from_hr_settings #22-06-2020 erpnext.patches.v13_0.check_is_income_tax_component #22-06-2020 erpnext.patches.v12_0.add_taxjar_integration_field +execute:frappe.reload_doc('account', 'doctype', 'item_tax_template') +erpnext.patches.v12_0.update_item_tax_template_company From 405b20884bf3a6f53628d1c398775a69627f6fcf Mon Sep 17 00:00:00 2001 From: mohammadahmad1990 Date: Fri, 3 Jul 2020 15:15:27 +0500 Subject: [PATCH 526/608] fix: patch --- erpnext/patches.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 7fe5c61c4fb..62dd775b9cd 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -707,5 +707,5 @@ erpnext.patches.v13_0.move_doctype_reports_and_notification_from_hr_to_payroll # erpnext.patches.v13_0.move_payroll_setting_separately_from_hr_settings #22-06-2020 erpnext.patches.v13_0.check_is_income_tax_component #22-06-2020 erpnext.patches.v12_0.add_taxjar_integration_field -execute:frappe.reload_doc('account', 'doctype', 'item_tax_template') +execute:frappe.reload_doc('accounts', 'doctype', 'item_tax_template') erpnext.patches.v12_0.update_item_tax_template_company From 8e620359919678a971806f3fe005d7c15da28a53 Mon Sep 17 00:00:00 2001 From: marination Date: Fri, 3 Jul 2020 16:19:55 +0530 Subject: [PATCH 527/608] fix: Quality Feedback and Template (minor) --- .../quality_feedback/quality_feedback.js | 37 +++++++++++-------- .../quality_feedback/quality_feedback.json | 6 ++- .../quality_feedback_template.json | 7 +++- 3 files changed, 31 insertions(+), 19 deletions(-) diff --git a/erpnext/quality_management/doctype/quality_feedback/quality_feedback.js b/erpnext/quality_management/doctype/quality_feedback/quality_feedback.js index 63747afb3f4..dac6ac40a71 100644 --- a/erpnext/quality_management/doctype/quality_feedback/quality_feedback.js +++ b/erpnext/quality_management/doctype/quality_feedback/quality_feedback.js @@ -5,21 +5,28 @@ frappe.ui.form.on('Quality Feedback', { refresh: function(frm) { frm.set_value("date", frappe.datetime.get_today()); }, - template: function(frm){ - frappe.call({ - "method": "frappe.client.get", - args: { - doctype: "Quality Feedback Template", - name: frm.doc.template - }, - callback: function(data){ - frm.fields_dict.parameters.grid.remove_all(); - for (var i in data.message.parameters){ - frm.add_child("parameters"); - frm.fields_dict.parameters.get_value()[i].parameter = data.message.parameters[i].parameter; + + template: function(frm) { + if (frm.doc.template) { + frappe.call({ + "method": "frappe.client.get", + args: { + doctype: "Quality Feedback Template", + name: frm.doc.template + }, + callback: function(data) { + if (data && data.message) { + frm.fields_dict.parameters.grid.remove_all(); + + // fetch parameters from template and autofill + for (let template_parameter of data.message.parameters) { + let row = frm.add_child("parameters"); + row.parameter = template_parameter.parameter; + } + frm.refresh(); + } } - frm.refresh(); - } - }); + }); + } } }); diff --git a/erpnext/quality_management/doctype/quality_feedback/quality_feedback.json b/erpnext/quality_management/doctype/quality_feedback/quality_feedback.json index 460438af621..ab9084fa79b 100644 --- a/erpnext/quality_management/doctype/quality_feedback/quality_feedback.json +++ b/erpnext/quality_management/doctype/quality_feedback/quality_feedback.json @@ -1,4 +1,5 @@ { + "actions": [], "autoname": "format:FDBK-{#####}", "creation": "2019-05-26 21:23:05.308379", "doctype": "DocType", @@ -53,12 +54,13 @@ { "fieldname": "document_name", "fieldtype": "Dynamic Link", - "label": "Name", + "label": "Feedback By", "options": "document_type", "reqd": 1 } ], - "modified": "2019-05-28 15:16:01.161662", + "links": [], + "modified": "2020-07-03 15:50:58.589302", "modified_by": "Administrator", "module": "Quality Management", "name": "Quality Feedback", diff --git a/erpnext/quality_management/doctype/quality_feedback_template/quality_feedback_template.json b/erpnext/quality_management/doctype/quality_feedback_template/quality_feedback_template.json index 31efd04682a..bdc9dbab492 100644 --- a/erpnext/quality_management/doctype/quality_feedback_template/quality_feedback_template.json +++ b/erpnext/quality_management/doctype/quality_feedback_template/quality_feedback_template.json @@ -1,4 +1,5 @@ { + "actions": [], "autoname": "format:TMPL-{template}", "creation": "2019-05-26 21:17:24.283061", "doctype": "DocType", @@ -30,10 +31,12 @@ "fieldname": "parameters", "fieldtype": "Table", "label": "Parameters", - "options": "Quality Feedback Template Parameter" + "options": "Quality Feedback Template Parameter", + "reqd": 1 } ], - "modified": "2019-05-26 21:48:47.770610", + "links": [], + "modified": "2020-07-03 16:06:03.749415", "modified_by": "Administrator", "module": "Quality Management", "name": "Quality Feedback Template", From d7adacd3e290db5cc1318ebddeb0ea27e6d2fb5b Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 3 Jul 2020 18:39:51 +0530 Subject: [PATCH 528/608] feat: Added all companies option in employee tree to view employee accress all companies --- erpnext/hr/doctype/employee/employee.py | 6 +++++- erpnext/hr/doctype/employee/employee_tree.js | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/erpnext/hr/doctype/employee/employee.py b/erpnext/hr/doctype/employee/employee.py index 4d49503d2dc..7338cbb6c85 100755 --- a/erpnext/hr/doctype/employee/employee.py +++ b/erpnext/hr/doctype/employee/employee.py @@ -413,7 +413,11 @@ def get_employee_emails(employee_list): @frappe.whitelist() def get_children(doctype, parent=None, company=None, is_root=False, is_tree=False): - filters = [['company', '=', company]] + + filters = [] + if company and company != 'All Companies': + filters = [['company', '=', company]] + fields = ['name as value', 'employee_name as title'] if is_root: diff --git a/erpnext/hr/doctype/employee/employee_tree.js b/erpnext/hr/doctype/employee/employee_tree.js index 0a2da63058a..9ab091a1eb6 100644 --- a/erpnext/hr/doctype/employee/employee_tree.js +++ b/erpnext/hr/doctype/employee/employee_tree.js @@ -4,7 +4,7 @@ frappe.treeview_settings['Employee'] = { { fieldname: "company", fieldtype:"Select", - options: erpnext.utils.get_tree_options("company"), + options: ['All Companies'].concat(erpnext.utils.get_tree_options("company")), label: __("Company"), default: erpnext.utils.get_tree_default("company") } From 2cbf598b4b7ca732a25768978bee332a36ff6df5 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 3 Jul 2020 16:02:55 +0530 Subject: [PATCH 529/608] fix: travis --- erpnext/loan_management/doctype/loan/test_loan.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/erpnext/loan_management/doctype/loan/test_loan.py b/erpnext/loan_management/doctype/loan/test_loan.py index 1f771c2090c..c65996e65f2 100644 --- a/erpnext/loan_management/doctype/loan/test_loan.py +++ b/erpnext/loan_management/doctype/loan/test_loan.py @@ -79,10 +79,12 @@ class TestLoan(unittest.TestCase): "qty": 4000.00, }] - loan_application = create_loan_application('_Test Company', self.applicant2, 'Stock Loan', pledge, "Repay Over Number of Periods", 12) + loan_application = create_loan_application('_Test Company', self.applicant2, + 'Stock Loan', pledge, "Repay Over Number of Periods", 12) create_pledge(loan_application) - loan = create_loan_with_security(self.applicant2, "Stock Loan", "Repay Over Number of Periods", 12, loan_application) + loan = create_loan_with_security(self.applicant2, "Stock Loan", "Repay Over Number of Periods", + 12, loan_application) self.assertEquals(loan.loan_amount, 1000000) def test_loan_disbursement(self): @@ -92,6 +94,7 @@ class TestLoan(unittest.TestCase): }] loan_application = create_loan_application('_Test Company', self.applicant2, 'Stock Loan', pledge, "Repay Over Number of Periods", 12) + create_pledge(loan_application) loan = create_loan_with_security(self.applicant2, "Stock Loan", "Repay Over Number of Periods", 12, loan_application) @@ -217,7 +220,8 @@ class TestLoan(unittest.TestCase): "qty": 2000.00 }] - loan_application = create_loan_application('_Test Company', self.applicant2, 'Stock Loan', pledges) + loan_application = create_loan_application('_Test Company', self.applicant2, 'Stock Loan', pledges, + "Repay Over Number of Periods", 12) create_pledge(loan_application) loan = create_loan_with_security(self.applicant2, "Stock Loan", "Repay Over Number of Periods", 12, loan_application, @@ -247,7 +251,9 @@ class TestLoan(unittest.TestCase): "haircut": 50, }] - loan_application = create_loan_application('_Test Company', self.applicant2, 'Stock Loan', pledges) + loan_application = create_loan_application('_Test Company', self.applicant2, + 'Stock Loan', pledges, "Repay Over Number of Periods", 12) + create_pledge(loan_application) loan = create_loan_with_security(self.applicant2, "Stock Loan", "Repay Over Number of Periods", 12, loan_application) From 04661a4e15e9b0cedbb93162b6a6652b767d5995 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 3 Jul 2020 21:23:23 +0530 Subject: [PATCH 530/608] fix: Update item tax only if item code available --- erpnext/regional/united_arab_emirates/utils.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/erpnext/regional/united_arab_emirates/utils.py b/erpnext/regional/united_arab_emirates/utils.py index 772bbf5914b..a0425f6b1c2 100644 --- a/erpnext/regional/united_arab_emirates/utils.py +++ b/erpnext/regional/united_arab_emirates/utils.py @@ -11,14 +11,17 @@ def update_itemised_tax_data(doc): for row in doc.items: tax_rate = 0.0 - item_tax_rate = frappe.parse_json(row.item_tax_rate) + item_tax_rate = 0.0 + + if row.item_tax_rate: + item_tax_rate = frappe.parse_json(row.item_tax_rate) # First check if tax rate is present # If not then look up in item_wise_tax_detail if item_tax_rate: for account, rate in iteritems(item_tax_rate): tax_rate += rate - elif itemised_tax.get(row.item_code): + elif row.item_code and itemised_tax.get(row.item_code): tax_rate = sum([tax.get('tax_rate', 0) for d, tax in itemised_tax.get(row.item_code).items()]) row.tax_rate = flt(tax_rate, row.precision("tax_rate")) From e27b996e38aef0caf233b60c911b484470541de0 Mon Sep 17 00:00:00 2001 From: marination Date: Fri, 3 Jul 2020 22:03:25 +0530 Subject: [PATCH 531/608] fix: Check for Company before trying to fetch party details --- erpnext/accounts/party.py | 2 +- erpnext/public/js/utils/party.js | 80 ++++++++++++++++++-------------- 2 files changed, 45 insertions(+), 37 deletions(-) diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index db91b6696eb..b764eff12c1 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -388,7 +388,7 @@ def set_taxes(party, party_type, posting_date, company, customer_group=None, sup from erpnext.accounts.doctype.tax_rule.tax_rule import get_tax_template, get_party_details args = { party_type.lower(): party, - "company": company + "company": company } if tax_category: diff --git a/erpnext/public/js/utils/party.js b/erpnext/public/js/utils/party.js index 99c1b8ae8f3..065326744c2 100644 --- a/erpnext/public/js/utils/party.js +++ b/erpnext/public/js/utils/party.js @@ -4,7 +4,7 @@ frappe.provide("erpnext.utils"); erpnext.utils.get_party_details = function(frm, method, args, callback) { - if(!method) { + if (!method) { method = "erpnext.accounts.party.get_party_details"; } @@ -22,12 +22,12 @@ erpnext.utils.get_party_details = function(frm, method, args, callback) { } } - if(!args) { - if((frm.doctype != "Purchase Order" && frm.doc.customer) + if (!args) { + if ((frm.doctype != "Purchase Order" && frm.doc.customer) || (frm.doc.party_name && in_list(['Quotation', 'Opportunity'], frm.doc.doctype))) { let party_type = "Customer"; - if(frm.doc.quotation_to && frm.doc.quotation_to === "Lead") { + if (frm.doc.quotation_to && frm.doc.quotation_to === "Lead") { party_type = "Lead"; } @@ -36,7 +36,7 @@ erpnext.utils.get_party_details = function(frm, method, args, callback) { party_type: party_type, price_list: frm.doc.selling_price_list }; - } else if(frm.doc.supplier) { + } else if (frm.doc.supplier) { args = { party: frm.doc.supplier, party_type: "Supplier", @@ -78,13 +78,17 @@ erpnext.utils.get_party_details = function(frm, method, args, callback) { args.posting_date = frm.doc.posting_date || frm.doc.transaction_date; } } - if(!args || !args.party) return; + if (!args || !args.party) return; - if(frappe.meta.get_docfield(frm.doc.doctype, "taxes")) { - if(!erpnext.utils.validate_mandatory(frm, "Posting/Transaction Date", + if (frappe.meta.get_docfield(frm.doc.doctype, "taxes")) { + if (!erpnext.utils.validate_mandatory(frm, "Posting / Transaction Date", args.posting_date, args.party_type=="Customer" ? "customer": "supplier")) return; } + if (!erpnext.utils.validate_mandatory(frm, "Company", frm.doc.company, args.party_type=="Customer" ? "customer": "supplier")) { + return; + } + args.currency = frm.doc.currency; args.company = frm.doc.company; args.doctype = frm.doc.doctype; @@ -92,14 +96,14 @@ erpnext.utils.get_party_details = function(frm, method, args, callback) { method: method, args: args, callback: function(r) { - if(r.message) { + if (r.message) { frm.supplier_tds = r.message.supplier_tds; frm.updating_party_details = true; frappe.run_serially([ () => frm.set_value(r.message), () => { frm.updating_party_details = false; - if(callback) callback(); + if (callback) callback(); frm.refresh(); erpnext.utils.add_item(frm); } @@ -110,9 +114,9 @@ erpnext.utils.get_party_details = function(frm, method, args, callback) { } erpnext.utils.add_item = function(frm) { - if(frm.is_new()) { + if (frm.is_new()) { var prev_route = frappe.get_prev_route(); - if(prev_route[1]==='Item' && !(frm.doc.items && frm.doc.items.length)) { + if (prev_route[1]==='Item' && !(frm.doc.items && frm.doc.items.length)) { // add row var item = frm.add_child('items'); frm.refresh_field('items'); @@ -124,23 +128,23 @@ erpnext.utils.add_item = function(frm) { } erpnext.utils.get_address_display = function(frm, address_field, display_field, is_your_company_address) { - if(frm.updating_party_details) return; + if (frm.updating_party_details) return; - if(!address_field) { - if(frm.doctype != "Purchase Order" && frm.doc.customer) { + if (!address_field) { + if (frm.doctype != "Purchase Order" && frm.doc.customer) { address_field = "customer_address"; - } else if(frm.doc.supplier) { + } else if (frm.doc.supplier) { address_field = "supplier_address"; } else return; } - if(!display_field) display_field = "address_display"; - if(frm.doc[address_field]) { + if (!display_field) display_field = "address_display"; + if (frm.doc[address_field]) { frappe.call({ method: "frappe.contacts.doctype.address.address.get_address_display", args: {"address_dict": frm.doc[address_field] }, callback: function(r) { - if(r.message) { + if (r.message) { frm.set_value(display_field, r.message) } } @@ -151,15 +155,15 @@ erpnext.utils.get_address_display = function(frm, address_field, display_field, }; erpnext.utils.set_taxes_from_address = function(frm, triggered_from_field, billing_address_field, shipping_address_field) { - if(frm.updating_party_details) return; + if (frm.updating_party_details) return; - if(frappe.meta.get_docfield(frm.doc.doctype, "taxes")) { - if(!erpnext.utils.validate_mandatory(frm, "Lead/Customer/Supplier", + if (frappe.meta.get_docfield(frm.doc.doctype, "taxes")) { + if (!erpnext.utils.validate_mandatory(frm, "Lead / Customer / Supplier", frm.doc.customer || frm.doc.supplier || frm.doc.lead || frm.doc.party_name, triggered_from_field)) { return; } - if(!erpnext.utils.validate_mandatory(frm, "Posting/Transaction Date", + if (!erpnext.utils.validate_mandatory(frm, "Posting / Transaction Date", frm.doc.posting_date || frm.doc.transaction_date, triggered_from_field)) { return; } @@ -175,8 +179,8 @@ erpnext.utils.set_taxes_from_address = function(frm, triggered_from_field, billi "shipping_address": frm.doc[shipping_address_field] }, callback: function(r) { - if(!r.exc){ - if(frm.doc.tax_category != r.message) { + if (!r.exc){ + if (frm.doc.tax_category != r.message) { frm.set_value("tax_category", r.message); } else { erpnext.utils.set_taxes(frm, triggered_from_field); @@ -187,13 +191,17 @@ erpnext.utils.set_taxes_from_address = function(frm, triggered_from_field, billi }; erpnext.utils.set_taxes = function(frm, triggered_from_field) { - if(frappe.meta.get_docfield(frm.doc.doctype, "taxes")) { - if(!erpnext.utils.validate_mandatory(frm, "Lead/Customer/Supplier", + if (frappe.meta.get_docfield(frm.doc.doctype, "taxes")) { + if (!erpnext.utils.validate_mandatory(frm, "Company", frm.doc.company, triggered_from_field)) { + return; + } + + if (!erpnext.utils.validate_mandatory(frm, "Lead / Customer / Supplier", frm.doc.customer || frm.doc.supplier || frm.doc.lead || frm.doc.party_name, triggered_from_field)) { return; } - if(!erpnext.utils.validate_mandatory(frm, "Posting/Transaction Date", + if (!erpnext.utils.validate_mandatory(frm, "Posting / Transaction Date", frm.doc.posting_date || frm.doc.transaction_date, triggered_from_field)) { return; } @@ -230,7 +238,7 @@ erpnext.utils.set_taxes = function(frm, triggered_from_field) { "shipping_address": frm.doc.shipping_address_name }, callback: function(r) { - if(r.message){ + if (r.message){ frm.set_value("taxes_and_charges", r.message) } } @@ -238,14 +246,14 @@ erpnext.utils.set_taxes = function(frm, triggered_from_field) { }; erpnext.utils.get_contact_details = function(frm) { - if(frm.updating_party_details) return; + if (frm.updating_party_details) return; - if(frm.doc["contact_person"]) { + if (frm.doc["contact_person"]) { frappe.call({ method: "frappe.contacts.doctype.contact.contact.get_contact_details", args: {contact: frm.doc.contact_person }, callback: function(r) { - if(r.message) + if (r.message) frm.set_value(r.message); } }) @@ -253,10 +261,10 @@ erpnext.utils.get_contact_details = function(frm) { } erpnext.utils.validate_mandatory = function(frm, label, value, trigger_on) { - if(!value) { + if (!value) { frm.doc[trigger_on] = ""; refresh_field(trigger_on); - frappe.msgprint(__("Please enter {0} first", [label])); + frappe.throw({message:__("Please enter {0} first", [label]), title:__("Mandatory")}); return false; } return true; @@ -271,12 +279,12 @@ erpnext.utils.get_shipping_address = function(frm, callback){ address: frm.doc.shipping_address }, callback: function(r){ - if(r.message){ + if (r.message){ frm.set_value("shipping_address", r.message[0]) //Address title or name frm.set_value("shipping_address_display", r.message[1]) //Address to be displayed on the page } - if(callback){ + if (callback){ return callback(); } } From 96d40ec9dacc992fd45b9bfbb2007cec0b682084 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 3 Jul 2020 22:59:00 +0530 Subject: [PATCH 532/608] fix: Consider company fiscal for getting balalnce --- erpnext/accounts/utils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 16146f4480b..69866e1566c 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -133,7 +133,7 @@ def get_balance_on(account=None, date=None, party_type=None, party=None, company acc = frappe.get_doc("Account", account) try: - year_start_date = get_fiscal_year(date, verbose=0)[1] + year_start_date = get_fiscal_year(date, company=company, verbose=0)[1] except FiscalYearError: if getdate(date) > getdate(nowdate()): # if fiscal year not found and the date is greater than today @@ -787,10 +787,10 @@ def get_children(doctype, parent, company, is_root=False): company_currency = frappe.get_cached_value('Company', company, "default_currency") for each in acc: each["company_currency"] = company_currency - each["balance"] = flt(get_balance_on(each.get("value"), in_account_currency=False)) + each["balance"] = flt(get_balance_on(each.get("value"), in_account_currency=False, company=company)) if each.account_currency != company_currency: - each["balance_in_account_currency"] = flt(get_balance_on(each.get("value"))) + each["balance_in_account_currency"] = flt(get_balance_on(each.get("value"), company=company)) return acc From f0f4f70a7663adc7698ac8ba76557f95ea48bc56 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Sat, 4 Jul 2020 13:30:22 +0530 Subject: [PATCH 533/608] fix: test case for salary slip --- erpnext/payroll/doctype/salary_slip/test_salary_slip.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py index be9a2d37284..37cd89a7349 100644 --- a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py +++ b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py @@ -105,7 +105,7 @@ class TestSalarySlip(unittest.TestCase): #Gross pay calculation based on attendances gross_pay = 78000 - ((78000 / (days_in_month - no_of_holidays)) * flt(ss.leave_without_pay)) - self.assertEqual(ss.gross_pay, gross_pay) + self.assertEqual(flt(ss.gross_pay, 2), flt(gross_pay, 2)) frappe.db.set_value("Payroll Settings", None, "payroll_based_on", "Leave") From be05c4e14ae3db924b1b904abe1eb6328f28d0a7 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 5 Jul 2020 18:16:16 +0530 Subject: [PATCH 534/608] fix: Codacy and minor fixes --- erpnext/accounts/doctype/item_tax_template/item_tax_template.js | 2 +- erpnext/patches.txt | 1 - erpnext/patches/v12_0/update_item_tax_template_company.py | 2 ++ 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/item_tax_template/item_tax_template.js b/erpnext/accounts/doctype/item_tax_template/item_tax_template.js index e30ef61a232..e921a0d949d 100644 --- a/erpnext/accounts/doctype/item_tax_template/item_tax_template.js +++ b/erpnext/accounts/doctype/item_tax_template/item_tax_template.js @@ -13,7 +13,7 @@ frappe.ui.form.on('Item Tax Template', { } }); }, - company: function (frm) { + company: function (frm) { frm.set_query("tax_type", "taxes", function(doc) { return { filters: [ diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 62dd775b9cd..cd4a221843c 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -707,5 +707,4 @@ erpnext.patches.v13_0.move_doctype_reports_and_notification_from_hr_to_payroll # erpnext.patches.v13_0.move_payroll_setting_separately_from_hr_settings #22-06-2020 erpnext.patches.v13_0.check_is_income_tax_component #22-06-2020 erpnext.patches.v12_0.add_taxjar_integration_field -execute:frappe.reload_doc('accounts', 'doctype', 'item_tax_template') erpnext.patches.v12_0.update_item_tax_template_company diff --git a/erpnext/patches/v12_0/update_item_tax_template_company.py b/erpnext/patches/v12_0/update_item_tax_template_company.py index 54ce78ed222..f7496999b33 100644 --- a/erpnext/patches/v12_0/update_item_tax_template_company.py +++ b/erpnext/patches/v12_0/update_item_tax_template_company.py @@ -2,6 +2,8 @@ from __future__ import unicode_literals import frappe def execute(): + frappe.reload_doc('accounts', 'doctype', 'item_tax_template') + item_tax_template_list = frappe.get_list('Item Tax Template') for template in item_tax_template_list: doc = frappe.get_doc('Item Tax Template', template.name) From e88f2bb05325c267ee28d5221aae53c0a6d8ffd5 Mon Sep 17 00:00:00 2001 From: Anupam K Date: Mon, 6 Jul 2020 12:34:05 +0530 Subject: [PATCH 535/608] number card filter --- erpnext/crm/dashboard_fixtures.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/erpnext/crm/dashboard_fixtures.py b/erpnext/crm/dashboard_fixtures.py index 0535cbbcc95..901c0581f4c 100644 --- a/erpnext/crm/dashboard_fixtures.py +++ b/erpnext/crm/dashboard_fixtures.py @@ -172,7 +172,9 @@ def get_number_cards(): "doctype": "Number Card", "document_type": "Lead", "name": "New Lead (Last 1 Month)", - "filters_json": json.dumps([["Lead","creation","Previous","1 month",False]]), + "filters_json": json.dumps([ + ["Lead", "creation", "Timespan", "last month"] + ]), "function": "Count", "is_public": 1, "label": _("New Lead (Last 1 Month)"), @@ -183,7 +185,9 @@ def get_number_cards(): "doctype": "Number Card", "document_type": "Opportunity", "name": "New Opportunity (Last 1 Month)", - "filters_json": json.dumps([["Opportunity","creation","Previous","1 month",False]]), + "filters_json": json.dumps([ + ["Opportunity", "creation", "Timespan", "last month"] + ]), "function": "Count", "is_public": 1, "label": _("New Opportunity (Last 1 Month)"), @@ -194,7 +198,10 @@ def get_number_cards(): "doctype": "Number Card", "document_type": "Opportunity", "name": "Won Opportunity (Last 1 Month)", - "filters_json": json.dumps([["Opportunity","creation","Previous","1 month",False]]), + "filters_json": json.dumps([ + ["Opportunity", "status", "=", "Converted",False], + ["Opportunity", "creation", "Timespan", "last month"] + ]), "function": "Count", "is_public": 1, "label": _("Won Opportunity (Last 1 Month)"), From 05738bd29aa7b46f86a3f3b7da5d06d3fb571bc4 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 6 Jul 2020 13:40:48 +0530 Subject: [PATCH 536/608] fix: Remove print statements --- erpnext/accounts/general_ledger.py | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index a1f8c037b30..bfe35ab0068 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -20,7 +20,6 @@ def make_gl_entries(gl_map, cancel=False, adv_adj=False, merge_entries=True, upd if not cancel: validate_accounting_period(gl_map) gl_map = process_gl_map(gl_map, merge_entries) - print(gl_map, "$$$$$$$$$") if gl_map and len(gl_map) > 1: save_entries(gl_map, adv_adj, update_outstanding) else: From e3e5fd7e7411e7f1e5976eb17fc4925f027eb3e7 Mon Sep 17 00:00:00 2001 From: Marica Date: Mon, 6 Jul 2020 18:26:19 +0530 Subject: [PATCH 537/608] fix: Skip Progress and Completed by fields on Task Duplication (#22565) --- erpnext/projects/doctype/task/task.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/projects/doctype/task/task.json b/erpnext/projects/doctype/task/task.json index f4b3d3e1ada..4db1f193ce2 100644 --- a/erpnext/projects/doctype/task/task.json +++ b/erpnext/projects/doctype/task/task.json @@ -183,7 +183,8 @@ { "fieldname": "progress", "fieldtype": "Percent", - "label": "% Progress" + "label": "% Progress", + "no_copy": 1 }, { "default": "0", @@ -356,6 +357,7 @@ "fieldname": "completed_by", "fieldtype": "Link", "label": "Completed By", + "no_copy": 1, "options": "User" } ], @@ -364,7 +366,7 @@ "is_tree": 1, "links": [], "max_attachments": 5, - "modified": "2020-03-18 18:08:44.153211", + "modified": "2020-07-03 12:36:04.960457", "modified_by": "Administrator", "module": "Projects", "name": "Task", From 5f299a08bd38c1a080636724dc035bedafb9e4d7 Mon Sep 17 00:00:00 2001 From: Marica Date: Mon, 6 Jul 2020 18:26:56 +0530 Subject: [PATCH 538/608] fix: General Message in previous doc validation for buying and selling (#22546) --- erpnext/utilities/transaction_base.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/erpnext/utilities/transaction_base.py b/erpnext/utilities/transaction_base.py index 2359648c95c..5bf85d1c64c 100644 --- a/erpnext/utilities/transaction_base.py +++ b/erpnext/utilities/transaction_base.py @@ -120,6 +120,16 @@ class TransactionBase(StatusUpdater): def validate_rate_with_reference_doc(self, ref_details): + buying_doctypes = ["Purchase Order", "Purchase Invoice", "Purchase Receipt"] + selling_doctypes = ["Sales Invoice", "Delivery Note"] + + if self.doctype in buying_doctypes: + to_disable = "Maintain same rate throughout Purchase cycle" + settings_page = "Buying Settings" + else: + to_disable = "Maintain same rate throughout Sales cycle" + settings_page = "Selling Settings" + for ref_dt, ref_dn_field, ref_link_field in ref_details: for d in self.get("items"): if d.get(ref_link_field): @@ -129,8 +139,8 @@ class TransactionBase(StatusUpdater): frappe.msgprint(_("Row #{0}: Rate must be same as {1}: {2} ({3} / {4}) ") .format(d.idx, ref_dt, d.get(ref_dn_field), d.rate, ref_rate)) frappe.throw(_("To allow different rates, disable the {0} checkbox in {1}.") - .format(frappe.bold(_("Maintain Same Rate Throughout Sales Cycle")), - get_link_to_form("Selling Settings", "Selling Settings", frappe.bold("Selling Settings")))) + .format(frappe.bold(_(to_disable)), + get_link_to_form(settings_page, settings_page, frappe.bold(settings_page)))) def get_link_filters(self, for_doctype): if hasattr(self, "prev_link_mapper") and self.prev_link_mapper.get(for_doctype): From 4355d3cf0fc3de447d653775f2ca79f9812b98e5 Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 6 Jul 2020 23:15:08 +0530 Subject: [PATCH 539/608] fix: Exploded Item Rate --- erpnext/manufacturing/doctype/bom/bom.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 7d31a1cd15e..58b79d1cac8 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -494,7 +494,7 @@ class BOM(WebsiteGenerator): 'image' : d.image, 'stock_uom' : d.stock_uom, 'stock_qty' : flt(d.stock_qty), - 'rate' : d.base_rate, + 'rate' : flt(d.base_rate) / flt(d.conversion_factor), 'include_item_in_manufacturing': d.include_item_in_manufacturing })) From 3819c0bf2845652f5a1c6f2c5ed63ebba22f03ea Mon Sep 17 00:00:00 2001 From: Afshan Date: Mon, 6 Jul 2020 23:52:11 +0530 Subject: [PATCH 540/608] fix: cost center should only show option of selectedcompany --- erpnext/hr/doctype/expense_claim/expense_claim.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.js b/erpnext/hr/doctype/expense_claim/expense_claim.js index 8f8dafdd7ff..b6651e271f0 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.js +++ b/erpnext/hr/doctype/expense_claim/expense_claim.js @@ -113,6 +113,14 @@ cur_frm.cscript.calculate_total_amount = function(doc,cdt,cdn){ cur_frm.cscript.calculate_total(doc,cdt,cdn); }; +cur_frm.fields_dict['cost_center'].get_query = function(doc) { + return { + filters: { + "company": doc.company + } + } +} + erpnext.expense_claim = { set_title: function(frm) { if (!frm.doc.task) { From 70b3edd502eb8144144ef2151adea11f60bea8f2 Mon Sep 17 00:00:00 2001 From: Afshan Date: Tue, 7 Jul 2020 00:03:45 +0530 Subject: [PATCH 541/608] fix: format according to cordacy --- erpnext/hr/doctype/expense_claim/expense_claim.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.js b/erpnext/hr/doctype/expense_claim/expense_claim.js index b6651e271f0..221300b519a 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.js +++ b/erpnext/hr/doctype/expense_claim/expense_claim.js @@ -119,7 +119,7 @@ cur_frm.fields_dict['cost_center'].get_query = function(doc) { "company": doc.company } } -} +}; erpnext.expense_claim = { set_title: function(frm) { From e2fdff57776e6627a6cc66565c6ff6440f2def6f Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 7 Jul 2020 14:14:07 +0530 Subject: [PATCH 542/608] fix: Payment reco error in multicompany setup --- .../payment_reconciliation.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index 30804961861..8dc28e878cb 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -101,10 +101,10 @@ class PaymentReconciliation(Document): Having amount > 0 """.format( - doc=voucher_type, - dr_or_cr=dr_or_cr, - reconciled_dr_or_cr=reconciled_dr_or_cr, - party_type_field=frappe.scrub(self.party_type)), + doc=voucher_type, + dr_or_cr=dr_or_cr, + reconciled_dr_or_cr=reconciled_dr_or_cr, + party_type_field=frappe.scrub(self.party_type)), { 'party': self.party, 'party_type': self.party_type, @@ -170,7 +170,7 @@ class PaymentReconciliation(Document): reconcile_against_document(lst) if dr_or_cr_notes: - reconcile_dr_cr_note(dr_or_cr_notes) + reconcile_dr_cr_note(dr_or_cr_notes, self.receivable_payable_account) msgprint(_("Successfully Reconciled")) self.get_unreconciled_entries() @@ -261,7 +261,8 @@ class PaymentReconciliation(Document): return cond -def reconcile_dr_cr_note(dr_cr_notes): +def reconcile_dr_cr_note(dr_cr_notes, receivable_payable_account): + company = frappe.db.get_value('Account', receivable_payable_account, 'company') for d in dr_cr_notes: voucher_type = ('Credit Note' if d.voucher_type == 'Sales Invoice' else 'Debit Note') @@ -273,6 +274,7 @@ def reconcile_dr_cr_note(dr_cr_notes): "doctype": "Journal Entry", "voucher_type": voucher_type, "posting_date": today(), + "company": company, "accounts": [ { 'account': d.account, From 32f3d24dac3699854f0947c83830a2ba93d35b2e Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 7 Jul 2020 14:31:49 +0530 Subject: [PATCH 543/608] fix: Employee benefit application & quality inspection whitelist --- .../employee_benefit_application.py | 2 +- erpnext/stock/doctype/quality_inspection/quality_inspection.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.py b/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.py index e166a704d60..d7d00e64809 100644 --- a/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.py +++ b/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.py @@ -222,7 +222,7 @@ def get_benefit_amount_based_on_pro_rata(sal_struct, component_max_benefit): return benefit_amount - +@frappe.whitelist() def get_earning_components(doctype, txt, searchfield, start, page_len, filters): if len(filters) < 2: return {} diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.py b/erpnext/stock/doctype/quality_inspection/quality_inspection.py index 7c54fd03f91..568e7428765 100644 --- a/erpnext/stock/doctype/quality_inspection/quality_inspection.py +++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.py @@ -87,6 +87,7 @@ def item_query(doctype, txt, searchfield, start, page_len, filters): page_len = page_len, qi_condition = qi_condition), {'parent': filters.get('parent'), 'txt': "%%%s%%" % txt}) +@frappe.whitelist() def quality_inspection_query(doctype, txt, searchfield, start, page_len, filters): return frappe.get_all('Quality Inspection', limit_start=start, From ca351e354915042c008d8a9e10464b76eeb6ff15 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 7 Jul 2020 14:37:17 +0530 Subject: [PATCH 544/608] fix: Pass company --- .../doctype/payment_reconciliation/payment_reconciliation.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index 8dc28e878cb..8eaad7acd4b 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -170,7 +170,7 @@ class PaymentReconciliation(Document): reconcile_against_document(lst) if dr_or_cr_notes: - reconcile_dr_cr_note(dr_or_cr_notes, self.receivable_payable_account) + reconcile_dr_cr_note(dr_or_cr_notes, self.company) msgprint(_("Successfully Reconciled")) self.get_unreconciled_entries() @@ -261,8 +261,7 @@ class PaymentReconciliation(Document): return cond -def reconcile_dr_cr_note(dr_cr_notes, receivable_payable_account): - company = frappe.db.get_value('Account', receivable_payable_account, 'company') +def reconcile_dr_cr_note(dr_cr_notes, company): for d in dr_cr_notes: voucher_type = ('Credit Note' if d.voucher_type == 'Sales Invoice' else 'Debit Note') From f642be97270373043e99370e79a9b952d347c00a Mon Sep 17 00:00:00 2001 From: Mitali Deshpande Date: Tue, 7 Jul 2020 15:59:41 +0530 Subject: [PATCH 545/608] fix: Changed error message in the Product Bundle --- erpnext/selling/doctype/product_bundle/product_bundle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/product_bundle/product_bundle.py b/erpnext/selling/doctype/product_bundle/product_bundle.py index c8a71677f93..55580c46715 100644 --- a/erpnext/selling/doctype/product_bundle/product_bundle.py +++ b/erpnext/selling/doctype/product_bundle/product_bundle.py @@ -26,7 +26,7 @@ class ProductBundle(Document): def validate_child_items(self): for item in self.items: if frappe.db.exists("Product Bundle", item.item_code): - frappe.throw(_("Child Item should not be a Product Bundle. Please remove item `{0}` and save").format(item.item_code)) + frappe.throw(_("Row #{0}: Child Item should not be a Product Bundle. Please remove item `{1}` and Save").format(item.idx, frappe.bold(item.item_code))) def get_new_item_code(doctype, txt, searchfield, start, page_len, filters): from erpnext.controllers.queries import get_match_cond From 3d14011d23dd7285f92d4cd4e2250b735fd510aa Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 7 Jul 2020 18:30:54 +0530 Subject: [PATCH 546/608] fix: allow creating SLA documents even if SLA tracking is not enabled --- .../service_level_agreement/service_level_agreement.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py index c6923157064..24ddbf8b285 100644 --- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py +++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py @@ -6,7 +6,7 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document from frappe import _ -from frappe.utils import getdate, get_weekdays +from frappe.utils import getdate, get_weekdays, get_link_to_form class ServiceLevelAgreement(Document): @@ -73,8 +73,9 @@ class ServiceLevelAgreement(Document): frappe.throw(_("Workday {0} has been repeated.").format(repeated_days)) def validate_doc(self): - if not frappe.db.get_single_value("Support Settings", "track_service_level_agreement"): - frappe.throw(_("Service Level Agreement tracking is not enabled.")) + if not frappe.db.get_single_value("Support Settings", "track_service_level_agreement") and self.enable: + frappe.throw(_("{0} is not enabled in {1}").format(frappe.bold("Track Service Level Agreement"), + get_link_to_form("Support Settings", "Support Settings"))) if self.default_service_level_agreement: if frappe.db.exists("Service Level Agreement", {"default_service_level_agreement": "1", "name": ["!=", self.name]}): From 8bc92b90f5cfa45aba101aa674962487b938968a Mon Sep 17 00:00:00 2001 From: Anupam K Date: Wed, 8 Jul 2020 00:56:21 +0530 Subject: [PATCH 547/608] updating lead.py --- erpnext/crm/doctype/lead/lead.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/erpnext/crm/doctype/lead/lead.py b/erpnext/crm/doctype/lead/lead.py index ec7d14d6ae3..315d298eb43 100644 --- a/erpnext/crm/doctype/lead/lead.py +++ b/erpnext/crm/doctype/lead/lead.py @@ -114,10 +114,12 @@ class Lead(SellingController): def set_lead_name(self): if not self.lead_name: # Check for leads being created through data import - if not self.company_name and not self.flags.ignore_mandatory: + if not self.company_name and not self.email_id and not self.flags.ignore_mandatory: frappe.throw(_("A Lead requires either a person's name or an organization's name")) - - self.lead_name = self.company_name + elif self.company_name: + self.lead_name = self.company_name + else: + self.lead_name = self.email_id.split("@")[0] def set_title(self): if self.organization_lead: From 6669f7457a86d57aebbdaafa1b67ab79f4369478 Mon Sep 17 00:00:00 2001 From: Anupam K Date: Wed, 8 Jul 2020 01:24:53 +0530 Subject: [PATCH 548/608] hr dashboard number card filter fix --- erpnext/hr/dashboard_fixtures.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/hr/dashboard_fixtures.py b/erpnext/hr/dashboard_fixtures.py index 6d8091be647..1e9b4f3c93a 100644 --- a/erpnext/hr/dashboard_fixtures.py +++ b/erpnext/hr/dashboard_fixtures.py @@ -123,7 +123,7 @@ def get_number_cards(): number_cards.append( get_number_cards_doc("Employee", "New Joinees (Last year)", filters_json = json.dumps([ - ["Employee","date_of_joining","Previous","1 year"], + ["Employee","date_of_joining","Timespan","last year"], ["Employee","status","=","Active"] ]) ) @@ -131,7 +131,7 @@ def get_number_cards(): number_cards.append( get_number_cards_doc("Employee", "Employees Left (Last year)", filters_json = json.dumps([ - ["Employee", "relieving_date", "Previous", "1 year"], + ["Employee", "relieving_date", "Timespan", "last year"], ["Employee", "status", "=", "Left"] ]) ) @@ -139,7 +139,7 @@ def get_number_cards(): number_cards.append( get_number_cards_doc("Job Applicant", "Total Applicants (Last month)", filters_json = json.dumps([ - ["Job Applicant", "creation", "Previous", "1 month"] + ["Job Applicant", "creation", "Timespan", "last month"] ]) ) ) From 8c5c74ca065cbcb7a10793de1f4bbfd4114eddd1 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Wed, 8 Jul 2020 09:20:13 +0530 Subject: [PATCH 549/608] fix: Remove trailing spaces from labels --- erpnext/stock/doctype/item_price/item_price.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/doctype/item_price/item_price.json b/erpnext/stock/doctype/item_price/item_price.json index 2e0ddfdaefc..5f62381f8b3 100644 --- a/erpnext/stock/doctype/item_price/item_price.json +++ b/erpnext/stock/doctype/item_price/item_price.json @@ -172,7 +172,7 @@ "default": "Today", "fieldname": "valid_from", "fieldtype": "Date", - "label": "Valid From " + "label": "Valid From" }, { "default": "0", @@ -187,7 +187,7 @@ { "fieldname": "valid_upto", "fieldtype": "Date", - "label": "Valid Upto " + "label": "Valid Upto" }, { "fieldname": "section_break_24", @@ -208,7 +208,7 @@ "icon": "fa fa-flag", "idx": 1, "links": [], - "modified": "2020-02-28 14:21:25.580331", + "modified": "2020-07-06 22:31:32.943475", "modified_by": "Administrator", "module": "Stock", "name": "Item Price", From f3a6e30e6ab73a6f81df698cd9216ef565a32e22 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Wed, 8 Jul 2020 10:45:55 +0530 Subject: [PATCH 550/608] fix: Check if homepage is not setup-wizard The new desk changes sets the default homepage to 'workspace'. To avoid problem in the future, check if the value is not setup-wizard. Also throw an exception if the check passes, so it exits with error. --- erpnext/setup/install.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/erpnext/setup/install.py b/erpnext/setup/install.py index 74ff0ecfd8f..44f26bfd4ab 100644 --- a/erpnext/setup/install.py +++ b/erpnext/setup/install.py @@ -29,12 +29,12 @@ def after_install(): def check_setup_wizard_not_completed(): - if frappe.db.get_default('desktop:home_page') == 'desktop': - print() - print("ERPNext can only be installed on a fresh site where the setup wizard is not completed") - print("You can reinstall this site (after saving your data) using: bench --site [sitename] reinstall") - print() - return False + if frappe.db.get_default('desktop:home_page') != 'setup-wizard': + message = """Cannot install ERPNext. + +ERPNext can only be installed on a fresh site where the setup wizard is not completed. +You can reinstall this site (after saving your data) using: bench --site [sitename] reinstall""" + frappe.throw(message, exc=frappe.IncompatibleApp) def set_single_defaults(): From 923ed8a7412e022c52c7d0f9215cb18bc4d75107 Mon Sep 17 00:00:00 2001 From: Kaviya Periyasamy Date: Wed, 8 Jul 2020 11:15:04 +0530 Subject: [PATCH 551/608] fix: log type mandatory error while exposing api call to employee checkin --- erpnext/hr/doctype/employee_checkin/employee_checkin.js | 4 ++++ erpnext/hr/doctype/employee_checkin/employee_checkin.json | 5 ++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/erpnext/hr/doctype/employee_checkin/employee_checkin.js b/erpnext/hr/doctype/employee_checkin/employee_checkin.js index c2403ca2bd9..43023b6034b 100644 --- a/erpnext/hr/doctype/employee_checkin/employee_checkin.js +++ b/erpnext/hr/doctype/employee_checkin/employee_checkin.js @@ -6,5 +6,9 @@ frappe.ui.form.on('Employee Checkin', { if(!frm.doc.time) { frm.set_value("time", frappe.datetime.now_datetime()); } + }, + refresh: (frm) => { + // make log type mandatory + frm.set_df_property('log_type', 'reqd', frm.doc.log_type ? 0 : 1); } }); diff --git a/erpnext/hr/doctype/employee_checkin/employee_checkin.json b/erpnext/hr/doctype/employee_checkin/employee_checkin.json index 75f699751b8..d34316dc0f3 100644 --- a/erpnext/hr/doctype/employee_checkin/employee_checkin.json +++ b/erpnext/hr/doctype/employee_checkin/employee_checkin.json @@ -41,8 +41,7 @@ "fieldtype": "Select", "in_list_view": 1, "label": "Log Type", - "options": "\nIN\nOUT", - "reqd": 1 + "options": "\nIN\nOUT" }, { "fieldname": "shift", @@ -108,7 +107,7 @@ } ], "links": [], - "modified": "2020-01-23 04:57:42.551355", + "modified": "2020-07-08 11:02:32.660986", "modified_by": "Administrator", "module": "HR", "name": "Employee Checkin", From 9071a7de82f1e3e663f3ec7bb998e566598853cf Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 8 Jul 2020 11:27:25 +0530 Subject: [PATCH 552/608] fix: Add default cost center in payment reconciliation JV --- .../payment_reconciliation/payment_reconciliation.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index 8eaad7acd4b..35d8d34c518 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -281,7 +281,8 @@ def reconcile_dr_cr_note(dr_cr_notes, company): 'party_type': d.party_type, d.dr_or_cr: abs(d.allocated_amount), 'reference_type': d.against_voucher_type, - 'reference_name': d.against_voucher + 'reference_name': d.against_voucher, + 'cost_center': erpnext.get_default_cost_center(company) }, { 'account': d.account, @@ -290,7 +291,8 @@ def reconcile_dr_cr_note(dr_cr_notes, company): reconcile_dr_or_cr: (abs(d.allocated_amount) if abs(d.unadjusted_amount) > abs(d.allocated_amount) else abs(d.unadjusted_amount)), 'reference_type': d.voucher_type, - 'reference_name': d.voucher_no + 'reference_name': d.voucher_no, + 'cost_center': erpnext.get_default_cost_center(company) } ] }) From 0a06580e2c5e66c59b66d42878d26846c8320dcd Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Wed, 8 Jul 2020 12:06:28 +0530 Subject: [PATCH 553/608] fix: Update message Co-authored-by: gavin --- erpnext/setup/install.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/erpnext/setup/install.py b/erpnext/setup/install.py index 44f26bfd4ab..d470d75a8fc 100644 --- a/erpnext/setup/install.py +++ b/erpnext/setup/install.py @@ -30,9 +30,7 @@ def after_install(): def check_setup_wizard_not_completed(): if frappe.db.get_default('desktop:home_page') != 'setup-wizard': - message = """Cannot install ERPNext. - -ERPNext can only be installed on a fresh site where the setup wizard is not completed. + message = """ERPNext can only be installed on a fresh site where the setup wizard is not completed. You can reinstall this site (after saving your data) using: bench --site [sitename] reinstall""" frappe.throw(message, exc=frappe.IncompatibleApp) @@ -105,4 +103,3 @@ def add_company_to_session_defaults(): "ref_doctype": "Company" }) settings.save() - From e862b3f9ce523422c986642111614f1059b20932 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Wed, 8 Jul 2020 12:34:15 +0530 Subject: [PATCH 554/608] Update payroll_entry.js --- erpnext/payroll/doctype/payroll_entry/payroll_entry.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry.js b/erpnext/payroll/doctype/payroll_entry/payroll_entry.js index 0415ebf2fce..8d35a7be471 100644 --- a/erpnext/payroll/doctype/payroll_entry/payroll_entry.js +++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.js @@ -51,7 +51,7 @@ frappe.ui.form.on('Payroll Entry', { doc: frm.doc, method: 'fill_employee_details', }).then(r => { - if (r.docs[0].employees){ + if (r.docs && r.docs[0].employees){ frm.employees = r.docs[0].employees; frm.dirty(); frm.save(); From 1fce8b7475b827c099656ef8447d65dd4b157b98 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Wed, 8 Jul 2020 12:40:01 +0530 Subject: [PATCH 555/608] Update leave_application.js --- erpnext/hr/doctype/leave_application/leave_application.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/hr/doctype/leave_application/leave_application.js b/erpnext/hr/doctype/leave_application/leave_application.js index 9413120bc5f..4001a455075 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.js +++ b/erpnext/hr/doctype/leave_application/leave_application.js @@ -41,7 +41,7 @@ frappe.ui.form.on("Leave Application", { if (frm.doc.from_date == frm.doc.to_date && frm.doc.half_day == 1){ frm.doc.half_day_date = frm.doc.from_date; }else if (frm.doc.half_day == 0){ - frm.doc.half_day_date = undefined; + frm.doc.half_day_date = ""; } frm.toggle_reqd("half_day_date", frm.doc.half_day == 1); }, From 5d514fe4df89575b4d4111be9d55d216293eafdc Mon Sep 17 00:00:00 2001 From: Michelle Alva <50285544+michellealva@users.noreply.github.com> Date: Wed, 8 Jul 2020 14:25:51 +0530 Subject: [PATCH 556/608] fix: grammatical fixes in module onboarding (#22580) * fix: grammatical fixes in module onboarding * fix: more grammatical changes for consistency * fix: Reorder Stock Onboarding * fix: update success message * fix: change order of stock onboarding steps Co-authored-by: Marica --- .../module_onboarding/accounts/accounts.json | 9 +++-- .../create_a_customer/create_a_customer.json | 2 +- .../create_a_product/create_a_product.json | 4 +-- .../module_onboarding/assets/assets.json | 11 +++---- .../create_a_fixed_asset_item.json | 5 ++- .../create_an_asset/create_an_asset.json | 5 ++- .../create_an_asset_category.json | 33 ++++++++++--------- .../introduction_to_assets.json | 3 ++ .../purchase_an_asset_item.json | 5 ++- .../module_onboarding/buying/buying.json | 7 ++-- .../buying_settings/buying_settings.json | 10 +++--- .../setup_your_warehouse.json | 4 +-- erpnext/crm/module_onboarding/crm/crm.json | 9 +++-- .../healthcare/healthcare.json | 9 +++-- .../human_resource/human_resource.json | 9 +++-- .../manufacturing/manufacturing.json | 10 +++--- .../module_onboarding/payroll/payroll.json | 9 +++-- .../create_employee/create_employee.json | 2 +- .../create_payroll_period.json | 2 +- .../payroll_settings/payroll_settings.json | 10 +++--- .../module_onboarding/selling/selling.json | 7 ++-- .../setup_your_warehouse.json | 4 +-- .../stock/module_onboarding/stock/stock.json | 4 +-- .../create_a_supplier/create_a_supplier.json | 2 +- .../setup_your_warehouse.json | 4 +-- 25 files changed, 93 insertions(+), 86 deletions(-) diff --git a/erpnext/accounts/module_onboarding/accounts/accounts.json b/erpnext/accounts/module_onboarding/accounts/accounts.json index 12da4400284..ba1a779b4c1 100644 --- a/erpnext/accounts/module_onboarding/accounts/accounts.json +++ b/erpnext/accounts/module_onboarding/accounts/accounts.json @@ -13,7 +13,7 @@ "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/accounts", "idx": 0, "is_complete": 0, - "modified": "2020-05-14 22:11:06.475938", + "modified": "2020-07-08 14:06:09.033880", "modified_by": "Administrator", "module": "Accounts", "name": "Accounts", @@ -44,8 +44,7 @@ "step": "Configure Account Settings" } ], - "subtitle": "Accounts, invoices and taxation.", - "success_message": "The Accounts module is now set up!", - "title": "Let's Setup Your Accounts and Taxes.", - "user_can_dismiss": 1 + "subtitle": "Accounts, Invoices, Taxation, and more.", + "success_message": "The Accounts Module is all set up!", + "title": "Let's Set Up Your Accounts and Taxes." } \ No newline at end of file diff --git a/erpnext/accounts/onboarding_step/create_a_customer/create_a_customer.json b/erpnext/accounts/onboarding_step/create_a_customer/create_a_customer.json index bb396d268ae..5a403b06cf0 100644 --- a/erpnext/accounts/onboarding_step/create_a_customer/create_a_customer.json +++ b/erpnext/accounts/onboarding_step/create_a_customer/create_a_customer.json @@ -8,7 +8,7 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-14 17:46:41.831517", + "modified": "2020-06-01 13:16:19.731719", "modified_by": "Administrator", "name": "Create a Customer", "owner": "Administrator", diff --git a/erpnext/accounts/onboarding_step/create_a_product/create_a_product.json b/erpnext/accounts/onboarding_step/create_a_product/create_a_product.json index 450bee1f40b..d2068e167b7 100644 --- a/erpnext/accounts/onboarding_step/create_a_product/create_a_product.json +++ b/erpnext/accounts/onboarding_step/create_a_product/create_a_product.json @@ -1,6 +1,6 @@ { "action": "Create Entry", - "creation": "2020-05-14 17:45:28.554605", + "creation": "2020-05-12 18:16:06.624554", "docstatus": 0, "doctype": "Onboarding Step", "idx": 0, @@ -8,7 +8,7 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-14 17:45:28.554605", + "modified": "2020-05-12 18:30:02.489949", "modified_by": "Administrator", "name": "Create a Product", "owner": "Administrator", diff --git a/erpnext/assets/module_onboarding/assets/assets.json b/erpnext/assets/module_onboarding/assets/assets.json index 66dd60ae81f..1086ab4bcdb 100644 --- a/erpnext/assets/module_onboarding/assets/assets.json +++ b/erpnext/assets/module_onboarding/assets/assets.json @@ -13,7 +13,7 @@ "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/asset", "idx": 0, "is_complete": 0, - "modified": "2020-05-08 16:17:31.685943", + "modified": "2020-07-08 14:05:51.828497", "modified_by": "Administrator", "module": "Assets", "name": "Assets", @@ -27,7 +27,7 @@ }, { "step": "Create an Asset Category" - }, + }, { "step": "Purchase an Asset Item" }, @@ -35,8 +35,7 @@ "step": "Create an Asset" } ], - "subtitle": "Assets, Depreciations, Repairs and more", - "success_message": "The Asset Module is all set up!", - "title": "Let's Setup Asset Management", - "user_can_dismiss": 1 + "subtitle": "Assets, Depreciations, Repairs, and more.", + "success_message": "The Assets Module is all set up!", + "title": "Let's Set Up the Assets Module." } \ No newline at end of file diff --git a/erpnext/assets/onboarding_step/create_a_fixed_asset_item/create_a_fixed_asset_item.json b/erpnext/assets/onboarding_step/create_a_fixed_asset_item/create_a_fixed_asset_item.json index f5818c091f1..51702d9cb52 100644 --- a/erpnext/assets/onboarding_step/create_a_fixed_asset_item/create_a_fixed_asset_item.json +++ b/erpnext/assets/onboarding_step/create_a_fixed_asset_item/create_a_fixed_asset_item.json @@ -6,11 +6,14 @@ "idx": 0, "is_complete": 0, "is_mandatory": 0, + "is_single": 0, "is_skipped": 0, "modified": "2020-05-08 13:20:00.259985", "modified_by": "Administrator", "name": "Create a Fixed Asset Item", "owner": "Administrator", "reference_document": "Item", - "title": "Create a Fixed Asset Item" + "show_full_form": 0, + "title": "Create a Fixed Asset Item", + "validate_action": 0 } \ No newline at end of file diff --git a/erpnext/assets/onboarding_step/create_an_asset/create_an_asset.json b/erpnext/assets/onboarding_step/create_an_asset/create_an_asset.json index 5488b1d7b4e..b4f8a05e378 100644 --- a/erpnext/assets/onboarding_step/create_an_asset/create_an_asset.json +++ b/erpnext/assets/onboarding_step/create_an_asset/create_an_asset.json @@ -6,11 +6,14 @@ "idx": 0, "is_complete": 0, "is_mandatory": 0, + "is_single": 0, "is_skipped": 0, "modified": "2020-05-08 13:21:53.332538", "modified_by": "Administrator", "name": "Create an Asset", "owner": "Administrator", "reference_document": "Asset", - "title": "Create an Asset" + "show_full_form": 0, + "title": "Create an Asset", + "validate_action": 0 } \ No newline at end of file diff --git a/erpnext/assets/onboarding_step/create_an_asset_category/create_an_asset_category.json b/erpnext/assets/onboarding_step/create_an_asset_category/create_an_asset_category.json index 3bf54af348a..ffdb954b951 100644 --- a/erpnext/assets/onboarding_step/create_an_asset_category/create_an_asset_category.json +++ b/erpnext/assets/onboarding_step/create_an_asset_category/create_an_asset_category.json @@ -1,16 +1,19 @@ { - "action": "Create Entry", - "creation": "2020-05-08 13:21:53.332538", - "docstatus": 0, - "doctype": "Onboarding Step", - "idx": 0, - "is_complete": 0, - "is_mandatory": 0, - "is_skipped": 0, - "modified": "2020-05-08 13:21:53.332538", - "modified_by": "Administrator", - "name": "Create an Asset Category", - "owner": "Administrator", - "reference_document": "Asset Category", - "title": "Create an Asset Category" - } \ No newline at end of file + "action": "Create Entry", + "creation": "2020-05-08 13:21:53.332538", + "docstatus": 0, + "doctype": "Onboarding Step", + "idx": 0, + "is_complete": 0, + "is_mandatory": 0, + "is_single": 0, + "is_skipped": 0, + "modified": "2020-05-08 13:21:53.332538", + "modified_by": "Administrator", + "name": "Create an Asset Category", + "owner": "Administrator", + "reference_document": "Asset Category", + "show_full_form": 0, + "title": "Create an Asset Category", + "validate_action": 0 +} \ No newline at end of file diff --git a/erpnext/assets/onboarding_step/introduction_to_assets/introduction_to_assets.json b/erpnext/assets/onboarding_step/introduction_to_assets/introduction_to_assets.json index d48dd1cd3d2..d89da271971 100644 --- a/erpnext/assets/onboarding_step/introduction_to_assets/introduction_to_assets.json +++ b/erpnext/assets/onboarding_step/introduction_to_assets/introduction_to_assets.json @@ -6,11 +6,14 @@ "idx": 0, "is_complete": 0, "is_mandatory": 0, + "is_single": 0, "is_skipped": 0, "modified": "2020-05-08 16:06:16.625646", "modified_by": "Administrator", "name": "Introduction to Assets", "owner": "Administrator", + "show_full_form": 0, "title": "Introduction to Assets", + "validate_action": 0, "video_url": "https://www.youtube.com/watch?v=I-K8pLRmvSo" } \ No newline at end of file diff --git a/erpnext/assets/onboarding_step/purchase_an_asset_item/purchase_an_asset_item.json b/erpnext/assets/onboarding_step/purchase_an_asset_item/purchase_an_asset_item.json index 732ff7f733b..ce3185ef449 100644 --- a/erpnext/assets/onboarding_step/purchase_an_asset_item/purchase_an_asset_item.json +++ b/erpnext/assets/onboarding_step/purchase_an_asset_item/purchase_an_asset_item.json @@ -6,11 +6,14 @@ "idx": 0, "is_complete": 0, "is_mandatory": 0, + "is_single": 0, "is_skipped": 0, "modified": "2020-05-08 13:21:28.208059", "modified_by": "Administrator", "name": "Purchase an Asset Item", "owner": "Administrator", "reference_document": "Purchase Receipt", - "title": "Purchase an Asset Item" + "show_full_form": 0, + "title": "Purchase an Asset Item", + "validate_action": 0 } \ No newline at end of file diff --git a/erpnext/buying/module_onboarding/buying/buying.json b/erpnext/buying/module_onboarding/buying/buying.json index 6e4bbc95a24..887f85b82d1 100644 --- a/erpnext/buying/module_onboarding/buying/buying.json +++ b/erpnext/buying/module_onboarding/buying/buying.json @@ -19,7 +19,7 @@ "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/buying", "idx": 0, "is_complete": 0, - "modified": "2020-06-01 12:55:09.234944", + "modified": "2020-07-08 14:05:28.273641", "modified_by": "Administrator", "module": "Buying", "name": "Buying", @@ -47,8 +47,7 @@ "step": "Buying Settings" } ], - "subtitle": "Products, Purchases, Analysis and more.", + "subtitle": "Products, Purchases, Analysis, and more.", "success_message": "The Buying Module is all set up!", - "title": "Let's Set Up the Buying Module.", - "user_can_dismiss": 1 + "title": "Let's Set Up the Buying Module." } \ No newline at end of file diff --git a/erpnext/buying/onboarding_step/buying_settings/buying_settings.json b/erpnext/buying/onboarding_step/buying_settings/buying_settings.json index 6d765af1373..a788ccd4cc9 100644 --- a/erpnext/buying/onboarding_step/buying_settings/buying_settings.json +++ b/erpnext/buying/onboarding_step/buying_settings/buying_settings.json @@ -1,19 +1,19 @@ { - "action": "Show Form Tour", + "action": "Update Settings", "creation": "2020-05-06 15:53:44.667414", "docstatus": 0, "doctype": "Onboarding Step", "idx": 0, "is_complete": 0, - "is_mandatory": 1, - "is_single": 1, + "is_mandatory": 0, + "is_single": 0, "is_skipped": 0, - "modified": "2020-06-01 12:52:57.668870", + "modified": "2020-05-12 18:30:06.323797", "modified_by": "Administrator", "name": "Buying Settings", "owner": "Administrator", "reference_document": "Buying Settings", "show_full_form": 0, "title": "Configure Buying Settings.", - "validate_action": 0 + "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/buying/onboarding_step/setup_your_warehouse/setup_your_warehouse.json b/erpnext/buying/onboarding_step/setup_your_warehouse/setup_your_warehouse.json index 557c905bd6c..9457deee262 100644 --- a/erpnext/buying/onboarding_step/setup_your_warehouse/setup_your_warehouse.json +++ b/erpnext/buying/onboarding_step/setup_your_warehouse/setup_your_warehouse.json @@ -8,13 +8,13 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-19 18:54:19.383397", + "modified": "2020-07-04 12:33:16.970031", "modified_by": "Administrator", "name": "Setup your Warehouse", "owner": "Administrator", "path": "Tree/Warehouse", "reference_document": "Warehouse", "show_full_form": 0, - "title": "Setup your Warehouse", + "title": "Set up your Warehouse", "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/crm/module_onboarding/crm/crm.json b/erpnext/crm/module_onboarding/crm/crm.json index 44d672a7b59..8315218c842 100644 --- a/erpnext/crm/module_onboarding/crm/crm.json +++ b/erpnext/crm/module_onboarding/crm/crm.json @@ -16,7 +16,7 @@ "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/CRM", "idx": 0, "is_complete": 0, - "modified": "2020-05-28 21:07:41.278784", + "modified": "2020-07-08 14:05:42.644448", "modified_by": "Administrator", "module": "CRM", "name": "CRM", @@ -35,8 +35,7 @@ "step": "Create and Send Quotation" } ], - "subtitle": "Lead, Opportunity, Customer and more.", - "success_message": "CRM Module is all Set Up!", - "title": "Let's Set Up Your CRM.", - "user_can_dismiss": 1 + "subtitle": "Lead, Opportunity, Customer, and more.", + "success_message": "The CRM Module is all set up!", + "title": "Let's Set Up Your CRM." } \ No newline at end of file diff --git a/erpnext/healthcare/module_onboarding/healthcare/healthcare.json b/erpnext/healthcare/module_onboarding/healthcare/healthcare.json index 3e50726060a..56c3c135599 100644 --- a/erpnext/healthcare/module_onboarding/healthcare/healthcare.json +++ b/erpnext/healthcare/module_onboarding/healthcare/healthcare.json @@ -10,7 +10,7 @@ "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/healthcare", "idx": 0, "is_complete": 0, - "modified": "2020-05-26 23:16:37.603361", + "modified": "2020-07-08 14:06:19.512946", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare", @@ -35,8 +35,7 @@ "step": "Explore Clinical Procedure Templates" } ], - "subtitle": "Patients, Practitioner Schedules, Settings and more.", - "success_message": "Yayy! The Healthcare Module is all set up!", - "title": "Let's Setup the Healthcare Module", - "user_can_dismiss": 1 + "subtitle": "Patients, Practitioner Schedules, Settings, and more.", + "success_message": "The Healthcare Module is all set up!", + "title": "Let's Set Up the Healthcare Module." } \ No newline at end of file diff --git a/erpnext/hr/module_onboarding/human_resource/human_resource.json b/erpnext/hr/module_onboarding/human_resource/human_resource.json index e64582b407c..518c002bcaa 100644 --- a/erpnext/hr/module_onboarding/human_resource/human_resource.json +++ b/erpnext/hr/module_onboarding/human_resource/human_resource.json @@ -13,7 +13,7 @@ "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/human-resources", "idx": 0, "is_complete": 0, - "modified": "2020-05-20 11:20:07.992597", + "modified": "2020-07-08 14:05:47.018799", "modified_by": "Administrator", "module": "HR", "name": "Human Resource", @@ -44,8 +44,7 @@ "step": "HR Settings" } ], - "subtitle": "Employee, Leaves and more.", - "success_message": "The HR Module is all set up!", - "title": "Let's Setup the Human Resource Module. ", - "user_can_dismiss": 0 + "subtitle": "Employee, Leaves, and more.", + "success_message": "The Human Resource Module is all set up!", + "title": "Let's Set Up the Human Resource Module. " } \ No newline at end of file diff --git a/erpnext/manufacturing/module_onboarding/manufacturing/manufacturing.json b/erpnext/manufacturing/module_onboarding/manufacturing/manufacturing.json index a36b63a1d95..7b5747e393f 100644 --- a/erpnext/manufacturing/module_onboarding/manufacturing/manufacturing.json +++ b/erpnext/manufacturing/module_onboarding/manufacturing/manufacturing.json @@ -19,7 +19,7 @@ "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/manufacturing", "idx": 0, "is_complete": 0, - "modified": "2020-06-29 20:25:36.899106", + "modified": "2020-07-08 14:05:56.197563", "modified_by": "Administrator", "module": "Manufacturing", "name": "Manufacturing", @@ -50,7 +50,7 @@ "step": "Explore Manufacturing Settings" } ], - "subtitle": "Products, Raw Materials, BOM, Work Order and more.", - "success_message": "Manufacturing module is all setup!", - "title": "Let's Set Up the Manufacturing Module" -} + "subtitle": "Products, Raw Materials, BOM, Work Order, and more.", + "success_message": "Manufacturing module is all set up!", + "title": "Let's Set Up the Manufacturing Module." +} \ No newline at end of file diff --git a/erpnext/payroll/module_onboarding/payroll/payroll.json b/erpnext/payroll/module_onboarding/payroll/payroll.json index 7ed786faeea..b5226b2aca3 100644 --- a/erpnext/payroll/module_onboarding/payroll/payroll.json +++ b/erpnext/payroll/module_onboarding/payroll/payroll.json @@ -13,7 +13,7 @@ "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/human-resources/payroll-entry", "idx": 0, "is_complete": 0, - "modified": "2020-06-29 17:00:25.113341", + "modified": "2020-07-08 14:06:13.994310", "modified_by": "Administrator", "module": "Payroll", "name": "Payroll", @@ -44,8 +44,7 @@ "step": "Payroll Settings" } ], - "subtitle": "Salary, Compensations and more.", - "success_message": "The Payroll is all set up!", - "title": "Let's Setup the Payroll Module. ", - "user_can_dismiss": 1 + "subtitle": "Salary, Compensation, and more.", + "success_message": "The Payroll Module is all set up!", + "title": "Let's Set Up the Payroll Module. " } \ No newline at end of file diff --git a/erpnext/payroll/onboarding_step/create_employee/create_employee.json b/erpnext/payroll/onboarding_step/create_employee/create_employee.json index 5839ae6ca4a..3aa33c6d862 100644 --- a/erpnext/payroll/onboarding_step/create_employee/create_employee.json +++ b/erpnext/payroll/onboarding_step/create_employee/create_employee.json @@ -15,5 +15,5 @@ "reference_document": "Employee", "show_full_form": 0, "title": "Create Employee", - "validate_action": 1 + "validate_action": 0 } \ No newline at end of file diff --git a/erpnext/payroll/onboarding_step/create_payroll_period/create_payroll_period.json b/erpnext/payroll/onboarding_step/create_payroll_period/create_payroll_period.json index b1a7cc27344..4bae67546c7 100644 --- a/erpnext/payroll/onboarding_step/create_payroll_period/create_payroll_period.json +++ b/erpnext/payroll/onboarding_step/create_payroll_period/create_payroll_period.json @@ -8,7 +8,7 @@ "is_mandatory": 1, "is_single": 0, "is_skipped": 0, - "modified": "2020-06-29 11:53:54.553947", + "modified": "2020-06-01 11:53:54.553947", "modified_by": "Administrator", "name": "Create Payroll Period", "owner": "Administrator", diff --git a/erpnext/payroll/onboarding_step/payroll_settings/payroll_settings.json b/erpnext/payroll/onboarding_step/payroll_settings/payroll_settings.json index a7cf7bf9884..946b8c8707a 100644 --- a/erpnext/payroll/onboarding_step/payroll_settings/payroll_settings.json +++ b/erpnext/payroll/onboarding_step/payroll_settings/payroll_settings.json @@ -1,19 +1,19 @@ { - "action": "Update Settings", + "action": "Go to Page", "creation": "2020-06-04 16:34:29.664917", "docstatus": 0, "doctype": "Onboarding Step", "idx": 0, "is_complete": 0, "is_mandatory": 0, - "is_single": 1, + "is_single": 0, "is_skipped": 0, - "modified": "2020-06-29 16:34:29.664917", + "modified": "2020-06-04 16:34:29.664917", "modified_by": "Administrator", "name": "Payroll Settings", "owner": "Administrator", - "reference_document": "Payroll Settings", + "path": "#Form/Payroll Settings", "show_full_form": 0, "title": "Payroll Settings", - "validate_action": 0 + "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/selling/module_onboarding/selling/selling.json b/erpnext/selling/module_onboarding/selling/selling.json index 10a33c9cf52..160208ff680 100644 --- a/erpnext/selling/module_onboarding/selling/selling.json +++ b/erpnext/selling/module_onboarding/selling/selling.json @@ -19,7 +19,7 @@ "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/selling", "idx": 0, "is_complete": 0, - "modified": "2020-06-01 13:35:16.100512", + "modified": "2020-07-08 14:05:37.669753", "modified_by": "Administrator", "module": "Selling", "name": "Selling", @@ -47,8 +47,7 @@ "step": "Selling Settings" } ], - "subtitle": "Products, Sales, Analysis and more.", + "subtitle": "Products, Sales, Analysis, and more.", "success_message": "The Selling Module is all set up!", - "title": "Let's Set Up the Selling Module.", - "user_can_dismiss": 1 + "title": "Let's Set Up the Selling Module." } \ No newline at end of file diff --git a/erpnext/selling/onboarding_step/setup_your_warehouse/setup_your_warehouse.json b/erpnext/selling/onboarding_step/setup_your_warehouse/setup_your_warehouse.json index 557c905bd6c..9457deee262 100644 --- a/erpnext/selling/onboarding_step/setup_your_warehouse/setup_your_warehouse.json +++ b/erpnext/selling/onboarding_step/setup_your_warehouse/setup_your_warehouse.json @@ -8,13 +8,13 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-19 18:54:19.383397", + "modified": "2020-07-04 12:33:16.970031", "modified_by": "Administrator", "name": "Setup your Warehouse", "owner": "Administrator", "path": "Tree/Warehouse", "reference_document": "Warehouse", "show_full_form": 0, - "title": "Setup your Warehouse", + "title": "Set up your Warehouse", "validate_action": 1 } \ No newline at end of file diff --git a/erpnext/stock/module_onboarding/stock/stock.json b/erpnext/stock/module_onboarding/stock/stock.json index 10f05d45201..1d5bf8c97ca 100644 --- a/erpnext/stock/module_onboarding/stock/stock.json +++ b/erpnext/stock/module_onboarding/stock/stock.json @@ -19,7 +19,7 @@ "documentation_url": "https://docs.erpnext.com/docs/user/manual/en/stock", "idx": 0, "is_complete": 0, - "modified": "2020-06-29 16:41:09.440378", + "modified": "2020-07-08 14:22:07.951891", "modified_by": "Administrator", "module": "Stock", "name": "Stock", @@ -47,7 +47,7 @@ "step": "Stock Settings" } ], - "subtitle": "Inventory, Warehouses, Analysis and more.", + "subtitle": "Inventory, Warehouses, Analysis, and more.", "success_message": "The Stock Module is all set up!", "title": "Let's Set Up the Stock Module." } \ No newline at end of file diff --git a/erpnext/stock/onboarding_step/create_a_supplier/create_a_supplier.json b/erpnext/stock/onboarding_step/create_a_supplier/create_a_supplier.json index 4e753f4d84e..7a64224bd43 100644 --- a/erpnext/stock/onboarding_step/create_a_supplier/create_a_supplier.json +++ b/erpnext/stock/onboarding_step/create_a_supplier/create_a_supplier.json @@ -8,7 +8,7 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-06-29 16:36:53.948242", + "modified": "2020-05-14 22:09:10.043554", "modified_by": "Administrator", "name": "Create a Supplier", "owner": "Administrator", diff --git a/erpnext/stock/onboarding_step/setup_your_warehouse/setup_your_warehouse.json b/erpnext/stock/onboarding_step/setup_your_warehouse/setup_your_warehouse.json index 557c905bd6c..9457deee262 100644 --- a/erpnext/stock/onboarding_step/setup_your_warehouse/setup_your_warehouse.json +++ b/erpnext/stock/onboarding_step/setup_your_warehouse/setup_your_warehouse.json @@ -8,13 +8,13 @@ "is_mandatory": 0, "is_single": 0, "is_skipped": 0, - "modified": "2020-05-19 18:54:19.383397", + "modified": "2020-07-04 12:33:16.970031", "modified_by": "Administrator", "name": "Setup your Warehouse", "owner": "Administrator", "path": "Tree/Warehouse", "reference_document": "Warehouse", "show_full_form": 0, - "title": "Setup your Warehouse", + "title": "Set up your Warehouse", "validate_action": 1 } \ No newline at end of file From e0ff0cfcb9f03fc09d40b2d640adfb21af8c1779 Mon Sep 17 00:00:00 2001 From: Afshan <33727827+AfshanKhan@users.noreply.github.com> Date: Wed, 8 Jul 2020 14:40:22 +0530 Subject: [PATCH 557/608] fix: fetch "Employee Leave Policy" from "Employee Grade" if available (#22570) * fix:fetch "Employee Leave Policy" from "Employee Grade" if available * fix: checked "Fetch IF Empty" checkbox Co-authored-by: Marica --- erpnext/hr/doctype/employee/employee.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/hr/doctype/employee/employee.json b/erpnext/hr/doctype/employee/employee.json index 7dacacf12b0..f2afe065d1e 100644 --- a/erpnext/hr/doctype/employee/employee.json +++ b/erpnext/hr/doctype/employee/employee.json @@ -410,6 +410,8 @@ "options": "Branch" }, { + "fetch_from": "grade.default_leave_policy", + "fetch_if_empty": 1, "fieldname": "leave_policy", "fieldtype": "Link", "label": "Leave Policy", @@ -804,16 +806,14 @@ "fieldname": "expense_approver", "fieldtype": "Link", "label": "Expense Approver", - "options": "User", - "show_days": 1, - "show_seconds": 1 + "options": "User" } ], "icon": "fa fa-user", "idx": 24, "image_field": "image", "links": [], - "modified": "2020-06-18 18:01:27.223535", + "modified": "2020-07-03 21:28:04.109189", "modified_by": "Administrator", "module": "HR", "name": "Employee", From 45b01d2c0242b95af45495362cecd9ccc814869b Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 8 Jul 2020 15:39:45 +0530 Subject: [PATCH 558/608] feat: add medical coding fields to Healthcare DocTypes (#22501) * feat: add medical coding fields to templates * feat: fetch medical codes from templates in forms * fix: codacy issues Co-authored-by: Marica --- .../clinical_procedure.json | 11 +- .../clinical_procedure_template.js | 10 + .../clinical_procedure_template.json | 25 ++- .../healthcare/doctype/lab_test/lab_test.json | 13 +- .../lab_test_template/lab_test_template.js | 11 +- .../lab_test_template/lab_test_template.json | 24 ++- .../doctype/medical_code/medical_code.json | 203 +++++------------- .../therapy_session/therapy_session.json | 11 +- .../doctype/therapy_type/therapy_type.js | 10 + .../doctype/therapy_type/therapy_type.json | 25 ++- 10 files changed, 189 insertions(+), 154 deletions(-) diff --git a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.json b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.json index eaf8d80ba8c..b1d62da0325 100644 --- a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.json +++ b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.json @@ -11,6 +11,7 @@ "title", "appointment", "procedure_template", + "medical_code", "column_break_30", "company", "invoiced", @@ -290,11 +291,19 @@ "no_copy": 1, "print_hide": 1, "read_only": 1 + }, + { + "fetch_from": "procedure_template.medical_code", + "fieldname": "medical_code", + "fieldtype": "Link", + "label": "Medical Code", + "options": "Medical Code", + "read_only": 1 } ], "is_submittable": 1, "links": [], - "modified": "2020-04-27 21:36:23.796924", + "modified": "2020-06-29 14:28:11.779815", "modified_by": "Administrator", "module": "Healthcare", "name": "Clinical Procedure", diff --git a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.js b/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.js index 16d4540c7c5..1ef110dc6f4 100644 --- a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.js +++ b/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.js @@ -30,6 +30,16 @@ frappe.ui.form.on('Clinical Procedure Template', { mark_change_in_item(frm); }, + medical_code: function(frm) { + frm.set_query("medical_code", function() { + return { + filters: { + medical_code_standard: frm.doc.medical_code_standard + } + }; + }); + }, + refresh: function(frm) { frm.fields_dict['items'].grid.set_column_disp('barcode', false); frm.fields_dict['items'].grid.set_column_disp('batch_no', false); diff --git a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.json b/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.json index 9cfd682f1d2..17ac7eb1f95 100644 --- a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.json +++ b/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.json @@ -21,6 +21,9 @@ "is_billable", "rate", "medical_department", + "medical_coding_section", + "medical_code_standard", + "medical_code", "consumables", "consume_stock", "items", @@ -46,7 +49,6 @@ "fieldname": "item_code", "fieldtype": "Data", "label": "Item Code", - "options": "Item", "read_only_depends_on": "eval: !doc.__islocal ", "reqd": 1 }, @@ -173,10 +175,29 @@ "no_copy": 1, "options": "Item", "read_only": 1 + }, + { + "collapsible": 1, + "fieldname": "medical_coding_section", + "fieldtype": "Section Break", + "label": "Medical Coding" + }, + { + "fieldname": "medical_code_standard", + "fieldtype": "Link", + "label": "Medical Code Standard", + "options": "Medical Code Standard" + }, + { + "depends_on": "medical_code_standard", + "fieldname": "medical_code", + "fieldtype": "Link", + "label": "Medical Code", + "options": "Medical Code" } ], "links": [], - "modified": "2020-02-28 14:16:13.184981", + "modified": "2020-06-29 14:12:27.158130", "modified_by": "Administrator", "module": "Healthcare", "name": "Clinical Procedure Template", diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.json b/erpnext/healthcare/doctype/lab_test/lab_test.json index 17dc1edd8c1..88eeb46a242 100644 --- a/erpnext/healthcare/doctype/lab_test/lab_test.json +++ b/erpnext/healthcare/doctype/lab_test/lab_test.json @@ -33,9 +33,10 @@ "user", "invoiced", "sb_first", + "template", "lab_test_name", "column_break_26", - "template", + "medical_code", "lab_test_group", "sb_normal", "normal_test_items", @@ -424,11 +425,19 @@ "print_hide": 1, "read_only": 1, "report_hide": 1 + }, + { + "fetch_from": "template.medical_code", + "fieldname": "medical_code", + "fieldtype": "Link", + "label": "Medical Code", + "options": "Medical Code", + "read_only": 1 } ], "is_submittable": 1, "links": [], - "modified": "2020-04-04 19:16:29.131168", + "modified": "2020-06-29 14:24:26.509721", "modified_by": "Administrator", "module": "Healthcare", "name": "Lab Test", diff --git a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.js b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.js index 5c9bf49e608..c3eedbbdf1f 100644 --- a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.js +++ b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.js @@ -8,7 +8,7 @@ frappe.ui.form.on("Lab Test Template",{ if (!frm.doc.lab_test_description) frm.set_value("lab_test_description", frm.doc.lab_test_name); }, - refresh : function(frm) { + refresh: function(frm) { // Restrict Special, Grouped type templates in Child TestGroups frm.set_query("lab_test_template", "lab_test_groups", function() { return { @@ -17,6 +17,15 @@ frappe.ui.form.on("Lab Test Template",{ } }; }); + }, + medical_code: function(frm) { + frm.set_query("medical_code", function() { + return { + filters: { + medical_code_standard: frm.doc.medical_code_standard + } + }; + }); } }); diff --git a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.json b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.json index a606bc4b1d0..ebd2ec0246f 100644 --- a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.json +++ b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.json @@ -19,6 +19,9 @@ "disabled", "is_billable", "lab_test_rate", + "medical_coding_section", + "medical_code_standard", + "medical_code", "section_break_normal", "lab_test_uom", "lab_test_normal_range", @@ -237,10 +240,29 @@ "fieldtype": "Text", "ignore_xss_filter": 1, "label": "Collection Details" + }, + { + "collapsible": 1, + "fieldname": "medical_coding_section", + "fieldtype": "Section Break", + "label": "Medical Coding" + }, + { + "depends_on": "medical_code_standard", + "fieldname": "medical_code", + "fieldtype": "Link", + "label": "Medical Code", + "options": "Medical Code" + }, + { + "fieldname": "medical_code_standard", + "fieldtype": "Link", + "label": "Medical Code Standard", + "options": "Medical Code Standard" } ], "links": [], - "modified": "2020-03-25 16:53:01.740103", + "modified": "2020-06-29 14:07:20.772219", "modified_by": "Administrator", "module": "Healthcare", "name": "Lab Test Template", diff --git a/erpnext/healthcare/doctype/medical_code/medical_code.json b/erpnext/healthcare/doctype/medical_code/medical_code.json index a2e72475179..5d698309076 100644 --- a/erpnext/healthcare/doctype/medical_code/medical_code.json +++ b/erpnext/healthcare/doctype/medical_code/medical_code.json @@ -1,156 +1,69 @@ { - "allow_copy": 1, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 1, - "beta": 1, - "creation": "2017-06-21 13:02:56.122897", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "allow_copy": 1, + "allow_import": 1, + "allow_rename": 1, + "beta": 1, + "creation": "2017-06-21 13:02:56.122897", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "medical_code_standard", + "code", + "description" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "medical_code_standard", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Medical Code Standard", - "length": 0, - "no_copy": 0, - "options": "Medical Code Standard", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "medical_code_standard", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Medical Code Standard", + "options": "Medical Code Standard", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "code", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 1, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Code", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "code", + "fieldtype": "Data", + "ignore_xss_filter": 1, + "in_list_view": 1, + "label": "Code", + "reqd": 1, + "unique": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 1, - "collapsible": 0, - "columns": 0, - "fieldname": "description", - "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 1, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Description", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 + "bold": 1, + "fieldname": "description", + "fieldtype": "Small Text", + "ignore_xss_filter": 1, + "in_list_view": 1, + "label": "Description" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2017-10-04 17:08:11.053418", - "modified_by": "Administrator", - "module": "Healthcare", - "name": "Medical Code", - "name_case": "", - "owner": "Administrator", + ], + "links": [], + "modified": "2020-06-29 14:02:30.980032", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Medical Code", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Physician", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Physician", + "share": 1, "write": 1 } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Healthcare", - "search_fields": "code, description", - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "", - "track_changes": 1, - "track_seen": 0 + ], + "quick_entry": 1, + "restrict_to_domain": "Healthcare", + "search_fields": "code, description", + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/healthcare/doctype/therapy_session/therapy_session.json b/erpnext/healthcare/doctype/therapy_session/therapy_session.json index 00d74a09495..c75d9342ef1 100644 --- a/erpnext/healthcare/doctype/therapy_session/therapy_session.json +++ b/erpnext/healthcare/doctype/therapy_session/therapy_session.json @@ -19,6 +19,7 @@ "practitioner", "department", "details_section", + "medical_code", "duration", "rate", "location", @@ -206,11 +207,19 @@ "fieldtype": "Data", "label": "Patient Name", "read_only": 1 + }, + { + "fetch_from": "therapy_type.medical_code", + "fieldname": "medical_code", + "fieldtype": "Link", + "label": "Medical Code", + "options": "Medical Code", + "read_only": 1 } ], "is_submittable": 1, "links": [], - "modified": "2020-04-29 16:49:16.286006", + "modified": "2020-06-29 14:33:34.836594", "modified_by": "Administrator", "module": "Healthcare", "name": "Therapy Session", diff --git a/erpnext/healthcare/doctype/therapy_type/therapy_type.js b/erpnext/healthcare/doctype/therapy_type/therapy_type.js index 7a61b0def03..6e155dc21f9 100644 --- a/erpnext/healthcare/doctype/therapy_type/therapy_type.js +++ b/erpnext/healthcare/doctype/therapy_type/therapy_type.js @@ -45,6 +45,16 @@ frappe.ui.form.on('Therapy Type', { medical_department: function(frm) { mark_change_in_item(frm); + }, + + medical_code: function(frm) { + frm.set_query("medical_code", function() { + return { + filters: { + medical_code_standard: frm.doc.medical_code_standard + } + }; + }); } }); diff --git a/erpnext/healthcare/doctype/therapy_type/therapy_type.json b/erpnext/healthcare/doctype/therapy_type/therapy_type.json index 0b3c3caeaab..f365b1df032 100644 --- a/erpnext/healthcare/doctype/therapy_type/therapy_type.json +++ b/erpnext/healthcare/doctype/therapy_type/therapy_type.json @@ -22,6 +22,9 @@ "item_group", "column_break_12", "description", + "medical_coding_section", + "medical_code_standard", + "medical_code", "section_break_18", "therapy_for", "add_exercises", @@ -160,10 +163,30 @@ { "fieldname": "section_break_18", "fieldtype": "Section Break" + }, + { + "collapsible": 1, + "fieldname": "medical_coding_section", + "fieldtype": "Section Break", + "label": "Medical Coding", + "options": "Medical Coding" + }, + { + "fieldname": "medical_code_standard", + "fieldtype": "Link", + "label": "Medical Code Standard", + "options": "Medical Code Standard" + }, + { + "depends_on": "medical_code_standard", + "fieldname": "medical_code", + "fieldtype": "Link", + "label": "Medical Code", + "options": "Medical Code" } ], "links": [], - "modified": "2020-04-21 13:09:04.006289", + "modified": "2020-06-29 14:18:50.669951", "modified_by": "Administrator", "module": "Healthcare", "name": "Therapy Type", From 6c6c36e7805efe461d15158d03a16db6c79c6b37 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 9 Jul 2020 11:23:41 +0530 Subject: [PATCH 559/608] fix: attribute error while cancelling patient encounter --- .../healthcare/doctype/patient_encounter/patient_encounter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py index 56401a3e742..262fc4650af 100644 --- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py +++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py @@ -73,7 +73,7 @@ def update_encounter_medical_record(encounter): insert_encounter_to_medical_record(encounter) def delete_medical_record(encounter): - frappe.db.delete_doc_if_exists('Patient Medical Record', 'reference_name', encounter.name) + frappe.delete_doc_if_exists('Patient Medical Record', 'reference_name', encounter.name) def set_subject_field(encounter): subject = frappe.bold(_('Healthcare Practitioner: ')) + encounter.practitioner + '
    ' From cdf55cef80ddaf170159237d1d558e1949e0c8f8 Mon Sep 17 00:00:00 2001 From: Rohan Date: Thu, 9 Jul 2020 11:36:01 +0530 Subject: [PATCH 560/608] fix: fetch project-related info in Timesheet (#22423) Co-authored-by: Marica --- erpnext/projects/doctype/task/task.js | 69 +++++++++---------------- erpnext/projects/doctype/task/task.json | 1 + erpnext/projects/doctype/task/task.py | 25 ++++++++- 3 files changed, 47 insertions(+), 48 deletions(-) diff --git a/erpnext/projects/doctype/task/task.js b/erpnext/projects/doctype/task/task.js index 5719276669a..a044e1dca89 100644 --- a/erpnext/projects/doctype/task/task.js +++ b/erpnext/projects/doctype/task/task.js @@ -3,55 +3,36 @@ frappe.provide("erpnext.projects"); -cur_frm.add_fetch("project", "company", "company"); - frappe.ui.form.on("Task", { - onload: function(frm) { - frm.set_query("task", "depends_on", function() { - var filters = { + setup: function (frm) { + frm.set_query("project", function () { + return { + query: "erpnext.projects.doctype.task.task.get_project" + } + }); + + frm.make_methods = { + 'Timesheet': () => frappe.model.open_mapped_doc({ + method: 'erpnext.projects.doctype.task.task.make_timesheet', + frm: frm + }) + } + }, + + onload: function (frm) { + frm.set_query("task", "depends_on", function () { + let filters = { name: ["!=", frm.doc.name] }; - if(frm.doc.project) filters["project"] = frm.doc.project; + if (frm.doc.project) filters["project"] = frm.doc.project; return { filters: filters }; }) }, - refresh: function(frm) { - frm.fields_dict['parent_task'].get_query = function () { - return { - filters: { - "is_group": 1, - } - } - } - - if (!frm.doc.is_group) { - if (!frm.is_new()) { - if (frappe.model.can_read("Timesheet")) { - frm.add_custom_button(__("Timesheet"), () => { - frappe.route_options = { "project": frm.doc.project, "task": frm.doc.name } - frappe.set_route("List", "Timesheet"); - }, __("View"), true); - } - - if (frappe.model.can_read("Expense Claim")) { - frm.add_custom_button(__("Expense Claims"), () => { - frappe.route_options = { "project": frm.doc.project, "task": frm.doc.name }; - frappe.set_route("List", "Expense Claim"); - }, __("View"), true); - } - } - } - }, - - setup: function(frm) { - frm.fields_dict.project.get_query = function() { - return { - query: "erpnext.projects.doctype.task.task.get_project" - } - }; + refresh: function (frm) { + frm.set_query("parent_task", { "is_group": 1 }); }, is_group: function (frm) { @@ -69,12 +50,8 @@ frappe.ui.form.on("Task", { }) }, - validate: function(frm) { + validate: function (frm) { frm.doc.project && frappe.model.remove_from_locals("Project", frm.doc.project); - }, - + } }); - -cur_frm.add_fetch('task', 'subject', 'subject'); -cur_frm.add_fetch('task', 'project', 'project'); diff --git a/erpnext/projects/doctype/task/task.json b/erpnext/projects/doctype/task/task.json index 4db1f193ce2..27f1a71a528 100644 --- a/erpnext/projects/doctype/task/task.json +++ b/erpnext/projects/doctype/task/task.json @@ -325,6 +325,7 @@ "options": "Department" }, { + "fetch_from": "project.company", "fieldname": "company", "fieldtype": "Link", "label": "Company", diff --git a/erpnext/projects/doctype/task/task.py b/erpnext/projects/doctype/task/task.py index 845cdba8bfa..3b75cf42158 100755 --- a/erpnext/projects/doctype/task/task.py +++ b/erpnext/projects/doctype/task/task.py @@ -7,10 +7,11 @@ import json import frappe from frappe import _, throw +from frappe.desk.form.assign_to import clear, close_all_assignments +from frappe.model.mapper import get_mapped_doc from frappe.utils import add_days, cstr, date_diff, get_link_to_form, getdate, today from frappe.utils.nestedset import NestedSet -from frappe.desk.form.assign_to import close_all_assignments, clear -from frappe.utils import date_diff + class CircularReferenceError(frappe.ValidationError): pass class EndDateCannotBeGreaterThanProjectEndDateError(frappe.ValidationError): pass @@ -220,6 +221,26 @@ def set_tasks_as_overdue(): continue frappe.get_doc("Task", task.name).update_status() + +@frappe.whitelist() +def make_timesheet(source_name, target_doc=None, ignore_permissions=False): + def set_missing_values(source, target): + target.append("time_logs", { + "hours": source.actual_time, + "completed": source.status == "Completed", + "project": source.project, + "task": source.name + }) + + doclist = get_mapped_doc("Task", source_name, { + "Task": { + "doctype": "Timesheet" + } + }, target_doc, postprocess=set_missing_values, ignore_permissions=ignore_permissions) + + return doclist + + @frappe.whitelist() def get_children(doctype, parent, task=None, project=None, is_root=False): From d7563f03274564d3907164df193de9e3df004cdf Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Thu, 9 Jul 2020 19:40:18 +0530 Subject: [PATCH 561/608] fix: default overwroite property n 'company' is not defined --- .../payroll/doctype/employee_incentive/employee_incentive.py | 1 + erpnext/payroll/doctype/retention_bonus/retention_bonus.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/payroll/doctype/employee_incentive/employee_incentive.py b/erpnext/payroll/doctype/employee_incentive/employee_incentive.py index 44763fc077e..84a97f6bb2e 100644 --- a/erpnext/payroll/doctype/employee_incentive/employee_incentive.py +++ b/erpnext/payroll/doctype/employee_incentive/employee_incentive.py @@ -13,6 +13,7 @@ class EmployeeIncentive(Document): additional_salary = frappe.new_doc('Additional Salary') additional_salary.employee = self.employee additional_salary.salary_component = self.salary_component + additional_salary.overwrite_salary_structure_amount = 0 additional_salary.amount = self.incentive_amount additional_salary.payroll_date = self.payroll_date additional_salary.company = company diff --git a/erpnext/payroll/doctype/retention_bonus/retention_bonus.py b/erpnext/payroll/doctype/retention_bonus/retention_bonus.py index ed0d36cfa5f..b8e56ae42aa 100644 --- a/erpnext/payroll/doctype/retention_bonus/retention_bonus.py +++ b/erpnext/payroll/doctype/retention_bonus/retention_bonus.py @@ -26,6 +26,7 @@ class RetentionBonus(Document): additional_salary.amount = self.bonus_amount additional_salary.payroll_date = self.bonus_payment_date additional_salary.company = company + additional_salary.overwrite_salary_structure_amount = 0 additional_salary.ref_doctype = self.doctype additional_salary.ref_docname = self.name additional_salary.submit() @@ -53,7 +54,7 @@ class RetentionBonus(Document): 'employee': self.employee, 'salary_component': self.salary_component, 'payroll_date': self.bonus_payment_date, - 'company': company, + 'company': self.company, 'docstatus': 1, 'ref_doctype': self.doctype, 'ref_docname': self.name From b74077faf5433848976b4c14b18ebeaed7994bd3 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 9 Jul 2020 19:50:41 +0530 Subject: [PATCH 562/608] fix(travis): Item Tax tempate template test --- erpnext/accounts/doctype/item_tax_template/test_records.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/erpnext/accounts/doctype/item_tax_template/test_records.json b/erpnext/accounts/doctype/item_tax_template/test_records.json index db540e86aac..4d9537d4b89 100644 --- a/erpnext/accounts/doctype/item_tax_template/test_records.json +++ b/erpnext/accounts/doctype/item_tax_template/test_records.json @@ -2,6 +2,7 @@ { "doctype": "Item Tax Template", "title": "_Test Account Excise Duty @ 10", + "company": "_Test Company", "taxes": [ { "doctype": "Item Tax Template Detail", @@ -14,6 +15,7 @@ { "doctype": "Item Tax Template", "title": "_Test Account Excise Duty @ 12", + "company": "_Test Company", "taxes": [ { "doctype": "Item Tax Template Detail", @@ -26,6 +28,7 @@ { "doctype": "Item Tax Template", "title": "_Test Account Excise Duty @ 15", + "company": "_Test Company", "taxes": [ { "doctype": "Item Tax Template Detail", @@ -38,6 +41,7 @@ { "doctype": "Item Tax Template", "title": "_Test Account Excise Duty @ 20", + "company": "_Test Company", "taxes": [ { "doctype": "Item Tax Template Detail", @@ -50,6 +54,7 @@ { "doctype": "Item Tax Template", "title": "_Test Item Tax Template 1", + "company": "_Test Company", "taxes": [ { "doctype": "Item Tax Template Detail", From 1ec2d962dbcc7ccee30e0d0a35727aa890910203 Mon Sep 17 00:00:00 2001 From: Marica Date: Thu, 9 Jul 2020 20:02:18 +0530 Subject: [PATCH 563/608] fix: Remove rename related code from Serial No (#22627) --- erpnext/stock/doctype/serial_no/serial_no.py | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py index f3514c7385d..90f0f5881d4 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.py +++ b/erpnext/stock/doctype/serial_no/serial_no.py @@ -190,23 +190,6 @@ class SerialNo(StockController): if sle_exists: frappe.throw(_("Cannot delete Serial No {0}, as it is used in stock transactions").format(self.name)) - def before_rename(self, old, new, merge=False): - if merge: - frappe.throw(_("Sorry, Serial Nos cannot be merged")) - - def after_rename(self, old, new, merge=False): - """rename serial_no text fields""" - for dt in frappe.db.sql("""select parent from tabDocField - where fieldname='serial_no' and fieldtype in ('Text', 'Small Text')"""): - - for item in frappe.db.sql("""select name, serial_no from `tab%s` - where serial_no like %s""" % (dt[0], frappe.db.escape('%' + old + '%'))): - - serial_nos = map(lambda i: new if i.upper()==old.upper() else i, item[1].split('\n')) - frappe.db.sql("""update `tab%s` set serial_no = %s - where name=%s""" % (dt[0], '%s', '%s'), - ('\n'.join(list(serial_nos)), item[0])) - def update_serial_no_reference(self, serial_no=None): last_sle = self.get_last_sle(serial_no) self.set_purchase_details(last_sle.get("purchase_sle")) From d14666e41914b866c8d24306bcfcda0a9fe6911b Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Thu, 9 Jul 2020 20:07:10 +0530 Subject: [PATCH 564/608] fix: incorrect delivered qty in Supplier-Wise Sales Analytics (#22631) --- .../supplier_wise_sales_analytics.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/report/supplier_wise_sales_analytics/supplier_wise_sales_analytics.py b/erpnext/stock/report/supplier_wise_sales_analytics/supplier_wise_sales_analytics.py index 6a86889aa3d..5873a7a3008 100644 --- a/erpnext/stock/report/supplier_wise_sales_analytics/supplier_wise_sales_analytics.py +++ b/erpnext/stock/report/supplier_wise_sales_analytics/supplier_wise_sales_analytics.py @@ -21,7 +21,7 @@ def execute(filters=None): for cd in consumed_details.get(item_code): if (cd.voucher_no not in material_transfer_vouchers): - if cd.voucher_type=="Delivery Note": + if cd.voucher_type in ["Delivery Note", "Sales Invoice"]: delivered_qty += abs(flt(cd.actual_qty)) delivered_amount += abs(flt(cd.stock_value_difference)) elif cd.voucher_type!="Delivery Note": From 3a3787f9e7594331e0285cd4485420d1ee7bb17f Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Thu, 9 Jul 2020 20:07:40 +0530 Subject: [PATCH 565/608] fix: Due to decimal issue make purchase receipt button not showing from PO (#22629) --- erpnext/buying/doctype/purchase_order/purchase_order.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js index 4a8146a797a..84e3a31904d 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.js +++ b/erpnext/buying/doctype/purchase_order/purchase_order.js @@ -123,14 +123,14 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend( } if(doc.status != "Closed") { if (doc.status != "On Hold") { - if(flt(doc.per_received, 2) < 100 && allow_receipt) { + if(flt(doc.per_received) < 100 && allow_receipt) { cur_frm.add_custom_button(__('Receipt'), this.make_purchase_receipt, __('Create')); if(doc.is_subcontracted==="Yes" && me.has_unsupplied_items()) { cur_frm.add_custom_button(__('Material to Supplier'), function() { me.make_stock_entry(); }, __("Transfer")); } } - if(flt(doc.per_billed, 2) < 100) + if(flt(doc.per_billed) < 100) cur_frm.add_custom_button(__('Invoice'), this.make_purchase_invoice, __('Create')); From fd28f1071ea0c9e5db50b87c38dc4575e07a6760 Mon Sep 17 00:00:00 2001 From: Afshan <33727827+AfshanKhan@users.noreply.github.com> Date: Fri, 10 Jul 2020 11:25:39 +0530 Subject: [PATCH 566/608] style: arrangements of filters for reports (#22636) --- .../accounts_payable/accounts_payable.js | 70 ++++++++-------- .../accounts_receivable.js | 82 +++++++++---------- 2 files changed, 76 insertions(+), 76 deletions(-) diff --git a/erpnext/accounts/report/accounts_payable/accounts_payable.js b/erpnext/accounts/report/accounts_payable/accounts_payable.js index 2aa9618e559..6abd6e5cf77 100644 --- a/erpnext/accounts/report/accounts_payable/accounts_payable.js +++ b/erpnext/accounts/report/accounts_payable/accounts_payable.js @@ -17,41 +17,6 @@ frappe.query_reports["Accounts Payable"] = { "fieldtype": "Date", "default": frappe.datetime.get_today() }, - { - "fieldname":"ageing_based_on", - "label": __("Ageing Based On"), - "fieldtype": "Select", - "options": 'Posting Date\nDue Date\nSupplier Invoice Date', - "default": "Due Date" - }, - { - "fieldname":"range1", - "label": __("Ageing Range 1"), - "fieldtype": "Int", - "default": "30", - "reqd": 1 - }, - { - "fieldname":"range2", - "label": __("Ageing Range 2"), - "fieldtype": "Int", - "default": "60", - "reqd": 1 - }, - { - "fieldname":"range3", - "label": __("Ageing Range 3"), - "fieldtype": "Int", - "default": "90", - "reqd": 1 - }, - { - "fieldname":"range4", - "label": __("Ageing Range 4"), - "fieldtype": "Int", - "default": "120", - "reqd": 1 - }, { "fieldname":"finance_book", "label": __("Finance Book"), @@ -88,6 +53,41 @@ frappe.query_reports["Accounts Payable"] = { } } }, + { + "fieldname":"ageing_based_on", + "label": __("Ageing Based On"), + "fieldtype": "Select", + "options": 'Posting Date\nDue Date\nSupplier Invoice Date', + "default": "Due Date" + }, + { + "fieldname":"range1", + "label": __("Ageing Range 1"), + "fieldtype": "Int", + "default": "30", + "reqd": 1 + }, + { + "fieldname":"range2", + "label": __("Ageing Range 2"), + "fieldtype": "Int", + "default": "60", + "reqd": 1 + }, + { + "fieldname":"range3", + "label": __("Ageing Range 3"), + "fieldtype": "Int", + "default": "90", + "reqd": 1 + }, + { + "fieldname":"range4", + "label": __("Ageing Range 4"), + "fieldtype": "Int", + "default": "120", + "reqd": 1 + }, { "fieldname":"payment_terms_template", "label": __("Payment Terms Template"), diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js index 8dc558a611d..c999eb9b8e9 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js @@ -17,41 +17,6 @@ frappe.query_reports["Accounts Receivable"] = { "fieldtype": "Date", "default": frappe.datetime.get_today() }, - { - "fieldname":"ageing_based_on", - "label": __("Ageing Based On"), - "fieldtype": "Select", - "options": 'Posting Date\nDue Date', - "default": "Due Date" - }, - { - "fieldname":"range1", - "label": __("Ageing Range 1"), - "fieldtype": "Int", - "default": "30", - "reqd": 1 - }, - { - "fieldname":"range2", - "label": __("Ageing Range 2"), - "fieldtype": "Int", - "default": "60", - "reqd": 1 - }, - { - "fieldname":"range3", - "label": __("Ageing Range 3"), - "fieldtype": "Int", - "default": "90", - "reqd": 1 - }, - { - "fieldname":"range4", - "label": __("Ageing Range 4"), - "fieldtype": "Int", - "default": "120", - "reqd": 1 - }, { "fieldname":"finance_book", "label": __("Finance Book"), @@ -101,6 +66,41 @@ frappe.query_reports["Accounts Receivable"] = { } } }, + { + "fieldname":"ageing_based_on", + "label": __("Ageing Based On"), + "fieldtype": "Select", + "options": 'Posting Date\nDue Date', + "default": "Due Date" + }, + { + "fieldname":"range1", + "label": __("Ageing Range 1"), + "fieldtype": "Int", + "default": "30", + "reqd": 1 + }, + { + "fieldname":"range2", + "label": __("Ageing Range 2"), + "fieldtype": "Int", + "default": "60", + "reqd": 1 + }, + { + "fieldname":"range3", + "label": __("Ageing Range 3"), + "fieldtype": "Int", + "default": "90", + "reqd": 1 + }, + { + "fieldname":"range4", + "label": __("Ageing Range 4"), + "fieldtype": "Int", + "default": "120", + "reqd": 1 + }, { "fieldname":"customer_group", "label": __("Customer Group"), @@ -113,12 +113,6 @@ frappe.query_reports["Accounts Receivable"] = { "fieldtype": "Link", "options": "Payment Terms Template" }, - { - "fieldname":"territory", - "label": __("Territory"), - "fieldtype": "Link", - "options": "Territory" - }, { "fieldname":"sales_partner", "label": __("Sales Partner"), @@ -131,6 +125,12 @@ frappe.query_reports["Accounts Receivable"] = { "fieldtype": "Link", "options": "Sales Person" }, + { + "fieldname":"territory", + "label": __("Territory"), + "fieldtype": "Link", + "options": "Territory" + }, { "fieldname": "group_by_party", "label": __("Group By Customer"), From eee12fbcb4ec7bb793f2d4516b8285eb86105a10 Mon Sep 17 00:00:00 2001 From: Marica Date: Fri, 10 Jul 2020 12:34:59 +0530 Subject: [PATCH 567/608] fix: Remove redundant variable declaration (#22641) --- erpnext/utilities/transaction_base.py | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/utilities/transaction_base.py b/erpnext/utilities/transaction_base.py index 5bf85d1c64c..c8e3330908f 100644 --- a/erpnext/utilities/transaction_base.py +++ b/erpnext/utilities/transaction_base.py @@ -121,7 +121,6 @@ class TransactionBase(StatusUpdater): def validate_rate_with_reference_doc(self, ref_details): buying_doctypes = ["Purchase Order", "Purchase Invoice", "Purchase Receipt"] - selling_doctypes = ["Sales Invoice", "Delivery Note"] if self.doctype in buying_doctypes: to_disable = "Maintain same rate throughout Purchase cycle" From 7cb195b304f4098aafc74e54b2e53d4753116bd1 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 10 Jul 2020 13:33:50 +0530 Subject: [PATCH 568/608] fix: Distributed cost center query --- erpnext/accounts/report/financial_statements.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index 533685d703d..ee1d54a92ed 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -405,12 +405,12 @@ def set_gl_entries_by_account( FROM `tabDistributed Cost Center` WHERE cost_center IN %(cost_center)s AND parent NOT IN %(cost_center)s - AND is_cancelled = 0 GROUP BY parent ) as DCC_allocation WHERE company=%(company)s {additional_conditions} AND posting_date <= %(to_date)s + AND is_cancelled = 0 AND cost_center = DCC_allocation.parent """.format(additional_conditions=additional_conditions.replace("and cost_center in %(cost_center)s ", '')) From 70aa3c0b1f94030db4c2d91ed91d46e6e3c16919 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Fri, 10 Jul 2020 16:13:21 +0530 Subject: [PATCH 569/608] fix: Remove explicit exception Co-authored-by: gavin --- erpnext/setup/install.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/setup/install.py b/erpnext/setup/install.py index d470d75a8fc..aa9fbc0a92c 100644 --- a/erpnext/setup/install.py +++ b/erpnext/setup/install.py @@ -32,7 +32,7 @@ def check_setup_wizard_not_completed(): if frappe.db.get_default('desktop:home_page') != 'setup-wizard': message = """ERPNext can only be installed on a fresh site where the setup wizard is not completed. You can reinstall this site (after saving your data) using: bench --site [sitename] reinstall""" - frappe.throw(message, exc=frappe.IncompatibleApp) + frappe.throw(message) def set_single_defaults(): From a6c10b1bc0fa05d5b20928f8f2c73048c1797bed Mon Sep 17 00:00:00 2001 From: marination Date: Fri, 10 Jul 2020 20:09:58 +0530 Subject: [PATCH 570/608] fix: Pricing Rule breaks if no item_code --- erpnext/accounts/doctype/pricing_rule/utils.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index 53115f92d01..ad983830f3b 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -319,7 +319,9 @@ def apply_internal_priority(pricing_rules, field_set, args): filtered_rules = [] for field in field_set: if args.get(field): - filtered_rules = filter(lambda x: x[field]==args[field], pricing_rules) + # filter function always returns a filter object even if empty + # list conversion is necessary to check for an empty result + filtered_rules = list(filter(lambda x: x.get(field)==args.get(field), pricing_rules)) if filtered_rules: break return filtered_rules or pricing_rules From 090bf73768d844c4ac0b299b54d350fb27434629 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Fri, 10 Jul 2020 17:43:23 +0200 Subject: [PATCH 571/608] bump pandas to 1.0.5 --- requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index cfd0ab8e075..108c776d8de 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ braintree==3.57.1 frappe gocardless-pro==1.11.0 googlemaps==3.1.1 -pandas==0.24.2 +pandas==1.0.5 plaid-python==3.4.0 pycountry==19.8.18 PyGithub==1.44.1 @@ -11,3 +11,4 @@ taxjar==1.9.0 tweepy==3.8.0 Unidecode==1.1.1 WooCommerce==2.1.1 +tweepy==3.8.0 From 23a65d8d01876267485ca4bfe83ac15e79d68cd5 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Fri, 10 Jul 2020 17:46:44 +0200 Subject: [PATCH 572/608] fix: duplicate tweepy --- requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 108c776d8de..912d61f7a6f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,4 +11,3 @@ taxjar==1.9.0 tweepy==3.8.0 Unidecode==1.1.1 WooCommerce==2.1.1 -tweepy==3.8.0 From 87eb2827ef9e1d2fea46a9ecb721bfa5930ffb9b Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 10 Jul 2020 22:45:45 +0530 Subject: [PATCH 573/608] fix: Update tests --- erpnext/controllers/tests/test_qty_based_taxes.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/controllers/tests/test_qty_based_taxes.py b/erpnext/controllers/tests/test_qty_based_taxes.py index fd9936bae99..aaeac5d9399 100644 --- a/erpnext/controllers/tests/test_qty_based_taxes.py +++ b/erpnext/controllers/tests/test_qty_based_taxes.py @@ -30,6 +30,7 @@ class TestTaxes(unittest.TestCase): self.item_tax_template = frappe.get_doc({ 'doctype': 'Item Tax Template', 'title': uuid4(), + 'company': self.company.name, 'taxes': [ { 'tax_type': self.account.name, From f15ff5fbfa1aec2ec4527c259e2405bc3556507f Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 11 Jul 2020 13:58:53 +0530 Subject: [PATCH 574/608] fix: Test --- erpnext/stock/doctype/item/test_records.json | 12 ++++-------- erpnext/stock/get_item_details.py | 2 +- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/erpnext/stock/doctype/item/test_records.json b/erpnext/stock/doctype/item/test_records.json index 6c1a55945c8..ce77988579a 100644 --- a/erpnext/stock/doctype/item/test_records.json +++ b/erpnext/stock/doctype/item/test_records.json @@ -92,8 +92,7 @@ { "doctype": "Item Tax", "parentfield": "taxes", - "item_tax_template": "_Test Account Excise Duty @ 10", - "tax_category": "" + "item_tax_template": "_Test Account Excise Duty @ 10" } ], "stock_uom": "_Test UOM 1" @@ -371,8 +370,7 @@ { "doctype": "Item Tax", "parentfield": "taxes", - "item_tax_template": "_Test Account Excise Duty @ 10", - "tax_category": "" + "item_tax_template": "_Test Account Excise Duty @ 10" }, { "doctype": "Item Tax", @@ -451,14 +449,12 @@ { "doctype": "Item Tax", "parentfield": "taxes", - "item_tax_template": "_Test Account Excise Duty @ 20", - "tax_category": "" + "item_tax_template": "_Test Account Excise Duty @ 20" }, { "doctype": "Item Tax", "parentfield": "taxes", - "item_tax_template": "_Test Item Tax Template 1", - "tax_category": "_Test Tax Category 1" + "item_tax_template": "_Test Item Tax Template 1" } ] } diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 0af018bbad8..385e477fbd5 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -636,7 +636,7 @@ def get_item_price(args, item_code, ignore_party=False): if args.get('transaction_date'): conditions += """ and %(transaction_date)s between ifnull(valid_from, '2000-01-01') and ifnull(valid_upto, '2500-12-31')""" - + if args.get('posting_date'): conditions += """ and %(posting_date)s between ifnull(valid_from, '2000-01-01') and ifnull(valid_upto, '2500-12-31')""" From 46bcb4642241ba757388cb2ba1e712163ee9d097 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 11 Jul 2020 18:36:32 +0530 Subject: [PATCH 575/608] fix: Do not use random customers --- erpnext/controllers/tests/test_mapper.py | 8 +++----- erpnext/stock/doctype/item/test_records.json | 1 + 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/erpnext/controllers/tests/test_mapper.py b/erpnext/controllers/tests/test_mapper.py index 8839e002a4c..5ebf8cfa7f1 100644 --- a/erpnext/controllers/tests/test_mapper.py +++ b/erpnext/controllers/tests/test_mapper.py @@ -14,12 +14,10 @@ class TestMapper(unittest.TestCase): make_test_records("Item") items = frappe.get_all("Item", fields = ["name", "item_code"], filters = {'is_sales_item': 1, 'has_variants': 0, 'disabled': 0}) - customers = frappe.get_all("Customer") - if items and customers: + if items: # Make source docs (quotations) and a target doc (sales order) - customer = random.choice(customers).name - qtn1, item_list_1 = self.make_quotation(items, customer) - qtn2, item_list_2 = self.make_quotation(items, customer) + qtn1, item_list_1 = self.make_quotation(items, '_Test Customer') + qtn2, item_list_2 = self.make_quotation(items, '_Test Customer') so, item_list_3 = self.make_sales_order() # Map source docs to target with corresponding mapper method diff --git a/erpnext/stock/doctype/item/test_records.json b/erpnext/stock/doctype/item/test_records.json index ce77988579a..9ca887c77e3 100644 --- a/erpnext/stock/doctype/item/test_records.json +++ b/erpnext/stock/doctype/item/test_records.json @@ -454,6 +454,7 @@ { "doctype": "Item Tax", "parentfield": "taxes", + "tax_category": "_Test Tax Category 1", "item_tax_template": "_Test Item Tax Template 1" } ] From a9820cdcf64bbff009e7775bb0d22154a4fea309 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 11 Jul 2020 19:38:51 +0530 Subject: [PATCH 576/608] fix: Remove every bit of randomness from this test --- erpnext/controllers/tests/test_mapper.py | 30 +++++------ .../doctype/sales_order/test_records.json | 52 +++++++++---------- 2 files changed, 38 insertions(+), 44 deletions(-) diff --git a/erpnext/controllers/tests/test_mapper.py b/erpnext/controllers/tests/test_mapper.py index 5ebf8cfa7f1..66459fdbf8a 100644 --- a/erpnext/controllers/tests/test_mapper.py +++ b/erpnext/controllers/tests/test_mapper.py @@ -13,12 +13,12 @@ class TestMapper(unittest.TestCase): '''Test mapping of multiple source docs on a single target doc''' make_test_records("Item") - items = frappe.get_all("Item", fields = ["name", "item_code"], filters = {'is_sales_item': 1, 'has_variants': 0, 'disabled': 0}) - if items: - # Make source docs (quotations) and a target doc (sales order) - qtn1, item_list_1 = self.make_quotation(items, '_Test Customer') - qtn2, item_list_2 = self.make_quotation(items, '_Test Customer') - so, item_list_3 = self.make_sales_order() + items = ['_Test Item', '_Test Item 2', '_Test FG Item'] + + # Make source docs (quotations) and a target doc (sales order) + qtn1, item_list_1 = self.make_quotation(items, '_Test Customer') + qtn2, item_list_2 = self.make_quotation(items, '_Test Customer') + so, item_list_3 = self.make_sales_order() # Map source docs to target with corresponding mapper method method = "erpnext.selling.doctype.quotation.quotation.make_sales_order" @@ -26,18 +26,12 @@ class TestMapper(unittest.TestCase): # Assert that all inserted items are present in updated sales order src_items = item_list_1 + item_list_2 + item_list_3 - self.assertEqual(set([d.item_code for d in src_items]), + self.assertEqual(set([d for d in src_items]), set([d.item_code for d in updated_so.items])) - def get_random_items(self, items, limit): - '''Get a number of random items from a list of given items''' - random_items = [] - for i in range(0, limit): - random_items.append(random.choice(items)) - return random_items - def make_quotation(self, items, customer): - item_list = self.get_random_items(items, 3) + def make_quotation(self, item_list, customer): + qtn = frappe.get_doc({ "doctype": "Quotation", "quotation_to": "Customer", @@ -47,7 +41,7 @@ class TestMapper(unittest.TestCase): "valid_till" : add_months(nowdate(), 1) }) for item in item_list: - qtn.append("items", {"qty": "2", "item_code": item.item_code}) + qtn.append("items", {"qty": "2", "item_code": item}) qtn.submit() return qtn, item_list @@ -58,7 +52,7 @@ class TestMapper(unittest.TestCase): "base_rate": 100.0, "description": "CPU", "doctype": "Sales Order Item", - "item_code": "_Test Item Home Desktop 100", + "item_code": "_Test Item", "item_name": "CPU", "parentfield": "items", "qty": 10.0, @@ -70,4 +64,4 @@ class TestMapper(unittest.TestCase): }) so = frappe.get_doc(frappe.get_test_records('Sales Order')[0]) so.insert(ignore_permissions=True) - return so, [item] + return so, [item.item_code] diff --git a/erpnext/selling/doctype/sales_order/test_records.json b/erpnext/selling/doctype/sales_order/test_records.json index 6cbd6c2fc17..8a090e6d3d3 100644 --- a/erpnext/selling/doctype/sales_order/test_records.json +++ b/erpnext/selling/doctype/sales_order/test_records.json @@ -1,39 +1,39 @@ [ { "advance_paid": 0.0, - "company": "_Test Company", - "conversion_rate": 1.0, - "currency": "INR", - "customer": "_Test Customer", - "customer_group": "_Test Customer Group", - "customer_name": "_Test Customer", - "doctype": "Sales Order", - "base_grand_total": 1000.0, - "grand_total": 1000.0, - "naming_series": "_T-Sales Order-", - "order_type": "Sales", - "plc_conversion_rate": 1.0, - "price_list_currency": "INR", + "company": "_Test Company", + "conversion_rate": 1.0, + "currency": "INR", + "customer": "_Test Customer", + "customer_group": "_Test Customer Group", + "customer_name": "_Test Customer", + "doctype": "Sales Order", + "base_grand_total": 1000.0, + "grand_total": 1000.0, + "naming_series": "_T-Sales Order-", + "order_type": "Sales", + "plc_conversion_rate": 1.0, + "price_list_currency": "INR", "items": [ { - "base_amount": 1000.0, - "base_rate": 100.0, - "description": "CPU", - "doctype": "Sales Order Item", - "item_code": "_Test Item Home Desktop 100", - "item_name": "CPU", - "delivery_date": "2013-02-23", - "parentfield": "items", - "qty": 10.0, - "rate": 100.0, + "base_amount": 1000.0, + "base_rate": 100.0, + "description": "CPU", + "doctype": "Sales Order Item", + "item_code": "_Test Item", + "item_name": "_Test Item 1", + "delivery_date": "2013-02-23", + "parentfield": "items", + "qty": 10.0, + "rate": 100.0, "warehouse": "_Test Warehouse - _TC", "stock_uom": "_Test UOM", "conversion_factor": 1.0, "uom": "_Test UOM" } - ], - "selling_price_list": "_Test Price List", - "territory": "_Test Territory", + ], + "selling_price_list": "_Test Price List", + "territory": "_Test Territory", "transaction_date": "2013-02-21" } ] \ No newline at end of file From c9b4ba6de84f5e3c9c6440ef47baf1f7b04c188f Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Sat, 11 Jul 2020 17:44:20 +0530 Subject: [PATCH 577/608] fix: ewaybill json had json dump of json dump, and other related fixes --- erpnext/regional/india/utils.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index 05ffa87f144..961b8c6c3bc 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -458,19 +458,23 @@ def generate_ewb_json(dt, dn): @frappe.whitelist() def download_ewb_json(): - data = frappe._dict(frappe.local.form_dict) - - frappe.local.response.filecontent = json.dumps(data['data'], indent=4, sort_keys=True) + data = json.loads(frappe.local.form_dict.data) + frappe.local.response.filecontent = json.dumps(data, indent=4, sort_keys=True) frappe.local.response.type = 'download' - billList = json.loads(data['data'])['billLists'] + filename_prefix = 'Bulk' + docname = frappe.local.form_dict.docname + if docname: + if docname.startswith('['): + docname = json.loads(docname) + if len(docname) == 1: + docname = docname[0] - if len(billList) > 1: - doc_name = 'Bulk' - else: - doc_name = data['docname'] + if not isinstance(docname, list): + # removes characters not allowed in a filename (https://stackoverflow.com/a/38766141/4767738) + filename_prefix = re.sub('[^\w_.)( -]', '', docname) - frappe.local.response.filename = '{0}_e-WayBill_Data_{1}.json'.format(doc_name, frappe.utils.random_string(5)) + frappe.local.response.filename = '{0}_e-WayBill_Data_{1}.json'.format(filename_prefix, frappe.utils.random_string(5)) @frappe.whitelist() def get_gstins_for_company(company): From 83cd1dcc4480f0d84179abce7ffba82dd3d9a8e5 Mon Sep 17 00:00:00 2001 From: Afshan <33727827+AfshanKhan@users.noreply.github.com> Date: Sat, 11 Jul 2020 21:54:54 +0530 Subject: [PATCH 578/608] fix: Add project filter in parent task field (#22655) * fix: Add project filter in parent task field --- erpnext/projects/doctype/task/task.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/erpnext/projects/doctype/task/task.js b/erpnext/projects/doctype/task/task.js index a044e1dca89..8c6a9cf8d7c 100644 --- a/erpnext/projects/doctype/task/task.js +++ b/erpnext/projects/doctype/task/task.js @@ -29,10 +29,16 @@ frappe.ui.form.on("Task", { filters: filters }; }) - }, - refresh: function (frm) { - frm.set_query("parent_task", { "is_group": 1 }); + frm.set_query("parent_task", function () { + let filters = { + "is_group": 1 + }; + if (frm.doc.project) filters["project"] = frm.doc.project; + return { + filters: filters + } + }); }, is_group: function (frm) { From 193e12f02a4d1ae8ad84a324dbc860851aae380b Mon Sep 17 00:00:00 2001 From: Raffael Meyer <14891507+barredterra@users.noreply.github.com> Date: Sun, 12 Jul 2020 13:56:27 +0200 Subject: [PATCH 579/608] fix: download button (#22652) --- erpnext/regional/report/datev/datev.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/regional/report/datev/datev.js b/erpnext/regional/report/datev/datev.js index d8638ab05de..55f12cf3738 100644 --- a/erpnext/regional/report/datev/datev.js +++ b/erpnext/regional/report/datev/datev.js @@ -30,7 +30,7 @@ frappe.query_reports["DATEV"] = { } ], onload: function(query_report) { - query_report.page.add_inner_button("Download DATEV Export", () => { + query_report.page.add_menu_item(__("Download DATEV File"), () => { const filters = JSON.stringify(query_report.get_values()); window.open(`/api/method/erpnext.regional.report.datev.datev.download_datev_csv?filters=${filters}`); }); From d058840094ed6b78c6dd4bb2b96b10297a206334 Mon Sep 17 00:00:00 2001 From: Afshan <33727827+AfshanKhan@users.noreply.github.com> Date: Sun, 12 Jul 2020 19:19:59 +0530 Subject: [PATCH 580/608] fix: removed fiscal year field from "Request for Quotation" doctype (#22446) * fix: removed fiscal year field from "Request for Quotation" doctype --- .../request_for_quotation.json | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.json b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.json index 97aa9221e2f..5cd8e6f4fa8 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.json +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.json @@ -1,4 +1,5 @@ { + "actions": "", "allow_import": 1, "autoname": "naming_series:", "creation": "2016-02-25 01:24:07.224790", @@ -28,7 +29,6 @@ "letter_head", "more_info", "status", - "fiscal_year", "column_break3", "amended_from" ], @@ -218,17 +218,6 @@ "reqd": 1, "search_index": 1 }, - { - "fieldname": "fiscal_year", - "fieldtype": "Link", - "label": "Fiscal Year", - "oldfieldname": "fiscal_year", - "oldfieldtype": "Select", - "options": "Fiscal Year", - "print_hide": 1, - "reqd": 1, - "search_index": 1 - }, { "fieldname": "column_break3", "fieldtype": "Column Break" @@ -245,7 +234,8 @@ ], "icon": "fa fa-shopping-cart", "is_submittable": 1, - "modified": "2019-09-24 15:08:32.750661", + "links": [], + "modified": "2020-06-25 14:37:21.140194", "modified_by": "Administrator", "module": "Buying", "name": "Request for Quotation", From cd445786fe1f01965f8f54191557d8c7c1a9a087 Mon Sep 17 00:00:00 2001 From: Anupam K Date: Sun, 12 Jul 2020 20:03:15 +0530 Subject: [PATCH 581/608] making owner fieldtype to link --- .../crm/report/lead_owner_efficiency/lead_owner_efficiency.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/crm/report/lead_owner_efficiency/lead_owner_efficiency.py b/erpnext/crm/report/lead_owner_efficiency/lead_owner_efficiency.py index 6172a75fdd8..53fc8cd810c 100644 --- a/erpnext/crm/report/lead_owner_efficiency/lead_owner_efficiency.py +++ b/erpnext/crm/report/lead_owner_efficiency/lead_owner_efficiency.py @@ -17,7 +17,8 @@ def get_columns(): { "fieldname": "lead_owner", "label": _("Lead Owner"), - "fieldtype": "Data", + "fieldtype": "Link", + "options":"User", "width": "130" }, { From 2e8d8b9aebdd2b7b372517167508ae030f995d05 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 13 Jul 2020 12:08:58 +0530 Subject: [PATCH 582/608] fix: '>' not supported between instances of 'str' and 'int' --- erpnext/projects/doctype/task/task.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/projects/doctype/task/task.py b/erpnext/projects/doctype/task/task.py index 3b75cf42158..4bdda68b693 100755 --- a/erpnext/projects/doctype/task/task.py +++ b/erpnext/projects/doctype/task/task.py @@ -9,7 +9,7 @@ import frappe from frappe import _, throw from frappe.desk.form.assign_to import clear, close_all_assignments from frappe.model.mapper import get_mapped_doc -from frappe.utils import add_days, cstr, date_diff, get_link_to_form, getdate, today +from frappe.utils import add_days, cstr, date_diff, get_link_to_form, getdate, today, flt from frappe.utils.nestedset import NestedSet @@ -63,10 +63,10 @@ class Task(NestedSet): close_all_assignments(self.doctype, self.name) def validate_progress(self): - if (self.progress or 0) > 100: + if flt(self.progress or 0) > 100: frappe.throw(_("Progress % for a task cannot be more than 100.")) - if self.progress == 100: + if flt(self.progress) == 100: self.status = 'Completed' if self.status == 'Completed': From 1791bc187b384ebd840d563e70b84099e1e75b84 Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 13 Jul 2020 12:32:09 +0530 Subject: [PATCH 583/608] fix: Block Invalid Serial No updates in Maintenance Schedule --- .../maintenance_schedule/maintenance_schedule.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py index b58f999cfba..add7bbfa57d 100644 --- a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py +++ b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py @@ -44,7 +44,7 @@ class MaintenanceSchedule(TransactionBase): for d in self.get('items'): if d.serial_no: serial_nos = get_valid_serial_nos(d.serial_no) - self.validate_serial_no(serial_nos, d.start_date) + self.validate_serial_no(d.item_code, serial_nos, d.start_date) self.update_amc_date(serial_nos, d.end_date) no_email_sp = [] @@ -178,14 +178,18 @@ class MaintenanceSchedule(TransactionBase): serial_no_doc.amc_expiry_date = amc_expiry_date serial_no_doc.save() - def validate_serial_no(self, serial_nos, amc_start_date): + def validate_serial_no(self, item_code, serial_nos, amc_start_date): for serial_no in serial_nos: sr_details = frappe.db.get_value("Serial No", serial_no, - ["warranty_expiry_date", "amc_expiry_date", "warehouse", "delivery_date"], as_dict=1) + ["warranty_expiry_date", "amc_expiry_date", "warehouse", "delivery_date", "item_code"], as_dict=1) if not sr_details: frappe.throw(_("Serial No {0} not found").format(serial_no)) + if sr_details.get("item_code") != item_code: + frappe.throw(_("Serial No {0} does not belong to Item {1}") + .format(frappe.bold(serial_no), frappe.bold(item_code)), title="Invalid") + if sr_details.warranty_expiry_date \ and getdate(sr_details.warranty_expiry_date) >= getdate(amc_start_date): throw(_("Serial No {0} is under warranty upto {1}") From eb69859d3ef0c4ec1189c4b77a3932cdc0b125c6 Mon Sep 17 00:00:00 2001 From: bhavesh95863 <34086262+bhavesh95863@users.noreply.github.com> Date: Mon, 13 Jul 2020 23:15:31 +0530 Subject: [PATCH 584/608] fix: Quotation list view blank if quotation_to field not set as a standard filter --- .../selling/doctype/quotation/quotation_list.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/erpnext/selling/doctype/quotation/quotation_list.js b/erpnext/selling/doctype/quotation/quotation_list.js index 802c0ba641d..f425acf180a 100644 --- a/erpnext/selling/doctype/quotation/quotation_list.js +++ b/erpnext/selling/doctype/quotation/quotation_list.js @@ -3,13 +3,15 @@ frappe.listview_settings['Quotation'] = { "company", "currency", 'valid_till'], onload: function(listview) { - listview.page.fields_dict.quotation_to.get_query = function() { - return { - "filters": { - "name": ["in", ["Customer", "Lead"]], - } + if (listview.page.fields_dict.quotation_to) { + listview.page.fields_dict.quotation_to.get_query = function() { + return { + "filters": { + "name": ["in", ["Customer", "Lead"]], + } + }; }; - }; + } }, get_indicator: function(doc) { From 72320afb0797d72426ed31827febbc49045da51f Mon Sep 17 00:00:00 2001 From: John Veness Date: Tue, 14 Jul 2020 06:14:15 +0100 Subject: [PATCH 585/608] Correct help link for Address (#22673) --- erpnext/public/js/help_links.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/public/js/help_links.js b/erpnext/public/js/help_links.js index 17b726ee18f..66ff46405d1 100644 --- a/erpnext/public/js/help_links.js +++ b/erpnext/public/js/help_links.js @@ -450,7 +450,7 @@ frappe.help.help_links['Form/Opportunity'] = [ ] frappe.help.help_links['Form/Address'] = [ - { label: 'Address', url: docsUrl + 'user/manual/en/CRM/contact' }, + { label: 'Address', url: docsUrl + 'user/manual/en/CRM/address' }, ] frappe.help.help_links['Form/Contact'] = [ From 88931f0677c4cb8a79f4fdf1055767a1db14616b Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 14 Jul 2020 11:46:11 +0530 Subject: [PATCH 586/608] fix(Support): TypeError while saving Service Level Agreement --- .../service_level_agreement/service_level_agreement.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py index c6923157064..61b66a9b13a 100644 --- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py +++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py @@ -21,8 +21,8 @@ class ServiceLevelAgreement(Document): for priority in self.priorities: # Check if response and resolution time is set for every priority - if not (priority.response_time or priority.resolution_time): - frappe.throw(_("Set Response Time and Resolution for Priority {0} at index {1}.").format(priority.priority, priority.idx)) + if not priority.response_time or not priority.resolution_time: + frappe.throw(_("Set Response Time and Resolution Time for Priority {0} in row {1}.").format(priority.priority, priority.idx)) priorities.append(priority.priority) @@ -33,7 +33,7 @@ class ServiceLevelAgreement(Document): resolution = priority.resolution_time if response > resolution: - frappe.throw(_("Response Time for {0} at index {1} can't be greater than Resolution Time.").format(priority.priority, priority.idx)) + frappe.throw(_("Response Time for {0} priority in row {1} can't be greater than Resolution Time.").format(priority.priority, priority.idx)) # Check if repeated priority if not len(set(priorities)) == len(priorities): From 82a606d04bd7568aec2206967d3cf11fddb0930b Mon Sep 17 00:00:00 2001 From: Marica Date: Tue, 14 Jul 2020 13:16:28 +0530 Subject: [PATCH 587/608] fix: Added Project Field in Purchase Receipt for Stock Ledger Tagging (#22666) --- .../purchase_receipt/purchase_receipt.json | 452 +++++------------- 1 file changed, 111 insertions(+), 341 deletions(-) diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json index 3b92dac220e..0cb85d32a6a 100755 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json @@ -103,6 +103,7 @@ "bill_no", "bill_date", "more_info", + "project", "status", "amended_from", "range", @@ -132,17 +133,13 @@ { "fieldname": "supplier_section", "fieldtype": "Section Break", - "options": "fa fa-user", - "show_days": 1, - "show_seconds": 1 + "options": "fa fa-user" }, { "fieldname": "column_break0", "fieldtype": "Column Break", "oldfieldtype": "Column Break", "print_width": "50%", - "show_days": 1, - "show_seconds": 1, "width": "50%" }, { @@ -153,9 +150,7 @@ "hidden": 1, "label": "Title", "no_copy": 1, - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "naming_series", @@ -167,9 +162,7 @@ "options": "MAT-PRE-.YYYY.-", "print_hide": 1, "reqd": 1, - "set_only_once": 1, - "show_days": 1, - "show_seconds": 1 + "set_only_once": 1 }, { "bold": 1, @@ -184,8 +177,6 @@ "print_width": "150px", "reqd": 1, "search_index": 1, - "show_days": 1, - "show_seconds": 1, "width": "150px" }, { @@ -196,24 +187,18 @@ "fieldtype": "Data", "in_global_search": 1, "label": "Supplier Name", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "supplier_delivery_note", "fieldtype": "Data", - "label": "Supplier Delivery Note", - "show_days": 1, - "show_seconds": 1 + "label": "Supplier Delivery Note" }, { "fieldname": "column_break1", "fieldtype": "Column Break", "oldfieldtype": "Column Break", "print_width": "50%", - "show_days": 1, - "show_seconds": 1, "width": "50%" }, { @@ -228,8 +213,6 @@ "print_width": "100px", "reqd": 1, "search_index": 1, - "show_days": 1, - "show_seconds": 1, "width": "100px" }, { @@ -243,8 +226,6 @@ "print_hide": 1, "print_width": "100px", "reqd": 1, - "show_days": 1, - "show_seconds": 1, "width": "100px" }, { @@ -253,9 +234,7 @@ "fieldname": "set_posting_time", "fieldtype": "Check", "label": "Edit Posting Date and Time", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "company", @@ -269,8 +248,6 @@ "print_width": "150px", "remember_last_selected_value": 1, "reqd": 1, - "show_days": 1, - "show_seconds": 1, "width": "150px" }, { @@ -280,9 +257,7 @@ "label": "Is Return", "no_copy": 1, "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "depends_on": "is_return", @@ -292,60 +267,46 @@ "no_copy": 1, "options": "Purchase Receipt", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "collapsible": 1, "fieldname": "section_addresses", "fieldtype": "Section Break", - "label": "Address and Contact", - "show_days": 1, - "show_seconds": 1 + "label": "Address and Contact" }, { "fieldname": "supplier_address", "fieldtype": "Link", "label": "Select Supplier Address", "options": "Address", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "contact_person", "fieldtype": "Link", "label": "Contact Person", "options": "Contact", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "address_display", "fieldtype": "Small Text", "label": "Address", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "contact_display", "fieldtype": "Small Text", "in_global_search": 1, "label": "Contact", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "contact_mobile", "fieldtype": "Small Text", "label": "Mobile No", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "contact_email", @@ -353,42 +314,32 @@ "label": "Contact Email", "options": "Email", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "col_break_address", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "fieldname": "shipping_address", "fieldtype": "Link", "label": "Select Shipping Address", "options": "Address", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "shipping_address_display", "fieldtype": "Small Text", "label": "Shipping Address", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "collapsible": 1, "fieldname": "currency_and_price_list", "fieldtype": "Section Break", "label": "Currency and Price List", - "options": "fa fa-tag", - "show_days": 1, - "show_seconds": 1 + "options": "fa fa-tag" }, { "fieldname": "currency", @@ -398,9 +349,7 @@ "oldfieldtype": "Select", "options": "Currency", "print_hide": 1, - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "description": "Rate at which supplier's currency is converted to company's base currency", @@ -411,17 +360,13 @@ "oldfieldtype": "Currency", "precision": "9", "print_hide": 1, - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "fieldname": "column_break2", "fieldtype": "Column Break", "oldfieldtype": "Column Break", "print_width": "50%", - "show_days": 1, - "show_seconds": 1, "width": "50%" }, { @@ -429,9 +374,7 @@ "fieldtype": "Link", "label": "Price List", "options": "Price List", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "depends_on": "buying_price_list", @@ -440,9 +383,7 @@ "label": "Price List Currency", "options": "Currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "depends_on": "buying_price_list", @@ -450,9 +391,7 @@ "fieldtype": "Float", "label": "Price List Exchange Rate", "precision": "9", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "default": "0", @@ -461,15 +400,11 @@ "label": "Ignore Pricing Rule", "no_copy": 1, "permlevel": 1, - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "sec_warehouse", - "fieldtype": "Section Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Section Break" }, { "description": "Sets 'Accepted Warehouse' in each row of the items table.", @@ -477,9 +412,7 @@ "fieldtype": "Link", "label": "Accepted Warehouse", "options": "Warehouse", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "description": "Sets 'Rejected Warehouse' in each row of the items table.", @@ -490,15 +423,11 @@ "oldfieldname": "rejected_warehouse", "oldfieldtype": "Link", "options": "Warehouse", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "col_break_warehouse", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "default": "No", @@ -508,9 +437,7 @@ "oldfieldname": "is_subcontracted", "oldfieldtype": "Select", "options": "No\nYes", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "depends_on": "eval:doc.is_subcontracted==\"Yes\"", @@ -523,17 +450,13 @@ "options": "Warehouse", "print_hide": 1, "print_width": "50px", - "show_days": 1, - "show_seconds": 1, "width": "50px" }, { "fieldname": "items_section", "fieldtype": "Section Break", "oldfieldtype": "Section Break", - "options": "fa fa-shopping-cart", - "show_days": 1, - "show_seconds": 1 + "options": "fa fa-shopping-cart" }, { "allow_bulk_edit": 1, @@ -543,26 +466,20 @@ "oldfieldname": "purchase_receipt_details", "oldfieldtype": "Table", "options": "Purchase Receipt Item", - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "collapsible": 1, "fieldname": "pricing_rule_details", "fieldtype": "Section Break", - "label": "Pricing Rules", - "show_days": 1, - "show_seconds": 1 + "label": "Pricing Rules" }, { "fieldname": "pricing_rules", "fieldtype": "Table", "label": "Pricing Rule Detail", "options": "Pricing Rule Detail", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "depends_on": "supplied_items", @@ -571,9 +488,7 @@ "label": "Get Current Stock", "oldfieldtype": "Button", "options": "get_current_stock", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "collapsible": 1, @@ -584,9 +499,7 @@ "oldfieldtype": "Section Break", "options": "fa fa-table", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "supplied_items", @@ -597,24 +510,18 @@ "oldfieldtype": "Table", "options": "Purchase Receipt Item Supplied", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "section_break0", "fieldtype": "Section Break", - "oldfieldtype": "Section Break", - "show_days": 1, - "show_seconds": 1 + "oldfieldtype": "Section Break" }, { "fieldname": "total_qty", "fieldtype": "Float", "label": "Total Quantity", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "base_total", @@ -622,9 +529,7 @@ "label": "Total (Company Currency)", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "base_net_total", @@ -637,24 +542,18 @@ "print_width": "150px", "read_only": 1, "reqd": 1, - "show_days": 1, - "show_seconds": 1, "width": "150px" }, { "fieldname": "column_break_27", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "fieldname": "total", "fieldtype": "Currency", "label": "Total", "options": "currency", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "net_total", @@ -664,56 +563,42 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "total_net_weight", "fieldtype": "Float", "label": "Total Net Weight", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "description": "Add / Edit Taxes and Charges", "fieldname": "taxes_charges_section", "fieldtype": "Section Break", "oldfieldtype": "Section Break", - "options": "fa fa-money", - "show_days": 1, - "show_seconds": 1 + "options": "fa fa-money" }, { "fieldname": "tax_category", "fieldtype": "Link", "label": "Tax Category", "options": "Tax Category", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "shipping_col", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "fieldname": "shipping_rule", "fieldtype": "Link", "label": "Shipping Rule", - "options": "Shipping Rule", - "show_days": 1, - "show_seconds": 1 + "options": "Shipping Rule" }, { "fieldname": "taxes_section", - "fieldtype": "Section Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Section Break" }, { "fieldname": "taxes_and_charges", @@ -722,9 +607,7 @@ "oldfieldname": "purchase_other_charges", "oldfieldtype": "Link", "options": "Purchase Taxes and Charges Template", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "taxes", @@ -732,17 +615,13 @@ "label": "Purchase Taxes and Charges", "oldfieldname": "purchase_tax_details", "oldfieldtype": "Table", - "options": "Purchase Taxes and Charges", - "show_days": 1, - "show_seconds": 1 + "options": "Purchase Taxes and Charges" }, { "collapsible": 1, "fieldname": "sec_tax_breakup", "fieldtype": "Section Break", - "label": "Tax Breakup", - "show_days": 1, - "show_seconds": 1 + "label": "Tax Breakup" }, { "fieldname": "other_charges_calculation", @@ -751,17 +630,13 @@ "no_copy": 1, "oldfieldtype": "HTML", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "totals", "fieldtype": "Section Break", "oldfieldtype": "Section Break", - "options": "fa fa-money", - "show_days": 1, - "show_seconds": 1 + "options": "fa fa-money" }, { "fieldname": "base_taxes_and_charges_added", @@ -771,9 +646,7 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "base_taxes_and_charges_deducted", @@ -783,9 +656,7 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "base_total_taxes_and_charges", @@ -795,16 +666,12 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "column_break3", "fieldtype": "Column Break", "print_width": "50%", - "show_days": 1, - "show_seconds": 1, "width": "50%" }, { @@ -815,9 +682,7 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "taxes_and_charges_deducted", @@ -827,9 +692,7 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "total_taxes_and_charges", @@ -837,18 +700,14 @@ "label": "Total Taxes and Charges", "options": "currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "collapsible": 1, "collapsible_depends_on": "discount_amount", "fieldname": "section_break_42", "fieldtype": "Section Break", - "label": "Additional Discount", - "show_days": 1, - "show_seconds": 1 + "label": "Additional Discount" }, { "default": "Grand Total", @@ -856,9 +715,7 @@ "fieldtype": "Select", "label": "Apply Additional Discount On", "options": "\nGrand Total\nNet Total", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "base_discount_amount", @@ -866,38 +723,28 @@ "label": "Additional Discount Amount (Company Currency)", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "column_break_44", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "fieldname": "additional_discount_percentage", "fieldtype": "Float", "label": "Additional Discount Percentage", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "discount_amount", "fieldtype": "Currency", "label": "Additional Discount Amount", "options": "currency", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "section_break_46", - "fieldtype": "Section Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Section Break" }, { "fieldname": "base_grand_total", @@ -907,9 +754,7 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "base_rounding_adjustment", @@ -918,9 +763,7 @@ "no_copy": 1, "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "base_in_words", @@ -929,9 +772,7 @@ "oldfieldname": "in_words", "oldfieldtype": "Data", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "base_rounded_total", @@ -941,15 +782,11 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "column_break_50", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "fieldname": "grand_total", @@ -959,9 +796,7 @@ "oldfieldname": "grand_total_import", "oldfieldtype": "Currency", "options": "currency", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "rounding_adjustment", @@ -970,9 +805,7 @@ "no_copy": 1, "options": "currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "depends_on": "eval:!doc.disable_rounded_total", @@ -982,9 +815,7 @@ "no_copy": 1, "options": "currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "in_words", @@ -993,17 +824,13 @@ "oldfieldname": "in_words_import", "oldfieldtype": "Data", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "default": "0", "fieldname": "disable_rounded_total", "fieldtype": "Check", - "label": "Disable Rounded Total", - "show_days": 1, - "show_seconds": 1 + "label": "Disable Rounded Total" }, { "collapsible": 1, @@ -1012,9 +839,7 @@ "fieldtype": "Section Break", "label": "Terms and Conditions", "oldfieldtype": "Section Break", - "options": "fa fa-legal", - "show_days": 1, - "show_seconds": 1 + "options": "fa fa-legal" }, { "fieldname": "tc_name", @@ -1023,18 +848,14 @@ "oldfieldname": "tc_name", "oldfieldtype": "Link", "options": "Terms and Conditions", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "terms", "fieldtype": "Text Editor", "label": "Terms and Conditions", "oldfieldname": "terms", - "oldfieldtype": "Text Editor", - "show_days": 1, - "show_seconds": 1 + "oldfieldtype": "Text Editor" }, { "fieldname": "bill_no", @@ -1043,9 +864,7 @@ "label": "Bill No", "oldfieldname": "bill_no", "oldfieldtype": "Data", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "bill_date", @@ -1054,9 +873,7 @@ "label": "Bill Date", "oldfieldname": "bill_date", "oldfieldtype": "Date", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "collapsible": 1, @@ -1064,9 +881,7 @@ "fieldtype": "Section Break", "label": "More Information", "oldfieldtype": "Section Break", - "options": "fa fa-file-text", - "show_days": 1, - "show_seconds": 1 + "options": "fa fa-file-text" }, { "default": "Draft", @@ -1083,8 +898,6 @@ "read_only": 1, "reqd": 1, "search_index": 1, - "show_days": 1, - "show_seconds": 1, "width": "150px" }, { @@ -1100,8 +913,6 @@ "print_hide": 1, "print_width": "150px", "read_only": 1, - "show_days": 1, - "show_seconds": 1, "width": "150px" }, { @@ -1111,9 +922,7 @@ "label": "Range", "oldfieldname": "range", "oldfieldtype": "Data", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "column_break4", @@ -1121,8 +930,6 @@ "oldfieldtype": "Column Break", "print_hide": 1, "print_width": "50%", - "show_days": 1, - "show_seconds": 1, "width": "50%" }, { @@ -1137,16 +944,12 @@ "label": "% Amount Billed", "no_copy": 1, "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "subscription_detail", "fieldtype": "Section Break", - "label": "Auto Repeat Detail", - "show_days": 1, - "show_seconds": 1 + "label": "Auto Repeat Detail" }, { "fieldname": "auto_repeat", @@ -1155,17 +958,13 @@ "no_copy": 1, "options": "Auto Repeat", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "collapsible": 1, "fieldname": "printing_settings", "fieldtype": "Section Break", - "label": "Printing Settings", - "show_days": 1, - "show_seconds": 1 + "label": "Printing Settings" }, { "allow_on_submit": 1, @@ -1173,9 +972,7 @@ "fieldtype": "Link", "label": "Letter Head", "options": "Letter Head", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "allow_on_submit": 1, @@ -1187,17 +984,13 @@ "oldfieldtype": "Link", "options": "Print Heading", "print_hide": 1, - "report_hide": 1, - "show_days": 1, - "show_seconds": 1 + "report_hide": 1 }, { "fieldname": "language", "fieldtype": "Data", "label": "Print Language", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "allow_on_submit": 1, @@ -1205,15 +998,11 @@ "fieldname": "group_same_items", "fieldtype": "Check", "label": "Group same items", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "column_break_97", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "fieldname": "other_details", @@ -1224,8 +1013,6 @@ "options": "
    Other Details
    ", "print_hide": 1, "print_width": "30%", - "show_days": 1, - "show_seconds": 1, "width": "30%" }, { @@ -1233,17 +1020,13 @@ "fieldtype": "Small Text", "label": "Instructions", "oldfieldname": "instructions", - "oldfieldtype": "Text", - "show_days": 1, - "show_seconds": 1 + "oldfieldtype": "Text" }, { "fieldname": "remarks", "fieldtype": "Small Text", "label": "Remarks", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "collapsible": 1, @@ -1251,25 +1034,19 @@ "fieldname": "transporter_info", "fieldtype": "Section Break", "label": "Transporter Details", - "options": "fa fa-truck", - "show_days": 1, - "show_seconds": 1 + "options": "fa fa-truck" }, { "fieldname": "transporter_name", "fieldtype": "Data", "label": "Transporter Name", "oldfieldname": "transporter_name", - "oldfieldtype": "Data", - "show_days": 1, - "show_seconds": 1 + "oldfieldtype": "Data" }, { "fieldname": "column_break5", "fieldtype": "Column Break", "print_width": "50%", - "show_days": 1, - "show_seconds": 1, "width": "50%" }, { @@ -1280,8 +1057,6 @@ "oldfieldname": "lr_no", "oldfieldtype": "Data", "print_width": "100px", - "show_days": 1, - "show_seconds": 1, "width": "100px" }, { @@ -1292,8 +1067,6 @@ "oldfieldname": "lr_date", "oldfieldtype": "Date", "print_width": "100px", - "show_days": 1, - "show_seconds": 1, "width": "100px" }, { @@ -1302,48 +1075,45 @@ "fieldname": "is_internal_supplier", "fieldtype": "Check", "label": "Is Internal Supplier", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "inter_company_reference", "fieldtype": "Link", "label": "Inter Company Reference", "options": "Delivery Note", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "scan_barcode", "fieldtype": "Data", - "label": "Scan Barcode", - "show_days": 1, - "show_seconds": 1 + "label": "Scan Barcode" }, { "fieldname": "billing_address", "fieldtype": "Link", "label": "Select Billing Address", - "options": "Address", - "show_days": 1, - "show_seconds": 1 + "options": "Address" }, { "fieldname": "billing_address_display", "fieldtype": "Small Text", "label": "Billing Address", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 + }, + { + "description": "Track this Purchase Receipt against any Project", + "fieldname": "project", + "fieldtype": "Link", + "label": "Project", + "options": "Project" } ], "icon": "fa fa-truck", "idx": 261, "is_submittable": 1, "links": [], - "modified": "2020-06-13 22:26:03.600092", + "modified": "2020-07-13 14:01:39.302238", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt", From 392f323d2de2a847e24d00ae000a9eca0174c8af Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 14 Jul 2020 17:03:17 +0530 Subject: [PATCH 588/608] fix: Dont overwrite default warehouse in Material Request --- .../doctype/material_request/material_request.js | 16 ++++++++-------- erpnext/stock/get_item_details.py | 6 ++++++ 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js index 3a8deb6d254..60f5ff3629b 100644 --- a/erpnext/stock/doctype/material_request/material_request.js +++ b/erpnext/stock/doctype/material_request/material_request.js @@ -180,9 +180,8 @@ frappe.ui.form.on('Material Request', { }); }, - get_item_data: function(frm, item) { + get_item_data: function(frm, item, overwrite_warehouse=false) { if (item && !item.item_code) { return; } - frm.call({ method: "erpnext.stock.get_item_details.get_item_details", child: item, @@ -203,7 +202,8 @@ frappe.ui.form.on('Material Request', { plc_conversion_rate: 1, rate: item.rate, conversion_factor: item.conversion_factor - } + }, + overwrite_warehouse: overwrite_warehouse }, callback: function(r) { const d = item; @@ -354,29 +354,29 @@ frappe.ui.form.on("Material Request Item", { } const item = locals[doctype][name]; - frm.events.get_item_data(frm, item); + frm.events.get_item_data(frm, item, false); }, from_warehouse: function(frm, doctype, name) { const item = locals[doctype][name]; - frm.events.get_item_data(frm, item); + frm.events.get_item_data(frm, item, false); }, warehouse: function(frm, doctype, name) { const item = locals[doctype][name]; - frm.events.get_item_data(frm, item); + frm.events.get_item_data(frm, item, false); }, rate: function(frm, doctype, name) { const item = locals[doctype][name]; - frm.events.get_item_data(frm, item); + frm.events.get_item_data(frm, item, false); }, item_code: function(frm, doctype, name) { const item = locals[doctype][name]; item.rate = 0; set_schedule_date(frm); - frm.events.get_item_data(frm, item); + frm.events.get_item_data(frm, item, true); }, schedule_date: function(frm, cdt, cdn) { diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index b1a16149b2d..b8554c83e24 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -47,6 +47,8 @@ def get_item_details(args, doc=None, for_validate=False, overwrite_warehouse=Tru """ args = process_args(args) + for_validate = process_string_args(for_validate) + overwrite_warehouse = process_string_args(overwrite_warehouse) item = frappe.get_cached_doc("Item", args.item_code) validate_item_details(args, item) @@ -166,6 +168,10 @@ def process_args(args): set_transaction_type(args) return args +def process_string_args(args): + if isinstance(args, string_types): + args = json.loads(args) + return args @frappe.whitelist() def get_item_code(barcode=None, serial_no=None): From c1636f8fab62472034169bccc24ac6ed9202421e Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Tue, 14 Jul 2020 18:58:02 +0530 Subject: [PATCH 589/608] fix: conversion rate as 1 if no conversion rate --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 59611bc74c2..cd2313ab8f4 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -1029,14 +1029,14 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount= if bank_amount: received_amount = bank_amount else: - received_amount = paid_amount * doc.conversion_rate + received_amount = paid_amount * (doc.get('conversion_rate') or 1) else: received_amount = abs(outstanding_amount) if bank_amount: paid_amount = bank_amount else: # if party account currency and bank currency is different then populate paid amount as well - paid_amount = received_amount * doc.conversion_rate + paid_amount = received_amount * (doc.get('conversion_rate') or 1) pe = frappe.new_doc("Payment Entry") pe.payment_type = payment_type From 5beac7a0cca91d4dccff578f23363ba86c90e54c Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Wed, 15 Jul 2020 12:17:43 +0530 Subject: [PATCH 590/608] fix: Period list fixes in financial statements (#22677) --- erpnext/accounts/report/financial_statements.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index ee1d54a92ed..3785ebf215f 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -8,6 +8,7 @@ from __future__ import unicode_literals import re from past.builtins import cmp import functools +import math import frappe, erpnext from erpnext.accounts.report.utils import get_currency, convert_to_presentation_currency @@ -45,10 +46,7 @@ def get_period_list(from_fiscal_year, to_fiscal_year, period_start_date, period_ start_date = year_start_date months = get_months(year_start_date, year_end_date) - if (months // months_to_add) != (months / months_to_add): - months += months_to_add - - for i in range(months // months_to_add): + for i in range(math.ceil(months / months_to_add)): period = frappe._dict({ "from_date": start_date }) From e1b3f16abd9484d59ba54072f21758ada46a8b65 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 15 Jul 2020 12:28:19 +0530 Subject: [PATCH 591/608] fix: project field added two time in purchase receipt --- .../doctype/purchase_receipt/purchase_receipt.json | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json index 0cb85d32a6a..df9eb50843f 100755 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json @@ -108,7 +108,6 @@ "amended_from", "range", "column_break4", - "project", "per_billed", "is_internal_supplier", "inter_company_reference", @@ -933,6 +932,7 @@ "width": "50%" }, { + "description": "Track this Purchase Receipt against any Project", "fieldname": "project", "fieldtype": "Link", "label": "Project", @@ -1100,20 +1100,13 @@ "fieldtype": "Small Text", "label": "Billing Address", "read_only": 1 - }, - { - "description": "Track this Purchase Receipt against any Project", - "fieldname": "project", - "fieldtype": "Link", - "label": "Project", - "options": "Project" } ], "icon": "fa fa-truck", "idx": 261, "is_submittable": 1, "links": [], - "modified": "2020-07-13 14:01:39.302238", + "modified": "2020-07-15 10:01:39.302238", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt", From 34617744e1f4cd2fac9a6271639c4df7c00eb482 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 15 Jul 2020 16:37:21 +0530 Subject: [PATCH 592/608] fix: Show total row in print format of financial statement --- erpnext/accounts/report/financial_statements.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/financial_statements.html b/erpnext/accounts/report/financial_statements.html index 50947ecf5ef..2bb09cf0dc5 100644 --- a/erpnext/accounts/report/financial_statements.html +++ b/erpnext/accounts/report/financial_statements.html @@ -44,7 +44,7 @@ - {% for(let j=0, k=data.length-1; j Date: Wed, 15 Jul 2020 16:41:32 +0530 Subject: [PATCH 593/608] fix: patch update_actual_start_and_end_date_in_wo --- erpnext/patches/v13_0/update_actual_start_and_end_date_in_wo.py | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/patches/v13_0/update_actual_start_and_end_date_in_wo.py b/erpnext/patches/v13_0/update_actual_start_and_end_date_in_wo.py index 331c5590e58..adfa20e368a 100644 --- a/erpnext/patches/v13_0/update_actual_start_and_end_date_in_wo.py +++ b/erpnext/patches/v13_0/update_actual_start_and_end_date_in_wo.py @@ -6,7 +6,6 @@ from __future__ import unicode_literals import frappe from frappe.utils import add_to_date -from frappe.utils.dashboard import get_config, make_records def execute(): frappe.reload_doc("manufacturing", "doctype", "work_order") From e0de0ac61751216632ebbefdb666ffcf430e8712 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 15 Jul 2020 17:06:42 +0530 Subject: [PATCH 594/608] fix: not able to submit sales invoice --- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index b2b4cb1ea76..bab5208370d 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1113,7 +1113,10 @@ class SalesInvoice(SellingController): expiry_date=self.posting_date, include_expired_entry=True) if lp_details and getdate(lp_details.from_date) <= getdate(self.posting_date) and \ (not lp_details.to_date or getdate(lp_details.to_date) >= getdate(self.posting_date)): - points_earned = cint(eligible_amount/lp_details.collection_factor) + + collection_factor = lp_details.collection_factor if lp_details.collection_factor else 1.0 + points_earned = cint(eligible_amount/collection_factor) + doc = frappe.get_doc({ "doctype": "Loyalty Point Entry", "company": self.company, From 132376750c965b7d371f8c90e04d1e7a6dc9a670 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 10 Jul 2020 18:11:04 +0530 Subject: [PATCH 595/608] fix: incorrect balance qty in stock ledger report --- erpnext/stock/report/stock_ledger/stock_ledger.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/report/stock_ledger/stock_ledger.py b/erpnext/stock/report/stock_ledger/stock_ledger.py index abf959eb0bf..a5f92e2c76c 100644 --- a/erpnext/stock/report/stock_ledger/stock_ledger.py +++ b/erpnext/stock/report/stock_ledger/stock_ledger.py @@ -4,10 +4,10 @@ from __future__ import unicode_literals import frappe +from frappe.utils import cint, flt from erpnext.stock.utils import update_included_uom_in_report from frappe import _ - def execute(filters=None): include_uom = filters.get("include_uom") columns = get_columns() @@ -15,6 +15,7 @@ def execute(filters=None): sl_entries = get_stock_ledger_entries(filters, items) item_details = get_item_details(items, sl_entries, include_uom) opening_row = get_opening_balance(filters, columns) + precision = cint(frappe.db.get_single_value("System Settings", "float_precision")) data = [] conversion_factors = [] @@ -29,7 +30,7 @@ def execute(filters=None): sle.update(item_detail) if filters.get("batch_no"): - actual_qty += sle.actual_qty + actual_qty += flt(sle.actual_qty, precision) stock_value += sle.stock_value_difference if sle.voucher_type == 'Stock Reconciliation': From 0fa4143bac50fcca97505c2d4d8a545843273ff0 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 15 Jul 2020 18:17:38 +0530 Subject: [PATCH 596/608] fix: Cess amount in GSTR 3B report --- .../doctype/gstr_3b_report/gstr_3b_report.js | 4 + .../doctype/gstr_3b_report/gstr_3b_report.py | 78 ++++++++++--------- 2 files changed, 47 insertions(+), 35 deletions(-) diff --git a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.js b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.js index a1cea8f6092..5170185158c 100644 --- a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.js +++ b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.js @@ -45,6 +45,10 @@ frappe.ui.form.on('GSTR 3B Report', { frm.set_df_property('year', 'options', options); }, + validate: function(frm) { + frm.dirty(); + }, + setup: function(frm) { frm.set_query('company_address', function(doc) { if(!doc.company) { diff --git a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py index 619734ff263..6d9b8dbe281 100644 --- a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py +++ b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py @@ -243,20 +243,15 @@ class GSTR3BReport(Document): osup_det = self.report_dict["sup_details"]["osup_det"] - for d in inter_state_supply.get("Unregistered", []): - self.report_dict["inter_sup"]["unreg_details"].append(d) - osup_det["txval"] = flt(osup_det["txval"] + d["txval"], 2) - osup_det["iamt"] = flt(osup_det["iamt"] + d["iamt"], 2) + for key, value in iteritems(inter_state_supply): + if key[0] == "Unregistered": + self.report_dict["inter_sup"]["unreg_details"].append(value) - for d in inter_state_supply.get("Registered Composition", []): - self.report_dict["inter_sup"]["comp_details"].append(d) - osup_det["txval"] = flt(osup_det["txval"] + d["txval"], 2) - osup_det["iamt"] = flt(osup_det["iamt"] + d["iamt"], 2) + if key[0] == "Registered Composition": + self.report_dict["inter_sup"]["comp_details"].append(value) - for d in inter_state_supply.get("UIN Holders", []): - self.report_dict["inter_sup"]["uin_details"].append(d) - osup_det["txval"] = flt(osup_det["txval"] + d["txval"], 2) - osup_det["iamt"] = flt(osup_det["iamt"] + d["iamt"], 2) + if key[0] == "UIN Holders": + self.report_dict["inter_sup"]["uin_details"].append(value) def get_total_taxable_value(self, doctype, reverse_charge): @@ -301,41 +296,54 @@ class GSTR3BReport(Document): (self.month_no, self.year, self.company, self.gst_details.get("gstin")), as_dict=1)[0].total def get_inter_state_supplies(self, state_number): - - inter_state_supply_taxable_value = frappe.db.sql(""" select sum(s.net_total) as total, s.place_of_supply, s.gst_category - from `tabSales Invoice` s where s.docstatus = 1 and month(s.posting_date) = %s and year(s.posting_date) = %s - and s.company = %s and s.company_gstin = %s and s.gst_category in ('Unregistered', 'Registered Composition', 'UIN Holders') - group by s.gst_category, s.place_of_supply""", (self.month_no, self.year, self.company, self.gst_details.get("gstin")), as_dict=1) - - inter_state_supply_tax = frappe.db.sql(""" select sum(t.tax_amount_after_discount_amount) as tax_amount, s.place_of_supply, s.gst_category - from `tabSales Invoice` s, `tabSales Taxes and Charges` t + inter_state_supply_tax = frappe.db.sql(""" select t.account_head, t.tax_amount_after_discount_amount as tax_amount, + s.name, s.net_total, s.place_of_supply, s.gst_category from `tabSales Invoice` s, `tabSales Taxes and Charges` t where t.parent = s.name and s.docstatus = 1 and month(s.posting_date) = %s and year(s.posting_date) = %s and s.company = %s and s.company_gstin = %s and s.gst_category in ('Unregistered', 'Registered Composition', 'UIN Holders') - group by s.gst_category, s.place_of_supply""", (self.month_no, self.year, self.company, self.gst_details.get("gstin")), as_dict=1) + """, (self.month_no, self.year, self.company, self.gst_details.get("gstin")), as_dict=1) - inter_state_supply_tax_mapping={} + inter_state_supply_tax_mapping = {} inter_state_supply_details = {} for d in inter_state_supply_tax: - inter_state_supply_tax_mapping.setdefault(d.place_of_supply, d.tax_amount) + inter_state_supply_tax_mapping.setdefault(d.name, { + 'place_of_supply': d.place_of_supply, + 'taxable_value': d.net_total, + 'camt': 0.0, + 'samt': 0.0, + 'iamt': 0.0, + 'csamt': 0.0 + }) - for d in inter_state_supply_taxable_value: - inter_state_supply_details.setdefault( - d.gst_category, [] - ) + if d.account_head in [d.cgst_account for d in self.account_heads]: + inter_state_supply_tax_mapping[d.name]['camt'] += d.tax_amount + if d.account_head in [d.sgst_account for d in self.account_heads]: + inter_state_supply_tax_mapping[d.name]['samt'] += d.tax_amount + + if d.account_head in [d.igst_account for d in self.account_heads]: + inter_state_supply_tax_mapping[d.name]['samt'] += d.tax_amount + + if d.account_head in [d.cess_account for d in self.account_heads]: + inter_state_supply_tax_mapping[d.name]['csamt'] += d.tax_amount + + for key, value in iteritems(inter_state_supply_tax_mapping): if d.place_of_supply: + osup_det = self.report_dict["sup_details"]["osup_det"] + osup_det["txval"] = flt(osup_det["txval"] + value['taxable_value'], 2) + osup_det["camt"] = flt(osup_det["camt"] + value['camt'], 2) + osup_det["samt"] = flt(osup_det["samt"] + value['samt'], 2) + osup_det["csamt"] = flt(osup_det["csamt"] + value['csamt'], 2) + if state_number != d.place_of_supply.split("-")[0]: - inter_state_supply_details[d.gst_category].append({ + inter_state_supply_details.setdefault((d.gst_category, d.place_of_supply), { + "txval": 0.0, "pos": d.place_of_supply.split("-")[0], - "txval": flt(d.total, 2), - "iamt": flt(inter_state_supply_tax_mapping.get(d.place_of_supply), 2) + "iamt": 0.0 }) - else: - osup_det = self.report_dict["sup_details"]["osup_det"] - osup_det["txval"] = flt(osup_det["txval"] + d.total, 2) - osup_det["camt"] = flt(osup_det["camt"] + inter_state_supply_tax_mapping.get(d.place_of_supply)/2, 2) - osup_det["samt"] = flt(osup_det["samt"] + inter_state_supply_tax_mapping.get(d.place_of_supply)/2, 2) + + inter_state_supply_details[(d.gst_category, d.place_of_supply)]['txval'] += value['taxable_value'] + inter_state_supply_details[(d.gst_category, d.place_of_supply)]['iamt'] += value['iamt'] return inter_state_supply_details From 2af43b3df60701e7545b95bb040e6e2f05a3fbff Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Wed, 15 Jul 2020 21:54:28 +0530 Subject: [PATCH 597/608] fix: linting --- .../crm/report/lead_owner_efficiency/lead_owner_efficiency.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/crm/report/lead_owner_efficiency/lead_owner_efficiency.py b/erpnext/crm/report/lead_owner_efficiency/lead_owner_efficiency.py index 53fc8cd810c..8fe16a2f4ce 100644 --- a/erpnext/crm/report/lead_owner_efficiency/lead_owner_efficiency.py +++ b/erpnext/crm/report/lead_owner_efficiency/lead_owner_efficiency.py @@ -18,7 +18,7 @@ def get_columns(): "fieldname": "lead_owner", "label": _("Lead Owner"), "fieldtype": "Link", - "options":"User", + "options": "User", "width": "130" }, { @@ -69,4 +69,4 @@ def get_columns(): "fieldtype": "Float", "width": "100" } - ] \ No newline at end of file + ] From 52c319cfa793108162acd6046b000ffd71293b1b Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 15 Jul 2020 23:57:03 +0530 Subject: [PATCH 598/608] fix: Update RCM only for indian countries --- erpnext/regional/india/utils.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index 24bb137e680..7d10892c4a7 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -668,6 +668,11 @@ def get_gst_accounts(company, account_wise=False): return gst_accounts def update_grand_total_for_rcm(doc, method): + country = frappe.get_cached_value('Company', doc.company, 'country') + + if country != 'India': + return + if doc.reverse_charge == 'Y': gst_accounts = get_gst_accounts(doc.company) gst_account_list = gst_accounts.get('cgst_account') + gst_accounts.get('sgst_account') \ From 50902cf30ca34802c24ebd1616ab68492cca55f4 Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Thu, 16 Jul 2020 12:21:46 +0530 Subject: [PATCH 599/608] Fix: Changes Requested --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index cd2313ab8f4..962006553e2 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -1029,14 +1029,14 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount= if bank_amount: received_amount = bank_amount else: - received_amount = paid_amount * (doc.get('conversion_rate') or 1) + received_amount = paid_amount * doc.get('conversion_rate', 1) else: received_amount = abs(outstanding_amount) if bank_amount: paid_amount = bank_amount else: # if party account currency and bank currency is different then populate paid amount as well - paid_amount = received_amount * (doc.get('conversion_rate') or 1) + paid_amount = received_amount * doc.get('conversion_rate', 1) pe = frappe.new_doc("Payment Entry") pe.payment_type = payment_type From ac23ac6a025018e6e707fcb904256ae55f7cd876 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Thu, 16 Jul 2020 13:10:43 +0530 Subject: [PATCH 600/608] fix: validation for additional salary (#22645) * fix: validation for additional salary * fix:changes requested Co-authored-by: Rucha Mahabal Co-authored-by: Rucha Mahabal --- .../doctype/additional_salary/additional_salary.js | 3 +-- .../doctype/additional_salary/additional_salary.py | 12 ++++++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/erpnext/payroll/doctype/additional_salary/additional_salary.js b/erpnext/payroll/doctype/additional_salary/additional_salary.js index fb42b6f410e..d56cd4e967d 100644 --- a/erpnext/payroll/doctype/additional_salary/additional_salary.js +++ b/erpnext/payroll/doctype/additional_salary/additional_salary.js @@ -8,8 +8,7 @@ frappe.ui.form.on('Additional Salary', { frm.set_query("employee", function() { return { filters: { - company: frm.doc.company, - status: "Active" + company: frm.doc.company } }; }); diff --git a/erpnext/payroll/doctype/additional_salary/additional_salary.py b/erpnext/payroll/doctype/additional_salary/additional_salary.py index e369ba7cef6..ef174bdea20 100644 --- a/erpnext/payroll/doctype/additional_salary/additional_salary.py +++ b/erpnext/payroll/doctype/additional_salary/additional_salary.py @@ -33,12 +33,16 @@ class AdditionalSalary(Document): frappe.throw(_("From Date can not be greater than To Date.")) if date_of_joining: - if getdate(self.payroll_date) < getdate(date_of_joining): + if self.payroll_date and getdate(self.payroll_date) < getdate(date_of_joining): frappe.throw(_("Payroll date can not be less than employee's joining date.")) - elif getdate(self.from_date) < getdate(date_of_joining): + elif self.from_date and getdate(self.from_date) < getdate(date_of_joining): frappe.throw(_("From date can not be less than employee's joining date.")) - elif relieving_date and getdate(self.to_date) > getdate(relieving_date): + + if relieving_date: + if self.to_date and getdate(self.to_date) > getdate(relieving_date): frappe.throw(_("To date can not be greater than employee's relieving date.")) + if self.payroll_date and getdate(self.payroll_date) > getdate(relieving_date): + frappe.throw(_("Payroll date can not be greater than employee's relieving date.")) def get_amount(self, sal_start_date, sal_end_date): start_date = getdate(sal_start_date) @@ -107,4 +111,4 @@ def get_additional_salary_component(employee, start_date, end_date, component_ty existing_salary_components.append(d.salary_component) - return salary_components_details, additional_salary_details \ No newline at end of file + return salary_components_details, additional_salary_details From ff6ba25386609781d72f8d02c359cc93e9c4bbe8 Mon Sep 17 00:00:00 2001 From: Karthikeyan S Date: Thu, 16 Jul 2020 13:33:38 +0530 Subject: [PATCH 601/608] fix: remove FE validation for log_type field --- erpnext/hr/doctype/employee_checkin/employee_checkin.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/erpnext/hr/doctype/employee_checkin/employee_checkin.js b/erpnext/hr/doctype/employee_checkin/employee_checkin.js index 43023b6034b..c2403ca2bd9 100644 --- a/erpnext/hr/doctype/employee_checkin/employee_checkin.js +++ b/erpnext/hr/doctype/employee_checkin/employee_checkin.js @@ -6,9 +6,5 @@ frappe.ui.form.on('Employee Checkin', { if(!frm.doc.time) { frm.set_value("time", frappe.datetime.now_datetime()); } - }, - refresh: (frm) => { - // make log type mandatory - frm.set_df_property('log_type', 'reqd', frm.doc.log_type ? 0 : 1); } }); From 6319073d819e44d607e32c4d44a423e7603f5259 Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 16 Jul 2020 15:38:51 +0530 Subject: [PATCH 602/608] fix: Move Issue List actions under 'Actions' dropdown --- erpnext/support/doctype/issue/issue_list.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/support/doctype/issue/issue_list.js b/erpnext/support/doctype/issue/issue_list.js index 6d702f6bdf9..513a8dca222 100644 --- a/erpnext/support/doctype/issue/issue_list.js +++ b/erpnext/support/doctype/issue/issue_list.js @@ -8,11 +8,11 @@ frappe.listview_settings['Issue'] = { var method = "erpnext.support.doctype.issue.issue.set_multiple_status"; - listview.page.add_menu_item(__("Set as Open"), function() { + listview.page.add_action_item(__("Set as Open"), function() { listview.call_for_selected_items(method, {"status": "Open"}); }); - listview.page.add_menu_item(__("Set as Closed"), function() { + listview.page.add_action_item(__("Set as Closed"), function() { listview.call_for_selected_items(method, {"status": "Closed"}); }); }, From d02465a68f788f33c3fbe6bdd122b1b841a9fcf1 Mon Sep 17 00:00:00 2001 From: michellealva Date: Fri, 17 Jul 2020 10:35:16 +0530 Subject: [PATCH 603/608] fix: Update modified timestamp for Delivery Note Item --- .../stock/doctype/delivery_note_item/delivery_note_item.json | 2 +- erpnext/www/book-appointment/__init__.py | 0 erpnext/www/book-appointment/verify/__init__.py | 0 3 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 erpnext/www/book-appointment/__init__.py create mode 100644 erpnext/www/book-appointment/verify/__init__.py diff --git a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json index 7c92ac7dbba..3d57f476010 100644 --- a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json +++ b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json @@ -720,7 +720,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2020-03-11 12:25:06.177894", + "modified": "2020-07-20 12:25:06.177894", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note Item", diff --git a/erpnext/www/book-appointment/__init__.py b/erpnext/www/book-appointment/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/www/book-appointment/verify/__init__.py b/erpnext/www/book-appointment/verify/__init__.py new file mode 100644 index 00000000000..e69de29bb2d From 188273564cc593d3bf991901a1606b18e8a5c8af Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 17 Jul 2020 11:31:15 +0530 Subject: [PATCH 604/608] fix: Multiple fixes in GST --- .../doctype/gstr_3b_report/gstr_3b_report.js | 5 +- .../doctype/gstr_3b_report/gstr_3b_report.py | 3 +- erpnext/regional/india/utils.py | 3 +- erpnext/regional/report/gstr_1/gstr_1.py | 10 ++-- erpnext/regional/report/gstr_2/gstr_2.py | 46 +++++++++---------- 5 files changed, 34 insertions(+), 33 deletions(-) diff --git a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.js b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.js index 5170185158c..c7442667c22 100644 --- a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.js +++ b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.js @@ -3,6 +3,7 @@ frappe.ui.form.on('GSTR 3B Report', { refresh : function(frm) { + frm.doc.__unsaved = 1; if(!frm.is_new()) { frm.set_intro(__("Please save the report again to rebuild or update")); frm.add_custom_button(__('Download JSON'), function() { @@ -45,10 +46,6 @@ frappe.ui.form.on('GSTR 3B Report', { frm.set_df_property('year', 'options', options); }, - validate: function(frm) { - frm.dirty(); - }, - setup: function(frm) { frm.set_query('company_address', function(doc) { if(!doc.company) { diff --git a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py index 6d9b8dbe281..2d306ba1726 100644 --- a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py +++ b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py @@ -322,7 +322,7 @@ class GSTR3BReport(Document): inter_state_supply_tax_mapping[d.name]['samt'] += d.tax_amount if d.account_head in [d.igst_account for d in self.account_heads]: - inter_state_supply_tax_mapping[d.name]['samt'] += d.tax_amount + inter_state_supply_tax_mapping[d.name]['iamt'] += d.tax_amount if d.account_head in [d.cess_account for d in self.account_heads]: inter_state_supply_tax_mapping[d.name]['csamt'] += d.tax_amount @@ -331,6 +331,7 @@ class GSTR3BReport(Document): if d.place_of_supply: osup_det = self.report_dict["sup_details"]["osup_det"] osup_det["txval"] = flt(osup_det["txval"] + value['taxable_value'], 2) + osup_det["iamt"] = flt(osup_det["iamt"] + value['iamt'], 2) osup_det["camt"] = flt(osup_det["camt"] + value['camt'], 2) osup_det["samt"] = flt(osup_det["samt"] + value['samt'], 2) osup_det["csamt"] = flt(osup_det["csamt"] + value['csamt'], 2) diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index 7d10892c4a7..fe7e0c807c5 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -704,9 +704,10 @@ def update_totals(gst_tax, doc): doc.rounding_adjustment += flt(doc.rounded_total - doc.grand_total, doc.precision("rounding_adjustment")) - doc.outstanding_amount = doc.base_rounded_total + doc.outstanding_amount = doc.rounded_total or doc.grand_total doc.in_words = money_in_words(doc.grand_total, doc.currency) + doc.set_payment_schedule() def make_regional_gl_entries(gl_entries, doc): country = frappe.get_cached_value('Company', doc.company, 'country') diff --git a/erpnext/regional/report/gstr_1/gstr_1.py b/erpnext/regional/report/gstr_1/gstr_1.py index 43b1ea83eb9..8885b88c2a4 100644 --- a/erpnext/regional/report/gstr_1/gstr_1.py +++ b/erpnext/regional/report/gstr_1/gstr_1.py @@ -118,7 +118,7 @@ class Gstr1Report(object): row.append(invoice_details.get(fieldname)) taxable_value = 0 - if invoice in self.cgst_igst_invoices: + if invoice in self.cgst_sgst_invoices: division_factor = 2 else: division_factor = 1 @@ -129,6 +129,8 @@ class Gstr1Report(object): taxable_value += abs(net_amount) elif not self.item_tax_rate.get(invoice): taxable_value += abs(net_amount) + elif tax_rate: + taxable_value += abs(net_amount) row += [tax_rate or 0, taxable_value] @@ -227,7 +229,7 @@ class Gstr1Report(object): self.items_based_on_tax_rate = {} self.invoice_cess = frappe._dict() - self.cgst_igst_invoices = [] + self.cgst_sgst_invoices = [] unidentified_gst_accounts = [] for parent, account, item_wise_tax_detail, tax_amount in self.tax_details: @@ -251,8 +253,8 @@ class Gstr1Report(object): tax_rate = tax_amounts[0] if cgst_or_sgst: tax_rate *= 2 - if parent not in self.cgst_igst_invoices: - self.cgst_igst_invoices.append(parent) + if parent not in self.cgst_sgst_invoices: + self.cgst_sgst_invoices.append(parent) rate_based_dict = self.items_based_on_tax_rate\ .setdefault(parent, {}).setdefault(tax_rate, []) diff --git a/erpnext/regional/report/gstr_2/gstr_2.py b/erpnext/regional/report/gstr_2/gstr_2.py index f326fe07cac..f899349ccc0 100644 --- a/erpnext/regional/report/gstr_2/gstr_2.py +++ b/erpnext/regional/report/gstr_2/gstr_2.py @@ -44,30 +44,30 @@ class Gstr2Report(Gstr1Report): for inv, items_based_on_rate in self.items_based_on_tax_rate.items(): invoice_details = self.invoices.get(inv) for rate, items in items_based_on_rate.items(): - if inv not in self.igst_invoices: - rate = rate / 2 - row, taxable_value = self.get_row_data_for_invoice(inv, invoice_details, rate, items) - tax_amount = taxable_value * rate / 100 - row += [0, tax_amount, tax_amount] - else: - row, taxable_value = self.get_row_data_for_invoice(inv, invoice_details, rate, items) - tax_amount = taxable_value * rate / 100 - row += [tax_amount, 0, 0] + if rate: + if inv not in self.igst_invoices: + rate = rate / 2 + row, taxable_value = self.get_row_data_for_invoice(inv, invoice_details, rate, items) + tax_amount = taxable_value * rate / 100 + row += [0, tax_amount, tax_amount] + else: + row, taxable_value = self.get_row_data_for_invoice(inv, invoice_details, rate, items) + tax_amount = taxable_value * rate / 100 + row += [tax_amount, 0, 0] + row += [ + self.invoice_cess.get(inv), + invoice_details.get('eligibility_for_itc'), + invoice_details.get('itc_integrated_tax'), + invoice_details.get('itc_central_tax'), + invoice_details.get('itc_state_tax'), + invoice_details.get('itc_cess_amount') + ] + if self.filters.get("type_of_business") == "CDNR": + row.append("Y" if invoice_details.posting_date <= date(2017, 7, 1) else "N") + row.append("C" if invoice_details.return_against else "R") - row += [ - self.invoice_cess.get(inv), - invoice_details.get('eligibility_for_itc'), - invoice_details.get('itc_integrated_tax'), - invoice_details.get('itc_central_tax'), - invoice_details.get('itc_state_tax'), - invoice_details.get('itc_cess_amount') - ] - if self.filters.get("type_of_business") == "CDNR": - row.append("Y" if invoice_details.posting_date <= date(2017, 7, 1) else "N") - row.append("C" if invoice_details.return_against else "R") - - self.data.append(row) + self.data.append(row) def get_igst_invoices(self): self.igst_invoices = [] @@ -86,7 +86,7 @@ class Gstr2Report(Gstr1Report): conditions += opts[1] if self.filters.get("type_of_business") == "B2B": - conditions += "and ifnull(gst_category, '') != 'Overseas' and is_return != 1 " + conditions += "and ifnull(gst_category, '') in ('Registered Regular', 'Deemed Export', 'SEZ') and is_return != 1 " elif self.filters.get("type_of_business") == "CDNR": conditions += """ and is_return = 1 """ From 9be6787b80ca51318e22024d385c8004feba13d4 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Fri, 17 Jul 2020 12:51:49 +0530 Subject: [PATCH 605/608] fix(maintenance-visit): change fieldtype of status to select --- .../doctype/maintenance_visit/maintenance_visit.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.json b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.json index c797b7ea77c..11925681dfd 100644 --- a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.json +++ b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.json @@ -701,7 +701,7 @@ "columns": 0, "default": "Draft", "fieldname": "status", - "fieldtype": "Data", + "fieldtype": "Select", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, @@ -1001,7 +1001,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-08-21 14:44:44.911402", + "modified": "2020-07-15 14:44:44.911402", "modified_by": "Administrator", "module": "Maintenance", "name": "Maintenance Visit", From 848568d023a0af96b3916d9600380f99c1b7fc4d Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 17 Jul 2020 11:24:40 +0530 Subject: [PATCH 606/608] fix: currency symbol not showing as per company currency in stock balance --- .../stock/report/stock_balance/stock_balance.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/erpnext/stock/report/stock_balance/stock_balance.py b/erpnext/stock/report/stock_balance/stock_balance.py index 74a4f6ef142..042087a4a77 100644 --- a/erpnext/stock/report/stock_balance/stock_balance.py +++ b/erpnext/stock/report/stock_balance/stock_balance.py @@ -2,7 +2,7 @@ # License: GNU General Public License v3. See license.txt from __future__ import unicode_literals -import frappe +import frappe, erpnext from frappe import _ from frappe.utils import flt, cint, getdate, now, date_diff from erpnext.stock.utils import add_additional_uom_columns @@ -20,6 +20,11 @@ def execute(filters=None): from_date = filters.get('from_date') to_date = filters.get('to_date') + if filters.get("company"): + company_currency = erpnext.get_company_currency(filters.get("company")) + else: + company_currency = frappe.db.get_single_value("Global Defaults", "default_currency") + include_uom = filters.get("include_uom") columns = get_columns(filters) items = get_items(filters) @@ -52,6 +57,7 @@ def execute(filters=None): item_reorder_qty = item_reorder_detail_map[item + warehouse]["warehouse_reorder_qty"] report_data = { + 'currency': company_currency, 'item_code': item, 'warehouse': warehouse, 'company': company, @@ -89,7 +95,6 @@ def execute(filters=None): def get_columns(filters): """return columns""" - columns = [ {"label": _("Item"), "fieldname": "item_code", "fieldtype": "Link", "options": "Item", "width": 100}, {"label": _("Item Name"), "fieldname": "item_name", "width": 150}, @@ -97,14 +102,14 @@ def get_columns(filters): {"label": _("Warehouse"), "fieldname": "warehouse", "fieldtype": "Link", "options": "Warehouse", "width": 100}, {"label": _("Stock UOM"), "fieldname": "stock_uom", "fieldtype": "Link", "options": "UOM", "width": 90}, {"label": _("Balance Qty"), "fieldname": "bal_qty", "fieldtype": "Float", "width": 100, "convertible": "qty"}, - {"label": _("Balance Value"), "fieldname": "bal_val", "fieldtype": "Currency", "width": 100}, + {"label": _("Balance Value"), "fieldname": "bal_val", "fieldtype": "Currency", "width": 100, "options": "currency"}, {"label": _("Opening Qty"), "fieldname": "opening_qty", "fieldtype": "Float", "width": 100, "convertible": "qty"}, - {"label": _("Opening Value"), "fieldname": "opening_val", "fieldtype": "Float", "width": 110}, + {"label": _("Opening Value"), "fieldname": "opening_val", "fieldtype": "Currency", "width": 110, "options": "currency"}, {"label": _("In Qty"), "fieldname": "in_qty", "fieldtype": "Float", "width": 80, "convertible": "qty"}, {"label": _("In Value"), "fieldname": "in_val", "fieldtype": "Float", "width": 80}, {"label": _("Out Qty"), "fieldname": "out_qty", "fieldtype": "Float", "width": 80, "convertible": "qty"}, {"label": _("Out Value"), "fieldname": "out_val", "fieldtype": "Float", "width": 80}, - {"label": _("Valuation Rate"), "fieldname": "val_rate", "fieldtype": "Currency", "width": 90, "convertible": "rate"}, + {"label": _("Valuation Rate"), "fieldname": "val_rate", "fieldtype": "Currency", "width": 90, "convertible": "rate", "options": "currency"}, {"label": _("Reorder Level"), "fieldname": "reorder_level", "fieldtype": "Float", "width": 80, "convertible": "qty"}, {"label": _("Reorder Qty"), "fieldname": "reorder_qty", "fieldtype": "Float", "width": 80, "convertible": "qty"}, {"label": _("Company"), "fieldname": "company", "fieldtype": "Link", "options": "Company", "width": 100} From 2347e69cb973112153acbc8336e42d89c29f6924 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 16 Jul 2020 22:13:00 +0530 Subject: [PATCH 607/608] fix: for past dated stock reco, batched item showing the current available qty instead of quantity as per posting date --- erpnext/stock/doctype/batch/batch.py | 9 +++++++-- .../stock_reconciliation/stock_reconciliation.js | 14 ++++++++++++++ .../stock_reconciliation/stock_reconciliation.py | 12 ++++++++++-- erpnext/stock/report/stock_ledger/stock_ledger.py | 2 +- 4 files changed, 32 insertions(+), 5 deletions(-) diff --git a/erpnext/stock/doctype/batch/batch.py b/erpnext/stock/doctype/batch/batch.py index a091ac7fae9..c8424f13e12 100644 --- a/erpnext/stock/doctype/batch/batch.py +++ b/erpnext/stock/doctype/batch/batch.py @@ -143,7 +143,7 @@ class Batch(Document): @frappe.whitelist() -def get_batch_qty(batch_no=None, warehouse=None, item_code=None): +def get_batch_qty(batch_no=None, warehouse=None, item_code=None, posting_date=None, posting_time=None): """Returns batch actual qty if warehouse is passed, or returns dict of qty by warehouse if warehouse is None @@ -155,9 +155,14 @@ def get_batch_qty(batch_no=None, warehouse=None, item_code=None): out = 0 if batch_no and warehouse: + cond = "" + if posting_date and posting_time: + cond = " and timestamp(posting_date, posting_time) <= timestamp('{0}', '{1}')".format(posting_date, + posting_time) + out = float(frappe.db.sql("""select sum(actual_qty) from `tabStock Ledger Entry` - where warehouse=%s and batch_no=%s""", + where warehouse=%s and batch_no=%s {0}""".format(cond), (warehouse, batch_no))[0][0] or 0) if batch_no and not warehouse: diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js index dd284e4a966..ecee97ce86a 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js @@ -74,6 +74,20 @@ frappe.ui.form.on("Stock Reconciliation", { , __("Get Items"), __("Update")); }, + posting_date: function(frm) { + frm.trigger("set_valuation_rate_and_qty_for_all_items"); + }, + + posting_time: function(frm) { + frm.trigger("set_valuation_rate_and_qty_for_all_items"); + }, + + set_valuation_rate_and_qty_for_all_items: function(frm) { + frm.doc.items.forEach(row => { + frm.events.set_valuation_rate_and_qty(frm, row.doctype, row.name); + }); + }, + set_valuation_rate_and_qty: function(frm, cdt, cdn) { var d = frappe.model.get_doc(cdt, cdn); diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index 5e469c24d72..43fbc004664 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -184,8 +184,12 @@ class StockReconciliation(StockController): sl_entries = [] has_serial_no = False + has_batch_no = False for row in self.items: item = frappe.get_doc("Item", row.item_code) + if item.has_batch_no: + has_batch_no = True + if item.has_serial_no or item.has_batch_no: has_serial_no = True self.get_sle_for_serialized_items(row, sl_entries) @@ -222,7 +226,11 @@ class StockReconciliation(StockController): if has_serial_no: sl_entries = self.merge_similar_item_serial_nos(sl_entries) - self.make_sl_entries(sl_entries) + allow_negative_stock = False + if has_batch_no: + allow_negative_stock = True + + self.make_sl_entries(sl_entries, allow_negative_stock=allow_negative_stock) if has_serial_no and sl_entries: self.update_valuation_rate_for_serial_no() @@ -493,7 +501,7 @@ def get_stock_balance_for(item_code, warehouse, qty, rate = data if item_dict.get("has_batch_no"): - qty = get_batch_qty(batch_no, warehouse) or 0 + qty = get_batch_qty(batch_no, warehouse, posting_date=posting_date, posting_time=posting_time) or 0 return { 'qty': qty, diff --git a/erpnext/stock/report/stock_ledger/stock_ledger.py b/erpnext/stock/report/stock_ledger/stock_ledger.py index a5f92e2c76c..fe8ad71b731 100644 --- a/erpnext/stock/report/stock_ledger/stock_ledger.py +++ b/erpnext/stock/report/stock_ledger/stock_ledger.py @@ -33,7 +33,7 @@ def execute(filters=None): actual_qty += flt(sle.actual_qty, precision) stock_value += sle.stock_value_difference - if sle.voucher_type == 'Stock Reconciliation': + if sle.voucher_type == 'Stock Reconciliation' and not sle.actual_qty: actual_qty = sle.qty_after_transaction stock_value = sle.stock_value From 07f8e749be0ad5562f9944991c45dea30ebf4582 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 18 Jul 2020 19:47:33 +0530 Subject: [PATCH 608/608] fix: Period list api changes for custom cash flow report --- erpnext/accounts/report/cash_flow/custom_cash_flow.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/report/cash_flow/custom_cash_flow.py b/erpnext/accounts/report/cash_flow/custom_cash_flow.py index 8566f5375ad..fe2bc725e00 100644 --- a/erpnext/accounts/report/cash_flow/custom_cash_flow.py +++ b/erpnext/accounts/report/cash_flow/custom_cash_flow.py @@ -334,10 +334,9 @@ def compute_data(filters, company_currency, profit_data, period_list, light_mapp def execute(filters=None): if not filters.periodicity: filters.periodicity = "Monthly" - period_list = get_period_list( - filters.from_fiscal_year, filters.to_fiscal_year, filters.periodicity, - filters.accumulated_values, filters.company - ) + period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year, + filters.period_start_date, filters.period_end_date, filters.filter_based_on, + filters.periodicity, company=filters.company) mappers = get_mappers_from_db() @@ -396,7 +395,7 @@ def _get_account_type_based_data(filters, account_names, period_list, accumulate gl_sum = frappe.db.sql_list(""" select sum(credit) - sum(debit) from `tabGL Entry` - where company=%s and posting_date >= %s and posting_date <= %s + where company=%s and posting_date >= %s and posting_date <= %s and voucher_type != 'Period Closing Voucher' and account in ( SELECT name FROM tabAccount WHERE name IN (%s) OR parent_account IN (%s)) @@ -405,7 +404,7 @@ def _get_account_type_based_data(filters, account_names, period_list, accumulate gl_sum = frappe.db.sql_list(""" select sum(credit) - sum(debit) from `tabGL Entry` - where company=%s and posting_date >= %s and posting_date <= %s + where company=%s and posting_date >= %s and posting_date <= %s and voucher_type != 'Period Closing Voucher' and account in ( SELECT name FROM tabAccount WHERE name IN (%s) OR parent_account IN (%s))