Merge branch 'develop' into payment-terms

This commit is contained in:
Nabin Hait
2017-11-17 12:41:56 +05:30
committed by GitHub
245 changed files with 45464 additions and 35143 deletions

View File

@@ -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):

View File

@@ -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()

View File

@@ -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"):

View File

@@ -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)