mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-25 16:04:46 +00:00
Merge branch 'version-12-hotfix' into validation-for-disabled-warehouse-v12
This commit is contained in:
@@ -41,6 +41,8 @@ frappe.ui.form.on('Accounting Dimension', {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
frm.toggle_enable('document_type', frm.doc.__islocal);
|
||||||
},
|
},
|
||||||
|
|
||||||
document_type: function(frm) {
|
document_type: function(frm) {
|
||||||
|
|||||||
@@ -29,6 +29,16 @@ class AccountingDimension(Document):
|
|||||||
if exists and self.is_new():
|
if exists and self.is_new():
|
||||||
frappe.throw("Document Type already used as a dimension")
|
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):
|
def after_insert(self):
|
||||||
if frappe.flags.in_test:
|
if frappe.flags.in_test:
|
||||||
make_dimension_in_accounting_doctypes(doc=self)
|
make_dimension_in_accounting_doctypes(doc=self)
|
||||||
|
|||||||
@@ -88,18 +88,18 @@ class PaymentReconciliation(Document):
|
|||||||
voucher_type = ('Sales Invoice'
|
voucher_type = ('Sales Invoice'
|
||||||
if self.party_type == 'Customer' else "Purchase 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,
|
return frappe.db.sql(""" SELECT 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,
|
(sum(gl.{dr_or_cr}) - sum(gl.{reconciled_dr_or_cr})) as amount,
|
||||||
account_currency as currency
|
account_currency as currency
|
||||||
FROM `tab{doc}`, `tabGL Entry`
|
FROM `tab{doc}` doc, `tabGL Entry` gl
|
||||||
WHERE
|
WHERE
|
||||||
(`tab{doc}`.name = `tabGL Entry`.against_voucher or `tab{doc}`.name = `tabGL Entry`.voucher_no)
|
(doc.name = gl.against_voucher or doc.name = gl.voucher_no)
|
||||||
and `tab{doc}`.{party_type_field} = %(party)s
|
and doc.{party_type_field} = %(party)s
|
||||||
and `tab{doc}`.is_return = 1 and `tab{doc}`.return_against IS NULL
|
and doc.is_return = 1 and ifnull(doc.return_against, "") = ""
|
||||||
and `tabGL Entry`.against_voucher_type = %(voucher_type)s
|
and gl.against_voucher_type = %(voucher_type)s
|
||||||
and `tab{doc}`.docstatus = 1 and `tabGL Entry`.party = %(party)s
|
and doc.docstatus = 1 and gl.party = %(party)s
|
||||||
and `tabGL Entry`.party_type = %(party_type)s and `tabGL Entry`.account = %(account)s
|
and gl.party_type = %(party_type)s and gl.account = %(account)s
|
||||||
GROUP BY `tab{doc}`.name
|
GROUP BY doc.name
|
||||||
Having
|
Having
|
||||||
amount > 0
|
amount > 0
|
||||||
""".format(
|
""".format(
|
||||||
@@ -112,7 +112,7 @@ class PaymentReconciliation(Document):
|
|||||||
'party_type': self.party_type,
|
'party_type': self.party_type,
|
||||||
'voucher_type': voucher_type,
|
'voucher_type': voucher_type,
|
||||||
'account': self.receivable_payable_account
|
'account': self.receivable_payable_account
|
||||||
}, as_dict=1)
|
}, as_dict=1, debug=1)
|
||||||
|
|
||||||
def add_payment_entries(self, entries):
|
def add_payment_entries(self, entries):
|
||||||
self.set('payments', [])
|
self.set('payments', [])
|
||||||
|
|||||||
@@ -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,
|
set_gl_entries_by_account(fiscal_year.year_start_date,
|
||||||
fiscal_year.year_end_date, root.lft, root.rgt, filters,
|
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)
|
calculate_values(accounts_by_name, gl_entries_by_account, companies, fiscal_year, filters)
|
||||||
accumulate_values_into_parents(accounts, accounts_by_name, companies)
|
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
|
return data
|
||||||
|
|
||||||
def set_gl_entries_by_account(from_date, to_date, root_lft, root_rgt, filters, gl_entries_by_account,
|
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], ... }"""
|
"""Returns a dict like { "account": [gl entries], ... }"""
|
||||||
|
|
||||||
company_lft, company_rgt = frappe.get_cached_value('Company',
|
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:
|
for entry in gl_entries:
|
||||||
key = entry.account_number or entry.account_name
|
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)
|
gl_entries_by_account.setdefault(key, []).append(entry)
|
||||||
|
|
||||||
return gl_entries_by_account
|
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:
|
if key not in accounts_by_name:
|
||||||
field = "Account number" if entry.account_number else "Account name"
|
args = get_account_details(entry.account)
|
||||||
frappe.throw(_("{0} {1} is not present in the parent company").format(field, key))
|
|
||||||
|
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):
|
def get_additional_conditions(from_date, ignore_closing_entries, filters):
|
||||||
additional_conditions = []
|
additional_conditions = []
|
||||||
|
|||||||
@@ -54,8 +54,8 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
|
|||||||
|
|
||||||
row = {
|
row = {
|
||||||
'item_code': d.item_code,
|
'item_code': d.item_code,
|
||||||
'item_name': item_record.item_name,
|
'item_name': item_record.item_name if item_record else d.item_name,
|
||||||
'item_group': item_record.item_group,
|
'item_group': item_record.item_group if item_record else d.item_group,
|
||||||
'description': d.description,
|
'description': d.description,
|
||||||
'invoice': d.parent,
|
'invoice': d.parent,
|
||||||
'posting_date': d.posting_date,
|
'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`.posting_date, `tabPurchase Invoice`.credit_to, `tabPurchase Invoice`.company,
|
||||||
`tabPurchase Invoice`.supplier, `tabPurchase Invoice`.remarks, `tabPurchase Invoice`.base_net_total,
|
`tabPurchase Invoice`.supplier, `tabPurchase Invoice`.remarks, `tabPurchase Invoice`.base_net_total,
|
||||||
`tabPurchase Invoice Item`.`item_code`, `tabPurchase Invoice Item`.description,
|
`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`.`project`, `tabPurchase Invoice Item`.`purchase_order`,
|
||||||
`tabPurchase Invoice Item`.`purchase_receipt`, `tabPurchase Invoice Item`.`po_detail`,
|
`tabPurchase Invoice Item`.`purchase_receipt`, `tabPurchase Invoice Item`.`po_detail`,
|
||||||
`tabPurchase Invoice Item`.`expense_account`, `tabPurchase Invoice Item`.`stock_qty`,
|
`tabPurchase Invoice Item`.`expense_account`, `tabPurchase Invoice Item`.`stock_qty`,
|
||||||
|
|||||||
@@ -53,8 +53,8 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
|
|||||||
|
|
||||||
row = {
|
row = {
|
||||||
'item_code': d.item_code,
|
'item_code': d.item_code,
|
||||||
'item_name': item_record.item_name,
|
'item_name': item_record.item_name if item_record else d.item_name,
|
||||||
'item_group': item_record.item_group,
|
'item_group': item_record.item_group if item_record else d.item_group,
|
||||||
'description': d.description,
|
'description': d.description,
|
||||||
'invoice': d.parent,
|
'invoice': d.parent,
|
||||||
'posting_date': d.posting_date,
|
'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`.project, `tabSales Invoice`.customer, `tabSales Invoice`.remarks,
|
||||||
`tabSales Invoice`.territory, `tabSales Invoice`.company, `tabSales Invoice`.base_net_total,
|
`tabSales Invoice`.territory, `tabSales Invoice`.company, `tabSales Invoice`.base_net_total,
|
||||||
`tabSales Invoice Item`.item_code, `tabSales Invoice Item`.description,
|
`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`.sales_order, `tabSales Invoice Item`.delivery_note,
|
||||||
`tabSales Invoice Item`.income_account, `tabSales Invoice Item`.cost_center,
|
`tabSales Invoice Item`.income_account, `tabSales Invoice Item`.cost_center,
|
||||||
`tabSales Invoice Item`.stock_qty, `tabSales Invoice Item`.stock_uom,
|
`tabSales Invoice Item`.stock_qty, `tabSales Invoice Item`.stock_uom,
|
||||||
|
|||||||
@@ -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.flags.ignore_validate_update_after_submit = True
|
||||||
parent.set_qty_as_per_stock_uom()
|
parent.set_qty_as_per_stock_uom()
|
||||||
parent.calculate_taxes_and_totals()
|
parent.calculate_taxes_and_totals()
|
||||||
|
parent.set_total_in_words()
|
||||||
if parent_doctype == "Sales Order":
|
if parent_doctype == "Sales Order":
|
||||||
make_packing_list(parent)
|
make_packing_list(parent)
|
||||||
parent.set_gross_profit()
|
parent.set_gross_profit()
|
||||||
|
|||||||
@@ -20,11 +20,13 @@ from frappe.utils.data import cstr, cint, formatdate as format_date, flt, time_d
|
|||||||
|
|
||||||
def validate_einvoice_fields(doc):
|
def validate_einvoice_fields(doc):
|
||||||
einvoicing_enabled = cint(frappe.db.get_value('E Invoice Settings', 'E Invoice Settings', 'enable'))
|
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']
|
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')
|
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.docstatus == 0 and doc._action == 'save':
|
||||||
if doc.irn:
|
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.'),
|
_('GSTIN is mandatory to fetch company GSTIN details. Please enter GSTIN in selected company address.'),
|
||||||
title=_('Missing Fields')
|
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(
|
frappe.throw(
|
||||||
_('GSTIN is mandatory to fetch customer GSTIN details. Please enter GSTIN in selected customer address.'),
|
_('GSTIN is mandatory to fetch customer GSTIN details. Please enter GSTIN in selected customer address.'),
|
||||||
title=_('Missing Fields')
|
title=_('Missing Fields')
|
||||||
@@ -330,7 +332,10 @@ def make_einvoice(invoice):
|
|||||||
|
|
||||||
shipping_details = payment_details = prev_doc_details = eway_bill_details = frappe._dict({})
|
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:
|
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:
|
if invoice.is_pos and invoice.base_paid_amount:
|
||||||
payment_details = get_payment_details(invoice)
|
payment_details = get_payment_details(invoice)
|
||||||
|
|||||||
@@ -32,6 +32,10 @@ def execute(filters=None):
|
|||||||
|
|
||||||
data = []
|
data = []
|
||||||
columns = get_columns()
|
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("""
|
data = frappe.db.sql("""
|
||||||
SELECT
|
SELECT
|
||||||
s.supplier_group as "supplier_group",
|
s.supplier_group as "supplier_group",
|
||||||
@@ -46,15 +50,17 @@ def execute(filters=None):
|
|||||||
AND s.irs_1099 = 1
|
AND s.irs_1099 = 1
|
||||||
AND gl.fiscal_year = %(fiscal_year)s
|
AND gl.fiscal_year = %(fiscal_year)s
|
||||||
AND gl.party_type = "Supplier"
|
AND gl.party_type = "Supplier"
|
||||||
|
AND gl.company = %(company)s
|
||||||
|
{conditions}
|
||||||
|
|
||||||
GROUP BY
|
GROUP BY
|
||||||
gl.party
|
gl.party
|
||||||
|
|
||||||
ORDER BY
|
ORDER BY
|
||||||
gl.party DESC
|
gl.party DESC""".format(conditions=conditions), {
|
||||||
""", {
|
"fiscal_year": filters.fiscal_year,
|
||||||
"fiscal_year": filters.fiscal_year,
|
"company": filters.company
|
||||||
"supplier_group": filters.supplier_group,
|
}, as_dict=True)
|
||||||
"company": filters.company
|
|
||||||
}, as_dict=True)
|
|
||||||
|
|
||||||
return columns, data
|
return columns, data
|
||||||
|
|
||||||
@@ -79,13 +85,13 @@ def get_columns():
|
|||||||
"fieldname": "tax_id",
|
"fieldname": "tax_id",
|
||||||
"label": _("Tax ID"),
|
"label": _("Tax ID"),
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"width": 120
|
"width": 200
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "payments",
|
"fieldname": "payments",
|
||||||
"label": _("Total Payments"),
|
"label": _("Total Payments"),
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"width": 120
|
"width": 200
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -324,6 +324,9 @@ class TestSalesOrder(unittest.TestCase):
|
|||||||
create_dn_against_so(so.name, 4)
|
create_dn_against_so(so.name, 4)
|
||||||
make_sales_invoice(so.name)
|
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]
|
first_item_of_so = so.get("items")[0]
|
||||||
trans_item = json.dumps([
|
trans_item = json.dumps([
|
||||||
{'item_code' : first_item_of_so.item_code, 'rate' : first_item_of_so.rate, \
|
{'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.get("items")[-1].amount, 1400)
|
||||||
self.assertEqual(so.status, 'To Deliver and Bill')
|
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):
|
def test_update_child_removing_item(self):
|
||||||
so = make_sales_order(**{
|
so = make_sales_order(**{
|
||||||
"item_list": [{
|
"item_list": [{
|
||||||
|
|||||||
Reference in New Issue
Block a user