Merge branch 'develop' into multi-barcode

This commit is contained in:
tundebabzy
2018-01-10 21:49:27 +01:00
committed by GitHub
414 changed files with 105304 additions and 77387 deletions

View File

@@ -64,15 +64,14 @@ class AccountsController(TransactionBase):
def validate_invoice_documents_schedule(self):
self.validate_payment_schedule_dates()
self.set_due_date()
self.validate_invoice_portion()
self.set_payment_schedule()
self.validate_payment_schedule_amount()
self.validate_due_date()
self.validate_advance_entries()
def validate_non_invoice_documents_schedule(self):
self.validate_invoice_portion()
self.set_payment_schedule()
self.validate_payment_schedule_dates()
self.validate_payment_schedule_amount()
def validate_all_documents_schedule(self):
@@ -233,9 +232,10 @@ class AccountsController(TransactionBase):
tax_master_doctype = self.meta.get_field("taxes_and_charges").options
if self.is_new() and not self.get("taxes"):
if not self.get("taxes_and_charges"):
if self.company and not self.get("taxes_and_charges"):
# get the default tax master
self.taxes_and_charges = frappe.db.get_value(tax_master_doctype, {"is_default": 1})
self.taxes_and_charges = frappe.db.get_value(tax_master_doctype,
{"is_default": 1, 'company': self.company})
self.append_taxes_from_master(tax_master_doctype)
@@ -375,6 +375,17 @@ class AccountsController(TransactionBase):
return res
def is_inclusive_tax(self):
is_inclusive = cint(frappe.db.get_single_value("Accounts Settings",
"show_inclusive_tax_in_print"))
if is_inclusive:
is_inclusive = 0
if self.get("taxes", filters={"included_in_print_rate": 1}):
is_inclusive = 1
return is_inclusive
def validate_advance_entries(self):
order_field = "sales_order" if self.doctype == "Sales Invoice" else "purchase_order"
order_list = list(set([d.get(order_field)
@@ -650,6 +661,8 @@ class AccountsController(TransactionBase):
date = self.get("due_date")
due_date = date or posting_date
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)
if not self.get("payment_schedule"):
if self.get("payment_terms_template"):
@@ -661,7 +674,8 @@ class AccountsController(TransactionBase):
self.append("payment_schedule", data)
else:
for d in self.get("payment_schedule"):
d.payment_amount = grand_total * flt(d.invoice_portion) / 100
if d.invoice_portion:
d.payment_amount = grand_total * flt(d.invoice_portion) / 100
def set_due_date(self):
due_dates = [d.due_date for d in self.get("payment_schedule") if d.due_date]
@@ -671,15 +685,12 @@ class AccountsController(TransactionBase):
def validate_payment_schedule_dates(self):
dates = []
li = []
if self.due_date and getdate(self.due_date) < getdate(self.posting_date):
frappe.throw(_("Due Date cannot be before posting date"))
for d in self.get("payment_schedule"):
if getdate(d.due_date) < getdate(self.posting_date):
if self.doctype == "Sales Order" and getdate(d.due_date) < getdate(self.transaction_date):
frappe.throw(_("Row {0}: Due Date cannot be before posting date").format(d.idx))
elif d.due_date in dates:
li.append('{0} in row {1}'.format(d.due_date, d.idx))
# frappe.throw(_("Row {0}: Duplicate due date found").format(d.idx))
dates.append(d.due_date)
if li:
@@ -692,20 +703,14 @@ class AccountsController(TransactionBase):
total = 0
for d in self.get("payment_schedule"):
total += flt(d.payment_amount)
total = flt(total, self.precision("grand_total"))
grand_total = self.get("rounded_total") or self.grand_total
grand_total = flt(self.get("rounded_total") or self.grand_total, self.precision('grand_total'))
if self.doctype in ("Sales Invoice", "Purchase Invoice"):
grand_total = grand_total - flt(self.write_off_amount)
if total != grand_total:
frappe.throw(_("Total Payment Amount in Payment Schedule must be equal to Grand / Rounded Total"))
def validate_invoice_portion(self):
if self.get("payment_schedule"):
total_portion = 0
for term in self.payment_schedule:
total_portion += flt(term.get('invoice_portion', 0))
if flt(total_portion, 2) != 100.00:
frappe.throw(_('Combined invoice portion must equal 100%'), indicator='red')
def is_rounded_total_disabled(self):
if self.meta.get_field("disable_rounded_total"):
return self.disable_rounded_total
@@ -717,8 +722,12 @@ def get_tax_rate(account_head):
return frappe.db.get_value("Account", account_head, ["tax_rate", "account_name"], as_dict=True)
@frappe.whitelist()
def get_default_taxes_and_charges(master_doctype):
default_tax = frappe.db.get_value(master_doctype, {"is_default": 1})
def get_default_taxes_and_charges(master_doctype, company=None):
if not company: return {}
default_tax = frappe.db.get_value(master_doctype,
{"is_default": 1, "company": company})
return {
'taxes_and_charges': default_tax,
'taxes': get_taxes_and_charges(master_doctype, default_tax)

View File

@@ -8,7 +8,7 @@ from frappe.utils import flt,cint, cstr, getdate
from erpnext.accounts.party import get_party_details
from erpnext.stock.get_item_details import get_conversion_factor
from erpnext.buying.utils import validate_for_items
from erpnext.buying.utils import validate_for_items, update_last_purchase_rate
from erpnext.stock.stock_ledger import get_valuation_rate
from erpnext.controllers.stock_controller import StockController
@@ -18,7 +18,10 @@ class BuyingController(StockController):
if hasattr(self, "taxes"):
self.flags.print_taxes_with_zero_amount = cint(frappe.db.get_single_value("Print Settings",
"print_taxes_with_zero_amount"))
self.flags.show_inclusive_tax_in_print = self.is_inclusive_tax()
self.print_templates = {
"total": "templates/print_formats/includes/total.html",
"taxes": "templates/print_formats/includes/taxes.html"
}
@@ -160,6 +163,11 @@ class BuyingController(StockController):
if item in self.sub_contracted_items and not item.bom:
frappe.throw(_("Please select BOM in BOM field for Item {0}").format(item.item_code))
if self.doctype == "Purchase Order":
for supplied_item in self.get("supplied_items"):
if not supplied_item.reserve_warehouse:
frappe.throw(_("Reserved Warehouse is mandatory for Item {0} in Raw Materials supplied").format(frappe.bold(supplied_item.rm_item_code)))
else:
for item in self.get("items"):
if item.bom:
@@ -189,8 +197,16 @@ class BuyingController(StockController):
def update_raw_materials_supplied(self, item, raw_material_table):
bom_items = self.get_items_from_bom(item.item_code, item.bom)
raw_materials_cost = 0
items = list(set([d.item_code for d in bom_items]))
item_wh = frappe._dict(frappe.db.sql("""select item_code, default_warehouse
from `tabItem` where name in ({0})""".format(", ".join(["%s"] * len(items))), items))
for bom_item in bom_items:
if self.doctype == "Purchase Order":
reserve_warehouse = bom_item.source_warehouse or item_wh.get(bom_item.item_code)
if frappe.db.get_value("Warehouse", reserve_warehouse, "company") != self.company:
reserve_warehouse = None
# check if exists
exists = 0
for d in self.get(raw_material_table):
@@ -210,6 +226,8 @@ class BuyingController(StockController):
rm.rm_item_code = bom_item.item_code
rm.stock_uom = bom_item.stock_uom
rm.required_qty = required_qty
if self.doctype == "Purchase Order" and not rm.reserve_warehouse:
rm.reserve_warehouse = reserve_warehouse
rm.conversion_factor = item.conversion_factor
@@ -261,7 +279,7 @@ class BuyingController(StockController):
def get_items_from_bom(self, item_code, bom):
bom_items = frappe.db.sql("""select t2.item_code,
t2.stock_qty / ifnull(t1.quantity, 1) as qty_consumed_per_unit,
t2.rate, t2.stock_uom, t2.name, t2.description
t2.rate, t2.stock_uom, t2.name, t2.description, t2.source_warehouse
from `tabBOM` t1, `tabBOM Item` t2, tabItem t3
where t2.parent = t1.name and t1.item = %s
and t1.docstatus = 1 and t1.is_active = 1 and t1.name = %s
@@ -336,7 +354,7 @@ class BuyingController(StockController):
frappe.get_meta(item_row.doctype).get_label(fieldname), item_row['item_code'])))
def update_stock_ledger(self, allow_negative_stock=False, via_landed_cost_voucher=False):
self.update_ordered_qty()
self.update_ordered_and_reserved_qty()
sl_entries = []
stock_items = self.get_stock_items()
@@ -378,7 +396,7 @@ class BuyingController(StockController):
self.make_sl_entries(sl_entries, allow_negative_stock=allow_negative_stock,
via_landed_cost_voucher=via_landed_cost_voucher)
def update_ordered_qty(self):
def update_ordered_and_reserved_qty(self):
po_map = {}
for d in self.get("items"):
if self.doctype=="Purchase Receipt" \
@@ -397,6 +415,8 @@ class BuyingController(StockController):
frappe.InvalidStatusError)
po_obj.update_ordered_qty(po_item_rows)
if self.is_subcontracted:
po_obj.update_reserved_qty_for_subcontract()
def make_sl_entries_for_supplier_warehouse(self, sl_entries):
if hasattr(self, 'supplied_items'):
@@ -409,6 +429,18 @@ class BuyingController(StockController):
"actual_qty": -1*flt(d.consumed_qty),
}))
def on_submit(self):
if self.get('is_return'):
return
update_last_purchase_rate(self, is_submit = 1)
def on_cancel(self):
if self.get('is_return'):
return
update_last_purchase_rate(self, is_submit = 0)
def validate_schedule_date(self):
if not self.schedule_date:
self.schedule_date = min([d.schedule_date for d in self.get("items")])
@@ -418,8 +450,9 @@ class BuyingController(StockController):
if not d.schedule_date:
d.schedule_date = self.schedule_date
if d.schedule_date and getdate(d.schedule_date) < getdate(self.transaction_date):
frappe.throw(_("Expected Date cannot be before Transaction Date"))
if (d.schedule_date and self.transaction_date and
getdate(d.schedule_date) < getdate(self.transaction_date)):
frappe.throw(_("Row #{0}: Reqd by Date cannot be before Transaction Date").format(d.idx))
else:
frappe.throw(_("Please enter Schedule Date"))
frappe.throw(_("Please enter Reqd by Date"))

View File

@@ -56,7 +56,7 @@ def validate_item_variant_attributes(item, args=None):
if not args:
args = {d.attribute.lower():d.attribute_value for d in item.attributes}
attribute_values, numeric_values = get_attribute_values()
attribute_values, numeric_values = get_attribute_values(item)
for attribute, value in args.items():
if not value:
@@ -96,16 +96,17 @@ def validate_item_attribute_value(attributes_list, attribute, attribute_value, i
frappe.throw(_("Value {0} for Attribute {1} does not exist in the list of valid Item Attribute Values for Item {2}").format(
attribute_value, attribute, item), InvalidItemAttributeValueError, title=_('Invalid Attribute'))
def get_attribute_values():
def get_attribute_values(item):
if not frappe.flags.attribute_values:
attribute_values = {}
numeric_values = {}
for t in frappe.get_all("Item Attribute Value", fields=["parent", "attribute_value"]):
attribute_values.setdefault(t.parent.lower(), []).append(t.attribute_value)
for t in frappe.get_all('Item Attribute',
fields=["name", "from_range", "to_range", "increment"], filters={'numeric_values': 1}):
numeric_values[t.name.lower()] = t
for t in frappe.get_all('Item Variant Attribute',
fields=["attribute", "from_range", "to_range", "increment"],
filters={'numeric_values': 1, 'parent': item.variant_of}):
numeric_values[t.attribute.lower()] = t
frappe.flags.attribute_values = attribute_values
frappe.flags.numeric_values = numeric_values

View File

@@ -418,6 +418,6 @@ def get_batch_numbers(doctype, txt, searchfield, start, page_len, filters):
'where (`tabBatch`.expiry_date >= CURDATE() or `tabBatch`.expiry_date IS NULL)'
if filters and filters.get('item_code'):
query += 'where item = %(item_code)s' % filters
query += 'and item = %(item_code)s'
return frappe.db.sql(query)
return frappe.db.sql(query, filters)

View File

@@ -16,7 +16,10 @@ class SellingController(StockController):
if hasattr(self, "taxes"):
self.flags.print_taxes_with_zero_amount = cint(frappe.db.get_single_value("Print Settings",
"print_taxes_with_zero_amount"))
self.flags.show_inclusive_tax_in_print = self.is_inclusive_tax()
self.print_templates = {
"total": "templates/print_formats/includes/total.html",
"taxes": "templates/print_formats/includes/taxes.html"
}
@@ -185,7 +188,10 @@ class SellingController(StockController):
'batch_no': cstr(p.batch_no).strip(),
'serial_no': cstr(p.serial_no).strip(),
'name': d.name,
'target_warehouse': p.target_warehouse
'target_warehouse': p.target_warehouse,
'company': self.company,
'voucher_type': self.doctype,
'allow_zero_valuation': d.allow_zero_valuation_rate
}))
else:
il.append(frappe._dict({
@@ -198,7 +204,10 @@ class SellingController(StockController):
'batch_no': cstr(d.get("batch_no")).strip(),
'serial_no': cstr(d.get("serial_no")).strip(),
'name': d.name,
'target_warehouse': d.target_warehouse
'target_warehouse': d.target_warehouse,
'company': self.company,
'voucher_type': self.doctype,
'allow_zero_valuation': d.allow_zero_valuation_rate
}))
return il
@@ -293,7 +302,11 @@ class SellingController(StockController):
"posting_date": self.posting_date,
"posting_time": self.posting_time,
"qty": -1*flt(d.qty),
"serial_no": d.serial_no
"serial_no": d.serial_no,
"company": d.company,
"voucher_type": d.voucher_type,
"voucher_no": d.name,
"allow_zero_valuation": d.allow_zero_valuation
})
target_warehouse_sle.update({
"incoming_rate": get_incoming_rate(args)

View File

@@ -70,7 +70,6 @@ class calculate_taxes_and_totals(object):
item.net_rate = item.rate
item.amount = flt(item.rate * item.qty, item.precision("amount"))
item.net_amount = item.amount
item.total_weight = flt(item.weight_per_unit * item.qty)
self._set_in_company_currency(item, ["price_list_rate", "rate", "net_rate", "amount", "net_amount"])
@@ -164,13 +163,12 @@ class calculate_taxes_and_totals(object):
return tax.rate
def calculate_net_total(self):
self.doc.total = self.doc.base_total = self.doc.net_total = self.doc.base_net_total = self.doc.total_net_weight= 0.0
self.doc.total = self.doc.base_total = self.doc.net_total = self.doc.base_net_total = 0.0
for item in self.doc.get("items"):
self.doc.total += item.amount
self.doc.base_total += item.base_amount
self.doc.net_total += item.net_amount
self.doc.base_net_total += item.base_net_amount
self.doc.total_net_weight += item.total_weight
self.doc.round_floats_in(self.doc, ["total", "base_total", "net_total", "base_net_total"])
@@ -400,7 +398,8 @@ class calculate_taxes_and_totals(object):
for tax in self.doc.get("taxes"):
if tax.charge_type == "Actual":
actual_taxes_dict.setdefault(tax.idx, tax.tax_amount)
tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax.tax_amount, tax)
actual_taxes_dict.setdefault(tax.idx, tax_amount)
elif tax.row_id in actual_taxes_dict:
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)
@@ -535,7 +534,7 @@ def get_itemised_tax_breakup_html(doc):
for tax in doc.taxes:
if getattr(tax, "category", None) and tax.category=="Valuation":
continue
if tax.description not in tax_accounts and tax.tax_amount_after_discount_amount:
if tax.description not in tax_accounts:
tax_accounts.append(tax.description)
headers = get_itemised_tax_breakup_header(doc.doctype + " Item", tax_accounts)
@@ -545,6 +544,7 @@ def get_itemised_tax_breakup_html(doc):
get_rounded_tax_amount(itemised_tax, doc.precision("tax_amount", "taxes"))
update_itemised_tax_data(doc)
frappe.flags.company = None
return frappe.render_template(
@@ -557,6 +557,12 @@ def get_itemised_tax_breakup_html(doc):
)
)
@erpnext.allow_regional
def update_itemised_tax_data(doc):
#Don't delete this method, used for localization
pass
@erpnext.allow_regional
def get_itemised_tax_breakup_header(item_doctype, tax_accounts):
return [_("Item"), _("Taxable Amount")] + tax_accounts