mirror of
https://github.com/frappe/erpnext.git
synced 2026-08-27 22:05:19 +00:00
Merge branch 'hotfix' of github.com:frappe/erpnext into hotfix
This commit is contained in:
@@ -63,6 +63,11 @@ class AccountsController(TransactionBase):
|
||||
if self.doctype == 'Purchase Invoice':
|
||||
self.validate_paid_amount()
|
||||
|
||||
def before_print(self):
|
||||
if self.doctype in ['Purchase Order', 'Sales Order']:
|
||||
if self.get("group_same_items"):
|
||||
self.group_similar_items()
|
||||
|
||||
def validate_paid_amount(self):
|
||||
if hasattr(self, "is_pos") or hasattr(self, "is_paid"):
|
||||
is_paid = self.get("is_pos") or self.get("is_paid")
|
||||
@@ -108,7 +113,7 @@ class AccountsController(TransactionBase):
|
||||
date_field = "transaction_date"
|
||||
|
||||
if date_field and self.get(date_field):
|
||||
validate_fiscal_year(self.get(date_field), self.fiscal_year,
|
||||
validate_fiscal_year(self.get(date_field), self.fiscal_year, self.company,
|
||||
self.meta.get_label(date_field), self)
|
||||
|
||||
def validate_due_date(self):
|
||||
@@ -122,6 +127,11 @@ class AccountsController(TransactionBase):
|
||||
validate_due_date(self.posting_date, self.due_date, "Supplier", self.supplier, self.company)
|
||||
|
||||
def set_price_list_currency(self, buying_or_selling):
|
||||
if self.meta.get_field("posting_date"):
|
||||
transaction_date = self.posting_date
|
||||
else:
|
||||
transaction_date = self.transaction_date
|
||||
|
||||
if self.meta.get_field("currency"):
|
||||
# price list part
|
||||
fieldname = "selling_price_list" if buying_or_selling.lower() == "selling" \
|
||||
@@ -134,8 +144,8 @@ class AccountsController(TransactionBase):
|
||||
self.plc_conversion_rate = 1.0
|
||||
|
||||
elif not self.plc_conversion_rate:
|
||||
self.plc_conversion_rate = get_exchange_rate(
|
||||
self.price_list_currency, self.company_currency)
|
||||
self.plc_conversion_rate = get_exchange_rate(self.price_list_currency,
|
||||
self.company_currency, transaction_date)
|
||||
|
||||
# currency
|
||||
if not self.currency:
|
||||
@@ -145,7 +155,7 @@ class AccountsController(TransactionBase):
|
||||
self.conversion_rate = 1.0
|
||||
elif not self.conversion_rate:
|
||||
self.conversion_rate = get_exchange_rate(self.currency,
|
||||
self.company_currency)
|
||||
self.company_currency, transaction_date)
|
||||
|
||||
def set_missing_item_details(self, for_validate=False):
|
||||
"""set missing item values"""
|
||||
@@ -416,7 +426,7 @@ class AccountsController(TransactionBase):
|
||||
max_allowed_amt = flt(ref_amt * (100 + tolerance) / 100)
|
||||
|
||||
if total_billed_amt - max_allowed_amt > 0.01:
|
||||
frappe.throw(_("Cannot overbill for Item {0} in row {1} more than {2}. To allow overbilling, please set in Stock Settings").format(item.item_code, item.idx, max_allowed_amt))
|
||||
frappe.throw(_("Cannot overbill for Item {0} in row {1} more than {2}. To allow over-billing, please set in Buying Settings").format(item.item_code, item.idx, max_allowed_amt))
|
||||
|
||||
def get_company_default(self, fieldname):
|
||||
from erpnext.accounts.utils import get_company_default
|
||||
@@ -561,6 +571,42 @@ class AccountsController(TransactionBase):
|
||||
frappe.throw(_("Row #{0}: Asset {1} cannot be submitted, it is already {2}")
|
||||
.format(d.idx, d.asset, asset.status))
|
||||
|
||||
def delink_advance_entries(self, linked_doc_name):
|
||||
total_allocated_amount = 0
|
||||
for adv in self.advances:
|
||||
consider_for_total_advance = True
|
||||
if adv.reference_name == linked_doc_name:
|
||||
frappe.db.sql("""delete from `tab{0} Advance`
|
||||
where name = %s""".format(self.doctype), adv.name)
|
||||
consider_for_total_advance = False
|
||||
|
||||
if consider_for_total_advance:
|
||||
total_allocated_amount += flt(adv.allocated_amount, adv.precision("allocated_amount"))
|
||||
|
||||
frappe.db.set_value(self.doctype, self.name, "total_advance",
|
||||
total_allocated_amount, update_modified=False)
|
||||
|
||||
def group_similar_items(self):
|
||||
group_item_qty = {}
|
||||
group_item_amount = {}
|
||||
|
||||
for item in self.items:
|
||||
group_item_qty[item.item_code] = group_item_qty.get(item.item_code, 0) + item.qty
|
||||
group_item_amount[item.item_code] = group_item_amount.get(item.item_code, 0) + item.amount
|
||||
|
||||
duplicate_list = []
|
||||
|
||||
for item in self.items:
|
||||
if item.item_code in group_item_qty:
|
||||
item.qty = group_item_qty[item.item_code]
|
||||
item.amount = group_item_amount[item.item_code]
|
||||
del group_item_qty[item.item_code]
|
||||
else:
|
||||
duplicate_list.append(item)
|
||||
|
||||
for item in duplicate_list:
|
||||
self.remove(item)
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_tax_rate(account_head):
|
||||
return frappe.db.get_value("Account", account_head, ["tax_rate", "account_name"], as_dict=True)
|
||||
@@ -665,7 +711,7 @@ def get_advance_journal_entries(party_type, party, party_account, amount_field,
|
||||
.format(order_doctype, order_condition))
|
||||
|
||||
reference_condition = " and (" + " or ".join(conditions) + ")" if conditions else ""
|
||||
|
||||
|
||||
journal_entries = frappe.db.sql("""
|
||||
select
|
||||
"Journal Entry" as reference_type, t1.name as reference_name,
|
||||
@@ -725,8 +771,8 @@ def get_advance_payment_entries(party_type, party, party_account,
|
||||
def update_invoice_status():
|
||||
# Daily update the status of the invoices
|
||||
|
||||
frappe.db.sql(""" update `tabSales Invoice` set status = 'Overdue'
|
||||
frappe.db.sql(""" update `tabSales Invoice` set status = 'Overdue'
|
||||
where due_date < CURDATE() and docstatus = 1 and outstanding_amount > 0""")
|
||||
|
||||
frappe.db.sql(""" update `tabPurchase Invoice` set status = 'Overdue'
|
||||
frappe.db.sql(""" update `tabPurchase Invoice` set status = 'Overdue'
|
||||
where due_date < CURDATE() and docstatus = 1 and outstanding_amount > 0""")
|
||||
@@ -178,6 +178,9 @@ class BuyingController(StockController):
|
||||
for item in self.get("items"):
|
||||
item.rm_supp_cost = 0.0
|
||||
|
||||
if self.is_subcontracted == "No" and self.get("supplied_items"):
|
||||
self.set('supplied_items', [])
|
||||
|
||||
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
|
||||
@@ -223,7 +226,8 @@ class BuyingController(StockController):
|
||||
})
|
||||
if not rm.rate:
|
||||
from erpnext.stock.stock_ledger import get_valuation_rate
|
||||
rm.rate = get_valuation_rate(bom_item.item_code, self.supplier_warehouse)
|
||||
rm.rate = get_valuation_rate(bom_item.item_code, self.supplier_warehouse,
|
||||
self.doctype, self.name)
|
||||
else:
|
||||
rm.rate = bom_item.rate
|
||||
|
||||
@@ -302,6 +306,7 @@ class BuyingController(StockController):
|
||||
# validate accepted and rejected qty
|
||||
def validate_accepted_rejected_qty(self):
|
||||
for d in self.get("items"):
|
||||
self.validate_negative_quantity(d, ["received_qty","qty", "rejected_qty"])
|
||||
if not flt(d.received_qty) and flt(d.qty):
|
||||
d.received_qty = flt(d.qty) - flt(d.rejected_qty)
|
||||
|
||||
@@ -315,6 +320,16 @@ class BuyingController(StockController):
|
||||
if ((flt(d.qty) + flt(d.rejected_qty)) != flt(d.received_qty)):
|
||||
frappe.throw(_("Accepted + Rejected Qty must be equal to Received quantity for Item {0}").format(d.item_code))
|
||||
|
||||
def validate_negative_quantity(self, item_row, field_list):
|
||||
if self.is_return:
|
||||
return
|
||||
|
||||
item_row = item_row.as_dict()
|
||||
for fieldname in field_list:
|
||||
if flt(item_row[fieldname]) < 0:
|
||||
frappe.throw(_("Row #{0}: {1} can not be negative for item {2}".format(item_row['idx'],
|
||||
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()
|
||||
|
||||
|
||||
@@ -12,19 +12,44 @@ class InvalidItemAttributeValueError(frappe.ValidationError): pass
|
||||
class ItemTemplateCannotHaveStock(frappe.ValidationError): pass
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_variant(template, args, variant=None):
|
||||
"""Validates Attributes and their Values, then looks for an exactly matching Item Variant
|
||||
def get_variant(template, args=None, variant=None, manufacturer=None,
|
||||
manufacturer_part_no=None):
|
||||
"""Validates Attributes and their Values, then looks for an exactly
|
||||
matching Item Variant
|
||||
|
||||
:param item: Template Item
|
||||
:param args: A dictionary with "Attribute" as key and "Attribute Value" as value
|
||||
"""
|
||||
if isinstance(args, basestring):
|
||||
args = json.loads(args)
|
||||
item_template = frappe.get_doc('Item', template)
|
||||
|
||||
if not args:
|
||||
frappe.throw(_("Please specify at least one attribute in the Attributes table"))
|
||||
if item_template.variant_based_on=='Manufacturer' and manufacturer:
|
||||
return make_variant_based_on_manufacturer(item_template, manufacturer,
|
||||
manufacturer_part_no)
|
||||
else:
|
||||
if isinstance(args, basestring):
|
||||
args = json.loads(args)
|
||||
|
||||
return find_variant(template, args, variant)
|
||||
if not args:
|
||||
frappe.throw(_("Please specify at least one attribute in the Attributes table"))
|
||||
return find_variant(template, args, variant)
|
||||
|
||||
def make_variant_based_on_manufacturer(template, manufacturer, manufacturer_part_no):
|
||||
'''Make and return a new variant based on manufacturer and
|
||||
manufacturer part no'''
|
||||
from frappe.model.naming import append_number_if_name_exists
|
||||
|
||||
variant = frappe.new_doc('Item')
|
||||
|
||||
copy_attributes_to_variant(template, variant)
|
||||
|
||||
variant.append("manufacturers", {
|
||||
"manufacturer": manufacturer,
|
||||
"manufacturer_part_no": manufacturer_part_no
|
||||
})
|
||||
|
||||
variant.item_code = append_number_if_name_exists('Item', template.name)
|
||||
|
||||
return variant
|
||||
|
||||
def validate_item_variant_attributes(item, args=None):
|
||||
if isinstance(item, basestring):
|
||||
@@ -41,30 +66,37 @@ def validate_item_variant_attributes(item, args=None):
|
||||
|
||||
if attribute.lower() in numeric_values:
|
||||
numeric_attribute = numeric_values[attribute.lower()]
|
||||
validate_is_incremental(numeric_attribute, attribute, value, item.name)
|
||||
|
||||
from_range = numeric_attribute.from_range
|
||||
to_range = numeric_attribute.to_range
|
||||
increment = numeric_attribute.increment
|
||||
else:
|
||||
attributes_list = attribute_values.get(attribute.lower(), [])
|
||||
validate_item_attribute_value(attributes_list, attribute, value, item.name)
|
||||
|
||||
if increment == 0:
|
||||
# defensive validation to prevent ZeroDivisionError
|
||||
frappe.throw(_("Increment for Attribute {0} cannot be 0").format(attribute))
|
||||
def validate_is_incremental(numeric_attribute, attribute, value, item):
|
||||
from_range = numeric_attribute.from_range
|
||||
to_range = numeric_attribute.to_range
|
||||
increment = numeric_attribute.increment
|
||||
|
||||
is_in_range = from_range <= flt(value) <= to_range
|
||||
precision = max(len(cstr(v).split(".")[-1].rstrip("0")) for v in (value, increment))
|
||||
#avoid precision error by rounding the remainder
|
||||
remainder = flt((flt(value) - from_range) % increment, precision)
|
||||
if increment == 0:
|
||||
# defensive validation to prevent ZeroDivisionError
|
||||
frappe.throw(_("Increment for Attribute {0} cannot be 0").format(attribute))
|
||||
|
||||
is_incremental = remainder==0 or remainder==increment
|
||||
is_in_range = from_range <= flt(value) <= to_range
|
||||
precision = max(len(cstr(v).split(".")[-1].rstrip("0")) for v in (value, increment))
|
||||
#avoid precision error by rounding the remainder
|
||||
remainder = flt((flt(value) - from_range) % increment, precision)
|
||||
|
||||
if not (is_in_range and is_incremental):
|
||||
frappe.throw(_("Value for Attribute {0} must be within the range of {1} to {2} in the increments of {3} for Item {4}")\
|
||||
.format(attribute, from_range, to_range, increment, item.name),
|
||||
InvalidItemAttributeValueError, title=_('Invalid Attribute'))
|
||||
is_incremental = remainder==0 or remainder==increment
|
||||
|
||||
elif value not in attribute_values.get(attribute.lower(), []):
|
||||
frappe.throw(_("Value {0} for Attribute {1} does not exist in the list of valid Item Attribute Values for Item {2}").format(
|
||||
value, attribute, item.name), InvalidItemAttributeValueError, title=_('Invalid Attribute'))
|
||||
if not (is_in_range and is_incremental):
|
||||
frappe.throw(_("Value for Attribute {0} must be within the range of {1} to {2} in the increments of {3} for Item {4}")\
|
||||
.format(attribute, from_range, to_range, increment, item),
|
||||
InvalidItemAttributeValueError, title=_('Invalid Attribute'))
|
||||
|
||||
def validate_item_attribute_value(attributes_list, attribute, attribute_value, item):
|
||||
if attribute_value not in attributes_list:
|
||||
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():
|
||||
if not frappe.flags.attribute_values:
|
||||
@@ -124,6 +156,7 @@ def create_variant(item, args):
|
||||
|
||||
template = frappe.get_doc("Item", item)
|
||||
variant = frappe.new_doc("Item")
|
||||
variant.variant_based_on = 'Item Attribute'
|
||||
variant_attributes = []
|
||||
|
||||
for d in template.attributes:
|
||||
@@ -140,17 +173,28 @@ def create_variant(item, args):
|
||||
|
||||
def copy_attributes_to_variant(item, variant):
|
||||
from frappe.model import no_value_fields
|
||||
|
||||
# copy non no-copy fields
|
||||
|
||||
exclude_fields = ["item_code", "item_name", "show_in_website"]
|
||||
|
||||
if item.variant_based_on=='Manufacturer':
|
||||
# don't copy manufacturer values if based on part no
|
||||
exclude_fields += ['manufacturer', 'manufacturer_part_no']
|
||||
|
||||
for field in item.meta.fields:
|
||||
if field.fieldtype not in no_value_fields and (not field.no_copy)\
|
||||
and field.fieldname not in ("item_code", "item_name", "show_in_website"):
|
||||
and field.fieldname not in exclude_fields:
|
||||
if variant.get(field.fieldname) != item.get(field.fieldname):
|
||||
variant.set(field.fieldname, item.get(field.fieldname))
|
||||
variant.variant_of = item.name
|
||||
variant.has_variants = 0
|
||||
if variant.attributes:
|
||||
variant.description += "\n"
|
||||
for d in variant.attributes:
|
||||
variant.description += "<p>" + d.attribute + ": " + cstr(d.attribute_value) + "</p>"
|
||||
|
||||
if item.variant_based_on=='Item Attribute':
|
||||
if variant.attributes:
|
||||
variant.description += "\n"
|
||||
for d in variant.attributes:
|
||||
variant.description += "<p>" + d.attribute + ": " + cstr(d.attribute_value) + "</p>"
|
||||
|
||||
def make_variant_item_code(template_item_code, variant):
|
||||
"""Uses template's item code and abbreviations to make variant's item code"""
|
||||
@@ -162,7 +206,7 @@ def make_variant_item_code(template_item_code, variant):
|
||||
item_attribute = frappe.db.sql("""select i.numeric_values, v.abbr
|
||||
from `tabItem Attribute` i left join `tabItem Attribute Value` v
|
||||
on (i.name=v.parent)
|
||||
where i.name=%(attribute)s and v.attribute_value=%(attribute_value)s""", {
|
||||
where i.name=%(attribute)s and (v.attribute_value=%(attribute_value)s or i.numeric_values = 1)""", {
|
||||
"attribute": attr.attribute,
|
||||
"attribute_value": attr.attribute_value
|
||||
}, as_dict=True)
|
||||
@@ -173,11 +217,8 @@ def make_variant_item_code(template_item_code, variant):
|
||||
# frappe.bold(attr.attribute_value)), title=_('Invalid Attribute'),
|
||||
# exc=InvalidItemAttributeValueError)
|
||||
|
||||
if item_attribute[0].numeric_values:
|
||||
# don't generate item code if one of the attributes is numeric
|
||||
return
|
||||
|
||||
abbreviations.append(item_attribute[0].abbr)
|
||||
abbr_or_value = cstr(attr.attribute_value) if item_attribute[0].numeric_values else item_attribute[0].abbr
|
||||
abbreviations.append(abbr_or_value)
|
||||
|
||||
if abbreviations:
|
||||
variant.item_code = "{0}-{1}".format(template_item_code, "-".join(abbreviations))
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
// License: GNU General Public License v3. See license.txt
|
||||
|
||||
cur_frm.cscript.onload = function(doc, cdt, cdn) {
|
||||
cur_frm.add_fetch('customer', 'customer_name', 'customer_name');
|
||||
cur_frm.add_fetch('supplier', 'supplier_name', 'supplier_name');
|
||||
|
||||
cur_frm.fields_dict.customer.get_query = erpnext.queries.customer;
|
||||
cur_frm.fields_dict.supplier.get_query = erpnext.queries.supplier;
|
||||
|
||||
if(cur_frm.fields_dict.lead) {
|
||||
cur_frm.fields_dict.lead.get_query = erpnext.queries.lead;
|
||||
cur_frm.add_fetch('lead', 'lead_name', 'lead_name');
|
||||
}
|
||||
|
||||
if(doc.__islocal) {
|
||||
var last_route = frappe.route_history.slice(-2, -1)[0];
|
||||
if(last_route && last_route[0]==="Form") {
|
||||
var doctype = last_route[1],
|
||||
docname = last_route.slice(2).join("/");
|
||||
|
||||
if(["Customer", "Quotation", "Sales Order", "Sales Invoice", "Delivery Note",
|
||||
"Installation Note", "Opportunity", "Warranty Claim", "Maintenance Visit",
|
||||
"Maintenance Schedule"]
|
||||
.indexOf(doctype)!==-1) {
|
||||
var refdoc = frappe.get_doc(doctype, docname);
|
||||
if((refdoc.doctype == "Quotation" && refdoc.quotation_to=="Customer") ||
|
||||
(refdoc.doctype == "Opportunity" && refdoc.enquiry_from=="Customer") ||
|
||||
!in_list(["Opportunity", "Quotation"], doctype)) {
|
||||
cur_frm.set_value("customer", refdoc.customer || refdoc.name);
|
||||
cur_frm.set_value("customer_name", refdoc.customer_name);
|
||||
if(cur_frm.doc.doctype==="Address")
|
||||
cur_frm.set_value("address_title", cur_frm.doc.customer_name);
|
||||
}
|
||||
}
|
||||
else if(["Supplier", "Supplier Quotation", "Purchase Order", "Purchase Invoice", "Purchase Receipt"]
|
||||
.indexOf(doctype)!==-1) {
|
||||
var refdoc = frappe.get_doc(doctype, docname);
|
||||
cur_frm.set_value("supplier", refdoc.supplier || refdoc.name);
|
||||
cur_frm.set_value("supplier_name", refdoc.supplier_name);
|
||||
if(cur_frm.doc.doctype==="Address")
|
||||
cur_frm.set_value("address_title", cur_frm.doc.supplier_name);
|
||||
}
|
||||
else if(["Lead", "Opportunity", "Quotation"]
|
||||
.indexOf(doctype)!==-1) {
|
||||
var refdoc = frappe.get_doc(doctype, docname);
|
||||
|
||||
if((refdoc.doctype == "Quotation" && refdoc.quotation_to=="Lead") ||
|
||||
(refdoc.doctype == "Opportunity" && refdoc.enquiry_from=="Lead") || (doctype=="Lead")) {
|
||||
cur_frm.set_value("lead", refdoc.lead || refdoc.name);
|
||||
cur_frm.set_value("lead_name", refdoc.customer_name || refdoc.company_name || refdoc.lead_name);
|
||||
if(cur_frm.doc.doctype==="Address")
|
||||
cur_frm.set_value("address_title", cur_frm.doc.lead_name);
|
||||
}
|
||||
}
|
||||
else if(doctype == "Sales Partner") {
|
||||
cur_frm.set_value("sales_partner", docname);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,14 +16,13 @@ def print_settings_for_item_table(doc):
|
||||
|
||||
if doc.flags.compact_item_print:
|
||||
doc.print_templates["description"] = "templates/print_formats/includes/item_table_description.html"
|
||||
doc.hide_in_print_layout += ["item_code", "item_name", "image"]
|
||||
|
||||
doc.flags.compact_item_fields = ["description", "qty", "rate", "amount"]
|
||||
doc.flags.show_in_description = []
|
||||
doc.flags.format_columns = format_columns
|
||||
|
||||
for df in doc.meta.fields:
|
||||
if df.fieldtype not in ("Section Break", "Column Break", "Button"):
|
||||
if not doc.is_print_hide(df.fieldname):
|
||||
if df.fieldname not in doc.hide_in_print_layout and df.fieldname not in doc.flags.compact_item_fields:
|
||||
doc.hide_in_print_layout.append(df.fieldname)
|
||||
doc.flags.show_in_description.append(df.fieldname)
|
||||
def format_columns(display_columns, compact_fields):
|
||||
compact_fields = compact_fields + ["image", "item_code", "item_name"]
|
||||
final_columns = []
|
||||
for column in display_columns:
|
||||
if column not in compact_fields:
|
||||
final_columns.append(column)
|
||||
return final_columns
|
||||
|
||||
@@ -3,32 +3,10 @@
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.desk.reportview import get_match_cond
|
||||
from frappe.model.db_query import DatabaseQuery
|
||||
from frappe.desk.reportview import get_match_cond, get_filters_cond
|
||||
from frappe.utils import nowdate
|
||||
from collections import defaultdict
|
||||
|
||||
def get_filters_cond(doctype, filters, conditions):
|
||||
if filters:
|
||||
flt = filters
|
||||
if isinstance(filters, dict):
|
||||
filters = filters.items()
|
||||
flt = []
|
||||
for f in filters:
|
||||
if isinstance(f[1], basestring) and f[1][0] == '!':
|
||||
flt.append([doctype, f[0], '!=', f[1][1:]])
|
||||
else:
|
||||
value = frappe.db.escape(f[1]) if isinstance(f[1], basestring) else f[1]
|
||||
flt.append([doctype, f[0], '=', value])
|
||||
|
||||
query = DatabaseQuery(doctype)
|
||||
query.filters = flt
|
||||
query.conditions = conditions
|
||||
query.build_filter_conditions(flt, conditions)
|
||||
|
||||
cond = ' and ' + ' and '.join(query.conditions)
|
||||
else:
|
||||
cond = ''
|
||||
return cond
|
||||
|
||||
# searches for active employees
|
||||
def employee_query(doctype, txt, searchfield, start, page_len, filters):
|
||||
@@ -88,7 +66,7 @@ def customer_query(doctype, txt, searchfield, start, page_len, filters):
|
||||
fields = ["name", "customer_group", "territory"]
|
||||
else:
|
||||
fields = ["name", "customer_name", "customer_group", "territory"]
|
||||
|
||||
|
||||
meta = frappe.get_meta("Customer")
|
||||
fields = fields + [f for f in meta.get_search_fields() if not f in fields]
|
||||
|
||||
@@ -371,3 +349,46 @@ def get_expense_account(doctype, txt, searchfield, start, page_len, filters):
|
||||
'company': filters.get("company", ""),
|
||||
'txt': "%%%s%%" % frappe.db.escape(txt)
|
||||
})
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def warehouse_query(doctype, txt, searchfield, start, page_len, filters):
|
||||
# Should be used when item code is passed in filters.
|
||||
conditions, bin_conditions = [], []
|
||||
filter_dict = get_doctype_wise_filters(filters)
|
||||
|
||||
sub_query = """ select round(`tabBin`.actual_qty, 2) from `tabBin`
|
||||
where `tabBin`.warehouse = `tabWarehouse`.name
|
||||
{bin_conditions} """.format(
|
||||
bin_conditions=get_filters_cond(doctype, filter_dict.get("Bin"), bin_conditions))
|
||||
|
||||
response = frappe.db.sql("""select `tabWarehouse`.name,
|
||||
CONCAT_WS(" : ", "Actual Qty", ifnull( ({sub_query}), 0) ) as actual_qty
|
||||
from `tabWarehouse`
|
||||
where
|
||||
`tabWarehouse`.`{key}` like %(txt)s
|
||||
{fcond} {mcond}
|
||||
order by
|
||||
`tabWarehouse`.name desc
|
||||
limit
|
||||
%(start)s, %(page_len)s
|
||||
""".format(
|
||||
sub_query=sub_query,
|
||||
key=frappe.db.escape(searchfield),
|
||||
fcond=get_filters_cond(doctype, filter_dict.get("Warehouse"), conditions),
|
||||
mcond=get_match_cond(doctype)
|
||||
),
|
||||
{
|
||||
"txt": "%%%s%%" % frappe.db.escape(txt),
|
||||
"start": start,
|
||||
"page_len": page_len
|
||||
})
|
||||
return response
|
||||
|
||||
|
||||
def get_doctype_wise_filters(filters):
|
||||
# Helper function to seperate filters doctype_wise
|
||||
filter_dict = defaultdict(list)
|
||||
for row in filters:
|
||||
filter_dict[row[0]].append(row)
|
||||
return filter_dict
|
||||
|
||||
@@ -93,6 +93,9 @@ def make_new_document(reference_doc, date_field, posting_date):
|
||||
"next_date": get_next_date(reference_doc.next_date, mcount,cint(reference_doc.repeat_on_day_of_month))
|
||||
})
|
||||
|
||||
if new_document.meta.get_field('set_posting_time'):
|
||||
new_document.set('set_posting_time', 1)
|
||||
|
||||
# copy document fields
|
||||
for fieldname in ("owner", "recurring_type", "repeat_on_day_of_month",
|
||||
"recurring_id", "notification_email_address", "is_recurring", "end_date",
|
||||
|
||||
@@ -184,7 +184,7 @@ def make_return_doc(doctype, source_name, target_doc=None):
|
||||
doc.return_against = source.name
|
||||
doc.ignore_pricing_rule = 1
|
||||
if doctype == "Sales Invoice":
|
||||
doc.is_pos = 0
|
||||
doc.is_pos = source.is_pos
|
||||
|
||||
# look for Print Heading "Credit Note"
|
||||
if not doc.select_print_heading:
|
||||
@@ -198,6 +198,21 @@ def make_return_doc(doctype, source_name, target_doc=None):
|
||||
if tax.charge_type == "Actual":
|
||||
tax.tax_amount = -1 * tax.tax_amount
|
||||
|
||||
if doc.get("is_return"):
|
||||
if doc.doctype == 'Sales Invoice':
|
||||
doc.set('payments', [])
|
||||
for data in source.payments:
|
||||
doc.append('payments', {
|
||||
'mode_of_payment': data.mode_of_payment,
|
||||
'type': data.type,
|
||||
'amount': -1 * data.amount,
|
||||
'base_amount': -1 * data.base_amount
|
||||
})
|
||||
elif doc.doctype == 'Purchase Invoice':
|
||||
doc.paid_amount = -1 * source.paid_amount
|
||||
doc.base_paid_amount = -1 * source.base_paid_amount
|
||||
|
||||
doc.discount_amount = -1 * source.discount_amount
|
||||
doc.run_method("calculate_taxes_and_totals")
|
||||
|
||||
def update_item(source_doc, target_doc, source_parent):
|
||||
|
||||
@@ -9,6 +9,7 @@ from frappe import _, throw
|
||||
from erpnext.stock.get_item_details import get_bin_details
|
||||
from erpnext.stock.utils import get_incoming_rate
|
||||
from erpnext.stock.stock_ledger import get_valuation_rate
|
||||
from erpnext.stock.get_item_details import get_conversion_factor
|
||||
|
||||
from erpnext.controllers.stock_controller import StockController
|
||||
|
||||
@@ -34,6 +35,7 @@ class SellingController(StockController):
|
||||
super(SellingController, self).validate()
|
||||
self.validate_max_discount()
|
||||
self.validate_selling_price()
|
||||
self.set_qty_as_per_stock_uom()
|
||||
check_active_sales_items(self)
|
||||
|
||||
def set_missing_values(self, for_validate=False):
|
||||
@@ -163,6 +165,13 @@ class SellingController(StockController):
|
||||
if discount and flt(d.discount_percentage) > discount:
|
||||
frappe.throw(_("Maxiumm discount for Item {0} is {1}%").format(d.item_code, discount))
|
||||
|
||||
def set_qty_as_per_stock_uom(self):
|
||||
for d in self.get("items"):
|
||||
if d.meta.get_field("stock_qty"):
|
||||
if not d.conversion_factor:
|
||||
frappe.throw(_("Row {0}: Conversion Factor is mandatory").format(d.idx))
|
||||
d.stock_qty = flt(d.qty) * flt(d.conversion_factor)
|
||||
|
||||
def validate_selling_price(self):
|
||||
def throw_message(item_name, rate, ref_rate_field):
|
||||
frappe.throw(_("""Selling price for item {0} is lower than its {1}. Selling price should be atleast {2}""")
|
||||
@@ -211,9 +220,10 @@ class SellingController(StockController):
|
||||
il.append(frappe._dict({
|
||||
'warehouse': d.warehouse,
|
||||
'item_code': d.item_code,
|
||||
'qty': d.qty,
|
||||
'uom': d.stock_uom,
|
||||
'qty': d.stock_qty,
|
||||
'uom': d.uom,
|
||||
'stock_uom': d.stock_uom,
|
||||
'conversion_factor': d.conversion_factor,
|
||||
'batch_no': cstr(d.get("batch_no")).strip(),
|
||||
'serial_no': cstr(d.get("serial_no")).strip(),
|
||||
'name': d.name,
|
||||
@@ -282,6 +292,8 @@ class SellingController(StockController):
|
||||
sl_entries = []
|
||||
for d in self.get_item_list():
|
||||
if frappe.db.get_value("Item", d.item_code, "is_stock_item") == 1 and flt(d.qty):
|
||||
if flt(d.conversion_factor)==0.0:
|
||||
d.conversion_factor = get_conversion_factor(d.item_code, d.uom).get("conversion_factor") or 1.0
|
||||
return_rate = 0
|
||||
if cint(self.is_return) and self.return_against and self.docstatus==1:
|
||||
return_rate = self.get_incoming_rate_for_sales_return(d.item_code, self.return_against)
|
||||
|
||||
@@ -6,7 +6,6 @@ import frappe
|
||||
from frappe.utils import flt, comma_or, nowdate, getdate
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from erpnext.accounts.party_status import notify_status
|
||||
|
||||
def validate_status(status, options):
|
||||
if status not in options:
|
||||
@@ -14,15 +13,17 @@ def validate_status(status, options):
|
||||
|
||||
status_map = {
|
||||
"Lead": [
|
||||
["Converted", "has_customer"],
|
||||
["Lost Quotation", "has_lost_quotation"],
|
||||
["Opportunity", "has_opportunity"],
|
||||
["Quotation", "has_quotation"],
|
||||
["Converted", "has_customer"],
|
||||
],
|
||||
"Opportunity": [
|
||||
["Quotation", "has_quotation"],
|
||||
["Converted", "has_ordered_quotation"],
|
||||
["Lost", "eval:self.status=='Lost'"],
|
||||
["Lost", "has_lost_quotation"],
|
||||
["Closed", "eval:self.status=='Closed'"]
|
||||
|
||||
],
|
||||
"Quotation": [
|
||||
["Draft", None],
|
||||
@@ -101,6 +102,8 @@ class StatusUpdater(Document):
|
||||
|
||||
def set_status(self, update=False, status=None, update_modified=True):
|
||||
if self.is_new():
|
||||
if self.get('amended_from'):
|
||||
self.status = 'Draft'
|
||||
return
|
||||
|
||||
if self.doctype in status_map:
|
||||
@@ -283,7 +286,6 @@ class StatusUpdater(Document):
|
||||
target = frappe.get_doc(args["target_parent_dt"], args["name"])
|
||||
target.set_status(update=True)
|
||||
target.notify_update()
|
||||
notify_status(target)
|
||||
|
||||
def _update_modified(self, args, update_modified):
|
||||
args['update_modified'] = ''
|
||||
|
||||
@@ -9,9 +9,14 @@ import frappe.defaults
|
||||
from erpnext.accounts.utils import get_fiscal_year
|
||||
from erpnext.accounts.general_ledger import make_gl_entries, delete_gl_entries, process_gl_map
|
||||
from erpnext.controllers.accounts_controller import AccountsController
|
||||
from erpnext.stock.stock_ledger import get_valuation_rate
|
||||
|
||||
class StockController(AccountsController):
|
||||
def make_gl_entries(self, repost_future_gle=True):
|
||||
def validate(self):
|
||||
super(StockController, self).validate()
|
||||
self.validate_inspection()
|
||||
|
||||
def make_gl_entries(self, gl_entries=None, repost_future_gle=True, from_repost=False):
|
||||
if self.docstatus == 2:
|
||||
delete_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
|
||||
|
||||
@@ -19,8 +24,9 @@ class StockController(AccountsController):
|
||||
warehouse_account = get_warehouse_account()
|
||||
|
||||
if self.docstatus==1:
|
||||
gl_entries = self.get_gl_entries(warehouse_account)
|
||||
make_gl_entries(gl_entries)
|
||||
if not gl_entries:
|
||||
gl_entries = self.get_gl_entries(warehouse_account)
|
||||
make_gl_entries(gl_entries, from_repost=from_repost)
|
||||
|
||||
if repost_future_gle:
|
||||
items, warehouses = self.get_items_and_warehouses()
|
||||
@@ -38,32 +44,43 @@ class StockController(AccountsController):
|
||||
|
||||
gl_list = []
|
||||
warehouse_with_no_account = []
|
||||
|
||||
for detail in voucher_details:
|
||||
sle_list = sle_map.get(detail.name)
|
||||
|
||||
for item_row in voucher_details:
|
||||
sle_list = sle_map.get(item_row.name)
|
||||
if sle_list:
|
||||
for sle in sle_list:
|
||||
if warehouse_account.get(sle.warehouse):
|
||||
# from warehouse account
|
||||
|
||||
self.check_expense_account(detail)
|
||||
self.check_expense_account(item_row)
|
||||
|
||||
# If item is not a sample item
|
||||
# and ( valuation rate not mentioned in an incoming entry
|
||||
# or incoming entry not found while delivering the item),
|
||||
# try to pick valuation rate from previous sle or Item master and update in SLE
|
||||
# Otherwise, throw an exception
|
||||
|
||||
if not sle.stock_value_difference and self.doctype != "Stock Reconciliation" \
|
||||
and not item_row.get("is_sample_item"):
|
||||
|
||||
sle = self.update_stock_ledger_entries(sle)
|
||||
|
||||
gl_list.append(self.get_gl_dict({
|
||||
"account": warehouse_account[sle.warehouse]["name"],
|
||||
"against": detail.expense_account,
|
||||
"cost_center": detail.cost_center,
|
||||
"against": item_row.expense_account,
|
||||
"cost_center": item_row.cost_center,
|
||||
"remarks": self.get("remarks") or "Accounting Entry for Stock",
|
||||
"debit": flt(sle.stock_value_difference, 2),
|
||||
}, warehouse_account[sle.warehouse]["account_currency"]))
|
||||
|
||||
# to target warehouse / expense account
|
||||
gl_list.append(self.get_gl_dict({
|
||||
"account": detail.expense_account,
|
||||
"account": item_row.expense_account,
|
||||
"against": warehouse_account[sle.warehouse]["name"],
|
||||
"cost_center": detail.cost_center,
|
||||
"cost_center": item_row.cost_center,
|
||||
"remarks": self.get("remarks") or "Accounting Entry for Stock",
|
||||
"credit": flt(sle.stock_value_difference, 2),
|
||||
"project": detail.get("project") or self.get("project")
|
||||
"project": item_row.get("project") or self.get("project")
|
||||
}))
|
||||
elif sle.warehouse not in warehouse_with_no_account:
|
||||
warehouse_with_no_account.append(sle.warehouse)
|
||||
@@ -72,12 +89,32 @@ class StockController(AccountsController):
|
||||
for wh in warehouse_with_no_account:
|
||||
if frappe.db.get_value("Warehouse", wh, "company"):
|
||||
frappe.throw(_("Warehouse {0} is not linked to any account, please create/link the corresponding (Asset) account for the warehouse.").format(wh))
|
||||
|
||||
|
||||
msgprint(_("No accounting entries for the following warehouses") + ": \n" +
|
||||
"\n".join(warehouse_with_no_account))
|
||||
|
||||
return process_gl_map(gl_list)
|
||||
|
||||
def update_stock_ledger_entries(self, sle):
|
||||
sle.valuation_rate = get_valuation_rate(sle.item_code, sle.warehouse,
|
||||
self.doctype, self.name)
|
||||
|
||||
sle.stock_value = flt(sle.qty_after_transaction) * flt(sle.valuation_rate)
|
||||
sle.stock_value_difference = flt(sle.actual_qty) * flt(sle.valuation_rate)
|
||||
|
||||
if sle.name:
|
||||
frappe.db.sql("""
|
||||
update
|
||||
`tabStock Ledger Entry`
|
||||
set
|
||||
stock_value = %(stock_value)s,
|
||||
valuation_rate = %(valuation_rate)s,
|
||||
stock_value_difference = %(stock_value_difference)s
|
||||
where
|
||||
name = %(name)s""", (sle))
|
||||
|
||||
return sle
|
||||
|
||||
def get_voucher_details(self, default_expense_account, default_cost_center, sle_map):
|
||||
if self.doctype == "Stock Reconciliation":
|
||||
return [frappe._dict({ "name": voucher_detail_no, "expense_account": default_expense_account,
|
||||
@@ -125,10 +162,18 @@ class StockController(AccountsController):
|
||||
|
||||
def get_stock_ledger_details(self):
|
||||
stock_ledger = {}
|
||||
for sle in frappe.db.sql("""select warehouse, stock_value_difference,
|
||||
voucher_detail_no, item_code, posting_date, actual_qty
|
||||
from `tabStock Ledger Entry` where voucher_type=%s and voucher_no=%s""",
|
||||
(self.doctype, self.name), as_dict=True):
|
||||
stock_ledger_entries = frappe.db.sql("""
|
||||
select
|
||||
name, warehouse, stock_value_difference, valuation_rate,
|
||||
voucher_detail_no, item_code, posting_date, posting_time,
|
||||
actual_qty, qty_after_transaction
|
||||
from
|
||||
`tabStock Ledger Entry`
|
||||
where
|
||||
voucher_type=%s and voucher_no=%s
|
||||
""", (self.doctype, self.name), as_dict=True)
|
||||
|
||||
for sle in stock_ledger_entries:
|
||||
stock_ledger.setdefault(sle.voucher_detail_no, []).append(sle)
|
||||
return stock_ledger
|
||||
|
||||
@@ -211,7 +256,7 @@ class StockController(AccountsController):
|
||||
def make_gl_entries_on_cancel(self, repost_future_gle=True):
|
||||
if frappe.db.sql("""select name from `tabGL Entry` where voucher_type=%s
|
||||
and voucher_no=%s""", (self.doctype, self.name)):
|
||||
self.make_gl_entries(repost_future_gle)
|
||||
self.make_gl_entries(repost_future_gle=repost_future_gle)
|
||||
|
||||
def get_serialized_items(self):
|
||||
serialized_items = []
|
||||
@@ -234,7 +279,7 @@ class StockController(AccountsController):
|
||||
incoming_rate = incoming_rate[0][0] if incoming_rate else 0.0
|
||||
|
||||
return incoming_rate
|
||||
|
||||
|
||||
def validate_warehouse(self):
|
||||
from erpnext.stock.utils import validate_warehouse_company
|
||||
|
||||
@@ -243,7 +288,7 @@ class StockController(AccountsController):
|
||||
|
||||
for w in warehouses:
|
||||
validate_warehouse_company(w, self.company)
|
||||
|
||||
|
||||
def update_billing_percentage(self, update_modified=True):
|
||||
self._update_percent_field({
|
||||
"target_dt": self.doctype + " Item",
|
||||
@@ -254,6 +299,28 @@ class StockController(AccountsController):
|
||||
"name": self.name,
|
||||
}, update_modified)
|
||||
|
||||
def validate_inspection(self):
|
||||
'''Checks if quality inspection is set for Items that require inspection.
|
||||
On submit, throw an exception'''
|
||||
|
||||
inspection_required_fieldname = None
|
||||
if self.doctype in ["Purchase Receipt", "Purchase Invoice"]:
|
||||
inspection_required_fieldname = "inspection_required_before_purchase"
|
||||
elif self.doctype in ["Delivery Note", "Sales Invoice"]:
|
||||
inspection_required_fieldname = "inspection_required_before_delivery"
|
||||
|
||||
if not inspection_required_fieldname or \
|
||||
(self.doctype in ["Sales Invoice", "Purchase Invoice"] and not self.update_stock):
|
||||
return
|
||||
|
||||
for d in self.get('items'):
|
||||
if (frappe.db.get_value("Item", d.item_code, inspection_required_fieldname)
|
||||
and not d.quality_inspection):
|
||||
|
||||
frappe.msgprint(_("Quality Inspection required for Item {0}").format(d.item_code))
|
||||
if self.docstatus==1:
|
||||
raise frappe.ValidationError
|
||||
|
||||
def update_gl_entries_after(posting_date, posting_time, for_warehouses=None, for_items=None,
|
||||
warehouse_account=None):
|
||||
def _delete_gl_entries(voucher_type, voucher_no):
|
||||
@@ -265,7 +332,7 @@ def update_gl_entries_after(posting_date, posting_time, for_warehouses=None, for
|
||||
|
||||
future_stock_vouchers = get_future_stock_vouchers(posting_date, posting_time, for_warehouses, for_items)
|
||||
gle = get_voucherwise_gl_entries(future_stock_vouchers, posting_date)
|
||||
|
||||
|
||||
for voucher_type, voucher_no in future_stock_vouchers:
|
||||
existing_gle = gle.get((voucher_type, voucher_no), [])
|
||||
voucher_obj = frappe.get_doc(voucher_type, voucher_no)
|
||||
@@ -273,7 +340,7 @@ def update_gl_entries_after(posting_date, posting_time, for_warehouses=None, for
|
||||
if expected_gle:
|
||||
if not existing_gle or not compare_existing_and_expected_gle(existing_gle, expected_gle):
|
||||
_delete_gl_entries(voucher_type, voucher_no)
|
||||
voucher_obj.make_gl_entries(repost_future_gle=False)
|
||||
voucher_obj.make_gl_entries(gl_entries=expected_gle, repost_future_gle=False, from_repost=True)
|
||||
else:
|
||||
_delete_gl_entries(voucher_type, voucher_no)
|
||||
|
||||
@@ -328,10 +395,19 @@ def get_voucherwise_gl_entries(future_stock_vouchers, posting_date):
|
||||
return gl_entries
|
||||
|
||||
def get_warehouse_account():
|
||||
warehouse_account = frappe._dict()
|
||||
if not frappe.flags.warehouse_account_map or frappe.flags.in_test:
|
||||
warehouse_account = frappe._dict()
|
||||
|
||||
for d in frappe.db.sql("""select warehouse, name, account_currency from tabAccount
|
||||
where account_type = 'Stock' and (warehouse is not null and warehouse != ''
|
||||
and is_group != 1) and is_group=0 """, as_dict=1):
|
||||
for d in frappe.db.sql("""select
|
||||
warehouse, name, account_currency
|
||||
from
|
||||
tabAccount
|
||||
where
|
||||
account_type = 'Stock'
|
||||
and (warehouse is not null and warehouse != '')
|
||||
and is_group=0 """, as_dict=1):
|
||||
warehouse_account.setdefault(d.warehouse, d)
|
||||
return warehouse_account
|
||||
|
||||
frappe.flags.warehouse_account_map = warehouse_account
|
||||
|
||||
return frappe.flags.warehouse_account_map
|
||||
|
||||
@@ -366,8 +366,9 @@ class calculate_taxes_and_totals(object):
|
||||
# discount amount rounding loss adjustment if no taxes
|
||||
if (not taxes or self.doc.apply_discount_on == "Net Total") \
|
||||
and i == len(self.doc.get("items")) - 1:
|
||||
discount_amount_loss = flt(self.doc.total - net_total - self.doc.discount_amount,
|
||||
discount_amount_loss = flt(self.doc.net_total - net_total - self.doc.discount_amount,
|
||||
self.doc.precision("net_total"))
|
||||
|
||||
item.net_amount = flt(item.net_amount + discount_amount_loss,
|
||||
item.precision("net_amount"))
|
||||
|
||||
@@ -452,13 +453,15 @@ class calculate_taxes_and_totals(object):
|
||||
|
||||
elif self.doc.doctype == "Purchase Invoice":
|
||||
self.doc.outstanding_amount = flt(total_amount_to_pay, self.doc.precision("outstanding_amount"))
|
||||
|
||||
|
||||
def calculate_paid_amount(self):
|
||||
paid_amount = base_paid_amount = 0.0
|
||||
for payment in self.doc.get('payments'):
|
||||
payment.base_amount = flt(payment.amount * self.doc.conversion_rate)
|
||||
paid_amount += payment.amount
|
||||
base_paid_amount += payment.base_amount
|
||||
|
||||
if self.doc.is_pos:
|
||||
for payment in self.doc.get('payments'):
|
||||
payment.base_amount = flt(payment.amount * self.doc.conversion_rate)
|
||||
paid_amount += payment.amount
|
||||
base_paid_amount += payment.base_amount
|
||||
|
||||
self.doc.paid_amount = flt(paid_amount, self.doc.precision("paid_amount"))
|
||||
self.doc.base_paid_amount = flt(base_paid_amount, self.doc.precision("base_paid_amount"))
|
||||
@@ -467,7 +470,7 @@ class calculate_taxes_and_totals(object):
|
||||
self.doc.change_amount = 0.0
|
||||
self.doc.base_change_amount = 0.0
|
||||
if self.doc.paid_amount > self.doc.grand_total:
|
||||
self.doc.change_amount = flt(self.doc.paid_amount - self.doc.grand_total +
|
||||
self.doc.change_amount = flt(self.doc.paid_amount - self.doc.grand_total +
|
||||
self.doc.write_off_amount, self.doc.precision("change_amount"))
|
||||
|
||||
self.doc.base_change_amount = flt(self.doc.base_paid_amount - self.doc.base_grand_total +
|
||||
@@ -483,7 +486,7 @@ class calculate_taxes_and_totals(object):
|
||||
def calculate_margin(self, item):
|
||||
total_margin = 0.0
|
||||
if item.price_list_rate:
|
||||
if item.pricing_rule and not self.doc.ignore_pricing_rule:
|
||||
if item.pricing_rule and not self.doc.ignore_pricing_rule:
|
||||
pricing_rule = frappe.get_doc('Pricing Rule', item.pricing_rule)
|
||||
item.margin_type = pricing_rule.margin_type
|
||||
item.margin_rate_or_amount = pricing_rule.margin_rate_or_amount
|
||||
@@ -492,4 +495,4 @@ class calculate_taxes_and_totals(object):
|
||||
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
|
||||
total_margin = flt(item.price_list_rate) + flt(margin_value)
|
||||
|
||||
return total_margin
|
||||
return total_margin
|
||||
|
||||
@@ -147,9 +147,9 @@ def period_wise_columns_query(filters, trans):
|
||||
else:
|
||||
pwc = [_(filters.get("fiscal_year")) + " ("+_("Qty") + "):Float:120",
|
||||
_(filters.get("fiscal_year")) + " ("+ _("Amt") + "):Currency:120"]
|
||||
query_details = " SUM(t2.qty), SUM(t2.base_net_amount),"
|
||||
query_details = " SUM(t2.stock_qty), SUM(t2.base_net_amount),"
|
||||
|
||||
query_details += 'SUM(t2.qty), SUM(t2.base_net_amount)'
|
||||
query_details += 'SUM(t2.stock_qty), SUM(t2.base_net_amount)'
|
||||
return pwc, query_details
|
||||
|
||||
def get_period_wise_columns(bet_dates, period, pwc):
|
||||
@@ -161,7 +161,7 @@ def get_period_wise_columns(bet_dates, period, pwc):
|
||||
_(get_mon(bet_dates[0])) + "-" + _(get_mon(bet_dates[1])) + " (" + _("Amt") + "):Currency:120"]
|
||||
|
||||
def get_period_wise_query(bet_dates, trans_date, query_details):
|
||||
query_details += """SUM(IF(t1.%(trans_date)s BETWEEN '%(sd)s' AND '%(ed)s', t2.qty, NULL)),
|
||||
query_details += """SUM(IF(t1.%(trans_date)s BETWEEN '%(sd)s' AND '%(ed)s', t2.stock_qty, NULL)),
|
||||
SUM(IF(t1.%(trans_date)s BETWEEN '%(sd)s' AND '%(ed)s', t2.base_net_amount, NULL)),
|
||||
""" % {"trans_date": trans_date, "sd": bet_dates[0],"ed": bet_dates[1]}
|
||||
return query_details
|
||||
|
||||
@@ -100,11 +100,12 @@ def post_process(doctype, data):
|
||||
|
||||
def get_customers_suppliers(doctype, user):
|
||||
meta = frappe.get_meta(doctype)
|
||||
contacts = frappe.get_all("Contact", fields=["customer", "supplier", "email_id"],
|
||||
filters={"email_id": user})
|
||||
contacts = frappe.db.sql(""" select `tabContact`.email_id, `tabDynamic Link`.link_doctype, `tabDynamic Link`.link_name
|
||||
from `tabContact`, `tabDynamic Link` where
|
||||
`tabContact`.name = `tabDynamic Link`.parent and `tabContact`.email_id =%s """, user, as_dict=1)
|
||||
|
||||
customers = [c.customer for c in contacts if c.customer] if meta.get_field("customer") else None
|
||||
suppliers = [c.supplier for c in contacts if c.supplier] if meta.get_field("supplier") else None
|
||||
customers = [c.link_name for c in contacts if c.link_doctype == 'Customer'] if meta.get_field("customer") else None
|
||||
suppliers = [c.link_name for c in contacts if c.link_doctype == 'Supplier'] if meta.get_field("supplier") else None
|
||||
|
||||
return customers, suppliers
|
||||
|
||||
|
||||
Reference in New Issue
Block a user