Merge branch 'develop' of https://github.com/frappe/erpnext into enconnex_erpnext
This commit is contained in:
@@ -7,6 +7,7 @@ import json
|
||||
from frappe import _, throw
|
||||
from frappe.utils import (today, flt, cint, fmt_money, formatdate,
|
||||
getdate, add_days, add_months, get_last_day, nowdate, get_link_to_form)
|
||||
from frappe.model.workflow import get_workflow_name, is_transition_condition_satisfied, WorkflowPermissionError
|
||||
from erpnext.stock.get_item_details import get_conversion_factor, get_item_details
|
||||
from erpnext.setup.utils import get_exchange_rate
|
||||
from erpnext.accounts.utils import get_fiscal_years, validate_fiscal_year, get_account_currency
|
||||
@@ -19,7 +20,7 @@ from erpnext.accounts.doctype.pricing_rule.utils import (apply_pricing_rule_on_t
|
||||
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.get_item_details import get_item_warehouse, _get_item_tax_template, get_item_tax_map
|
||||
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")
|
||||
@@ -331,7 +332,7 @@ class AccountsController(TransactionBase):
|
||||
apply_pricing_rule_for_free_items(self, pricing_rule_args.get('free_item_data'))
|
||||
|
||||
elif pricing_rule_args.get("validate_applied_rule"):
|
||||
for pricing_rule in get_applied_pricing_rules(item):
|
||||
for pricing_rule in get_applied_pricing_rules(item.get('pricing_rules')):
|
||||
pricing_rule_doc = frappe.get_cached_doc("Pricing Rule", pricing_rule)
|
||||
for field in ['discount_percentage', 'discount_amount', 'rate']:
|
||||
if item.get(field) < pricing_rule_doc.get(field):
|
||||
@@ -485,7 +486,11 @@ class AccountsController(TransactionBase):
|
||||
if d.against_order:
|
||||
allocated_amount = flt(d.amount)
|
||||
else:
|
||||
amount = self.rounded_total or self.grand_total
|
||||
if self.get('party_account_currency') == self.company_currency:
|
||||
amount = self.get('base_rounded_total') or self.base_grand_total
|
||||
else:
|
||||
amount = self.get('rounded_total') or self.grand_total
|
||||
|
||||
allocated_amount = min(amount - advance_allocated, d.amount)
|
||||
advance_allocated += flt(allocated_amount)
|
||||
|
||||
@@ -808,10 +813,22 @@ class AccountsController(TransactionBase):
|
||||
self.payment_terms_template = ''
|
||||
return
|
||||
|
||||
party_account_currency = self.get('party_account_currency')
|
||||
if not party_account_currency:
|
||||
party_type, party = self.get_party()
|
||||
|
||||
if party_type and party:
|
||||
party_account_currency = get_party_account_currency(party_type, party, self.company)
|
||||
|
||||
posting_date = self.get("bill_date") or self.get("posting_date") or self.get("transaction_date")
|
||||
date = self.get("due_date")
|
||||
due_date = date or posting_date
|
||||
grand_total = self.get("rounded_total") or self.grand_total
|
||||
|
||||
if party_account_currency == self.company_currency:
|
||||
grand_total = self.get("base_rounded_total") or self.base_grand_total
|
||||
else:
|
||||
grand_total = self.get("rounded_total") or self.grand_total
|
||||
|
||||
if self.doctype in ("Sales Invoice", "Purchase Invoice"):
|
||||
grand_total = grand_total - flt(self.write_off_amount)
|
||||
|
||||
@@ -856,13 +873,25 @@ class AccountsController(TransactionBase):
|
||||
def validate_payment_schedule_amount(self):
|
||||
if self.doctype == 'Sales Invoice' and self.is_pos: return
|
||||
|
||||
party_account_currency = self.get('party_account_currency')
|
||||
if not party_account_currency:
|
||||
party_type, party = self.get_party()
|
||||
|
||||
if party_type and party:
|
||||
party_account_currency = get_party_account_currency(party_type, party, self.company)
|
||||
|
||||
if self.get("payment_schedule"):
|
||||
total = 0
|
||||
for d in self.get("payment_schedule"):
|
||||
total += flt(d.payment_amount)
|
||||
total = flt(total, self.precision("grand_total"))
|
||||
|
||||
grand_total = flt(self.get("rounded_total") or self.grand_total, self.precision('grand_total'))
|
||||
if party_account_currency == self.company_currency:
|
||||
total = flt(total, self.precision("base_grand_total"))
|
||||
grand_total = flt(self.get("base_rounded_total") or self.base_grand_total, self.precision('base_grand_total'))
|
||||
else:
|
||||
total = flt(total, self.precision("grand_total"))
|
||||
grand_total = flt(self.get("rounded_total") or self.grand_total, self.precision('grand_total'))
|
||||
|
||||
if self.get("total_advance"):
|
||||
grand_total -= self.get("total_advance")
|
||||
|
||||
@@ -963,7 +992,7 @@ def validate_inclusive_tax(tax, doc):
|
||||
# all rows about the reffered tax should be inclusive
|
||||
_on_previous_row_error("1 - %d" % (tax.row_id,))
|
||||
elif tax.get("category") == "Valuation":
|
||||
frappe.throw(_("Valuation type charges can not marked as Inclusive"))
|
||||
frappe.throw(_("Valuation type charges can not be marked as Inclusive"))
|
||||
|
||||
|
||||
def set_balance_in_account_currency(gl_dict, account_currency=None, conversion_rate=None, company_currency=None):
|
||||
@@ -1135,6 +1164,18 @@ def get_supplier_block_status(party_name):
|
||||
}
|
||||
return info
|
||||
|
||||
def set_child_tax_template_and_map(item, child_item, parent_doc):
|
||||
args = {
|
||||
'item_code': item.item_code,
|
||||
'posting_date': parent_doc.transaction_date,
|
||||
'tax_category': parent_doc.get('tax_category'),
|
||||
'company': parent_doc.get('company')
|
||||
}
|
||||
|
||||
child_item.item_tax_template = _get_item_tax_template(args, item.taxes)
|
||||
if child_item.get("item_tax_template"):
|
||||
child_item.item_tax_rate = get_item_tax_map(parent_doc.get('company'), child_item.item_tax_template, as_json=True)
|
||||
|
||||
def set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, trans_item):
|
||||
"""
|
||||
Returns a Sales Order Item child item containing the default values
|
||||
@@ -1146,8 +1187,10 @@ 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(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.uom = trans_item.get("uom") or item.stock_uom
|
||||
conversion_factor = flt(get_conversion_factor(item.item_code, child_item.uom).get("conversion_factor"))
|
||||
child_item.conversion_factor = flt(trans_item.get('conversion_factor')) or conversion_factor
|
||||
set_child_tax_template_and_map(item, child_item, p_doc)
|
||||
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.")
|
||||
@@ -1166,13 +1209,15 @@ 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(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.uom = trans_item.get("uom") or item.stock_uom
|
||||
conversion_factor = flt(get_conversion_factor(item.item_code, child_item.uom).get("conversion_factor"))
|
||||
child_item.conversion_factor = flt(trans_item.get('conversion_factor')) or conversion_factor
|
||||
child_item.base_rate = 1 # Initiallize value will update in parent validation
|
||||
child_item.base_amount = 1 # Initiallize value will update in parent validation
|
||||
set_child_tax_template_and_map(item, child_item, p_doc)
|
||||
return child_item
|
||||
|
||||
def check_and_delete_children(parent, data):
|
||||
def validate_and_delete_children(parent, data):
|
||||
deleted_children = []
|
||||
updated_item_names = [d.get("docname") for d in data]
|
||||
for item in parent.items:
|
||||
@@ -1199,18 +1244,40 @@ 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'):
|
||||
def check_doc_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"))
|
||||
except frappe.PermissionError:
|
||||
actions = { 'create': 'add', 'write': 'update'}
|
||||
|
||||
frappe.throw(_("You do not have permissions to {} items in a {}.")
|
||||
.format(actions[perm_type], parent_doctype), title=_("Insufficient Permissions"))
|
||||
|
||||
def validate_workflow_conditions(doc):
|
||||
workflow = get_workflow_name(doc.doctype)
|
||||
if not workflow:
|
||||
return
|
||||
|
||||
workflow_doc = frappe.get_doc("Workflow", workflow)
|
||||
current_state = doc.get(workflow_doc.workflow_state_field)
|
||||
roles = frappe.get_roles()
|
||||
|
||||
transitions = []
|
||||
for transition in workflow_doc.transitions:
|
||||
if transition.next_state == current_state and transition.allowed in roles:
|
||||
if not is_transition_condition_satisfied(transition, doc):
|
||||
continue
|
||||
transitions.append(transition.as_dict())
|
||||
|
||||
if not transitions:
|
||||
frappe.throw(
|
||||
_("You are not allowed to update as per the conditions set in {} Workflow.").format(get_link_to_form("Workflow", workflow)),
|
||||
title=_("Insufficient Permissions")
|
||||
)
|
||||
|
||||
def get_new_child_item(item_row):
|
||||
if parent_doctype == "Sales Order":
|
||||
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, item_row)
|
||||
new_child_function = set_sales_order_defaults if parent_doctype == "Sales Order" else set_purchase_order_defaults
|
||||
return new_child_function(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):
|
||||
@@ -1224,21 +1291,23 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
|
||||
sales_doctypes = ['Sales Order', 'Sales Invoice', 'Delivery Note', 'Quotation']
|
||||
parent = frappe.get_doc(parent_doctype, parent_doctype_name)
|
||||
|
||||
check_and_delete_children(parent, data)
|
||||
check_doc_permissions(parent, 'write')
|
||||
validate_and_delete_children(parent, data)
|
||||
|
||||
for d in data:
|
||||
new_child_flag = False
|
||||
if not d.get("docname"):
|
||||
new_child_flag = True
|
||||
check_permissions(parent, 'create')
|
||||
check_doc_permissions(parent, 'create')
|
||||
child_item = get_new_child_item(d)
|
||||
else:
|
||||
check_permissions(parent, 'write')
|
||||
check_doc_permissions(parent, 'write')
|
||||
child_item = frappe.get_doc(parent_doctype + ' Item', d.get("docname"))
|
||||
|
||||
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"))
|
||||
prev_uom, new_uom = child_item.get("uom"), d.get("uom")
|
||||
|
||||
if parent_doctype == 'Sales Order':
|
||||
prev_date, new_date = child_item.get("delivery_date"), d.get("delivery_date")
|
||||
@@ -1247,27 +1316,35 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
|
||||
|
||||
rate_unchanged = prev_rate == new_rate
|
||||
qty_unchanged = prev_qty == new_qty
|
||||
uom_unchanged = prev_uom == new_uom
|
||||
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:
|
||||
if rate_unchanged and qty_unchanged and conversion_factor_unchanged and uom_unchanged and date_unchanged:
|
||||
continue
|
||||
|
||||
validate_quantity(child_item, d)
|
||||
|
||||
child_item.qty = flt(d.get("qty"))
|
||||
precision = child_item.precision("rate") or 2
|
||||
rate_precision = child_item.precision("rate") or 2
|
||||
conv_fac_precision = child_item.precision("conversion_factor") or 2
|
||||
qty_precision = child_item.precision("qty") or 2
|
||||
|
||||
if flt(child_item.billed_amt, precision) > flt(flt(d.get("rate")) * flt(d.get("qty")), precision):
|
||||
if flt(child_item.billed_amt, rate_precision) > flt(flt(d.get("rate"), rate_precision) * flt(d.get("qty"), qty_precision), rate_precision):
|
||||
frappe.throw(_("Row #{0}: Cannot set Rate if amount is greater than billed amount for Item {1}.")
|
||||
.format(child_item.idx, child_item.item_code))
|
||||
else:
|
||||
child_item.rate = flt(d.get("rate"))
|
||||
child_item.rate = flt(d.get("rate"), rate_precision)
|
||||
|
||||
if 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'))
|
||||
child_item.conversion_factor = flt(d.get('conversion_factor'), conv_fac_precision)
|
||||
|
||||
if d.get("uom"):
|
||||
child_item.uom = d.get("uom")
|
||||
conversion_factor = flt(get_conversion_factor(child_item.item_code, child_item.uom).get("conversion_factor"))
|
||||
child_item.conversion_factor = flt(d.get('conversion_factor'), conv_fac_precision) or conversion_factor
|
||||
|
||||
if d.get("delivery_date") and parent_doctype == 'Sales Order':
|
||||
child_item.delivery_date = d.get('delivery_date')
|
||||
@@ -1334,12 +1411,17 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
|
||||
parent.update_receiving_percentage()
|
||||
if parent.is_subcontracted == "Yes":
|
||||
parent.update_reserved_qty_for_subcontract()
|
||||
parent.create_raw_materials_supplied("supplied_items")
|
||||
parent.save()
|
||||
else:
|
||||
parent.update_reserved_qty()
|
||||
parent.update_project()
|
||||
parent.update_prevdoc_status('submit')
|
||||
parent.update_delivery_status()
|
||||
|
||||
parent.reload()
|
||||
validate_workflow_conditions(parent)
|
||||
|
||||
parent.update_blanket_order()
|
||||
parent.update_billing_percentage()
|
||||
parent.set_status()
|
||||
|
||||
Reference in New Issue
Block a user