Merge branch 'develop'
This commit is contained in:
commit
fa0adafa82
@ -1,2 +1,2 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
__version__ = '6.6.4'
|
__version__ = '6.6.5'
|
||||||
|
@ -147,6 +147,8 @@ class Account(Document):
|
|||||||
self.validate_warehouse(old_warehouse)
|
self.validate_warehouse(old_warehouse)
|
||||||
if self.warehouse:
|
if self.warehouse:
|
||||||
self.validate_warehouse(self.warehouse)
|
self.validate_warehouse(self.warehouse)
|
||||||
|
elif self.warehouse:
|
||||||
|
self.warehouse = None
|
||||||
|
|
||||||
def validate_warehouse(self, warehouse):
|
def validate_warehouse(self, warehouse):
|
||||||
if frappe.db.get_value("Stock Ledger Entry", {"warehouse": warehouse}):
|
if frappe.db.get_value("Stock Ledger Entry", {"warehouse": warehouse}):
|
||||||
|
@ -498,14 +498,17 @@ def get_default_bank_cash_account(company, voucher_type, mode_of_payment=None):
|
|||||||
if voucher_type=="Bank Entry":
|
if voucher_type=="Bank Entry":
|
||||||
account = frappe.db.get_value("Company", company, "default_bank_account")
|
account = frappe.db.get_value("Company", company, "default_bank_account")
|
||||||
if not account:
|
if not account:
|
||||||
account = frappe.db.get_value("Account", {"company": company, "account_type": "Bank", "is_group": 0})
|
account = frappe.db.get_value("Account",
|
||||||
|
{"company": company, "account_type": "Bank", "is_group": 0})
|
||||||
elif voucher_type=="Cash Entry":
|
elif voucher_type=="Cash Entry":
|
||||||
account = frappe.db.get_value("Company", company, "default_cash_account")
|
account = frappe.db.get_value("Company", company, "default_cash_account")
|
||||||
if not account:
|
if not account:
|
||||||
account = frappe.db.get_value("Account", {"company": company, "account_type": "Cash", "is_group": 0})
|
account = frappe.db.get_value("Account",
|
||||||
|
{"company": company, "account_type": "Cash", "is_group": 0})
|
||||||
|
|
||||||
if account:
|
if account:
|
||||||
account_details = frappe.db.get_value("Account", account, ["account_currency", "account_type"], as_dict=1)
|
account_details = frappe.db.get_value("Account", account,
|
||||||
|
["account_currency", "account_type"], as_dict=1)
|
||||||
return {
|
return {
|
||||||
"account": account,
|
"account": account,
|
||||||
"balance": get_balance_on(account),
|
"balance": get_balance_on(account),
|
||||||
@ -814,11 +817,19 @@ def get_account_balance_and_party_type(account, date, company, debit=None, credi
|
|||||||
return grid_values
|
return grid_values
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_exchange_rate(account, account_currency, company,
|
def get_exchange_rate(account, account_currency=None, company=None,
|
||||||
reference_type=None, reference_name=None, debit=None, credit=None, exchange_rate=None):
|
reference_type=None, reference_name=None, debit=None, credit=None, exchange_rate=None):
|
||||||
from erpnext.setup.utils import get_exchange_rate
|
from erpnext.setup.utils import get_exchange_rate
|
||||||
|
account_details = frappe.db.get_value("Account", account,
|
||||||
|
["account_type", "root_type", "account_currency", "company"], as_dict=1)
|
||||||
|
|
||||||
|
if not company:
|
||||||
|
company = account_details.company
|
||||||
|
|
||||||
|
if not account_currency:
|
||||||
|
account_currency = account_details.account_currency
|
||||||
|
|
||||||
company_currency = get_company_currency(company)
|
company_currency = get_company_currency(company)
|
||||||
account_details = frappe.db.get_value("Account", account, ["account_type", "root_type"], as_dict=1)
|
|
||||||
|
|
||||||
if account_currency != company_currency:
|
if account_currency != company_currency:
|
||||||
if reference_type in ("Sales Invoice", "Purchase Invoice") and reference_name:
|
if reference_type in ("Sales Invoice", "Purchase Invoice") and reference_name:
|
||||||
|
@ -160,6 +160,9 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
|
|||||||
me.set_dynamic_labels();
|
me.set_dynamic_labels();
|
||||||
me.calculate_taxes_and_totals();
|
me.calculate_taxes_and_totals();
|
||||||
if(callback_fn) callback_fn();
|
if(callback_fn) callback_fn();
|
||||||
|
frappe.after_ajax(function() {
|
||||||
|
cur_frm.doc.__missing_values_set = false;
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -109,7 +109,8 @@ def get_party_details(party, party_type, args=None):
|
|||||||
def get_tax_template(posting_date, args):
|
def get_tax_template(posting_date, args):
|
||||||
"""Get matching tax rule"""
|
"""Get matching tax rule"""
|
||||||
args = frappe._dict(args)
|
args = frappe._dict(args)
|
||||||
conditions = []
|
conditions = ["""(from_date is null or from_date = '' or from_date <= '{0}')
|
||||||
|
and (to_date is null or to_date = '' or to_date >= '{0}')""".format(posting_date)]
|
||||||
|
|
||||||
for key, value in args.iteritems():
|
for key, value in args.iteritems():
|
||||||
if key in "use_for_shopping_cart":
|
if key in "use_for_shopping_cart":
|
||||||
@ -117,16 +118,16 @@ def get_tax_template(posting_date, args):
|
|||||||
else:
|
else:
|
||||||
conditions.append("ifnull({0}, '') in ('', '{1}')".format(key, frappe.db.escape(cstr(value))))
|
conditions.append("ifnull({0}, '') in ('', '{1}')".format(key, frappe.db.escape(cstr(value))))
|
||||||
|
|
||||||
matching = frappe.db.sql("""select * from `tabTax Rule`
|
tax_rule = frappe.db.sql("""select * from `tabTax Rule`
|
||||||
where {0}""".format(" and ".join(conditions)), as_dict = True)
|
where {0}""".format(" and ".join(conditions)), as_dict = True)
|
||||||
|
|
||||||
if not matching:
|
if not tax_rule:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
for rule in matching:
|
for rule in tax_rule:
|
||||||
rule.no_of_keys_matched = 0
|
rule.no_of_keys_matched = 0
|
||||||
for key in args:
|
for key in args:
|
||||||
if rule.get(key): rule.no_of_keys_matched += 1
|
if rule.get(key): rule.no_of_keys_matched += 1
|
||||||
|
|
||||||
rule = sorted(matching, lambda b, a: cmp(a.no_of_keys_matched, b.no_of_keys_matched) or cmp(a.priority, b.priority))[0]
|
rule = sorted(tax_rule, lambda b, a: cmp(a.no_of_keys_matched, b.no_of_keys_matched) or cmp(a.priority, b.priority))[0]
|
||||||
return rule.sales_tax_template or rule.purchase_tax_template
|
return rule.sales_tax_template or rule.purchase_tax_template
|
||||||
|
@ -209,13 +209,12 @@ erpnext.AccountsChart = Class.extend({
|
|||||||
{fieldtype:'Check', fieldname:'is_group', label:__('Is Group'),
|
{fieldtype:'Check', fieldname:'is_group', label:__('Is Group'),
|
||||||
description: __('Further accounts can be made under Groups, but entries can be made against non-Groups')},
|
description: __('Further accounts can be made under Groups, but entries can be made against non-Groups')},
|
||||||
{fieldtype:'Select', fieldname:'account_type', label:__('Account Type'),
|
{fieldtype:'Select', fieldname:'account_type', label:__('Account Type'),
|
||||||
options: ['', 'Bank', 'Cash', 'Warehouse', 'Receivable', 'Payable',
|
options: ['', 'Bank', 'Cash', 'Warehouse', 'Tax', 'Chargeable'].join('\n'),
|
||||||
'Equity', 'Cost of Goods Sold', 'Fixed Asset', 'Expense Account',
|
|
||||||
'Income Account', 'Tax', 'Chargeable', 'Temporary'].join('\n'),
|
|
||||||
description: __("Optional. This setting will be used to filter in various transactions.") },
|
description: __("Optional. This setting will be used to filter in various transactions.") },
|
||||||
{fieldtype:'Float', fieldname:'tax_rate', label:__('Tax Rate')},
|
{fieldtype:'Float', fieldname:'tax_rate', label:__('Tax Rate')},
|
||||||
{fieldtype:'Link', fieldname:'warehouse', label:__('Warehouse'), options:"Warehouse"},
|
{fieldtype:'Link', fieldname:'warehouse', label:__('Warehouse'), options:"Warehouse"},
|
||||||
{fieldtype:'Link', fieldname:'account_currency', label:__('Currency'), options:"Currency"}
|
{fieldtype:'Link', fieldname:'account_currency', label:__('Currency'), options:"Currency",
|
||||||
|
description: __("Optional. Sets company's default currency, if not specified.")}
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ blogs.
|
|||||||
"""
|
"""
|
||||||
app_icon = "icon-th"
|
app_icon = "icon-th"
|
||||||
app_color = "#e74c3c"
|
app_color = "#e74c3c"
|
||||||
app_version = "6.6.4"
|
app_version = "6.6.5"
|
||||||
github_link = "https://github.com/frappe/erpnext"
|
github_link = "https://github.com/frappe/erpnext"
|
||||||
|
|
||||||
error_report_email = "support@erpnext.com"
|
error_report_email = "support@erpnext.com"
|
||||||
|
@ -18,24 +18,25 @@ erpnext.hr.ExpenseClaimController = frappe.ui.form.Controller.extend({
|
|||||||
jv.voucher_type = 'Bank Entry';
|
jv.voucher_type = 'Bank Entry';
|
||||||
jv.company = cur_frm.doc.company;
|
jv.company = cur_frm.doc.company;
|
||||||
jv.remark = 'Payment against Expense Claim: ' + cur_frm.doc.name;
|
jv.remark = 'Payment against Expense Claim: ' + cur_frm.doc.name;
|
||||||
jv.fiscal_year = cur_frm.doc.fiscal_year;
|
|
||||||
var expense = cur_frm.doc.expenses || [];
|
var expense = cur_frm.doc.expenses || [];
|
||||||
for(var i = 0; i < expense.length; i++){
|
for(var i = 0; i < expense.length; i++){
|
||||||
var d1 = frappe.model.add_child(jv, 'Journal Entry Account', 'accounts');
|
var d1 = frappe.model.add_child(jv, 'Journal Entry Account', 'accounts');
|
||||||
d1.debit = expense[i].sanctioned_amount;
|
|
||||||
d1.account = expense[i].default_account;
|
d1.account = expense[i].default_account;
|
||||||
|
d1.debit_in_account_currency = expense[i].sanctioned_amount;
|
||||||
d1.reference_type = cur_frm.doc.doctype;
|
d1.reference_type = cur_frm.doc.doctype;
|
||||||
d1.reference_name = cur_frm.doc.name;
|
d1.reference_name = cur_frm.doc.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
// credit to bank
|
// credit to bank
|
||||||
var d1 = frappe.model.add_child(jv, 'Journal Entry Account', 'accounts');
|
var d1 = frappe.model.add_child(jv, 'Journal Entry Account', 'accounts');
|
||||||
d1.credit = cur_frm.doc.total_sanctioned_amount;
|
d1.credit_in_account_currency = cur_frm.doc.total_sanctioned_amount;
|
||||||
d1.reference_type = cur_frm.doc.doctype;
|
d1.reference_type = cur_frm.doc.doctype;
|
||||||
d1.reference_name = cur_frm.doc.name;
|
d1.reference_name = cur_frm.doc.name;
|
||||||
if(r.message) {
|
if(r.message) {
|
||||||
d1.account = r.message.account;
|
d1.account = r.message.account;
|
||||||
d1.balance = r.message.balance;
|
d1.balance = r.message.balance;
|
||||||
|
d1.account_currency = r.message.account_currency;
|
||||||
|
d1.account_type = r.message.account_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
loaddoc('Journal Entry', jv.name);
|
loaddoc('Journal Entry', jv.name);
|
||||||
|
@ -22,11 +22,16 @@ erpnext.utils.get_party_details = function(frm, method, args, callback) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (args) {
|
if (args) {
|
||||||
args.posting_date = frm.doc.transaction_date;
|
args.posting_date = frm.doc.posting_date || frm.doc.transaction_date;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(!args) return;
|
if(!args) return;
|
||||||
|
|
||||||
|
if(frappe.meta.get_docfield(frm.doc.doctype, "taxes")) {
|
||||||
|
if(!erpnext.utils.validate_mandatory(frm, "Posting/Transaction Date",
|
||||||
|
args.posting_date, args.party_type=="Customer" ? "customer": "supplier")) return;
|
||||||
|
}
|
||||||
|
|
||||||
args.currency = frm.doc.currency;
|
args.currency = frm.doc.currency;
|
||||||
args.company = frm.doc.company;
|
args.company = frm.doc.company;
|
||||||
args.doctype = frm.doc.doctype;
|
args.doctype = frm.doc.doctype;
|
||||||
@ -64,6 +69,15 @@ erpnext.utils.get_address_display = function(frm, address_field, display_field)
|
|||||||
if(r.message){
|
if(r.message){
|
||||||
frm.set_value(display_field, r.message)
|
frm.set_value(display_field, r.message)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(frappe.meta.get_docfield(frm.doc.doctype, "taxes")) {
|
||||||
|
if(!erpnext.utils.validate_mandatory(frm, "Customer/Supplier",
|
||||||
|
frm.doc.customer || frm.doc.supplier, address_field)) return;
|
||||||
|
|
||||||
|
if(!erpnext.utils.validate_mandatory(frm, "Posting/Transaction Date",
|
||||||
|
frm.doc.posting_date || frm.doc.transaction_date, address_field)) return;
|
||||||
|
} else return;
|
||||||
|
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: "erpnext.accounts.party.set_taxes",
|
method: "erpnext.accounts.party.set_taxes",
|
||||||
args: {
|
args: {
|
||||||
@ -99,3 +113,13 @@ erpnext.utils.get_contact_details = function(frm) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
erpnext.utils.validate_mandatory = function(frm, label, value, trigger_on) {
|
||||||
|
if(!value) {
|
||||||
|
frm.doc[trigger_on] = "";
|
||||||
|
refresh_field(trigger_on);
|
||||||
|
frappe.msgprint(__("Please enter {0} first", [label]));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
@ -22,8 +22,10 @@ def delete_company_transactions(company_name):
|
|||||||
|
|
||||||
for doctype in frappe.db.sql_list("""select parent from
|
for doctype in frappe.db.sql_list("""select parent from
|
||||||
tabDocField where fieldtype='Link' and options='Company'"""):
|
tabDocField where fieldtype='Link' and options='Company'"""):
|
||||||
if doctype not in ("Account", "Cost Center", "Warehouse", "Budget Detail", "Party Account", "Employee"):
|
if doctype not in ("Account", "Cost Center", "Warehouse", "Budget Detail",
|
||||||
delete_for_doctype(doctype, company_name)
|
"Party Account", "Employee", "Sales Taxes and Charges Template",
|
||||||
|
"Purchase Taxes and Charges Template", "POS Profile"):
|
||||||
|
delete_for_doctype(doctype, company_name)
|
||||||
|
|
||||||
# Clear notification counts
|
# Clear notification counts
|
||||||
clear_notifications()
|
clear_notifications()
|
||||||
|
@ -139,7 +139,7 @@ class EmailDigest(Document):
|
|||||||
|
|
||||||
for i, e in enumerate(events):
|
for i, e in enumerate(events):
|
||||||
e.starts_on_label = format_time(e.starts_on)
|
e.starts_on_label = format_time(e.starts_on)
|
||||||
e.ends_on_label = format_time(e.ends_on)
|
e.ends_on_label = format_time(e.ends_on) if e.ends_on else None
|
||||||
e.date = formatdate(e.starts)
|
e.date = formatdate(e.starts)
|
||||||
e.link = get_url_to_form("Event", e.name)
|
e.link = get_url_to_form("Event", e.name)
|
||||||
|
|
||||||
@ -346,7 +346,7 @@ class EmailDigest(Document):
|
|||||||
self.get_next_sending()
|
self.get_next_sending()
|
||||||
|
|
||||||
def fmt_money(self, value):
|
def fmt_money(self, value):
|
||||||
return fmt_money(value, currency = self.currency)
|
return fmt_money(abs(value), currency = self.currency)
|
||||||
|
|
||||||
def send():
|
def send():
|
||||||
now_date = now_datetime().date()
|
now_date = now_datetime().date()
|
||||||
|
@ -52,6 +52,8 @@
|
|||||||
<span style="{{ label_css }}">
|
<span style="{{ label_css }}">
|
||||||
{% if e.all_day %}
|
{% if e.all_day %}
|
||||||
{{ _("All Day") }}
|
{{ _("All Day") }}
|
||||||
|
{% elif (not e.ends_on_label or e.starts_on_label == e.ends_on_label)%}
|
||||||
|
{{ e.starts_on_label }}
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ e.starts_on_label }} - {{ e.ends_on_label }}
|
{{ e.starts_on_label }} - {{ e.ends_on_label }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -735,7 +735,7 @@
|
|||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Warehouse",
|
"label": "From Warehouse",
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
"oldfieldname": "warehouse",
|
"oldfieldname": "warehouse",
|
||||||
"oldfieldtype": "Link",
|
"oldfieldtype": "Link",
|
||||||
@ -755,13 +755,15 @@
|
|||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
|
"depends_on": "",
|
||||||
|
"description": "",
|
||||||
"fieldname": "target_warehouse",
|
"fieldname": "target_warehouse",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
"label": "Target Warehouse",
|
"label": "To Warehouse (Optional)",
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
"options": "Warehouse",
|
"options": "Warehouse",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
@ -831,7 +833,7 @@
|
|||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
"label": "Available Qty at Warehouse",
|
"label": "Available Qty at From Warehouse",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"oldfieldname": "actual_qty",
|
"oldfieldname": "actual_qty",
|
||||||
"oldfieldtype": "Currency",
|
"oldfieldtype": "Currency",
|
||||||
@ -857,7 +859,7 @@
|
|||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
"label": "Available Batch Qty at Warehouse",
|
"label": "Available Batch Qty at From Warehouse",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"precision": "",
|
"precision": "",
|
||||||
@ -1160,7 +1162,7 @@
|
|||||||
"is_submittable": 0,
|
"is_submittable": 0,
|
||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"modified": "2015-10-19 03:04:50.887288",
|
"modified": "2015-10-26 02:19:18.053222",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Delivery Note Item",
|
"name": "Delivery Note Item",
|
||||||
|
@ -5,6 +5,7 @@ from __future__ import unicode_literals
|
|||||||
import frappe
|
import frappe
|
||||||
import json
|
import json
|
||||||
import urllib
|
import urllib
|
||||||
|
import itertools
|
||||||
from frappe import msgprint, _
|
from frappe import msgprint, _
|
||||||
from frappe.utils import cstr, flt, cint, getdate, now_datetime, formatdate
|
from frappe.utils import cstr, flt, cint, getdate, now_datetime, formatdate
|
||||||
from frappe.website.website_generator import WebsiteGenerator
|
from frappe.website.website_generator import WebsiteGenerator
|
||||||
@ -130,6 +131,8 @@ class Item(WebsiteGenerator):
|
|||||||
|
|
||||||
self.set_attribute_context(context)
|
self.set_attribute_context(context)
|
||||||
|
|
||||||
|
self.set_disabled_attributes(context)
|
||||||
|
|
||||||
context.parents = self.get_parents(context)
|
context.parents = self.get_parents(context)
|
||||||
|
|
||||||
return context
|
return context
|
||||||
@ -189,15 +192,63 @@ class Item(WebsiteGenerator):
|
|||||||
for attr in self.attributes:
|
for attr in self.attributes:
|
||||||
values = context.attribute_values.setdefault(attr.attribute, [])
|
values = context.attribute_values.setdefault(attr.attribute, [])
|
||||||
|
|
||||||
# get list of values defined (for sequence)
|
if cint(frappe.db.get_value("Item Attribute", attr.attribute, "numeric_values")):
|
||||||
for attr_value in frappe.db.get_all("Item Attribute Value",
|
for val in sorted(attribute_values_available.get(attr.attribute, []), key=flt):
|
||||||
fields=["attribute_value"], filters={"parent": attr.attribute}, order_by="idx asc"):
|
values.append(val)
|
||||||
|
|
||||||
if attr_value.attribute_value in attribute_values_available.get(attr.attribute, []):
|
else:
|
||||||
values.append(attr_value.attribute_value)
|
# get list of values defined (for sequence)
|
||||||
|
for attr_value in frappe.db.get_all("Item Attribute Value",
|
||||||
|
fields=["attribute_value"], filters={"parent": attr.attribute}, order_by="idx asc"):
|
||||||
|
|
||||||
|
if attr_value.attribute_value in attribute_values_available.get(attr.attribute, []):
|
||||||
|
values.append(attr_value.attribute_value)
|
||||||
|
|
||||||
context.variant_info = json.dumps(context.variants)
|
context.variant_info = json.dumps(context.variants)
|
||||||
|
|
||||||
|
def set_disabled_attributes(self, context):
|
||||||
|
"""Disable selection options of attribute combinations that do not result in a variant"""
|
||||||
|
if not self.attributes:
|
||||||
|
return
|
||||||
|
|
||||||
|
context.disabled_attributes = {}
|
||||||
|
attributes = [attr.attribute for attr in self.attributes]
|
||||||
|
|
||||||
|
def find_variant(combination):
|
||||||
|
for variant in context.variants:
|
||||||
|
if len(variant.attributes) < len(attributes):
|
||||||
|
continue
|
||||||
|
|
||||||
|
if "combination" not in variant:
|
||||||
|
ref_combination = []
|
||||||
|
|
||||||
|
for attr in variant.attributes:
|
||||||
|
idx = attributes.index(attr.attribute)
|
||||||
|
ref_combination.insert(idx, attr.attribute_value)
|
||||||
|
|
||||||
|
variant["combination"] = ref_combination
|
||||||
|
|
||||||
|
if not (set(combination) - set(variant["combination"])):
|
||||||
|
# check if the combination is a subset of a variant combination
|
||||||
|
# eg. [Blue, 0.5] is a possible combination if exists [Blue, Large, 0.5]
|
||||||
|
return True
|
||||||
|
|
||||||
|
for i, attr in enumerate(self.attributes):
|
||||||
|
if i==0:
|
||||||
|
continue
|
||||||
|
|
||||||
|
combination_source = []
|
||||||
|
|
||||||
|
# loop through previous attributes
|
||||||
|
for prev_attr in self.attributes[:i]:
|
||||||
|
combination_source.append([context.selected_attributes[prev_attr.attribute]])
|
||||||
|
|
||||||
|
combination_source.append(context.attribute_values[attr.attribute])
|
||||||
|
|
||||||
|
for combination in itertools.product(*combination_source):
|
||||||
|
if not find_variant(combination):
|
||||||
|
context.disabled_attributes.setdefault(attr.attribute, []).append(combination[-1])
|
||||||
|
|
||||||
def check_warehouse_is_set_for_stock_item(self):
|
def check_warehouse_is_set_for_stock_item(self):
|
||||||
if self.is_stock_item==1 and not self.default_warehouse and frappe.get_all("Warehouse"):
|
if self.is_stock_item==1 and not self.default_warehouse and frappe.get_all("Warehouse"):
|
||||||
frappe.msgprint(_("Default Warehouse is mandatory for stock Item."),
|
frappe.msgprint(_("Default Warehouse is mandatory for stock Item."),
|
||||||
|
@ -155,7 +155,7 @@
|
|||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
"label": "Warehouse",
|
"label": "From Warehouse",
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
"oldfieldname": "warehouse",
|
"oldfieldname": "warehouse",
|
||||||
"oldfieldtype": "Link",
|
"oldfieldtype": "Link",
|
||||||
@ -179,7 +179,7 @@
|
|||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
"label": "Target Warehouse",
|
"label": "To Warehouse (Optional)",
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
"options": "Warehouse",
|
"options": "Warehouse",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
@ -511,7 +511,7 @@
|
|||||||
"is_submittable": 0,
|
"is_submittable": 0,
|
||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"modified": "2015-10-12 07:38:58.896987",
|
"modified": "2015-10-26 02:25:47.718911",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Packed Item",
|
"name": "Packed Item",
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
|
from frappe.utils import flt, today
|
||||||
|
|
||||||
def execute(filters=None):
|
def execute(filters=None):
|
||||||
filters = frappe._dict(filters or {})
|
filters = frappe._dict(filters or {})
|
||||||
@ -18,12 +19,19 @@ def get_columns():
|
|||||||
_("Shortage Qty") + ":Float:100"]
|
_("Shortage Qty") + ":Float:100"]
|
||||||
|
|
||||||
def get_data(filters):
|
def get_data(filters):
|
||||||
item_map = {}
|
bin_list = get_bin_list(filters)
|
||||||
|
item_map = get_item_map(filters.get("item_code"))
|
||||||
warehouse_company = {}
|
warehouse_company = {}
|
||||||
data = []
|
data = []
|
||||||
|
|
||||||
for bin in get_bin_list(filters):
|
for bin in bin_list:
|
||||||
item = item_map.setdefault(bin.item_code, frappe.get_doc("Item", bin.item_code))
|
item = item_map.get(bin.item_code)
|
||||||
|
|
||||||
|
if not item:
|
||||||
|
# likely an item that has reached its end of life
|
||||||
|
continue
|
||||||
|
|
||||||
|
# item = item_map.setdefault(bin.item_code, get_item(bin.item_code))
|
||||||
company = warehouse_company.setdefault(bin.warehouse, frappe.db.get_value("Warehouse", bin.warehouse, "company"))
|
company = warehouse_company.setdefault(bin.warehouse, frappe.db.get_value("Warehouse", bin.warehouse, "company"))
|
||||||
|
|
||||||
if filters.brand and filters.brand != item.brand:
|
if filters.brand and filters.brand != item.brand:
|
||||||
@ -45,7 +53,7 @@ def get_data(filters):
|
|||||||
|
|
||||||
data.append([item.name, item.item_name, item.description, item.item_group, item.brand, bin.warehouse,
|
data.append([item.name, item.item_name, item.description, item.item_group, item.brand, bin.warehouse,
|
||||||
item.stock_uom, bin.actual_qty, bin.planned_qty, bin.indented_qty, bin.ordered_qty, bin.reserved_qty,
|
item.stock_uom, bin.actual_qty, bin.planned_qty, bin.indented_qty, bin.ordered_qty, bin.reserved_qty,
|
||||||
bin.projected_qty, re_order_level, re_order_qty, re_order_level - bin.projected_qty])
|
bin.projected_qty, re_order_level, re_order_qty, re_order_level - flt(bin.projected_qty)])
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
@ -61,3 +69,35 @@ def get_bin_list(filters):
|
|||||||
filters=bin_filters, order_by="item_code, warehouse")
|
filters=bin_filters, order_by="item_code, warehouse")
|
||||||
|
|
||||||
return bin_list
|
return bin_list
|
||||||
|
|
||||||
|
def get_item_map(item_code):
|
||||||
|
"""Optimization: get only the item doc and re_order_levels table"""
|
||||||
|
|
||||||
|
condition = ""
|
||||||
|
if item_code:
|
||||||
|
condition = 'and item_code = "{0}"'.format(frappe.db.escape(item_code))
|
||||||
|
|
||||||
|
items = frappe.db.sql("""select * from `tabItem` item
|
||||||
|
where is_stock_item = 1
|
||||||
|
{condition}
|
||||||
|
and (end_of_life > %(today)s or end_of_life is null or end_of_life='0000-00-00')
|
||||||
|
and exists (select name from `tabBin` bin where bin.item_code=item.name)"""\
|
||||||
|
.format(condition=condition), {"today": today()}, as_dict=True)
|
||||||
|
|
||||||
|
condition = ""
|
||||||
|
if item_code:
|
||||||
|
condition = 'where parent="{0}"'.format(frappe.db.escape(item_code))
|
||||||
|
|
||||||
|
reorder_levels = frappe._dict()
|
||||||
|
for ir in frappe.db.sql("""select * from `tabItem Reorder` {condition}""".format(condition=condition), as_dict=1):
|
||||||
|
if ir.parent not in reorder_levels:
|
||||||
|
reorder_levels[ir.parent] = []
|
||||||
|
|
||||||
|
reorder_levels[ir.parent].append(ir)
|
||||||
|
|
||||||
|
item_map = frappe._dict()
|
||||||
|
for item in items:
|
||||||
|
item["reorder_levels"] = reorder_levels.get(item.name) or []
|
||||||
|
item_map[item.name] = item
|
||||||
|
|
||||||
|
return item_map
|
||||||
|
@ -29,7 +29,8 @@
|
|||||||
<div class="item-attribute-selectors">
|
<div class="item-attribute-selectors">
|
||||||
{% if has_variants %}
|
{% if has_variants %}
|
||||||
{% for d in attributes %}
|
{% for d in attributes %}
|
||||||
<div class="item-view-attribute"
|
{% if attribute_values[d.attribute] -%}
|
||||||
|
<div class="item-view-attribute {% if (attribute_values[d.attribute] | len)==1 -%} hidden {%- endif %}"
|
||||||
style="margin-bottom: 10px;">
|
style="margin-bottom: 10px;">
|
||||||
<h6 class="text-muted">{{ _(d.attribute) }}</h6>
|
<h6 class="text-muted">{{ _(d.attribute) }}</h6>
|
||||||
<select class="form-control"
|
<select class="form-control"
|
||||||
@ -37,12 +38,17 @@
|
|||||||
data-attribute="{{ d.attribute }}">
|
data-attribute="{{ d.attribute }}">
|
||||||
{% for value in attribute_values[d.attribute] %}
|
{% for value in attribute_values[d.attribute] %}
|
||||||
<option value="{{ value }}"
|
<option value="{{ value }}"
|
||||||
{% if selected_attributes and selected_attributes[d.attribute]==value -%} selected {%- endif %}>
|
{% if selected_attributes and selected_attributes[d.attribute]==value -%}
|
||||||
|
selected
|
||||||
|
{%- elif disabled_attributes and value in disabled_attributes.get(d.attribute, []) -%}
|
||||||
|
disabled
|
||||||
|
{%- endif %}>
|
||||||
{{ _(value) }}
|
{{ _(value) }}
|
||||||
</option>
|
</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
{%- endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
@ -64,8 +64,23 @@ frappe.ready(function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
$("[itemscope] .item-view-attribute select").on("change", function() {
|
$("[itemscope] .item-view-attribute .form-control").on("change", function() {
|
||||||
var item_code = encodeURIComponent(get_item_code());
|
try {
|
||||||
|
var item_code = encodeURIComponent(get_item_code());
|
||||||
|
} catch(e) {
|
||||||
|
// unable to find variant
|
||||||
|
// then chose the closest available one
|
||||||
|
|
||||||
|
var attribute = $(this).attr("data-attribute");
|
||||||
|
var attribute_value = $(this).val()
|
||||||
|
var item_code = update_attribute_selectors(attribute, attribute_value);
|
||||||
|
|
||||||
|
if (!item_code) {
|
||||||
|
msgprint(__("Please select some other value for {0}", [attribute]))
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (window.location.search.indexOf(item_code)!==-1) {
|
if (window.location.search.indexOf(item_code)!==-1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -83,10 +98,8 @@ var toggle_update_cart = function(qty) {
|
|||||||
|
|
||||||
function get_item_code() {
|
function get_item_code() {
|
||||||
if(window.variant_info) {
|
if(window.variant_info) {
|
||||||
attributes = {};
|
var attributes = get_selected_attributes();
|
||||||
$('[itemscope]').find(".item-view-attribute select").each(function() {
|
|
||||||
attributes[$(this).attr('data-attribute')] = $(this).val();
|
|
||||||
});
|
|
||||||
for(var i in variant_info) {
|
for(var i in variant_info) {
|
||||||
var variant = variant_info[i];
|
var variant = variant_info[i];
|
||||||
var match = true;
|
var match = true;
|
||||||
@ -106,3 +119,51 @@ function get_item_code() {
|
|||||||
return item_code;
|
return item_code;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function update_attribute_selectors(selected_attribute, selected_attribute_value) {
|
||||||
|
// find the closest match keeping the selected attribute in focus and get the item code
|
||||||
|
|
||||||
|
var attributes = get_selected_attributes();
|
||||||
|
|
||||||
|
var previous_match_score = 0;
|
||||||
|
var matched;
|
||||||
|
for(var i in variant_info) {
|
||||||
|
var variant = variant_info[i];
|
||||||
|
var match_score = 0;
|
||||||
|
var has_selected_attribute = false;
|
||||||
|
|
||||||
|
for(var j in variant.attributes) {
|
||||||
|
if(attributes[variant.attributes[j].attribute]===variant.attributes[j].attribute_value) {
|
||||||
|
match_score = match_score + 1;
|
||||||
|
|
||||||
|
if (variant.attributes[j].attribute==selected_attribute && variant.attributes[j].attribute_value==selected_attribute_value) {
|
||||||
|
has_selected_attribute = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_selected_attribute && (match_score > previous_match_score)) {
|
||||||
|
previous_match_score = match_score;
|
||||||
|
matched = variant;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matched) {
|
||||||
|
for (var j in matched.attributes) {
|
||||||
|
var attr = matched.attributes[j];
|
||||||
|
$('[itemscope]')
|
||||||
|
.find(repl('.item-view-attribute .form-control[data-attribute="%(attribute)s"]', attr))
|
||||||
|
.val(attr.attribute_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return matched.name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_selected_attributes() {
|
||||||
|
var attributes = {};
|
||||||
|
$('[itemscope]').find(".item-view-attribute .form-control").each(function() {
|
||||||
|
attributes[$(this).attr('data-attribute')] = $(this).val();
|
||||||
|
});
|
||||||
|
return attributes;
|
||||||
|
}
|
||||||
|
2
setup.py
2
setup.py
@ -1,6 +1,6 @@
|
|||||||
from setuptools import setup, find_packages
|
from setuptools import setup, find_packages
|
||||||
|
|
||||||
version = "6.6.4"
|
version = "6.6.5"
|
||||||
|
|
||||||
with open("requirements.txt", "r") as f:
|
with open("requirements.txt", "r") as f:
|
||||||
install_requires = f.readlines()
|
install_requires = f.readlines()
|
||||||
|
Loading…
Reference in New Issue
Block a user