mirror of
https://github.com/frappe/erpnext.git
synced 2026-06-05 05:09:11 +00:00
Merge branch 'develop' into payment-terms
This commit is contained in:
@@ -92,9 +92,9 @@ class AccountsController(TransactionBase):
|
||||
if cint(is_paid) == 1:
|
||||
if flt(self.paid_amount) == 0 and flt(self.outstanding_amount) > 0:
|
||||
if self.cash_bank_account:
|
||||
self.paid_amount = flt(flt(self.grand_total) - flt(self.write_off_amount),
|
||||
self.precision("paid_amount"))
|
||||
self.base_paid_amount = flt(self.paid_amount * self.conversion_rate, self.precision("base_paid_amount"))
|
||||
self.paid_amount = flt(flt(self.outstanding_amount), self.precision("paid_amount"))
|
||||
self.base_paid_amount = flt(self.paid_amount * self.conversion_rate,
|
||||
self.precision("base_paid_amount"))
|
||||
else:
|
||||
# show message that the amount is not paid
|
||||
self.paid_amount = 0
|
||||
@@ -109,9 +109,6 @@ class AccountsController(TransactionBase):
|
||||
self.set(fieldname, today())
|
||||
break
|
||||
|
||||
# set taxes table if missing from `taxes_and_charges`
|
||||
self.set_taxes()
|
||||
|
||||
def calculate_taxes_and_totals(self):
|
||||
from erpnext.controllers.taxes_and_totals import calculate_taxes_and_totals
|
||||
calculate_taxes_and_totals(self)
|
||||
@@ -683,6 +680,11 @@ class AccountsController(TransactionBase):
|
||||
if flt(total_portion, 2) != 100.00:
|
||||
frappe.msgprint(_('Combined invoice portion must equal 100%'), raise_exception=1, indicator='red')
|
||||
|
||||
def is_rounded_total_disabled(self):
|
||||
if self.meta.get_field("disable_rounded_total"):
|
||||
return self.disable_rounded_total
|
||||
else:
|
||||
return frappe.db.get_single_value("Global Defaults", "disable_rounded_total")
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_tax_rate(account_head):
|
||||
|
||||
@@ -169,6 +169,74 @@ def create_variant(item, args):
|
||||
|
||||
return variant
|
||||
|
||||
@frappe.whitelist()
|
||||
def enqueue_multiple_variant_creation(item, args):
|
||||
# There can be innumerable attribute combinations, enqueue
|
||||
frappe.enqueue("erpnext.controllers.item_variant.create_multiple_variants",
|
||||
item=item, args=args, now=frappe.flags.in_test);
|
||||
|
||||
def create_multiple_variants(item, args):
|
||||
if isinstance(args, basestring):
|
||||
args = json.loads(args)
|
||||
|
||||
args_set = generate_keyed_value_combinations(args)
|
||||
|
||||
for attribute_values in args_set:
|
||||
if not get_variant(item, args=attribute_values):
|
||||
variant = create_variant(item, attribute_values)
|
||||
variant.save()
|
||||
|
||||
def generate_keyed_value_combinations(args):
|
||||
"""
|
||||
From this:
|
||||
|
||||
args = {"attr1": ["a", "b", "c"], "attr2": ["1", "2"], "attr3": ["A"]}
|
||||
|
||||
To this:
|
||||
|
||||
[
|
||||
{u'attr1': u'a', u'attr2': u'1', u'attr3': u'A'},
|
||||
{u'attr1': u'b', u'attr2': u'1', u'attr3': u'A'},
|
||||
{u'attr1': u'c', u'attr2': u'1', u'attr3': u'A'},
|
||||
{u'attr1': u'a', u'attr2': u'2', u'attr3': u'A'},
|
||||
{u'attr1': u'b', u'attr2': u'2', u'attr3': u'A'},
|
||||
{u'attr1': u'c', u'attr2': u'2', u'attr3': u'A'}
|
||||
]
|
||||
|
||||
"""
|
||||
# Return empty list if empty
|
||||
if not args:
|
||||
return []
|
||||
|
||||
# Turn `args` into a list of lists of key-value tuples:
|
||||
# [
|
||||
# [(u'attr2', u'1'), (u'attr2', u'2')],
|
||||
# [(u'attr3', u'A')],
|
||||
# [(u'attr1', u'a'), (u'attr1', u'b'), (u'attr1', u'c')]
|
||||
# ]
|
||||
key_value_lists = [[(key, val) for val in args[key]] for key in args.keys()]
|
||||
|
||||
# Store the first, but as objects
|
||||
# [{u'attr2': u'1'}, {u'attr2': u'2'}]
|
||||
results = key_value_lists.pop(0)
|
||||
results = [{d[0]: d[1]} for d in results]
|
||||
|
||||
# Iterate the remaining
|
||||
# Take the next list to fuse with existing results
|
||||
for l in key_value_lists:
|
||||
new_results = []
|
||||
for res in results:
|
||||
for key_val in l:
|
||||
# create a new clone of object in result
|
||||
obj = copy.deepcopy(res)
|
||||
# to be used with every incoming new value
|
||||
obj[key_val[0]] = key_val[1]
|
||||
# and pushed into new_results
|
||||
new_results.append(obj)
|
||||
results = new_results
|
||||
|
||||
return results
|
||||
|
||||
def copy_attributes_to_variant(item, variant):
|
||||
from frappe.model import no_value_fields
|
||||
|
||||
@@ -208,7 +276,7 @@ def copy_attributes_to_variant(item, variant):
|
||||
attributes_description = ""
|
||||
for d in variant.attributes:
|
||||
attributes_description += "<div>" + d.attribute + ": " + cstr(d.attribute_value) + "</div>"
|
||||
|
||||
|
||||
if attributes_description not in variant.description:
|
||||
variant.description += attributes_description
|
||||
|
||||
@@ -239,3 +307,20 @@ def make_variant_item_code(template_item_code, template_item_name, variant):
|
||||
if abbreviations:
|
||||
variant.item_code = "{0}-{1}".format(template_item_code, "-".join(abbreviations))
|
||||
variant.item_name = "{0}-{1}".format(template_item_name, "-".join(abbreviations))
|
||||
|
||||
@frappe.whitelist()
|
||||
def create_variant_doc_for_quick_entry(template, args):
|
||||
variant_based_on = frappe.db.get_value("Item", template, "variant_based_on")
|
||||
args = json.loads(args)
|
||||
if variant_based_on == "Manufacturer":
|
||||
variant = get_variant(template, **args)
|
||||
else:
|
||||
existing_variant = get_variant(template, args)
|
||||
if existing_variant:
|
||||
return existing_variant
|
||||
else:
|
||||
variant = create_variant(template, args=args)
|
||||
variant.name = variant.item_code
|
||||
validate_item_variant_attributes(variant, args)
|
||||
return variant.as_dict()
|
||||
|
||||
|
||||
@@ -28,8 +28,7 @@ class SellingController(StockController):
|
||||
super(SellingController, self).onload()
|
||||
if self.doctype in ("Sales Order", "Delivery Note", "Sales Invoice"):
|
||||
for item in self.get("items"):
|
||||
item.update(get_bin_details(item.item_code,
|
||||
item.warehouse))
|
||||
item.update(get_bin_details(item.item_code, item.warehouse))
|
||||
|
||||
def validate(self):
|
||||
super(SellingController, self).validate()
|
||||
@@ -114,14 +113,15 @@ class SellingController(StockController):
|
||||
|
||||
def set_total_in_words(self):
|
||||
from frappe.utils import money_in_words
|
||||
disable_rounded_total = cint(frappe.db.get_value("Global Defaults", None, "disable_rounded_total"))
|
||||
|
||||
if self.meta.get_field("base_in_words"):
|
||||
self.base_in_words = money_in_words(disable_rounded_total and
|
||||
abs(self.base_grand_total) or abs(self.base_rounded_total), self.company_currency)
|
||||
base_amount = abs(self.base_grand_total
|
||||
if self.is_rounded_total_disabled() else self.base_rounded_total)
|
||||
self.base_in_words = money_in_words(base_amount, self.company_currency)
|
||||
|
||||
if self.meta.get_field("in_words"):
|
||||
self.in_words = money_in_words(disable_rounded_total and
|
||||
abs(self.grand_total) or abs(self.rounded_total), self.currency)
|
||||
amount = abs(self.grand_total if self.is_rounded_total_disabled() else self.rounded_total)
|
||||
self.in_words = money_in_words(amount, self.currency)
|
||||
|
||||
def calculate_commission(self):
|
||||
if self.meta.get_field("commission_rate"):
|
||||
|
||||
@@ -62,7 +62,7 @@ class calculate_taxes_and_totals(object):
|
||||
(1.0 - (item.discount_percentage / 100.0)), item.precision("rate"))
|
||||
|
||||
if item.doctype in ['Quotation Item', 'Sales Order Item', 'Delivery Note Item', 'Sales Invoice Item']:
|
||||
item.rate_with_margin = self.calculate_margin(item)
|
||||
item.rate_with_margin, item.base_rate_with_margin = self.calculate_margin(item)
|
||||
|
||||
item.rate = flt(item.rate_with_margin * (1.0 - (item.discount_percentage / 100.0)), item.precision("rate"))\
|
||||
if item.rate_with_margin > 0 else item.rate
|
||||
@@ -328,14 +328,18 @@ class calculate_taxes_and_totals(object):
|
||||
self.set_rounded_total()
|
||||
|
||||
def set_rounded_total(self):
|
||||
if frappe.db.get_single_value("Global Defaults", "disable_rounded_total"):
|
||||
self.doc.rounded_total = self.doc.base_rounded_total = 0
|
||||
return
|
||||
|
||||
if self.doc.meta.get_field("rounded_total"):
|
||||
if self.doc.is_rounded_total_disabled():
|
||||
self.doc.rounded_total = self.doc.base_rounded_total = 0
|
||||
return
|
||||
|
||||
self.doc.rounded_total = round_based_on_smallest_currency_fraction(self.doc.grand_total,
|
||||
self.doc.currency, self.doc.precision("rounded_total"))
|
||||
|
||||
#if print_in_rate is set, we would have already calculated rounding adjustment
|
||||
self.doc.rounding_adjustment += flt(self.doc.rounded_total - self.doc.grand_total,
|
||||
self.doc.precision("rounding_adjustment"))
|
||||
|
||||
if self.doc.meta.get_field("base_rounded_total"):
|
||||
company_currency = erpnext.get_company_currency(self.doc.company)
|
||||
|
||||
@@ -343,6 +347,9 @@ class calculate_taxes_and_totals(object):
|
||||
round_based_on_smallest_currency_fraction(self.doc.base_grand_total,
|
||||
company_currency, self.doc.precision("base_rounded_total"))
|
||||
|
||||
self.doc.base_rounding_adjustment += flt(self.doc.base_rounded_total - self.doc.base_grand_total,
|
||||
self.doc.precision("base_rounding_adjustment"))
|
||||
|
||||
def _cleanup(self):
|
||||
for tax in self.doc.get("taxes"):
|
||||
tax.item_wise_tax_detail = json.dumps(tax.item_wise_tax_detail, separators=(',', ':'))
|
||||
@@ -404,7 +411,8 @@ class calculate_taxes_and_totals(object):
|
||||
actual_tax_amount = flt(actual_taxes_dict.get(tax.row_id, 0)) * flt(tax.rate) / 100
|
||||
actual_taxes_dict.setdefault(tax.idx, actual_tax_amount)
|
||||
|
||||
return flt(self.doc.grand_total - sum(actual_taxes_dict.values()), self.doc.precision("grand_total"))
|
||||
return flt(self.doc.grand_total - sum(actual_taxes_dict.values()),
|
||||
self.doc.precision("grand_total"))
|
||||
|
||||
|
||||
def calculate_total_advance(self):
|
||||
@@ -442,30 +450,31 @@ class calculate_taxes_and_totals(object):
|
||||
self.doc.round_floats_in(self.doc, ["grand_total", "total_advance", "write_off_amount"])
|
||||
self._set_in_company_currency(self.doc, ['write_off_amount'])
|
||||
|
||||
if self.doc.party_account_currency == self.doc.currency:
|
||||
total_amount_to_pay = flt(self.doc.grand_total - self.doc.total_advance
|
||||
- flt(self.doc.write_off_amount), self.doc.precision("grand_total"))
|
||||
else:
|
||||
total_amount_to_pay = flt(flt(self.doc.grand_total *
|
||||
self.doc.conversion_rate, self.doc.precision("grand_total")) - self.doc.total_advance
|
||||
- flt(self.doc.base_write_off_amount), self.doc.precision("grand_total"))
|
||||
if self.doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
|
||||
grand_total = self.doc.rounded_total or self.doc.grand_total
|
||||
if self.doc.party_account_currency == self.doc.currency:
|
||||
total_amount_to_pay = flt(grand_total - self.doc.total_advance
|
||||
- flt(self.doc.write_off_amount), self.doc.precision("grand_total"))
|
||||
else:
|
||||
total_amount_to_pay = flt(flt(grand_total *
|
||||
self.doc.conversion_rate, self.doc.precision("grand_total")) - self.doc.total_advance
|
||||
- flt(self.doc.base_write_off_amount), self.doc.precision("grand_total"))
|
||||
|
||||
if self.doc.doctype == "Sales Invoice":
|
||||
self.doc.round_floats_in(self.doc, ["paid_amount"])
|
||||
self.calculate_write_off_amount()
|
||||
self.calculate_change_amount()
|
||||
|
||||
change_amount = 0
|
||||
|
||||
if self.doc.doctype == "Sales Invoice":
|
||||
self.calculate_write_off_amount()
|
||||
self.calculate_change_amount()
|
||||
change_amount = self.doc.change_amount \
|
||||
if self.doc.party_account_currency == self.doc.currency else self.doc.base_change_amount
|
||||
|
||||
paid_amount = self.doc.paid_amount \
|
||||
if self.doc.party_account_currency == self.doc.currency else self.doc.base_paid_amount
|
||||
|
||||
change_amount = self.doc.change_amount \
|
||||
if self.doc.party_account_currency == self.doc.currency else self.doc.base_change_amount
|
||||
|
||||
self.doc.outstanding_amount = flt(total_amount_to_pay - flt(paid_amount) +
|
||||
flt(change_amount), self.doc.precision("outstanding_amount"))
|
||||
|
||||
elif self.doc.doctype == "Purchase Invoice":
|
||||
self.doc.outstanding_amount = flt(total_amount_to_pay, self.doc.precision("outstanding_amount"))
|
||||
self.doc.outstanding_amount = flt(total_amount_to_pay - flt(paid_amount) + flt(change_amount),
|
||||
self.doc.precision("outstanding_amount"))
|
||||
|
||||
def calculate_paid_amount(self):
|
||||
paid_amount = base_paid_amount = 0.0
|
||||
@@ -485,7 +494,9 @@ class calculate_taxes_and_totals(object):
|
||||
def calculate_change_amount(self):
|
||||
self.doc.change_amount = 0.0
|
||||
self.doc.base_change_amount = 0.0
|
||||
if self.doc.paid_amount > self.doc.grand_total and not self.doc.is_return \
|
||||
|
||||
if self.doc.doctype == "Sales Invoice" \
|
||||
and self.doc.paid_amount > self.doc.grand_total and not self.doc.is_return \
|
||||
and any([d.type == "Cash" for d in self.doc.payments]):
|
||||
|
||||
self.doc.change_amount = flt(self.doc.paid_amount - self.doc.grand_total +
|
||||
@@ -496,13 +507,14 @@ class calculate_taxes_and_totals(object):
|
||||
|
||||
def calculate_write_off_amount(self):
|
||||
if flt(self.doc.change_amount) > 0:
|
||||
self.doc.write_off_amount = flt(self.doc.grand_total - self.doc.paid_amount + self.doc.change_amount,
|
||||
self.doc.precision("write_off_amount"))
|
||||
self.doc.write_off_amount = flt(self.doc.grand_total - self.doc.paid_amount
|
||||
+ self.doc.change_amount, self.doc.precision("write_off_amount"))
|
||||
self.doc.base_write_off_amount = flt(self.doc.write_off_amount * self.doc.conversion_rate,
|
||||
self.doc.precision("base_write_off_amount"))
|
||||
|
||||
def calculate_margin(self, item):
|
||||
rate_with_margin = 0.0
|
||||
base_rate_with_margin = 0.0
|
||||
if item.price_list_rate:
|
||||
if item.pricing_rule and not self.doc.ignore_pricing_rule:
|
||||
pricing_rule = frappe.get_doc('Pricing Rule', item.pricing_rule)
|
||||
@@ -512,8 +524,9 @@ class calculate_taxes_and_totals(object):
|
||||
if item.margin_type and item.margin_rate_or_amount:
|
||||
margin_value = item.margin_rate_or_amount if item.margin_type == 'Amount' else flt(item.price_list_rate) * flt(item.margin_rate_or_amount) / 100
|
||||
rate_with_margin = flt(item.price_list_rate) + flt(margin_value)
|
||||
base_rate_with_margin = flt(rate_with_margin) * flt(self.doc.conversion_rate)
|
||||
|
||||
return rate_with_margin
|
||||
return rate_with_margin, base_rate_with_margin
|
||||
|
||||
def set_item_wise_tax_breakup(self):
|
||||
self.doc.other_charges_calculation = get_itemised_tax_breakup_html(self.doc)
|
||||
|
||||
Reference in New Issue
Block a user