diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js index 3c12f85f938..025b98799e1 100644 --- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js +++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js @@ -41,6 +41,8 @@ frappe.ui.form.on('Accounting Dimension', { }); }); } + + frm.toggle_enable('document_type', frm.doc.__islocal); }, document_type: function(frm) { diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py index 894ec5bdec5..e0d18f297a9 100644 --- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py +++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py @@ -29,6 +29,16 @@ class AccountingDimension(Document): if exists and self.is_new(): frappe.throw("Document Type already used as a dimension") + if not self.is_new(): + self.validate_document_type_change() + + def validate_document_type_change(self): + doctype_before_save = frappe.db.get_value("Accounting Dimension", self.name, "document_type") + if doctype_before_save != self.document_type: + message = _("Cannot change Reference Document Type.") + message += _("Please create a new Accounting Dimension if required.") + frappe.throw(message) + def after_insert(self): if frappe.flags.in_test: make_dimension_in_accounting_doctypes(doc=self) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index 2f8b634664c..b324c8bcf19 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -88,18 +88,18 @@ class PaymentReconciliation(Document): voucher_type = ('Sales Invoice' if self.party_type == 'Customer' else "Purchase Invoice") - return frappe.db.sql(""" SELECT `tab{doc}`.name as reference_name, %(voucher_type)s as reference_type, - (sum(`tabGL Entry`.{dr_or_cr}) - sum(`tabGL Entry`.{reconciled_dr_or_cr})) as amount, + return frappe.db.sql(""" SELECT doc.name as reference_name, %(voucher_type)s as reference_type, + (sum(gl.{dr_or_cr}) - sum(gl.{reconciled_dr_or_cr})) as amount, account_currency as currency - FROM `tab{doc}`, `tabGL Entry` + FROM `tab{doc}` doc, `tabGL Entry` gl WHERE - (`tab{doc}`.name = `tabGL Entry`.against_voucher or `tab{doc}`.name = `tabGL Entry`.voucher_no) - and `tab{doc}`.{party_type_field} = %(party)s - and `tab{doc}`.is_return = 1 and `tab{doc}`.return_against IS NULL - and `tabGL Entry`.against_voucher_type = %(voucher_type)s - and `tab{doc}`.docstatus = 1 and `tabGL Entry`.party = %(party)s - and `tabGL Entry`.party_type = %(party_type)s and `tabGL Entry`.account = %(account)s - GROUP BY `tab{doc}`.name + (doc.name = gl.against_voucher or doc.name = gl.voucher_no) + and doc.{party_type_field} = %(party)s + and doc.is_return = 1 and ifnull(doc.return_against, "") = "" + and gl.against_voucher_type = %(voucher_type)s + and doc.docstatus = 1 and gl.party = %(party)s + and gl.party_type = %(party_type)s and gl.account = %(account)s + GROUP BY doc.name Having amount > 0 """.format( @@ -112,7 +112,7 @@ class PaymentReconciliation(Document): 'party_type': self.party_type, 'voucher_type': voucher_type, 'account': self.receivable_payable_account - }, as_dict=1) + }, as_dict=1, debug=1) def add_payment_entries(self, entries): self.set('payments', []) diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py index 4a79b6a340e..e15ae5cba9d 100644 --- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py +++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py @@ -206,7 +206,7 @@ def get_data(companies, root_type, balance_must_be, fiscal_year, filters=None, i set_gl_entries_by_account(fiscal_year.year_start_date, fiscal_year.year_end_date, root.lft, root.rgt, filters, - gl_entries_by_account, accounts_by_name, ignore_closing_entries=False) + gl_entries_by_account, accounts_by_name, accounts, ignore_closing_entries=False) calculate_values(accounts_by_name, gl_entries_by_account, companies, fiscal_year, filters) accumulate_values_into_parents(accounts, accounts_by_name, companies) @@ -325,7 +325,7 @@ def prepare_data(accounts, fiscal_year, balance_must_be, companies, company_curr return data def set_gl_entries_by_account(from_date, to_date, root_lft, root_rgt, filters, gl_entries_by_account, - accounts_by_name, ignore_closing_entries=False): + accounts_by_name, accounts, ignore_closing_entries=False): """Returns a dict like { "account": [gl entries], ... }""" company_lft, company_rgt = frappe.get_cached_value('Company', @@ -368,15 +368,31 @@ def set_gl_entries_by_account(from_date, to_date, root_lft, root_rgt, filters, g for entry in gl_entries: key = entry.account_number or entry.account_name - validate_entries(key, entry, accounts_by_name) + validate_entries(key, entry, accounts_by_name, accounts) gl_entries_by_account.setdefault(key, []).append(entry) return gl_entries_by_account -def validate_entries(key, entry, accounts_by_name): +def get_account_details(account): + return frappe.get_cached_value('Account', account, ['name', 'report_type', 'root_type', 'company', + 'is_group', 'account_name', 'account_number', 'parent_account', 'lft', 'rgt'], as_dict=1) + +def validate_entries(key, entry, accounts_by_name, accounts): if key not in accounts_by_name: - field = "Account number" if entry.account_number else "Account name" - frappe.throw(_("{0} {1} is not present in the parent company").format(field, key)) + args = get_account_details(entry.account) + + if args.parent_account: + parent_args = get_account_details(args.parent_account) + + args.update({ + 'lft': parent_args.lft + 1, + 'rgt': parent_args.rgt - 1, + 'root_type': parent_args.root_type, + 'report_type': parent_args.report_type + }) + + accounts_by_name.setdefault(key, args) + accounts.append(args) def get_additional_conditions(from_date, ignore_closing_entries, filters): additional_conditions = [] 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 b435a9a53cc..402537802e9 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 @@ -54,8 +54,8 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum row = { 'item_code': d.item_code, - 'item_name': item_record.item_name, - 'item_group': item_record.item_group, + 'item_name': item_record.item_name if item_record else d.item_name, + 'item_group': item_record.item_group if item_record else d.item_group, 'description': d.description, 'invoice': d.parent, 'posting_date': d.posting_date, @@ -324,6 +324,7 @@ def get_items(filters, additional_query_columns): `tabPurchase Invoice`.posting_date, `tabPurchase Invoice`.credit_to, `tabPurchase Invoice`.company, `tabPurchase Invoice`.supplier, `tabPurchase Invoice`.remarks, `tabPurchase Invoice`.base_net_total, `tabPurchase Invoice Item`.`item_code`, `tabPurchase Invoice Item`.description, + `tabPurchase Invoice Item`.`item_name`, `tabPurchase Invoice Item`.`item_group`, `tabPurchase Invoice Item`.`project`, `tabPurchase Invoice Item`.`purchase_order`, `tabPurchase Invoice Item`.`purchase_receipt`, `tabPurchase Invoice Item`.`po_detail`, `tabPurchase Invoice Item`.`expense_account`, `tabPurchase Invoice Item`.`stock_qty`, 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 d22111c9f5a..8ebc4d18692 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 @@ -53,8 +53,8 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum row = { 'item_code': d.item_code, - 'item_name': item_record.item_name, - 'item_group': item_record.item_group, + 'item_name': item_record.item_name if item_record else d.item_name, + 'item_group': item_record.item_group if item_record else d.item_group, 'description': d.description, 'invoice': d.parent, 'posting_date': d.posting_date, @@ -390,6 +390,7 @@ def get_items(filters, additional_query_columns): `tabSales Invoice`.project, `tabSales Invoice`.customer, `tabSales Invoice`.remarks, `tabSales Invoice`.territory, `tabSales Invoice`.company, `tabSales Invoice`.base_net_total, `tabSales Invoice Item`.item_code, `tabSales Invoice Item`.description, + `tabSales Invoice Item`.`item_name`, `tabSales Invoice Item`.`item_group`, `tabSales Invoice Item`.sales_order, `tabSales Invoice Item`.delivery_note, `tabSales Invoice Item`.income_account, `tabSales Invoice Item`.cost_center, `tabSales Invoice Item`.stock_qty, `tabSales Invoice Item`.stock_uom, diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 69ed1ee8439..99a9522b6a7 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1397,6 +1397,7 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil parent.flags.ignore_validate_update_after_submit = True parent.set_qty_as_per_stock_uom() parent.calculate_taxes_and_totals() + parent.set_total_in_words() if parent_doctype == "Sales Order": make_packing_list(parent) parent.set_gross_profit() diff --git a/erpnext/regional/india/e_invoice/utils.py b/erpnext/regional/india/e_invoice/utils.py index 461deb9a772..85eeb40c786 100644 --- a/erpnext/regional/india/e_invoice/utils.py +++ b/erpnext/regional/india/e_invoice/utils.py @@ -20,11 +20,13 @@ from frappe.utils.data import cstr, cint, formatdate as format_date, flt, time_d def validate_einvoice_fields(doc): einvoicing_enabled = cint(frappe.db.get_value('E Invoice Settings', 'E Invoice Settings', 'enable')) - invalid_doctype = doc.doctype not in ['Sales Invoice'] + invalid_doctype = doc.doctype != 'Sales Invoice' invalid_supply_type = doc.get('gst_category') not in ['Registered Regular', 'SEZ', 'Overseas', 'Deemed Export'] company_transaction = doc.get('billing_address_gstin') == doc.get('company_gstin') + no_taxes_applied = len(doc.get('taxes', [])) == 0 - if not einvoicing_enabled or invalid_doctype or invalid_supply_type or company_transaction: return + if not einvoicing_enabled or invalid_doctype or invalid_supply_type or company_transaction or no_taxes_applied: + return if doc.docstatus == 0 and doc._action == 'save': if doc.irn: @@ -303,7 +305,7 @@ def validate_mandatory_fields(invoice): _('GSTIN is mandatory to fetch company GSTIN details. Please enter GSTIN in selected company address.'), title=_('Missing Fields') ) - if not frappe.db.get_value('Address', invoice.customer_address, 'gstin'): + if invoice.gst_category != 'Overseas' and not frappe.db.get_value('Address', invoice.customer_address, 'gstin'): frappe.throw( _('GSTIN is mandatory to fetch customer GSTIN details. Please enter GSTIN in selected customer address.'), title=_('Missing Fields') @@ -330,7 +332,10 @@ def make_einvoice(invoice): shipping_details = payment_details = prev_doc_details = eway_bill_details = frappe._dict({}) if invoice.shipping_address_name and invoice.customer_address != invoice.shipping_address_name: - shipping_details = get_party_details(invoice.shipping_address_name) + if invoice.gst_category == 'Overseas': + shipping_details = get_overseas_address_details(invoice.shipping_address_name) + else: + shipping_details = get_party_details(invoice.shipping_address_name) if invoice.is_pos and invoice.base_paid_amount: payment_details = get_payment_details(invoice) diff --git a/erpnext/regional/report/irs_1099/irs_1099.py b/erpnext/regional/report/irs_1099/irs_1099.py index 13e848c542e..c8b953139f1 100644 --- a/erpnext/regional/report/irs_1099/irs_1099.py +++ b/erpnext/regional/report/irs_1099/irs_1099.py @@ -32,6 +32,10 @@ def execute(filters=None): data = [] columns = get_columns() + conditions = "" + if filters.supplier_group: + conditions += "AND s.supplier_group = %s" %frappe.db.escape(filters.get("supplier_group")) + data = frappe.db.sql(""" SELECT s.supplier_group as "supplier_group", @@ -46,15 +50,17 @@ def execute(filters=None): AND s.irs_1099 = 1 AND gl.fiscal_year = %(fiscal_year)s AND gl.party_type = "Supplier" + AND gl.company = %(company)s + {conditions} + GROUP BY gl.party + ORDER BY - gl.party DESC - """, { - "fiscal_year": filters.fiscal_year, - "supplier_group": filters.supplier_group, - "company": filters.company - }, as_dict=True) + gl.party DESC""".format(conditions=conditions), { + "fiscal_year": filters.fiscal_year, + "company": filters.company + }, as_dict=True) return columns, data @@ -79,13 +85,13 @@ def get_columns(): "fieldname": "tax_id", "label": _("Tax ID"), "fieldtype": "Data", - "width": 120 + "width": 200 }, { "fieldname": "payments", "label": _("Total Payments"), "fieldtype": "Currency", - "width": 120 + "width": 200 } ] diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index 7aefbd0a127..000d8eb1834 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -324,6 +324,9 @@ class TestSalesOrder(unittest.TestCase): create_dn_against_so(so.name, 4) make_sales_invoice(so.name) + prev_total = so.get("base_total") + prev_total_in_words = so.get("base_in_words") + first_item_of_so = so.get("items")[0] trans_item = json.dumps([ {'item_code' : first_item_of_so.item_code, 'rate' : first_item_of_so.rate, \ @@ -339,6 +342,12 @@ class TestSalesOrder(unittest.TestCase): self.assertEqual(so.get("items")[-1].amount, 1400) self.assertEqual(so.status, 'To Deliver and Bill') + updated_total = so.get("base_total") + updated_total_in_words = so.get("base_in_words") + + self.assertEqual(updated_total, prev_total+1400) + self.assertNotEqual(updated_total_in_words, prev_total_in_words) + def test_update_child_removing_item(self): so = make_sales_order(**{ "item_list": [{