Merge branch 'wsgi' of https://github.com/webnotes/erpnext into i18n
Conflicts:
accounts/doctype/purchase_taxes_and_charges_master/purchase_taxes_and_charges_master.js
accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.js
public/js/complete_setup.js
selling/doctype/opportunity/opportunity.js
selling/doctype/quotation/quotation.js
diff --git a/accounts/doctype/account/account.py b/accounts/doctype/account/account.py
index fba51a9..4961d72 100644
--- a/accounts/doctype/account/account.py
+++ b/accounts/doctype/account/account.py
@@ -7,7 +7,6 @@
from webnotes.utils import flt, fmt_money, cstr, cint
from webnotes import msgprint, _
-sql = webnotes.conn.sql
get_value = webnotes.conn.get_value
class DocType:
@@ -31,17 +30,6 @@
self.validate_duplicate_account()
self.validate_root_details()
self.validate_mandatory()
- self.validate_warehouse_account()
-
- if not self.doc.parent_account:
- self.doc.parent_account = ''
-
- def validate(self):
- self.validate_master_name()
- self.validate_parent()
- self.validate_duplicate_account()
- self.validate_root_details()
- self.validate_mandatory()
self.validate_frozen_accounts_modifier()
if not self.doc.parent_account:
@@ -56,7 +44,7 @@
def validate_parent(self):
"""Fetch Parent Details and validation for account not to be created under ledger"""
if self.doc.parent_account:
- par = sql("""select name, group_or_ledger, is_pl_account, debit_or_credit
+ par = webnotes.conn.sql("""select name, group_or_ledger, is_pl_account, debit_or_credit
from tabAccount where name =%s""", self.doc.parent_account)
if not par:
msgprint("Parent account does not exists", raise_exception=1)
@@ -84,7 +72,7 @@
def validate_duplicate_account(self):
if self.doc.fields.get('__islocal') or not self.doc.name:
company_abbr = webnotes.conn.get_value("Company", self.doc.company, "abbr")
- if sql("""select name from tabAccount where name=%s""",
+ if webnotes.conn.sql("""select name from tabAccount where name=%s""",
(self.doc.account_name + " - " + company_abbr)):
msgprint("Account Name: %s already exists, please rename"
% self.doc.account_name, raise_exception=1)
@@ -133,7 +121,7 @@
return webnotes.conn.get_value("GL Entry", {"account": self.doc.name})
def check_if_child_exists(self):
- return sql("""select name from `tabAccount` where parent_account = %s
+ return webnotes.conn.sql("""select name from `tabAccount` where parent_account = %s
and docstatus != 2""", self.doc.name)
def validate_mandatory(self):
@@ -181,7 +169,7 @@
# Get credit limit
credit_limit_from = 'Customer'
- cr_limit = sql("""select t1.credit_limit from tabCustomer t1, `tabAccount` t2
+ cr_limit = webnotes.conn.sql("""select t1.credit_limit from tabCustomer t1, `tabAccount` t2
where t2.name=%s and t1.name = t2.master_name""", account)
credit_limit = cr_limit and flt(cr_limit[0][0]) or 0
if not credit_limit:
@@ -221,7 +209,7 @@
# rename account name
new_account_name = " - ".join(parts[:-1])
- sql("update `tabAccount` set account_name = %s where name = %s", (new_account_name, old))
+ webnotes.conn.sql("update `tabAccount` set account_name = %s where name = %s", (new_account_name, old))
if merge:
new_name = " - ".join(parts)
diff --git a/accounts/doctype/bank_reconciliation/bank_reconciliation.py b/accounts/doctype/bank_reconciliation/bank_reconciliation.py
index 479b579..132358e 100644
--- a/accounts/doctype/bank_reconciliation/bank_reconciliation.py
+++ b/accounts/doctype/bank_reconciliation/bank_reconciliation.py
@@ -10,7 +10,6 @@
from webnotes.model.bean import getlist, copy_doclist
from webnotes import msgprint
-sql = webnotes.conn.sql
@@ -23,7 +22,7 @@
msgprint("Bank Account, From Date and To Date are Mandatory")
return
- dl = sql("select t1.name, t1.cheque_no, t1.cheque_date, t2.debit, t2.credit, t1.posting_date, t2.against_account from `tabJournal Voucher` t1, `tabJournal Voucher Detail` t2 where t2.parent = t1.name and t2.account = %s and (clearance_date is null or clearance_date = '0000-00-00' or clearance_date = '') and t1.posting_date >= %s and t1.posting_date <= %s and t1.docstatus=1", (self.doc.bank_account, self.doc.from_date, self.doc.to_date))
+ dl = webnotes.conn.sql("select t1.name, t1.cheque_no, t1.cheque_date, t2.debit, t2.credit, t1.posting_date, t2.against_account from `tabJournal Voucher` t1, `tabJournal Voucher Detail` t2 where t2.parent = t1.name and t2.account = %s and (clearance_date is null or clearance_date = '0000-00-00' or clearance_date = '') and t1.posting_date >= %s and t1.posting_date <= %s and t1.docstatus=1", (self.doc.bank_account, self.doc.from_date, self.doc.to_date))
self.doclist = self.doc.clear_table(self.doclist, 'entries')
self.doc.total_amount = 0.0
@@ -47,7 +46,7 @@
msgprint("Clearance Date can not be before Cheque Date (Row #%s)" %
d.idx, raise_exception=1)
- sql("""update `tabJournal Voucher`
+ webnotes.conn.sql("""update `tabJournal Voucher`
set clearance_date = %s, modified = %s where name=%s""",
(d.clearance_date, nowdate(), d.voucher_id))
vouchers.append(d.voucher_id)
diff --git a/accounts/doctype/gl_entry/gl_entry.py b/accounts/doctype/gl_entry/gl_entry.py
index db6de44..7a73b06 100644
--- a/accounts/doctype/gl_entry/gl_entry.py
+++ b/accounts/doctype/gl_entry/gl_entry.py
@@ -107,7 +107,7 @@
_(" does not belong to the company") + ": " + self.doc.company)
def check_negative_balance(account, adv_adj=False):
- if not adv_adj:
+ if not adv_adj and account:
account_details = webnotes.conn.get_value("Account", account,
["allow_negative_balance", "debit_or_credit"], as_dict=True)
if not account_details["allow_negative_balance"]:
@@ -161,16 +161,6 @@
webnotes.conn.sql("update `tab%s` set outstanding_amount=%s where name='%s'" %
(against_voucher_type, bal, against_voucher))
-def validate_freezed_account(account, adv_adj=False):
- """Account has been freezed for other users except account manager"""
-
- freezed_account = webnotes.conn.get_value("Account", account, "freeze_account")
-
- if freezed_account == 'Yes' and not adv_adj \
- and 'Accounts Manager' not in webnotes.user.get_roles():
- webnotes.throw(_("Account") + ": " + account + _(" has been freezed. \
- Only Accounts Manager can do transaction against this account"))
-
def validate_frozen_account(account, adv_adj):
frozen_account = webnotes.conn.get_value("Account", account, "freeze_account")
if frozen_account == 'Yes' and not adv_adj:
@@ -183,4 +173,4 @@
elif frozen_accounts_modifier not in webnotes.user.get_roles():
webnotes.throw(account + _(" is a frozen account. ") +
_("To create / edit transactions against this account, you need role") + ": " +
- frozen_accounts_modifier)
\ No newline at end of file
+ frozen_accounts_modifier)
diff --git a/accounts/doctype/journal_voucher/journal_voucher.js b/accounts/doctype/journal_voucher/journal_voucher.js
index 644cec2..ae15f93 100644
--- a/accounts/doctype/journal_voucher/journal_voucher.js
+++ b/accounts/doctype/journal_voucher/journal_voucher.js
@@ -59,6 +59,50 @@
};
});
},
+
+ against_voucher: function(doc, cdt, cdn) {
+ var d = wn.model.get_doc(cdt, cdn);
+ if (d.against_voucher && !flt(d.debit)) {
+ this.get_outstanding({
+ 'doctype': 'Purchase Invoice',
+ 'docname': d.against_voucher
+ }, d)
+ }
+ },
+
+ against_invoice: function(doc, cdt, cdn) {
+ var d = wn.model.get_doc(cdt, cdn);
+ if (d.against_invoice && !flt(d.credit)) {
+ this.get_outstanding({
+ 'doctype': 'Sales Invoice',
+ 'docname': d.against_invoice
+ }, d)
+ }
+ },
+
+ against_jv: function(doc, cdt, cdn) {
+ var d = wn.model.get_doc(cdt, cdn);
+ if (d.against_jv && !flt(d.credit) && !flt(d.debit)) {
+ this.get_outstanding({
+ 'doctype': 'Journal Voucher',
+ 'docname': d.against_jv,
+ 'account': d.account
+ }, d)
+ }
+ },
+
+ get_outstanding: function(args, child) {
+ var me = this;
+ return this.frm.call({
+ child: child,
+ method: "get_outstanding",
+ args: { args: args},
+ callback: function(r) {
+ cur_frm.cscript.update_totals(me.frm.doc);
+ }
+ });
+ }
+
});
cur_frm.script_manager.make(erpnext.accounts.JournalVoucher);
@@ -88,24 +132,6 @@
if (doc.is_opening == 'Yes') unhide_field('aging_date');
}
-cur_frm.cscript.against_voucher = function(doc,cdt,cdn) {
- var d = locals[cdt][cdn];
- if (d.against_voucher && !flt(d.debit)) {
- args = {'doctype': 'Purchase Invoice', 'docname': d.against_voucher }
- return get_server_fields('get_outstanding',docstring(args),'entries',doc,cdt,cdn,1,function(r,rt) { cur_frm.cscript.update_totals(doc); });
- }
-}
-
-cur_frm.cscript.against_invoice = function(doc,cdt,cdn) {
- var d = locals[cdt][cdn];
- if (d.against_invoice && !flt(d.credit)) {
- args = {'doctype': 'Sales Invoice', 'docname': d.against_invoice }
- return get_server_fields('get_outstanding',docstring(args),'entries',doc,cdt,cdn,1,function(r,rt) { cur_frm.cscript.update_totals(doc); });
- }
-}
-
-// Update Totals
-
cur_frm.cscript.update_totals = function(doc) {
var td=0.0; var tc =0.0;
var el = getchildren('Journal Voucher Detail', doc.name, 'entries');
diff --git a/accounts/doctype/journal_voucher/journal_voucher.py b/accounts/doctype/journal_voucher/journal_voucher.py
index ed4a0d7..ade93c6 100644
--- a/accounts/doctype/journal_voucher/journal_voucher.py
+++ b/accounts/doctype/journal_voucher/journal_voucher.py
@@ -260,15 +260,6 @@
if gl_map:
make_gl_entries(gl_map, cancel=cancel, adv_adj=adv_adj)
- def get_outstanding(self, args):
- args = eval(args)
- o_s = webnotes.conn.sql("""select outstanding_amount from `tab%s` where name = %s""" %
- (args['doctype'], '%s'), args['docname'])
- if args['doctype'] == 'Purchase Invoice':
- return {'debit': o_s and flt(o_s[0][0]) or 0}
- if args['doctype'] == 'Sales Invoice':
- return {'credit': o_s and flt(o_s[0][0]) or 0}
-
def get_balance(self):
if not getlist(self.doclist,'entries'):
msgprint("Please enter atleast 1 entry in 'GL Entries' table")
@@ -434,4 +425,31 @@
where jv_detail.parent = jv.name and jv_detail.account = %s and jv.docstatus = 1
and jv.%s like %s order by jv.name desc limit %s, %s""" %
("%s", searchfield, "%s", "%s", "%s"),
- (filters["account"], "%%%s%%" % txt, start, page_len))
\ No newline at end of file
+ (filters["account"], "%%%s%%" % txt, start, page_len))
+
+@webnotes.whitelist()
+def get_outstanding(args):
+ args = eval(args)
+ if args.get("doctype") == "Journal Voucher" and args.get("account"):
+ against_jv_amount = webnotes.conn.sql("""
+ select sum(ifnull(debit, 0)) - sum(ifnull(credit, 0))
+ from `tabJournal Voucher Detail` where parent=%s and account=%s
+ and ifnull(against_invoice, '')='' and ifnull(against_voucher, '')=''
+ and ifnull(against_jv, '')=''""", (args['docname'], args['account']))
+
+ against_jv_amount = flt(against_jv_amount[0][0]) if against_jv_amount else 0
+ if against_jv_amount > 0:
+ return {"credit": against_jv_amount}
+ else:
+ return {"debit": -1* against_jv_amount}
+
+ elif args.get("doctype") == "Sales Invoice":
+ return {
+ "credit": flt(webnotes.conn.get_value("Sales Invoice", args["docname"],
+ "outstanding_amount"))
+ }
+ elif args.get("doctype") == "Purchase Invoice":
+ return {
+ "debit": flt(webnotes.conn.get_value("Purchase Invoice", args["docname"],
+ "outstanding_amount"))
+ }
diff --git a/accounts/doctype/mis_control/mis_control.py b/accounts/doctype/mis_control/mis_control.py
index 84350dc..f10e3d7 100644
--- a/accounts/doctype/mis_control/mis_control.py
+++ b/accounts/doctype/mis_control/mis_control.py
@@ -11,7 +11,6 @@
import webnotes.defaults
-sql = webnotes.conn.sql
from accounts.utils import get_balance_on, get_fiscal_year
@@ -44,7 +43,7 @@
ret['company'] = get_companies()
#--- to get fiscal year and start_date of that fiscal year -----
- res = sql("select name, year_start_date from `tabFiscal Year`")
+ res = webnotes.conn.sql("select name, year_start_date from `tabFiscal Year`")
ret['fiscal_year'] = [r[0] for r in res]
ret['start_dates'] = {}
for r in res:
@@ -52,7 +51,7 @@
#--- from month and to month (for MIS - Comparison Report) -------
month_list = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']
- fiscal_start_month = sql("select MONTH(year_start_date) from `tabFiscal Year` where name = %s",(webnotes.defaults.get_global_default("fiscal_year")))
+ fiscal_start_month = webnotes.conn.sql("select MONTH(year_start_date) from `tabFiscal Year` where name = %s",(webnotes.defaults.get_global_default("fiscal_year")))
fiscal_start_month = fiscal_start_month and fiscal_start_month[0][0] or 1
mon = ['']
for i in range(fiscal_start_month,13): mon.append(month_list[i-1])
@@ -107,7 +106,7 @@
def dates(self,fiscal_year,from_date,to_date):
import datetime
ret = ''
- start_date = cstr(sql("select year_start_date from `tabFiscal Year` where name = %s",fiscal_year)[0][0])
+ start_date = cstr(webnotes.conn.sql("select year_start_date from `tabFiscal Year` where name = %s",fiscal_year)[0][0])
st_mon = cint(from_date.split('-')[1])
ed_mon = cint(to_date.split('-')[1])
st_day = cint(from_date.split('-')[2])
@@ -152,7 +151,7 @@
def get_totals(self, args):
args = eval(args)
#msgprint(args)
- totals = sql("SELECT %s FROM %s WHERE %s %s %s %s" %(cstr(args['query_val']), cstr(args['tables']), cstr(args['company']), cstr(args['cond']), cstr(args['add_cond']), cstr(args['fil_cond'])), as_dict = 1)[0]
+ totals = webnotes.conn.sql("SELECT %s FROM %s WHERE %s %s %s %s" %(cstr(args['query_val']), cstr(args['tables']), cstr(args['company']), cstr(args['cond']), cstr(args['add_cond']), cstr(args['fil_cond'])), as_dict = 1)[0]
#msgprint(totals)
tot_keys = totals.keys()
# return in flt because JSON doesn't accept Decimal
@@ -185,7 +184,7 @@
# Get Children
# ------------
def get_children(self, parent_account, level, pl, company, fy):
- cl = sql("select distinct account_name, name, debit_or_credit, lft, rgt from `tabAccount` where ifnull(parent_account, '') = %s and ifnull(is_pl_account, 'No')=%s and company=%s and docstatus != 2 order by name asc", (parent_account, pl, company))
+ cl = webnotes.conn.sql("select distinct account_name, name, debit_or_credit, lft, rgt from `tabAccount` where ifnull(parent_account, '') = %s and ifnull(is_pl_account, 'No')=%s and company=%s and docstatus != 2 order by name asc", (parent_account, pl, company))
level0_diff = [0 for p in self.period_list]
if pl=='Yes' and level==0: # switch for income & expenses
cl = [c for c in cl]
@@ -238,7 +237,7 @@
def define_periods(self, year, period):
# get year start date
- ysd = sql("select year_start_date from `tabFiscal Year` where name=%s", year)
+ ysd = webnotes.conn.sql("select year_start_date from `tabFiscal Year` where name=%s", year)
ysd = ysd and ysd[0][0] or ''
self.ysd = ysd
diff --git a/accounts/doctype/period_closing_voucher/period_closing_voucher.py b/accounts/doctype/period_closing_voucher/period_closing_voucher.py
index 99282f5..70a4992 100644
--- a/accounts/doctype/period_closing_voucher/period_closing_voucher.py
+++ b/accounts/doctype/period_closing_voucher/period_closing_voucher.py
@@ -69,7 +69,6 @@
def get_pl_balances(self):
"""Get balance for pl accounts"""
-
return webnotes.conn.sql("""
select t1.account, sum(ifnull(t1.debit,0))-sum(ifnull(t1.credit,0)) as balance
from `tabGL Entry` t1, `tabAccount` t2
@@ -101,4 +100,4 @@
}))
from accounts.general_ledger import make_gl_entries
- make_gl_entries(gl_entries)
\ No newline at end of file
+ make_gl_entries(gl_entries)
diff --git a/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py b/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py
index c21d63f..b9ac8bd 100644
--- a/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py
+++ b/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py
@@ -8,6 +8,9 @@
class TestPeriodClosingVoucher(unittest.TestCase):
def test_closing_entry(self):
+ # clear GL Entries
+ webnotes.conn.sql("""delete from `tabGL Entry`""")
+
from accounts.doctype.journal_voucher.test_journal_voucher import test_records as jv_records
jv = webnotes.bean(copy=jv_records[2])
jv.insert()
diff --git a/accounts/doctype/pos_setting/pos_setting.txt b/accounts/doctype/pos_setting/pos_setting.txt
index 2420ad5..7eda7fd 100755
--- a/accounts/doctype/pos_setting/pos_setting.txt
+++ b/accounts/doctype/pos_setting/pos_setting.txt
@@ -2,7 +2,7 @@
{
"creation": "2013-05-24 12:15:51",
"docstatus": 0,
- "modified": "2013-08-28 19:13:42",
+ "modified": "2013-10-15 11:12:02",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -82,16 +82,6 @@
},
{
"doctype": "DocField",
- "fieldname": "conversion_rate",
- "fieldtype": "Float",
- "label": "Conversion Rate",
- "oldfieldname": "conversion_rate",
- "oldfieldtype": "Currency",
- "read_only": 0,
- "reqd": 1
- },
- {
- "doctype": "DocField",
"fieldname": "selling_price_list",
"fieldtype": "Link",
"label": "Price List",
diff --git a/accounts/doctype/purchase_invoice/purchase_invoice.js b/accounts/doctype/purchase_invoice/purchase_invoice.js
index 40e6433..ed75934 100644
--- a/accounts/doctype/purchase_invoice/purchase_invoice.js
+++ b/accounts/doctype/purchase_invoice/purchase_invoice.js
@@ -8,6 +8,7 @@
wn.provide("erpnext.accounts");
wn.require('app/accounts/doctype/purchase_taxes_and_charges_master/purchase_taxes_and_charges_master.js');
wn.require('app/buying/doctype/purchase_common/purchase_common.js');
+wn.require('app/accounts/doctype/sales_invoice/pos.js');
erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
onload: function() {
diff --git a/accounts/doctype/purchase_invoice/purchase_invoice.py b/accounts/doctype/purchase_invoice/purchase_invoice.py
index 2b84564..c65b9ac 100644
--- a/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -12,7 +12,6 @@
import webnotes.defaults
-sql = webnotes.conn.sql
from controllers.buying_controller import BuyingController
class DocType(BuyingController):
@@ -62,19 +61,23 @@
"purchase_receipt_details")
def get_credit_to(self):
- acc_head = sql("""select name, credit_days from `tabAccount`
- where (name = %s or (master_name = %s and master_type = 'supplier'))
- and docstatus != 2 and company = %s""",
- (cstr(self.doc.supplier) + " - " + self.company_abbr,
- self.doc.supplier, self.doc.company))
-
ret = {}
- if acc_head and acc_head[0][0]:
- ret['credit_to'] = acc_head[0][0]
- if not self.doc.due_date:
- ret['due_date'] = add_days(cstr(self.doc.posting_date), acc_head and cint(acc_head[0][1]) or 0)
- elif not acc_head:
- msgprint("%s does not have an Account Head in %s. You must first create it from the Supplier Master" % (self.doc.supplier, self.doc.company))
+ if self.doc.supplier:
+ acc_head = webnotes.conn.sql("""select name, credit_days from `tabAccount`
+ where (name = %s or (master_name = %s and master_type = 'supplier'))
+ and docstatus != 2 and company = %s""",
+ (cstr(self.doc.supplier) + " - " + self.company_abbr,
+ self.doc.supplier, self.doc.company))
+
+ if acc_head and acc_head[0][0]:
+ ret['credit_to'] = acc_head[0][0]
+ if not self.doc.due_date:
+ ret['due_date'] = add_days(cstr(self.doc.posting_date),
+ acc_head and cint(acc_head[0][1]) or 0)
+ elif not acc_head:
+ msgprint("%s does not have an Account Head in %s. \
+ You must first create it from the Supplier Master" % \
+ (self.doc.supplier, self.doc.company))
return ret
def set_supplier_defaults(self):
@@ -85,18 +88,10 @@
super(DocType, self).get_advances(self.doc.credit_to,
"Purchase Invoice Advance", "advance_allocation_details", "debit")
- def get_rate(self,arg):
- return get_obj('Purchase Common').get_rate(arg,self)
-
- def get_rate1(self,acc):
- rate = sql("select tax_rate from `tabAccount` where name='%s'"%(acc))
- ret={'add_tax_rate' :rate and flt(rate[0][0]) or 0 }
- return ret
-
def check_active_purchase_items(self):
for d in getlist(self.doclist, 'entries'):
if d.item_code: # extra condn coz item_code is not mandatory in PV
- valid_item = sql("select docstatus,is_purchase_item from tabItem where name = %s",d.item_code)
+ valid_item = webnotes.conn.sql("select docstatus,is_purchase_item from tabItem where name = %s",d.item_code)
if valid_item[0][0] == 2:
msgprint("Item : '%s' is Inactive, you can restore it from Trash" %(d.item_code))
raise Exception
@@ -116,7 +111,7 @@
def validate_bill_no(self):
if self.doc.bill_no and self.doc.bill_no.lower().strip() \
not in ['na', 'not applicable', 'none']:
- b_no = sql("""select bill_no, name, ifnull(is_opening,'') from `tabPurchase Invoice`
+ b_no = webnotes.conn.sql("""select bill_no, name, ifnull(is_opening,'') from `tabPurchase Invoice`
where bill_no = %s and credit_to = %s and docstatus = 1 and name != %s""",
(self.doc.bill_no, self.doc.credit_to, self.doc.name))
if b_no and cstr(b_no[0][2]) == cstr(self.doc.is_opening):
@@ -132,7 +127,7 @@
self.doc.remarks = "No Remarks"
def validate_credit_acc(self):
- acc = sql("select debit_or_credit, is_pl_account from tabAccount where name = %s",
+ acc = webnotes.conn.sql("select debit_or_credit, is_pl_account from tabAccount where name = %s",
self.doc.credit_to)
if not acc:
msgprint("Account: "+ self.doc.credit_to + "does not exist")
@@ -148,7 +143,7 @@
# ------------------------------------------------------------
def check_for_acc_head_of_supplier(self):
if self.doc.supplier and self.doc.credit_to:
- acc_head = sql("select master_name from `tabAccount` where name = %s", self.doc.credit_to)
+ acc_head = webnotes.conn.sql("select master_name from `tabAccount` where name = %s", self.doc.credit_to)
if (acc_head and cstr(acc_head[0][0]) != cstr(self.doc.supplier)) or (not acc_head and (self.doc.credit_to != cstr(self.doc.supplier) + " - " + self.company_abbr)):
msgprint("Credit To: %s do not match with Supplier: %s for Company: %s.\n If both correctly entered, please select Master Type and Master Name in account master." %(self.doc.credit_to,self.doc.supplier,self.doc.company), raise_exception=1)
@@ -160,7 +155,7 @@
for d in getlist(self.doclist,'entries'):
if d.purchase_order and not d.purchase_order in check_list and not d.purchase_receipt:
check_list.append(d.purhcase_order)
- stopped = sql("select name from `tabPurchase Order` where status = 'Stopped' and name = '%s'" % d.purchase_order)
+ stopped = webnotes.conn.sql("select name from `tabPurchase Order` where status = 'Stopped' and name = '%s'" % d.purchase_order)
if stopped:
msgprint("One cannot do any transaction against 'Purchase Order' : %s, it's status is 'Stopped'" % (d.purhcase_order))
raise Exception
@@ -260,11 +255,11 @@
def check_prev_docstatus(self):
for d in getlist(self.doclist,'entries'):
if d.purchase_order:
- submitted = sql("select name from `tabPurchase Order` where docstatus = 1 and name = '%s'" % d.purchase_order)
+ submitted = webnotes.conn.sql("select name from `tabPurchase Order` where docstatus = 1 and name = '%s'" % d.purchase_order)
if not submitted:
webnotes.throw("Purchase Order : "+ cstr(d.purchase_order) +" is not submitted")
if d.purchase_receipt:
- submitted = sql("select name from `tabPurchase Receipt` where docstatus = 1 and name = '%s'" % d.purchase_receipt)
+ submitted = webnotes.conn.sql("select name from `tabPurchase Receipt` where docstatus = 1 and name = '%s'" % d.purchase_receipt)
if not submitted:
webnotes.throw("Purchase Receipt : "+ cstr(d.purchase_receipt) +" is not submitted")
@@ -334,7 +329,7 @@
)
# tax table gl entries
- valuation_tax = 0
+ valuation_tax = {}
for tax in self.doclist.get({"parentfield": "purchase_tax_details"}):
if tax.category in ("Total", "Valuation and Total") and flt(tax.tax_amount):
gl_entries.append(
@@ -349,8 +344,11 @@
)
# accumulate valuation tax
- if tax.category in ("Valuation", "Valuation and Total") and flt(tax.tax_amount):
- valuation_tax += (tax.add_deduct_tax == "Add" and 1 or -1) * flt(tax.tax_amount)
+ if tax.category in ("Valuation", "Valuation and Total") and flt(tax.tax_amount) \
+ and tax.cost_center:
+ valuation_tax.setdefault(tax.cost_center, 0)
+ valuation_tax[tax.cost_center] += \
+ (tax.add_deduct_tax == "Add" and 1 or -1) * flt(tax.tax_amount)
# item gl entries
stock_item_and_auto_accounting_for_stock = False
@@ -391,15 +389,19 @@
if stock_item_and_auto_accounting_for_stock and valuation_tax:
# credit valuation tax amount in "Expenses Included In Valuation"
# this will balance out valuation amount included in cost of goods sold
- gl_entries.append(
- self.get_gl_dict({
- "account": self.get_company_default("expenses_included_in_valuation"),
- "cost_center": self.get_company_default("cost_center"),
- "against": self.doc.credit_to,
- "credit": valuation_tax,
- "remarks": self.doc.remarks or "Accounting Entry for Stock"
- })
- )
+ expenses_included_in_valuation = \
+ self.get_company_default("expenses_included_in_valuation")
+
+ for cost_center, amount in valuation_tax.items():
+ gl_entries.append(
+ self.get_gl_dict({
+ "account": expenses_included_in_valuation,
+ "cost_center": cost_center,
+ "against": self.doc.credit_to,
+ "credit": amount,
+ "remarks": self.doc.remarks or "Accounting Entry for Stock"
+ })
+ )
# writeoff account includes petty difference in the invoice amount
# and the amount that is paid
@@ -432,7 +434,7 @@
def update_raw_material_cost(self):
if self.sub_contracted_items:
for d in self.doclist.get({"parentfield": "entries"}):
- rm_cost = webnotes.conn.sql(""" select raw_material_cost / quantity
+ rm_cost = webnotes.conn.sql("""select raw_material_cost / quantity
from `tabBOM` where item = %s and is_default = 1 and docstatus = 1
and is_active = 1 """, (d.item_code,))
rm_cost = rm_cost and flt(rm_cost[0][0]) or 0
diff --git a/accounts/doctype/purchase_invoice/purchase_invoice.txt b/accounts/doctype/purchase_invoice/purchase_invoice.txt
index f5bdd93..8f77227 100755
--- a/accounts/doctype/purchase_invoice/purchase_invoice.txt
+++ b/accounts/doctype/purchase_invoice/purchase_invoice.txt
@@ -2,12 +2,13 @@
{
"creation": "2013-05-21 16:16:39",
"docstatus": 0,
- "modified": "2013-08-09 14:45:35",
+ "modified": "2013-10-02 14:24:55",
"modified_by": "Administrator",
"owner": "Administrator"
},
{
"allow_attach": 1,
+ "allow_import": 1,
"autoname": "naming_series:",
"doctype": "DocType",
"icon": "icon-file-text",
diff --git a/accounts/doctype/purchase_taxes_and_charges_master/purchase_taxes_and_charges_master.js b/accounts/doctype/purchase_taxes_and_charges_master/purchase_taxes_and_charges_master.js
index c8fdeaf..294923c 100644
--- a/accounts/doctype/purchase_taxes_and_charges_master/purchase_taxes_and_charges_master.js
+++ b/accounts/doctype/purchase_taxes_and_charges_master/purchase_taxes_and_charges_master.js
@@ -4,6 +4,8 @@
//
//--------- ONLOAD -------------
+wn.require("app/js/controllers/accounts.js");
+
cur_frm.cscript.onload = function(doc, cdt, cdn) {
}
@@ -134,6 +136,7 @@
}
}
+<<<<<<< HEAD
cur_frm.cscript.account_head = function(doc, cdt, cdn) {
var d = locals[cdt][cdn];
if(!d.charge_type && d.account_head){
@@ -148,6 +151,8 @@
refresh_field('account_head',d.name,'purchase_tax_details');
}
+=======
+>>>>>>> f146e8b7f52a3e46e335c0fefd92c347717b370b
cur_frm.cscript.rate = function(doc, cdt, cdn) {
var d = locals[cdt][cdn];
if(!d.charge_type && d.rate) {
diff --git a/accounts/doctype/purchase_taxes_and_charges_master/purchase_taxes_and_charges_master.py b/accounts/doctype/purchase_taxes_and_charges_master/purchase_taxes_and_charges_master.py
index a4534ae..3d003f6 100644
--- a/accounts/doctype/purchase_taxes_and_charges_master/purchase_taxes_and_charges_master.py
+++ b/accounts/doctype/purchase_taxes_and_charges_master/purchase_taxes_and_charges_master.py
@@ -8,16 +8,10 @@
from webnotes.model.bean import copy_doclist
from webnotes.model.code import get_obj
-sql = webnotes.conn.sql
class DocType:
def __init__(self, doc, doclist=[]):
self.doc = doc
- self.doclist = doclist
-
- # Get Tax Rate if account type is Tax
- # ===================================================================
- def get_rate(self, arg):
- return get_obj('Purchase Common').get_rate(arg, self)
\ No newline at end of file
+ self.doclist = doclist
\ No newline at end of file
diff --git a/accounts/doctype/sales_invoice/pos.js b/accounts/doctype/sales_invoice/pos.js
index 8837aed..c68b991 100644
--- a/accounts/doctype/sales_invoice/pos.js
+++ b/accounts/doctype/sales_invoice/pos.js
@@ -7,7 +7,7 @@
this.frm = frm;
this.wrapper.html('<div class="container">\
<div class="row">\
- <div class="customer-area col-sm-3 col-xs-6"></div>\
+ <div class="party-area col-sm-3 col-xs-6"></div>\
<div class="barcode-area col-sm-3 col-xs-6"></div>\
<div class="search-area col-sm-3 col-xs-6"></div>\
<div class="item-group-area col-sm-3 col-xs-6"></div>\
@@ -71,7 +71,18 @@
</div>\
</div>\
</div></div>');
-
+
+ if (wn.meta.has_field(cur_frm.doc.doctype, "customer")) {
+ this.party = "Customer";
+ this.price_list = this.frm.doc.selling_price_list;
+ this.sales_or_purchase = "Sales";
+ }
+ else if (wn.meta.has_field(cur_frm.doc.doctype, "supplier")) {
+ this.party = "Supplier";
+ this.price_list = this.frm.doc.buying_price_list;
+ this.sales_or_purchase = "Purchase";
+ }
+
this.make();
var me = this;
@@ -88,28 +99,29 @@
});
},
make: function() {
- this.make_customer();
+ this.make_party();
this.make_item_group();
this.make_search();
this.make_barcode();
this.make_item_list();
},
- make_customer: function() {
+ make_party: function() {
var me = this;
- this.customer = wn.ui.form.make_control({
+ this.party_field = wn.ui.form.make_control({
df: {
"fieldtype": "Link",
- "options": "Customer",
- "label": "Customer",
- "fieldname": "pos_customer",
- "placeholder": "Customer"
+ "options": this.party,
+ "label": this.party,
+ "fieldname": "pos_party",
+ "placeholder": this.party
},
- parent: this.wrapper.find(".customer-area")
+ parent: this.wrapper.find(".party-area")
});
- this.customer.make_input();
- this.customer.$input.on("change", function() {
- if(!me.customer.autocomplete_open)
- wn.model.set_value("Sales Invoice", me.frm.docname, "customer", this.value);
+ this.party_field.make_input();
+ this.party_field.$input.on("change", function() {
+ if(!me.party_field.autocomplete_open)
+ wn.model.set_value(me.frm.doctype, me.frm.docname,
+ me.party.toLowerCase(), this.value);
});
},
make_item_group: function() {
@@ -120,7 +132,7 @@
"options": "Item Group",
"label": "Item Group",
"fieldname": "pos_item_group",
- "placeholder": "Filter by Item Group"
+ "placeholder": "Item Group"
},
parent: this.wrapper.find(".item-group-area")
});
@@ -138,7 +150,7 @@
"options": "Item",
"label": "Item",
"fieldname": "pos_item",
- "placeholder": "Select Item"
+ "placeholder": "Item"
},
parent: this.wrapper.find(".search-area")
});
@@ -155,7 +167,7 @@
"fieldtype": "Data",
"label": "Barcode",
"fieldname": "pos_barcode",
- "placeholder": "Select Barcode"
+ "placeholder": "Barcode"
},
parent: this.wrapper.find(".barcode-area")
});
@@ -171,7 +183,8 @@
wn.call({
method: 'accounts.doctype.sales_invoice.pos.get_items',
args: {
- price_list: cur_frm.doc.selling_price_list,
+ sales_or_purchase: this.sales_or_purchase,
+ price_list: this.price_list,
item_group: this.item_group.$input.val(),
item: this.search.$input.val()
},
@@ -200,15 +213,18 @@
});
// if form is local then allow this function
- if (cur_frm.doc.docstatus===0) {
- $("div.pos-item").on("click", function() {
- if(!cur_frm.doc.customer) {
- msgprint("Please select customer first.");
+ $(me.wrapper).find("div.pos-item").on("click", function() {
+ if(me.frm.doc.docstatus==0) {
+ if(!me.frm.doc[me.party.toLowerCase()] && ((me.frm.doctype == "Quotation" &&
+ me.frm.doc.quotation_to == "Customer")
+ || me.frm.doctype != "Quotation")) {
+ msgprint("Please select " + me.party + " first.");
return;
}
- me.add_to_cart($(this).attr("data-item_code"));
- });
- }
+ else
+ me.add_to_cart($(this).attr("data-item_code"));
+ }
+ });
}
});
},
@@ -217,12 +233,12 @@
var caught = false;
// get no_of_items
- no_of_items = me.wrapper.find("#cart tbody").length;
-
+ var no_of_items = me.wrapper.find("#cart tbody tr").length;
+
// check whether the item is already added
if (no_of_items != 0) {
- $.each(wn.model.get_children("Sales Invoice Item", this.frm.doc.name, "entries",
- "Sales Invoice"), function(i, d) {
+ $.each(wn.model.get_children(this.frm.doctype + " Item", this.frm.doc.name,
+ this.frm.cscript.fname, this.frm.doctype), function(i, d) {
if (d.item_code == item_code)
caught = true;
});
@@ -233,15 +249,16 @@
me.update_qty(item_code, 1);
}
else {
- var child = wn.model.add_child(me.frm.doc, "Sales Invoice Item", "entries");
+ var child = wn.model.add_child(me.frm.doc, this.frm.doctype + " Item",
+ this.frm.cscript.fname);
child.item_code = item_code;
me.frm.cscript.item_code(me.frm.doc, child.doctype, child.name);
}
},
update_qty: function(item_code, qty, textbox_qty) {
var me = this;
- $.each(wn.model.get_children("Sales Invoice Item", this.frm.doc.name, "entries",
- "Sales Invoice"), function(i, d) {
+ $.each(wn.model.get_children(this.frm.doctype + " Item", this.frm.doc.name,
+ this.frm.cscript.fname, this.frm.doctype), function(i, d) {
if (d.item_code == item_code) {
if (textbox_qty) {
if (qty == 0 && d.item_code == item_code)
@@ -259,14 +276,24 @@
},
refresh: function() {
var me = this;
- this.customer.set_input(this.frm.doc.customer);
+ this.party_field.set_input(this.frm.doc[this.party.toLowerCase()]);
this.barcode.set_input("");
// add items
var $items = me.wrapper.find("#cart tbody").empty();
- $.each(wn.model.get_children("Sales Invoice Item", this.frm.doc.name, "entries",
- "Sales Invoice"), function(i, d) {
+ $.each(wn.model.get_children(this.frm.doctype + " Item", this.frm.doc.name,
+ this.frm.cscript.fname, this.frm.doctype), function(i, d) {
+
+ if (me.sales_or_purchase == "Sales") {
+ item_amount = d.export_amount;
+ rate = d.export_rate;
+ }
+ else {
+ item_amount = d.import_amount;
+ rate = d.import_rate;
+ }
+
$(repl('<tr id="%(item_code)s" data-selected="false">\
<td>%(item_code)s%(item_name)s</td>\
<td><input type="text" value="%(qty)s" \
@@ -277,16 +304,16 @@
item_code: d.item_code,
item_name: d.item_name===d.item_code ? "" : ("<br>" + d.item_name),
qty: d.qty,
- rate: format_currency(d.ref_rate, cur_frm.doc.price_list_currency),
- amount: format_currency(d.export_amount, cur_frm.doc.price_list_currency)
+ rate: format_currency(rate, me.frm.doc.currency),
+ amount: format_currency(item_amount, me.frm.doc.currency)
}
)).appendTo($items);
});
// taxes
- var taxes = wn.model.get_children("Sales Taxes and Charges", this.frm.doc.name, "other_charges",
- "Sales Invoice");
- $(".tax-table")
+ var taxes = wn.model.get_children(this.sales_or_purchase + " Taxes and Charges",
+ this.frm.doc.name, this.frm.cscript.other_fname, this.frm.doctype);
+ $(this.wrapper).find(".tax-table")
.toggle((taxes && taxes.length) ? true : false)
.find("tbody").empty();
@@ -297,30 +324,39 @@
<tr>', {
description: d.description,
rate: d.rate,
- tax_amount: format_currency(d.tax_amount, me.frm.doc.price_list_currency)
+ tax_amount: format_currency(flt(d.tax_amount)/flt(me.frm.doc.conversion_rate),
+ me.frm.doc.currency)
})).appendTo(".tax-table tbody");
});
// set totals
- this.wrapper.find(".net-total").text(format_currency(this.frm.doc.net_total_export,
- cur_frm.doc.price_list_currency));
- this.wrapper.find(".grand-total").text(format_currency(this.frm.doc.grand_total_export,
- cur_frm.doc.price_list_currency));
+ if (this.sales_or_purchase == "Sales") {
+ this.wrapper.find(".net-total").text(format_currency(this.frm.doc.net_total_export,
+ me.frm.doc.currency));
+ this.wrapper.find(".grand-total").text(format_currency(this.frm.doc.grand_total_export,
+ me.frm.doc.currency));
+ }
+ else {
+ this.wrapper.find(".net-total").text(format_currency(this.frm.doc.net_total_import,
+ me.frm.doc.currency));
+ this.wrapper.find(".grand-total").text(format_currency(this.frm.doc.grand_total_import,
+ me.frm.doc.currency));
+ }
// if form is local then only run all these functions
- if (cur_frm.doc.docstatus===0) {
- $("input.qty").on("focus", function() {
+ if (this.frm.doc.docstatus===0) {
+ $(this.wrapper).find("input.qty").on("focus", function() {
$(this).select();
});
// append quantity to the respective item after change from input box
- $("input.qty").on("change", function() {
+ $(this.wrapper).find("input.qty").on("change", function() {
var item_code = $(this).closest("tr")[0].id;
me.update_qty(item_code, $(this).val(), true);
});
// on td click toggle the highlighting of row
- $("#cart tbody tr td").on("click", function() {
+ $(this.wrapper).find("#cart tbody tr td").on("click", function() {
var row = $(this).closest("tr");
if (row.attr("data-selected") == "false") {
row.attr("class", "warning");
@@ -335,20 +371,37 @@
});
me.refresh_delete_btn();
- cur_frm.pos.barcode.$input.focus();
+ this.barcode.$input.focus();
}
// if form is submitted & cancelled then disable all input box & buttons
- if (cur_frm.doc.docstatus>=1 && cint(cur_frm.doc.is_pos)) {
- me.wrapper.find('input, button').each(function () {
+ if (this.frm.doc.docstatus>=1) {
+ $(this.wrapper).find('input, button').each(function () {
$(this).prop('disabled', true);
});
- $(".delete-items").hide();
- $(".make-payment").hide();
+ $(this.wrapper).find(".delete-items").hide();
+ $(this.wrapper).find(".make-payment").hide();
+ }
+ else {
+ $(this.wrapper).find('input, button').each(function () {
+ $(this).prop('disabled', false);
+ });
+ $(this.wrapper).find(".make-payment").show();
+ }
+
+ // Show Make Payment button only in Sales Invoice
+ if (this.frm.doctype != "Sales Invoice")
+ $(this.wrapper).find(".make-payment").hide();
+
+ // If quotation to is not Customer then remove party
+ if (this.frm.doctype == "Quotation") {
+ this.party_field.$wrapper.remove();
+ if (this.frm.doc.quotation_to == "Customer")
+ this.make_party();
}
},
refresh_delete_btn: function() {
- $(".delete-items").toggle($(".item-cart .warning").length ? true : false);
+ $(this.wrapper).find(".delete-items").toggle($(".item-cart .warning").length ? true : false);
},
add_item_thru_barcode: function() {
var me = this;
@@ -370,32 +423,32 @@
remove_selected_item: function() {
var me = this;
var selected_items = [];
- var no_of_items = $("#cart tbody tr").length;
+ var no_of_items = $(this.wrapper).find("#cart tbody tr").length;
for(var x=0; x<=no_of_items - 1; x++) {
- var row = $("#cart tbody tr:eq(" + x + ")");
+ var row = $(this.wrapper).find("#cart tbody tr:eq(" + x + ")");
if(row.attr("data-selected") == "true") {
selected_items.push(row.attr("id"));
}
}
-
- var child = wn.model.get_children("Sales Invoice Item", this.frm.doc.name, "entries",
- "Sales Invoice");
+
+ var child = wn.model.get_children(this.frm.doctype + " Item", this.frm.doc.name,
+ this.frm.cscript.fname, this.frm.doctype);
+
$.each(child, function(i, d) {
for (var i in selected_items) {
if (d.item_code == selected_items[i]) {
- // cur_frm.fields_dict["entries"].grid.grid_rows[d.idx].remove();
wn.model.clear_doc(d.doctype, d.name);
}
}
});
- cur_frm.fields_dict["entries"].grid.refresh();
- cur_frm.script_manager.trigger("calculate_taxes_and_totals");
+ this.frm.fields_dict[this.frm.cscript.fname].grid.refresh();
+ this.frm.script_manager.trigger("calculate_taxes_and_totals");
me.frm.dirty();
me.refresh();
},
make_payment: function() {
var me = this;
- var no_of_items = $("#cart tbody tr").length;
+ var no_of_items = $(this.wrapper).find("#cart tbody tr").length;
var mode_of_payment = [];
if (no_of_items == 0)
@@ -423,15 +476,15 @@
"total_amount": $(".grand-total").text()
});
dialog.show();
- cur_frm.pos.barcode.$input.focus();
+ me.barcode.$input.focus();
dialog.get_input("total_amount").prop("disabled", true);
dialog.fields_dict.pay.input.onclick = function() {
- cur_frm.set_value("mode_of_payment", dialog.get_values().mode_of_payment);
- cur_frm.set_value("paid_amount", dialog.get_values().total_amount);
- cur_frm.cscript.mode_of_payment(cur_frm.doc);
- cur_frm.save();
+ me.frm.set_value("mode_of_payment", dialog.get_values().mode_of_payment);
+ me.frm.set_value("paid_amount", dialog.get_values().total_amount);
+ me.frm.cscript.mode_of_payment(me.frm.doc);
+ me.frm.save();
dialog.hide();
me.refresh();
};
diff --git a/accounts/doctype/sales_invoice/pos.py b/accounts/doctype/sales_invoice/pos.py
index 08340f7..44fe40d 100644
--- a/accounts/doctype/sales_invoice/pos.py
+++ b/accounts/doctype/sales_invoice/pos.py
@@ -3,17 +3,21 @@
from __future__ import unicode_literals
import webnotes
-from webnotes import msgprint
@webnotes.whitelist()
-def get_items(price_list, item=None, item_group=None):
+def get_items(price_list, sales_or_purchase, item=None, item_group=None):
condition = ""
+ if sales_or_purchase == "Sales":
+ condition = "i.is_sales_item='Yes'"
+ else:
+ condition = "i.is_purchase_item='Yes'"
+
if item_group and item_group != "All Item Groups":
- condition = "and i.item_group='%s'" % item_group
+ condition += " and i.item_group='%s'" % item_group
if item:
- condition = "and i.name='%s'" % item
+ condition += " and i.name='%s'" % item
return webnotes.conn.sql("""select i.name, i.item_name, i.image,
pl_items.ref_rate, pl_items.currency
@@ -24,7 +28,7 @@
ON
pl_items.item_code=i.name
where
- i.is_sales_item='Yes'%s""" % ('%s', condition), (price_list), as_dict=1)
+ %s""" % ('%s', condition), (price_list), as_dict=1)
@webnotes.whitelist()
def get_item_from_barcode(barcode):
@@ -32,5 +36,10 @@
(barcode), as_dict=1)
@webnotes.whitelist()
+def get_item_from_serial_no(serial_no):
+ return webnotes.conn.sql("""select name, item_code from `tabSerial No` where
+ name=%s""", (serial_no), as_dict=1)
+
+@webnotes.whitelist()
def get_mode_of_payment():
return webnotes.conn.sql("""select name from `tabMode of Payment`""", as_dict=1)
\ No newline at end of file
diff --git a/accounts/doctype/sales_invoice/sales_invoice.css b/accounts/doctype/sales_invoice/sales_invoice.css
deleted file mode 100644
index e4b61b6..0000000
--- a/accounts/doctype/sales_invoice/sales_invoice.css
+++ /dev/null
@@ -1,15 +0,0 @@
-.pos-item {
- height: 200px;
- overflow: hidden;
- cursor: pointer;
- padding-left: 5px !important;
- padding-right: 5px !important;
-}
-
-.pos-bill {
- padding: 20px 5px;
- font-family: Monospace;
- border: 1px solid #eee;
- -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
- box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
-}
\ No newline at end of file
diff --git a/accounts/doctype/sales_invoice/sales_invoice.js b/accounts/doctype/sales_invoice/sales_invoice.js
index b87f6ef..a926e31 100644
--- a/accounts/doctype/sales_invoice/sales_invoice.js
+++ b/accounts/doctype/sales_invoice/sales_invoice.js
@@ -29,9 +29,10 @@
// toggle to pos view if is_pos is 1 in user_defaults
if ((cint(wn.defaults.get_user_defaults("is_pos"))===1 || cur_frm.doc.is_pos) &&
cint(wn.defaults.get_user_defaults("fs_pos_view"))===1) {
- this.frm.set_value("is_pos", 1);
- this.is_pos();
- cur_frm.cscript.toggle_pos(true);
+ if(this.frm.doc.__islocal && !this.frm.doc.amended_from) {
+ this.frm.set_value("is_pos", 1);
+ this.is_pos(function() {cur_frm.cscript.toggle_pos(true);});
+ }
}
// if document is POS then change default print format to "POS Invoice"
@@ -78,14 +79,11 @@
cur_frm.add_custom_button(wn._('Make Payment Entry'), cur_frm.cscript.make_bank_voucher);
}
- if (doc.docstatus===0) {
+ // Show buttons only when pos view is active
+ if (doc.docstatus===0 && !this.pos_active) {
cur_frm.cscript.sales_order_btn();
cur_frm.cscript.delivery_note_btn();
}
-
- // Show POS button only if it enabled from features setup
- if(cint(sys_defaults.fs_pos_view)===1)
- cur_frm.cscript.pos_btn();
},
sales_order_btn: function() {
@@ -124,62 +122,13 @@
});
});
},
-
- pos_btn: function() {
- if(cur_frm.$pos_btn)
- cur_frm.$pos_btn.remove();
-
- if(!cur_frm.pos_active) {
- var btn_label = wn._("POS View"),
- icon = "icon-desktop";
-
- cur_frm.cscript.sales_order_btn();
- cur_frm.cscript.delivery_note_btn();
- } else {
- var btn_label = wn._("Invoice View"),
- icon = "icon-file-text";
-
- if (cur_frm.doc.docstatus===0) {
- this.$delivery_note_btn.remove();
- this.$sales_order_btn.remove();
- }
- }
-
- cur_frm.$pos_btn = cur_frm.add_custom_button(btn_label, function() {
- cur_frm.cscript.toggle_pos();
- cur_frm.cscript.pos_btn();
- }, icon);
- },
-
- toggle_pos: function(show) {
- if (!this.frm.doc.selling_price_list)
- msgprint(wn._("Please select Price List"))
- else {
- if((show===true && cur_frm.pos_active) || (show===false && !cur_frm.pos_active)) return;
-
- // make pos
- if(!cur_frm.pos) {
- cur_frm.layout.add_view("pos");
- cur_frm.pos = new erpnext.POS(cur_frm.layout.views.pos, cur_frm);
- }
-
- // toggle view
- cur_frm.layout.set_view(cur_frm.pos_active ? "" : "pos");
- cur_frm.pos_active = !cur_frm.pos_active;
-
- // refresh
- if(cur_frm.pos_active)
- cur_frm.pos.refresh();
- }
- },
tc_name: function() {
this.get_terms();
},
- is_pos: function() {
+ is_pos: function(callback_fn) {
cur_frm.cscript.hide_fields(this.frm.doc);
-
if(cint(this.frm.doc.is_pos)) {
if(!this.frm.doc.company) {
this.frm.set_value("is_pos", 0);
@@ -192,6 +141,7 @@
callback: function(r) {
if(!r.exc) {
me.frm.script_manager.trigger("update_stock");
+ if(callback_fn) callback_fn()
}
}
});
@@ -256,7 +206,6 @@
'total_commission', 'advances'];
item_flds_normal = ['sales_order', 'delivery_note']
- item_flds_pos = ['serial_no', 'batch_no', 'actual_qty', 'expense_account']
if(cint(doc.is_pos) == 1) {
hide_field(par_flds);
@@ -271,7 +220,9 @@
cur_frm.fields_dict['entries'].grid.set_column_disp(item_flds_normal, true);
}
- cur_frm.fields_dict['entries'].grid.set_column_disp(item_flds_pos, (cint(doc.update_stock)==1?true:false));
+ item_flds_stock = ['serial_no', 'batch_no', 'actual_qty', 'expense_account', 'warehouse']
+ cur_frm.fields_dict['entries'].grid.set_column_disp(item_flds_stock,
+ (cint(doc.update_stock)==1 ? true : false));
// India related fields
var cp = wn.control_panel;
diff --git a/accounts/doctype/sales_invoice/sales_invoice.py b/accounts/doctype/sales_invoice/sales_invoice.py
index 12deed7..92c1680 100644
--- a/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/accounts/doctype/sales_invoice/sales_invoice.py
@@ -83,7 +83,6 @@
def on_submit(self):
if cint(self.doc.update_stock) == 1:
self.update_stock_ledger()
- self.update_serial_nos()
else:
# Check for Approving Authority
if not self.doc.recurring_id:
@@ -111,7 +110,6 @@
def on_cancel(self):
if cint(self.doc.update_stock) == 1:
self.update_stock_ledger()
- self.update_serial_nos(cancel = True)
sales_com_obj = get_obj(dt = 'Sales Common')
sales_com_obj.check_stop_sales_order(self)
@@ -195,7 +193,7 @@
pos = get_pos_settings(self.doc.company)
if pos:
- if not for_validate:
+ if not for_validate and not self.doc.customer:
self.doc.customer = pos.customer
self.set_customer_defaults()
@@ -266,13 +264,7 @@
def get_adj_percent(self, arg=''):
"""Fetch ref rate from item master as per selected price list"""
- get_obj('Sales Common').get_adj_percent(self)
-
-
- def get_rate(self,arg):
- """Get tax rate if account type is tax"""
- get_obj('Sales Common').get_rate(arg)
-
+ get_obj('Sales Common').get_adj_percent(self)
def get_comm_rate(self, sales_partner):
"""Get Commission rate of Sales Partner"""
@@ -965,8 +957,7 @@
"doctype": "Delivery Note Item",
"field_map": {
"name": "prevdoc_detail_docname",
- "parent": "prevdoc_docname",
- "parenttype": "prevdoc_doctype",
+ "parent": "against_sales_invoice",
"serial_no": "serial_no"
},
"postprocess": update_item
diff --git a/accounts/doctype/sales_invoice/sales_invoice.txt b/accounts/doctype/sales_invoice/sales_invoice.txt
index f921f24..331a503 100644
--- a/accounts/doctype/sales_invoice/sales_invoice.txt
+++ b/accounts/doctype/sales_invoice/sales_invoice.txt
@@ -2,12 +2,13 @@
{
"creation": "2013-05-24 19:29:05",
"docstatus": 0,
- "modified": "2013-09-01 05:26:13",
+ "modified": "2013-10-11 13:12:38",
"modified_by": "Administrator",
"owner": "Administrator"
},
{
"allow_attach": 1,
+ "allow_import": 1,
"autoname": "naming_series:",
"default_print_format": "Standard",
"doctype": "DocType",
@@ -225,7 +226,6 @@
"reqd": 1
},
{
- "default": "1.00",
"description": "Rate at which Customer Currency is converted to customer's base currency",
"doctype": "DocField",
"fieldname": "conversion_rate",
@@ -411,7 +411,7 @@
"doctype": "DocField",
"fieldname": "other_charges",
"fieldtype": "Table",
- "label": "Taxes and Charges1",
+ "label": "Sales Taxes and Charges",
"oldfieldname": "other_charges",
"oldfieldtype": "Table",
"options": "Sales Taxes and Charges",
diff --git a/accounts/doctype/sales_invoice/test_sales_invoice.py b/accounts/doctype/sales_invoice/test_sales_invoice.py
index dee098a..9f5b95c 100644
--- a/accounts/doctype/sales_invoice/test_sales_invoice.py
+++ b/accounts/doctype/sales_invoice/test_sales_invoice.py
@@ -641,8 +641,8 @@
return new_si
- # if yearly, test 3 repetitions, else test 13 repetitions
- count = 3 if no_of_months == 12 else 13
+ # if yearly, test 1 repetition, else test 5 repetitions
+ count = 1 if (no_of_months == 12) else 5
for i in xrange(count):
base_si = _test(i)
@@ -653,7 +653,7 @@
def test_serialized(self):
from stock.doctype.stock_entry.test_stock_entry import make_serialized_item
- from stock.doctype.stock_ledger_entry.stock_ledger_entry import get_serial_nos
+ from stock.doctype.serial_no.serial_no import get_serial_nos
se = make_serialized_item()
serial_nos = get_serial_nos(se.doclist[1].serial_no)
@@ -674,7 +674,7 @@
return si
def test_serialized_cancel(self):
- from stock.doctype.stock_ledger_entry.stock_ledger_entry import get_serial_nos
+ from stock.doctype.serial_no.serial_no import get_serial_nos
si = self.test_serialized()
si.cancel()
@@ -686,7 +686,7 @@
"delivery_document_no"))
def test_serialize_status(self):
- from stock.doctype.stock_ledger_entry.stock_ledger_entry import SerialNoStatusError, get_serial_nos
+ from stock.doctype.serial_no.serial_no import SerialNoStatusError, get_serial_nos
from stock.doctype.stock_entry.test_stock_entry import make_serialized_item
se = make_serialized_item()
diff --git a/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.js b/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.js
index 56e64bc..0e623a1 100644
--- a/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.js
+++ b/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.js
@@ -2,6 +2,9 @@
// License: GNU General Public License v3. See license.txt
//--------- ONLOAD -------------
+
+wn.require("app/js/controllers/accounts.js");
+
cur_frm.cscript.onload = function(doc, cdt, cdn) {
if(doc.doctype === "Sales Taxes and Charges Master")
erpnext.add_for_territory();
@@ -142,6 +145,7 @@
}
}
+<<<<<<< HEAD
cur_frm.cscript.account_head = function(doc, cdt, cdn) {
var d = locals[cdt][cdn];
@@ -157,6 +161,8 @@
refresh_field('account_head',d.name,'other_charges');
}
+=======
+>>>>>>> f146e8b7f52a3e46e335c0fefd92c347717b370b
cur_frm.cscript.rate = function(doc, cdt, cdn) {
var d = locals[cdt][cdn];
if(!d.charge_type && d.rate) {
diff --git a/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.py b/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.py
index 019edcb..67ba9cb 100644
--- a/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.py
+++ b/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.py
@@ -6,11 +6,7 @@
from webnotes.utils import cint
from webnotes.model.controller import DocListController
-class DocType(DocListController):
- def get_rate(self, arg):
- from webnotes.model.code import get_obj
- return get_obj('Sales Common').get_rate(arg)
-
+class DocType(DocListController):
def validate(self):
if self.doc.is_default == 1:
webnotes.conn.sql("""update `tabSales Taxes and Charges Master` set is_default = 0
diff --git a/accounts/page/accounts_browser/accounts_browser.py b/accounts/page/accounts_browser/accounts_browser.py
index 61f4bfc..7bc9549 100644
--- a/accounts/page/accounts_browser/accounts_browser.py
+++ b/accounts/page/accounts_browser/accounts_browser.py
@@ -15,7 +15,7 @@
@webnotes.whitelist()
def get_children():
- args = webnotes.form_dict
+ args = webnotes.local.form_dict
ctype, company = args['ctype'], args['comp']
# root
diff --git a/accounts/page/voucher_import_tool/voucher_import_tool.py b/accounts/page/voucher_import_tool/voucher_import_tool.py
index 9425afc..14e30be 100644
--- a/accounts/page/voucher_import_tool/voucher_import_tool.py
+++ b/accounts/page/voucher_import_tool/voucher_import_tool.py
@@ -64,10 +64,10 @@
data, start_idx = get_data(rows, company_abbr, rows[0][0])
except Exception, e:
- err_msg = webnotes.message_log and "<br>".join(webnotes.message_log) or cstr(e)
+ err_msg = webnotes.local.message_log and "<br>".join(webnotes.local.message_log) or cstr(e)
messages.append("""<p style='color: red'>%s</p>""" % (err_msg or "No message"))
webnotes.errprint(webnotes.getTraceback())
- webnotes.message_log = []
+ webnotes.local.message_log = []
return messages
return import_vouchers(common_values, data, start_idx, rows[0][0])
@@ -117,11 +117,11 @@
d = data[i][0]
if import_type == "Voucher Import: Two Accounts" and flt(d.get("amount")) == 0:
- webnotes.message_log = ["Amount not specified"]
+ webnotes.local.message_log = ["Amount not specified"]
raise Exception
elif import_type == "Voucher Import: Multiple Accounts" and \
(flt(d.get("total_debit")) == 0 or flt(d.get("total_credit")) == 0):
- webnotes.message_log = ["Total Debit and Total Credit amount can not be zero"]
+ webnotes.local.message_log = ["Total Debit and Total Credit amount can not be zero"]
raise Exception
else:
d.posting_date = parse_date(d.posting_date)
@@ -174,7 +174,7 @@
details.append(detail)
if not details:
- webnotes.message_log = ["""No accounts found.
+ webnotes.local.message_log = ["""No accounts found.
If you entered accounts correctly, please check template once"""]
raise Exception
@@ -193,12 +193,12 @@
webnotes.conn.commit()
except Exception, e:
webnotes.conn.rollback()
- err_msg = webnotes.message_log and "<br>".join(webnotes.message_log) or cstr(e)
+ err_msg = webnotes.local.message_log and "<br>".join(webnotes.local.message_log) or cstr(e)
messages.append("""<p style='color: red'>[row #%s] %s failed: %s</p>"""
% ((start_idx + 1) + i, jv.name or "", err_msg or "No message"))
messages.append("<p style='color: red'>All transactions rolled back</p>")
webnotes.errprint(webnotes.getTraceback())
- webnotes.message_log = []
+ webnotes.local.message_log = []
return messages
diff --git a/accounts/report/gross_profit/gross_profit.py b/accounts/report/gross_profit/gross_profit.py
index d9c20d5..9917b69 100644
--- a/accounts/report/gross_profit/gross_profit.py
+++ b/accounts/report/gross_profit/gross_profit.py
@@ -55,7 +55,7 @@
from `tabStock Ledger Entry`"""
if filters.get("company"):
- query += """ and company=%(company)s"""
+ query += """ where company=%(company)s"""
query += " order by item_code desc, warehouse desc, posting_date desc, posting_time desc, name desc"
diff --git a/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py b/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py
index bd0726e..1c3cef3 100644
--- a/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py
+++ b/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py
@@ -81,12 +81,12 @@
if account_head not in tax_accounts:
tax_accounts.append(account_head)
- invoice = item_tax.setdefault(parent, {})
if item_wise_tax_detail:
try:
item_wise_tax_detail = json.loads(item_wise_tax_detail)
for item, tax_amount in item_wise_tax_detail.items():
- invoice.setdefault(item, {})[account_head] = flt(tax_amount)
+ item_tax.setdefault(parent, {}).setdefault(item, {})[account_head] = \
+ flt(tax_amount[1])
except ValueError:
continue
diff --git a/accounts/report/item_wise_sales_register/item_wise_sales_register.py b/accounts/report/item_wise_sales_register/item_wise_sales_register.py
index 77fb6f2..48bc463 100644
--- a/accounts/report/item_wise_sales_register/item_wise_sales_register.py
+++ b/accounts/report/item_wise_sales_register/item_wise_sales_register.py
@@ -9,7 +9,7 @@
if not filters: filters = {}
columns = get_columns()
last_col = len(columns)
-
+
item_list = get_items(filters)
item_tax, tax_accounts = get_tax_accounts(item_list, columns)
@@ -21,7 +21,7 @@
for tax in tax_accounts:
row.append(item_tax.get(d.parent, {}).get(d.item_code, {}).get(tax, 0))
-
+
total_tax = sum(row[last_col:])
row += [total_tax, d.amount + total_tax]
@@ -71,19 +71,19 @@
tax_details = webnotes.conn.sql("""select parent, account_head, item_wise_tax_detail
from `tabSales Taxes and Charges` where parenttype = 'Sales Invoice'
and docstatus = 1 and ifnull(account_head, '') != ''
- and parent in (%s)""" % ', '.join(['%s']*len(item_list)), tuple([item.parent for item in item_list]))
+ and parent in (%s)""" % ', '.join(['%s']*len(item_list)),
+ tuple([item.parent for item in item_list]))
for parent, account_head, item_wise_tax_detail in tax_details:
if account_head not in tax_accounts:
tax_accounts.append(account_head)
-
- invoice = item_tax.setdefault(parent, {})
+
if item_wise_tax_detail:
try:
item_wise_tax_detail = json.loads(item_wise_tax_detail)
for item, tax_amount in item_wise_tax_detail.items():
- invoice.setdefault(item, {})[account_head] = flt(tax_amount)
-
+ item_tax.setdefault(parent, {}).setdefault(item, {})[account_head] = \
+ flt(tax_amount[1])
except ValueError:
continue
diff --git a/accounts/utils.py b/accounts/utils.py
index 5c6c16b..ac82312 100644
--- a/accounts/utils.py
+++ b/accounts/utils.py
@@ -108,7 +108,7 @@
@webnotes.whitelist()
def add_ac(args=None):
if not args:
- args = webnotes.form_dict
+ args = webnotes.local.form_dict
args.pop("cmd")
ac = webnotes.bean(args)
@@ -121,7 +121,7 @@
@webnotes.whitelist()
def add_cc(args=None):
if not args:
- args = webnotes.form_dict
+ args = webnotes.local.form_dict
args.pop("cmd")
cc = webnotes.bean(args)
diff --git a/buying/doctype/purchase_common/purchase_common.js b/buying/doctype/purchase_common/purchase_common.js
index 2dfe655..023c7f3 100644
--- a/buying/doctype/purchase_common/purchase_common.js
+++ b/buying/doctype/purchase_common/purchase_common.js
@@ -8,6 +8,7 @@
wn.provide("erpnext.buying");
wn.require("app/js/transaction.js");
+wn.require("app/js/controllers/accounts.js");
erpnext.buying.BuyingController = erpnext.TransactionController.extend({
onload: function() {
@@ -108,8 +109,7 @@
var item = wn.model.get_doc(cdt, cdn);
if(item.item_code) {
if(!this.validate_company_and_party("supplier")) {
- item.item_code = null;
- refresh_field("item_code", item.name, item.parentfield);
+ cur_frm.fields_dict[me.frm.cscript.fname].grid.grid_rows[item.idx - 1].remove();
} else {
return this.frm.call({
method: "buying.utils.get_item_details",
diff --git a/buying/doctype/purchase_common/purchase_common.py b/buying/doctype/purchase_common/purchase_common.py
index 9b6dc6c..8637e5f 100644
--- a/buying/doctype/purchase_common/purchase_common.py
+++ b/buying/doctype/purchase_common/purchase_common.py
@@ -10,7 +10,6 @@
from buying.utils import get_last_purchase_details
-sql = webnotes.conn.sql
from controllers.buying_controller import BuyingController
class DocType(BuyingController):
@@ -23,27 +22,20 @@
msgprint(_("You need to put at least one item in the item table."), raise_exception=True)
def get_supplier_details(self, name = ''):
- details = sql("select supplier_name,address from `tabSupplier` where name = '%s' and docstatus != 2" %(name), as_dict = 1)
+ details = webnotes.conn.sql("select supplier_name,address from `tabSupplier` where name = '%s' and docstatus != 2" %(name), as_dict = 1)
if details:
ret = {
'supplier_name' : details and details[0]['supplier_name'] or '',
'supplier_address' : details and details[0]['address'] or ''
}
# ********** get primary contact details (this is done separately coz. , in case there is no primary contact thn it would not be able to fetch customer details in case of join query)
- contact_det = sql("select contact_name, contact_no, email_id from `tabContact` where supplier = '%s' and is_supplier = 1 and is_primary_contact = 'Yes' and docstatus != 2" %(name), as_dict = 1)
+ contact_det = webnotes.conn.sql("select contact_name, contact_no, email_id from `tabContact` where supplier = '%s' and is_supplier = 1 and is_primary_contact = 'Yes' and docstatus != 2" %(name), as_dict = 1)
ret['contact_person'] = contact_det and contact_det[0]['contact_name'] or ''
return ret
else:
msgprint("Supplier : %s does not exists" % (name))
raise Exception
- # Get Available Qty at Warehouse
- def get_bin_details( self, arg = ''):
- arg = eval(arg)
- bin = sql("select projected_qty from `tabBin` where item_code = %s and warehouse = %s", (arg['item_code'], arg['warehouse']), as_dict=1)
- ret = { 'projected_qty' : bin and flt(bin[0]['projected_qty']) or 0 }
- return ret
-
def update_last_purchase_rate(self, obj, is_submit):
"""updates last_purchase_rate in item table for each item"""
@@ -70,7 +62,7 @@
# update last purchsae rate
if last_purchase_rate:
- sql("update `tabItem` set last_purchase_rate = %s where name = %s",
+ webnotes.conn.sql("update `tabItem` set last_purchase_rate = %s where name = %s",
(flt(last_purchase_rate),d.item_code))
def get_last_purchase_rate(self, obj):
@@ -107,7 +99,7 @@
raise Exception
# udpate with latest quantities
- bin = sql("select projected_qty from `tabBin` where item_code = %s and warehouse = %s", (d.item_code, d.warehouse), as_dict = 1)
+ bin = webnotes.conn.sql("select projected_qty from `tabBin` where item_code = %s and warehouse = %s", (d.item_code, d.warehouse), as_dict = 1)
f_lst ={'projected_qty': bin and flt(bin[0]['projected_qty']) or 0, 'ordered_qty': 0, 'received_qty' : 0}
if d.doctype == 'Purchase Receipt Item':
@@ -116,7 +108,7 @@
if d.fields.has_key(x):
d.fields[x] = f_lst[x]
- item = sql("select is_stock_item, is_purchase_item, is_sub_contracted_item, end_of_life from tabItem where name=%s",
+ item = webnotes.conn.sql("select is_stock_item, is_purchase_item, is_sub_contracted_item, end_of_life from tabItem where name=%s",
d.item_code)
if not item:
msgprint("Item %s does not exist in Item Master." % cstr(d.item_code), raise_exception=True)
@@ -139,7 +131,7 @@
# if is not stock item
f = [d.schedule_date, d.item_code, d.description]
- ch = sql("select is_stock_item from `tabItem` where name = '%s'"%d.item_code)
+ ch = webnotes.conn.sql("select is_stock_item from `tabItem` where name = '%s'"%d.item_code)
if ch and ch[0][0] == 'Yes':
# check for same items
@@ -165,18 +157,18 @@
# but if in Material Request uom KG it can change in PO
get_qty = (transaction == 'Material Request - Purchase Order') and 'qty * conversion_factor' or 'qty'
- qty = sql("select sum(%s) from `tab%s` where %s = '%s' and docstatus = 1 and parent != '%s'"% ( get_qty, curr_doctype, ref_tab_fname, ref_tab_dn, curr_parent_name))
+ qty = webnotes.conn.sql("select sum(%s) from `tab%s` where %s = '%s' and docstatus = 1 and parent != '%s'"% ( get_qty, curr_doctype, ref_tab_fname, ref_tab_dn, curr_parent_name))
qty = qty and flt(qty[0][0]) or 0
# get total qty of ref doctype
#--------------------
- max_qty = sql("select qty from `tab%s` where name = '%s' and docstatus = 1"% (ref_doc_tname, ref_tab_dn))
+ max_qty = webnotes.conn.sql("select qty from `tab%s` where name = '%s' and docstatus = 1"% (ref_doc_tname, ref_tab_dn))
max_qty = max_qty and flt(max_qty[0][0]) or 0
return cstr(qty)+'~~~'+cstr(max_qty)
def check_for_stopped_status(self, doctype, docname):
- stopped = sql("select name from `tab%s` where name = '%s' and status = 'Stopped'" %
+ stopped = webnotes.conn.sql("select name from `tab%s` where name = '%s' and status = 'Stopped'" %
( doctype, docname))
if stopped:
msgprint("One cannot do any transaction against %s : %s, it's status is 'Stopped'" %
@@ -184,7 +176,7 @@
def check_docstatus(self, check, doctype, docname , detail_doctype = ''):
if check == 'Next':
- submitted = sql("""select t1.name from `tab%s` t1,`tab%s` t2
+ submitted = webnotes.conn.sql("""select t1.name from `tab%s` t1,`tab%s` t2
where t1.name = t2.parent and t2.prevdoc_docname = %s and t1.docstatus = 1"""
% (doctype, detail_doctype, '%s'), docname)
if submitted:
@@ -192,23 +184,8 @@
+ _(" has already been submitted."), raise_exception=1)
if check == 'Previous':
- submitted = sql("""select name from `tab%s`
+ submitted = webnotes.conn.sql("""select name from `tab%s`
where docstatus = 1 and name = %s"""% (doctype, '%s'), docname)
if not submitted:
msgprint(cstr(doctype) + ": " + cstr(submitted[0][0])
+ _(" not submitted"), raise_exception=1)
-
- def get_rate(self, arg, obj):
- arg = eval(arg)
- rate = sql("select account_type, tax_rate from `tabAccount` where name = %s"
- , (arg['account_head']), as_dict=1)
-
- return {'rate': rate and (rate[0]['account_type'] == 'Tax' \
- and not arg['charge_type'] == 'Actual') and flt(rate[0]['tax_rate']) or 0 }
-
- def get_prevdoc_date(self, obj):
- for d in getlist(obj.doclist, obj.fname):
- if d.prevdoc_doctype and d.prevdoc_docname:
- dt = sql("select transaction_date from `tab%s` where name = %s"
- % (d.prevdoc_doctype, '%s'), (d.prevdoc_docname))
- d.prevdoc_date = dt and dt[0][0].strftime('%Y-%m-%d') or ''
\ No newline at end of file
diff --git a/buying/doctype/purchase_order/purchase_order.js b/buying/doctype/purchase_order/purchase_order.js
index fb45203..c134223 100644
--- a/buying/doctype/purchase_order/purchase_order.js
+++ b/buying/doctype/purchase_order/purchase_order.js
@@ -10,6 +10,7 @@
wn.require('app/accounts/doctype/purchase_taxes_and_charges_master/purchase_taxes_and_charges_master.js');
wn.require('app/utilities/doctype/sms_control/sms_control.js');
wn.require('app/buying/doctype/purchase_common/purchase_common.js');
+wn.require('app/accounts/doctype/sales_invoice/pos.js');
erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend({
refresh: function(doc, cdt, cdn) {
@@ -184,9 +185,9 @@
if(cl[i].prevdoc_doctype == 'Material Request' && cl[i].prevdoc_docname && prevdoc_list.indexOf(cl[i].prevdoc_docname) == -1) {
prevdoc_list.push(cl[i].prevdoc_docname);
if(prevdoc_list.length ==1)
- out += make_row(cl[i].prevdoc_doctype, cl[i].prevdoc_docname, cl[i].prevdoc_date,0);
+ out += make_row(cl[i].prevdoc_doctype, cl[i].prevdoc_docname, null,0);
else
- out += make_row('', cl[i].prevdoc_docname, cl[i].prevdoc_date,0);
+ out += make_row('', cl[i].prevdoc_docname,null,0);
}
}
}
diff --git a/buying/doctype/purchase_order/purchase_order.py b/buying/doctype/purchase_order/purchase_order.py
index 64f89e3..cf207bb 100644
--- a/buying/doctype/purchase_order/purchase_order.py
+++ b/buying/doctype/purchase_order/purchase_order.py
@@ -9,7 +9,6 @@
from webnotes.model.code import get_obj
from webnotes import msgprint
-sql = webnotes.conn.sql
from controllers.buying_controller import BuyingController
class DocType(BuyingController):
@@ -42,7 +41,6 @@
pc_obj = get_obj(dt='Purchase Common')
pc_obj.validate_for_items(self)
- pc_obj.get_prevdoc_date(self)
self.check_for_stopped_status(pc_obj)
self.validate_uom_is_integer("uom", "qty")
@@ -66,10 +64,6 @@
}
})
- # get available qty at warehouse
- def get_bin_details(self, arg = ''):
- return get_obj(dt='Purchase Common').get_bin_details(arg)
-
def get_schedule_dates(self):
for d in getlist(self.doclist, 'po_details'):
if d.prevdoc_detail_docname and not d.schedule_date:
@@ -133,8 +127,8 @@
update_bin(args)
def check_modified_date(self):
- mod_db = sql("select modified from `tabPurchase Order` where name = '%s'" % self.doc.name)
- date_diff = sql("select TIMEDIFF('%s', '%s')" % ( mod_db[0][0],cstr(self.doc.modified)))
+ mod_db = webnotes.conn.sql("select modified from `tabPurchase Order` where name = '%s'" % self.doc.name)
+ date_diff = webnotes.conn.sql("select TIMEDIFF('%s', '%s')" % ( mod_db[0][0],cstr(self.doc.modified)))
if date_diff and date_diff[0][0]:
msgprint(cstr(self.doc.doctype) +" => "+ cstr(self.doc.name) +" has been modified. Please Refresh. ")
@@ -173,7 +167,7 @@
pc_obj.check_docstatus(check = 'Next', doctype = 'Purchase Receipt', docname = self.doc.name, detail_doctype = 'Purchase Receipt Item')
# Check if Purchase Invoice has been submitted against current Purchase Order
- submitted = sql("select t1.name from `tabPurchase Invoice` t1,`tabPurchase Invoice Item` t2 where t1.name = t2.parent and t2.purchase_order = '%s' and t1.docstatus = 1" % self.doc.name)
+ submitted = webnotes.conn.sql("select t1.name from `tabPurchase Invoice` t1,`tabPurchase Invoice Item` t2 where t1.name = t2.parent and t2.purchase_order = '%s' and t1.docstatus = 1" % self.doc.name)
if submitted:
msgprint("Purchase Invoice : " + cstr(submitted[0][0]) + " has already been submitted !")
raise Exception
@@ -186,9 +180,6 @@
def on_update(self):
pass
- def get_rate(self,arg):
- return get_obj('Purchase Common').get_rate(arg,self)
-
@webnotes.whitelist()
def make_purchase_receipt(source_name, target_doclist=None):
from webnotes.model.mapper import get_mapped_doclist
diff --git a/buying/doctype/purchase_order/purchase_order.txt b/buying/doctype/purchase_order/purchase_order.txt
index d3c1620..7169aaf 100644
--- a/buying/doctype/purchase_order/purchase_order.txt
+++ b/buying/doctype/purchase_order/purchase_order.txt
@@ -2,12 +2,13 @@
{
"creation": "2013-05-21 16:16:39",
"docstatus": 0,
- "modified": "2013-09-12 18:34:54",
+ "modified": "2013-10-02 14:24:49",
"modified_by": "Administrator",
"owner": "Administrator"
},
{
"allow_attach": 1,
+ "allow_import": 1,
"autoname": "naming_series:",
"doctype": "DocType",
"document_type": "Transaction",
@@ -132,7 +133,6 @@
"fieldtype": "Date",
"in_filter": 1,
"label": "Purchase Order Date",
- "no_copy": 1,
"oldfieldname": "transaction_date",
"oldfieldtype": "Date",
"reqd": 1,
diff --git a/buying/doctype/purchase_order/test_purchase_order.py b/buying/doctype/purchase_order/test_purchase_order.py
index cef5e4a..bd5f410 100644
--- a/buying/doctype/purchase_order/test_purchase_order.py
+++ b/buying/doctype/purchase_order/test_purchase_order.py
@@ -98,11 +98,11 @@
self.assertEquals(len(po.doclist.get({"parentfield": "po_raw_material_details"})), 2)
def test_warehouse_company_validation(self):
- from controllers.buying_controller import WrongWarehouseCompany
+ from stock.utils import InvalidWarehouseCompany
po = webnotes.bean(copy=test_records[0])
po.doc.company = "_Test Company 1"
po.doc.conversion_rate = 0.0167
- self.assertRaises(WrongWarehouseCompany, po.insert)
+ self.assertRaises(InvalidWarehouseCompany, po.insert)
def test_uom_integer_validation(self):
from utilities.transaction_base import UOMMustBeIntegerError
diff --git a/buying/doctype/purchase_order_item/purchase_order_item.txt b/buying/doctype/purchase_order_item/purchase_order_item.txt
index 36b81d2..fade98f 100755
--- a/buying/doctype/purchase_order_item/purchase_order_item.txt
+++ b/buying/doctype/purchase_order_item/purchase_order_item.txt
@@ -2,7 +2,7 @@
{
"creation": "2013-05-24 19:29:06",
"docstatus": 0,
- "modified": "2013-08-07 14:44:12",
+ "modified": "2013-10-10 17:01:57",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -308,21 +308,6 @@
},
{
"doctype": "DocField",
- "fieldname": "prevdoc_date",
- "fieldtype": "Date",
- "hidden": 1,
- "in_filter": 1,
- "in_list_view": 0,
- "label": "Material Request Date",
- "no_copy": 1,
- "oldfieldname": "prevdoc_date",
- "oldfieldtype": "Date",
- "print_hide": 1,
- "read_only": 1,
- "search_index": 0
- },
- {
- "doctype": "DocField",
"fieldname": "prevdoc_detail_docname",
"fieldtype": "Data",
"hidden": 1,
diff --git a/buying/doctype/supplier/supplier.py b/buying/doctype/supplier/supplier.py
index 15e1d62..3c01633 100644
--- a/buying/doctype/supplier/supplier.py
+++ b/buying/doctype/supplier/supplier.py
@@ -9,7 +9,6 @@
from webnotes import msgprint, _
from webnotes.model.doc import make_autoname
-sql = webnotes.conn.sql
from utilities.transaction_base import TransactionBase
@@ -29,7 +28,7 @@
self.doc.name = make_autoname(self.doc.naming_series + '.#####')
def update_credit_days_limit(self):
- sql("""update tabAccount set credit_days = %s where name = %s""",
+ webnotes.conn.sql("""update tabAccount set credit_days = %s where name = %s""",
(cint(self.doc.credit_days), self.doc.name + " - " + self.get_company_abbr()))
def on_update(self):
@@ -43,7 +42,7 @@
self.update_credit_days_limit()
def get_payables_group(self):
- g = sql("select payables_group from tabCompany where name=%s", self.doc.company)
+ g = webnotes.conn.sql("select payables_group from tabCompany where name=%s", self.doc.company)
g = g and g[0][0] or ''
if not g:
msgprint("Update Company master, assign a default group for Payables")
@@ -65,14 +64,14 @@
msgprint(_("Created Group ") + ac)
def get_company_abbr(self):
- return sql("select abbr from tabCompany where name=%s", self.doc.company)[0][0]
+ return webnotes.conn.sql("select abbr from tabCompany where name=%s", self.doc.company)[0][0]
def get_parent_account(self, abbr):
if (not self.doc.supplier_type):
msgprint("Supplier Type is mandatory")
raise Exception
- if not sql("select name from tabAccount where name=%s and debit_or_credit = 'Credit' and ifnull(is_pl_account, 'No') = 'No'", (self.doc.supplier_type + " - " + abbr)):
+ if not webnotes.conn.sql("select name from tabAccount where name=%s and debit_or_credit = 'Credit' and ifnull(is_pl_account, 'No') = 'No'", (self.doc.supplier_type + " - " + abbr)):
# if not group created , create it
self.add_account(self.doc.supplier_type, self.get_payables_group(), abbr)
@@ -90,7 +89,7 @@
abbr = self.get_company_abbr()
parent_account = self.get_parent_account(abbr)
- if not sql("select name from tabAccount where name=%s", (self.doc.name + " - " + abbr)):
+ if not webnotes.conn.sql("select name from tabAccount where name=%s", (self.doc.name + " - " + abbr)):
ac_bean = webnotes.bean({
"doctype": "Account",
'account_name': self.doc.name,
@@ -121,15 +120,15 @@
def get_contacts(self,nm):
if nm:
- contact_details =webnotes.conn.convert_to_lists(sql("select name, CONCAT(IFNULL(first_name,''),' ',IFNULL(last_name,'')),contact_no,email_id from `tabContact` where supplier = '%s'"%nm))
+ contact_details =webnotes.conn.convert_to_lists(webnotes.conn.sql("select name, CONCAT(IFNULL(first_name,''),' ',IFNULL(last_name,'')),contact_no,email_id from `tabContact` where supplier = '%s'"%nm))
return contact_details
else:
return ''
def delete_supplier_address(self):
- for rec in sql("select * from `tabAddress` where supplier=%s", (self.doc.name,), as_dict=1):
- sql("delete from `tabAddress` where name=%s",(rec['name']))
+ for rec in webnotes.conn.sql("select * from `tabAddress` where supplier=%s", (self.doc.name,), as_dict=1):
+ webnotes.conn.sql("delete from `tabAddress` where name=%s",(rec['name']))
def delete_supplier_contact(self):
for contact in webnotes.conn.sql_list("""select name from `tabContact`
@@ -138,7 +137,7 @@
def delete_supplier_account(self):
"""delete supplier's ledger if exist and check balance before deletion"""
- acc = sql("select name from `tabAccount` where master_type = 'Supplier' \
+ acc = webnotes.conn.sql("select name from `tabAccount` where master_type = 'Supplier' \
and master_name = %s and docstatus < 2", self.doc.name)
if acc:
from webnotes.model import delete_doc
@@ -161,7 +160,7 @@
('Purchase Receipt', 'supplier'),
('Serial No', 'supplier')]
for rec in update_fields:
- sql("update `tab%s` set supplier_name = %s where `%s` = %s" % \
+ webnotes.conn.sql("update `tab%s` set supplier_name = %s where `%s` = %s" % \
(rec[0], '%s', rec[1], '%s'), (new, old))
for account in webnotes.conn.sql("""select name, account_name from
diff --git a/buying/doctype/supplier_quotation/supplier_quotation.js b/buying/doctype/supplier_quotation/supplier_quotation.js
index ccb903e..4e01782 100644
--- a/buying/doctype/supplier_quotation/supplier_quotation.js
+++ b/buying/doctype/supplier_quotation/supplier_quotation.js
@@ -9,6 +9,7 @@
// attach required files
wn.require('app/accounts/doctype/purchase_taxes_and_charges_master/purchase_taxes_and_charges_master.js');
wn.require('app/buying/doctype/purchase_common/purchase_common.js');
+wn.require('app/accounts/doctype/sales_invoice/pos.js');
erpnext.buying.SupplierQuotationController = erpnext.buying.BuyingController.extend({
refresh: function() {
diff --git a/buying/doctype/supplier_quotation/supplier_quotation.py b/buying/doctype/supplier_quotation/supplier_quotation.py
index c3d2f78..8c5224e 100644
--- a/buying/doctype/supplier_quotation/supplier_quotation.py
+++ b/buying/doctype/supplier_quotation/supplier_quotation.py
@@ -54,7 +54,6 @@
def validate_common(self):
pc = get_obj('Purchase Common')
pc.validate_for_items(self)
- pc.get_prevdoc_date(self)
@webnotes.whitelist()
def make_purchase_order(source_name, target_doclist=None):
diff --git a/buying/doctype/supplier_quotation/supplier_quotation.txt b/buying/doctype/supplier_quotation/supplier_quotation.txt
index 36747d3..ddd1730 100644
--- a/buying/doctype/supplier_quotation/supplier_quotation.txt
+++ b/buying/doctype/supplier_quotation/supplier_quotation.txt
@@ -2,12 +2,13 @@
{
"creation": "2013-05-21 16:16:45",
"docstatus": 0,
- "modified": "2013-08-09 14:45:58",
+ "modified": "2013-10-02 14:24:44",
"modified_by": "Administrator",
"owner": "Administrator"
},
{
"allow_attach": 1,
+ "allow_import": 1,
"autoname": "naming_series:",
"doctype": "DocType",
"document_type": "Transaction",
diff --git a/buying/doctype/supplier_quotation_item/supplier_quotation_item.txt b/buying/doctype/supplier_quotation_item/supplier_quotation_item.txt
index 1d16291..515cdb2 100644
--- a/buying/doctype/supplier_quotation_item/supplier_quotation_item.txt
+++ b/buying/doctype/supplier_quotation_item/supplier_quotation_item.txt
@@ -2,7 +2,7 @@
{
"creation": "2013-05-22 12:43:10",
"docstatus": 0,
- "modified": "2013-08-07 14:44:18",
+ "modified": "2013-10-10 17:02:11",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -261,21 +261,6 @@
},
{
"doctype": "DocField",
- "fieldname": "prevdoc_date",
- "fieldtype": "Date",
- "hidden": 1,
- "in_filter": 1,
- "in_list_view": 0,
- "label": "Material Request Date",
- "no_copy": 1,
- "oldfieldname": "prevdoc_date",
- "oldfieldtype": "Date",
- "print_hide": 1,
- "read_only": 1,
- "search_index": 0
- },
- {
- "doctype": "DocField",
"fieldname": "prevdoc_detail_docname",
"fieldtype": "Data",
"hidden": 1,
diff --git a/buying/page/buying_home/buying_home.js b/buying/page/buying_home/buying_home.js
index 1e08a3b..f41f619 100644
--- a/buying/page/buying_home/buying_home.js
+++ b/buying/page/buying_home/buying_home.js
@@ -145,6 +145,11 @@
route: "query-report/Purchase Order Trends",
doctype: "Purchase Order"
},
+ {
+ "label":wn._("Supplier Addresses And Contacts"),
+ route: "query-report/Supplier Addresses and Contacts",
+ doctype: "Supplier"
+ },
]
}
]
diff --git a/buying/report/supplier_addresses_and_contacts/__init__.py b/buying/report/supplier_addresses_and_contacts/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/buying/report/supplier_addresses_and_contacts/__init__.py
diff --git a/buying/report/supplier_addresses_and_contacts/supplier_addresses_and_contacts.txt b/buying/report/supplier_addresses_and_contacts/supplier_addresses_and_contacts.txt
new file mode 100644
index 0000000..fac1e9e
--- /dev/null
+++ b/buying/report/supplier_addresses_and_contacts/supplier_addresses_and_contacts.txt
@@ -0,0 +1,22 @@
+[
+ {
+ "creation": "2013-10-09 10:38:40",
+ "docstatus": 0,
+ "modified": "2013-10-09 10:53:52",
+ "modified_by": "Administrator",
+ "owner": "Administrator"
+ },
+ {
+ "doctype": "Report",
+ "is_standard": "Yes",
+ "name": "__common__",
+ "query": "SELECT\n `tabSupplier`.name as \"Supplier:Link/Supplier:120\",\n\t`tabSupplier`.supplier_name as \"Supplier Name::120\",\n\t`tabSupplier`.supplier_type as \"Supplier Type:Link/Supplier Type:120\",\n\tconcat_ws(', ', \n\t\ttrim(',' from `tabAddress`.address_line1), \n\t\ttrim(',' from tabAddress.address_line2), \n\t\ttabAddress.state, tabAddress.pincode, tabAddress.country\n\t) as 'Address::180',\n concat_ws(', ', `tabContact`.first_name, `tabContact`.last_name) as 'Contact Name::180',\n\t`tabContact`.phone as \"Phone\",\n\t`tabContact`.mobile_no as \"Mobile No\",\n\t`tabContact`.email_id as \"Email Id::120\",\n\t`tabContact`.is_primary_contact as \"Is Primary Contact::120\"\nFROM\n\t`tabSupplier`\n\tleft join `tabAddress` on (\n\t\t`tabAddress`.supplier=`tabSupplier`.name\n\t)\n\tleft join `tabContact` on (\n\t\t`tabContact`.supplier=`tabSupplier`.name\n\t)\nWHERE\n\t`tabSupplier`.docstatus<2\nORDER BY\n\t`tabSupplier`.name asc",
+ "ref_doctype": "Supplier",
+ "report_name": "Supplier Addresses and Contacts",
+ "report_type": "Query Report"
+ },
+ {
+ "doctype": "Report",
+ "name": "Supplier Addresses and Contacts"
+ }
+]
\ No newline at end of file
diff --git a/buying/utils.py b/buying/utils.py
index ce81b6b..115b023 100644
--- a/buying/utils.py
+++ b/buying/utils.py
@@ -65,7 +65,7 @@
out = webnotes._dict({
"description": item.description_html or item.description,
- "qty": 0.0,
+ "qty": 1.0,
"uom": item.stock_uom,
"conversion_factor": 1.0,
"warehouse": args.warehouse or item.default_warehouse,
diff --git a/controllers/accounts_controller.py b/controllers/accounts_controller.py
index 927b249..5121d69 100644
--- a/controllers/accounts_controller.py
+++ b/controllers/accounts_controller.py
@@ -52,7 +52,7 @@
msgprint(_("Account for this ") + fieldname + _(" has been freezed. ") +
self.doc.doctype + _(" can not be made."), raise_exception=1)
- def set_price_list_currency(self, buying_or_selling):
+ def set_price_list_currency(self, buying_or_selling, for_validate=False):
if self.meta.get_field("currency"):
company_currency = get_company_currency(self.doc.company)
@@ -60,14 +60,13 @@
fieldname = "selling_price_list" if buying_or_selling.lower() == "selling" \
else "buying_price_list"
if self.meta.get_field(fieldname) and self.doc.fields.get(fieldname):
- if not self.doc.price_list_currency:
- self.doc.price_list_currency = webnotes.conn.get_value("Price List",
- self.doc.fields.get(fieldname), "currency")
+ self.doc.price_list_currency = webnotes.conn.get_value("Price List",
+ self.doc.fields.get(fieldname), "currency")
if self.doc.price_list_currency == company_currency:
self.doc.plc_conversion_rate = 1.0
- elif not self.doc.plc_conversion_rate:
+ elif not self.doc.plc_conversion_rate or not for_validate:
self.doc.plc_conversion_rate = self.get_exchange_rate(
self.doc.price_list_currency, company_currency)
@@ -77,7 +76,7 @@
self.doc.conversion_rate = self.doc.plc_conversion_rate
elif self.doc.currency == company_currency:
self.doc.conversion_rate = 1.0
- elif not self.doc.conversion_rate:
+ elif not self.doc.conversion_rate or not for_validate:
self.doc.conversion_rate = self.get_exchange_rate(self.doc.currency,
company_currency)
@@ -423,3 +422,7 @@
self._abbr = webnotes.conn.get_value("Company", self.doc.company, "abbr")
return self._abbr
+
+@webnotes.whitelist()
+def get_tax_rate(account_head):
+ return webnotes.conn.get_value("Account", account_head, "tax_rate")
diff --git a/controllers/buying_controller.py b/controllers/buying_controller.py
index 7e49e60..5176b16 100644
--- a/controllers/buying_controller.py
+++ b/controllers/buying_controller.py
@@ -2,7 +2,7 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import webnotes
+import webnotes, json
from webnotes import _, msgprint
from webnotes.utils import flt, _round
@@ -11,9 +11,8 @@
from controllers.stock_controller import StockController
-class WrongWarehouseCompany(Exception): pass
-
class BuyingController(StockController):
+
def onload_post_render(self):
# contact, address, item details
self.set_missing_values()
@@ -24,13 +23,13 @@
self.doc.supplier_name = webnotes.conn.get_value("Supplier",
self.doc.supplier, "supplier_name")
self.validate_stock_or_nonstock_items()
- self.validate_warehouse_belongs_to_company()
+ self.validate_warehouse()
def set_missing_values(self, for_validate=False):
super(BuyingController, self).set_missing_values(for_validate)
self.set_supplier_from_item_default()
- self.set_price_list_currency("Buying")
+ self.set_price_list_currency("Buying", for_validate)
# set contact and address details for supplier, if they are not mentioned
if self.doc.supplier and not (self.doc.contact_person and self.doc.supplier_address):
@@ -49,17 +48,20 @@
if supplier:
self.doc.supplier = supplier
break
+
+ def validate_warehouse(self):
+ from stock.utils import validate_warehouse_user, validate_warehouse_company
+
+ warehouses = list(set([d.warehouse for d in
+ self.doclist.get({"doctype": self.tname}) if d.warehouse]))
+
+ for w in warehouses:
+ validate_warehouse_user(w)
+ validate_warehouse_company(w, self.doc.company)
def get_purchase_tax_details(self):
self.doclist = self.doc.clear_table(self.doclist, "purchase_tax_details")
self.set_taxes("purchase_tax_details", "purchase_other_charges")
-
- def validate_warehouse_belongs_to_company(self):
- for warehouse, company in webnotes.conn.get_values("Warehouse",
- self.doclist.get_distinct_values("warehouse"), "company").items():
- if company and company != self.doc.company:
- webnotes.msgprint(_("Company mismatch for Warehouse") + (": %s" % (warehouse,)),
- raise_exception=WrongWarehouseCompany)
def validate_stock_or_nonstock_items(self):
if not self.get_stock_items():
diff --git a/controllers/selling_controller.py b/controllers/selling_controller.py
index f1117ed..5c7b66c 100644
--- a/controllers/selling_controller.py
+++ b/controllers/selling_controller.py
@@ -14,13 +14,16 @@
def onload_post_render(self):
# contact, address, item details and pos details (if applicable)
self.set_missing_values()
-
+
+ def get_sender(self, comm):
+ return webnotes.conn.get_value('Sales Email Settings', None, 'email_id')
+
def set_missing_values(self, for_validate=False):
super(SellingController, self).set_missing_values(for_validate)
# set contact and address details for customer, if they are not mentioned
self.set_missing_lead_customer_details()
- self.set_price_list_and_item_details()
+ self.set_price_list_and_item_details(for_validate)
if self.doc.fields.get("__islocal"):
self.set_taxes("other_charges", "charge")
@@ -38,8 +41,8 @@
if not self.doc.fields.get(fieldname) and self.meta.get_field(fieldname):
self.doc.fields[fieldname] = val
- def set_price_list_and_item_details(self):
- self.set_price_list_currency("Selling")
+ def set_price_list_and_item_details(self, for_validate=False):
+ self.set_price_list_currency("Selling", for_validate)
self.set_missing_item_details(get_item_details)
def get_other_charges(self):
@@ -233,34 +236,4 @@
self.doc.order_type = "Sales"
elif self.doc.order_type not in valid_types:
msgprint(_(self.meta.get_label("order_type")) + " " +
- _("must be one of") + ": " + comma_or(valid_types),
- raise_exception=True)
-
- def update_serial_nos(self, cancel=False):
- from stock.doctype.stock_ledger_entry.stock_ledger_entry import update_serial_nos_after_submit, get_serial_nos
- update_serial_nos_after_submit(self, self.doc.doctype, self.fname)
- update_serial_nos_after_submit(self, self.doc.doctype, "packing_details")
-
- for table_fieldname in (self.fname, "packing_details"):
- for d in self.doclist.get({"parentfield": table_fieldname}):
- for serial_no in get_serial_nos(d.serial_no):
- sr = webnotes.bean("Serial No", serial_no)
- if cancel:
- sr.doc.status = "Available"
- for fieldname in ("warranty_expiry_date", "delivery_document_type",
- "delivery_document_no", "delivery_date", "delivery_time", "customer",
- "customer_name"):
- sr.doc.fields[fieldname] = None
- else:
- sr.doc.delivery_document_type = self.doc.doctype
- sr.doc.delivery_document_no = self.doc.name
- sr.doc.delivery_date = self.doc.posting_date
- sr.doc.delivery_time = self.doc.posting_time
- sr.doc.customer = self.doc.customer
- sr.doc.customer_name = self.doc.customer_name
- if sr.doc.warranty_period:
- sr.doc.warranty_expiry_date = add_days(cstr(self.doc.posting_date),
- cint(sr.doc.warranty_period))
- sr.doc.status = 'Delivered'
-
- sr.save()
+ _("must be one of") + ": " + comma_or(valid_types), raise_exception=True)
diff --git a/controllers/status_updater.py b/controllers/status_updater.py
index 070f200..b2a0e17 100644
--- a/controllers/status_updater.py
+++ b/controllers/status_updater.py
@@ -8,6 +8,51 @@
from webnotes.model.controller import DocListController
+status_map = {
+ "Contact": [
+ ["Replied", "communication_sent"],
+ ["Open", "communication_received"]
+ ],
+ "Job Applicant": [
+ ["Replied", "communication_sent"],
+ ["Open", "communication_received"]
+ ],
+ "Lead": [
+ ["Replied", "communication_sent"],
+ ["Converted", "has_customer"],
+ ["Opportunity", "has_opportunity"],
+ ["Open", "communication_received"],
+ ],
+ "Opportunity": [
+ ["Draft", None],
+ ["Submitted", "eval:self.doc.docstatus==1"],
+ ["Lost", "eval:self.doc.status=='Lost'"],
+ ["Quotation", "has_quotation"],
+ ["Replied", "communication_sent"],
+ ["Cancelled", "eval:self.doc.docstatus==2"],
+ ["Open", "communication_received"],
+ ],
+ "Quotation": [
+ ["Draft", None],
+ ["Submitted", "eval:self.doc.docstatus==1"],
+ ["Lost", "eval:self.doc.status=='Lost'"],
+ ["Ordered", "has_sales_order"],
+ ["Replied", "communication_sent"],
+ ["Cancelled", "eval:self.doc.docstatus==2"],
+ ["Open", "communication_received"],
+ ],
+ "Sales Order": [
+ ["Draft", None],
+ ["Submitted", "eval:self.doc.docstatus==1"],
+ ["Stopped", "eval:self.doc.status=='Stopped'"],
+ ["Cancelled", "eval:self.doc.docstatus==2"],
+ ],
+ "Support Ticket": [
+ ["Replied", "communication_sent"],
+ ["Open", "communication_received"]
+ ],
+}
+
class StatusUpdater(DocListController):
"""
Updates the status of the calling records
@@ -20,6 +65,45 @@
self.update_qty()
self.validate_qty()
+ def set_status(self, update=False):
+ if self.doc.get("__islocal"):
+ return
+
+ if self.doc.doctype in status_map:
+ sl = status_map[self.doc.doctype][:]
+ sl.reverse()
+ for s in sl:
+ if not s[1]:
+ self.doc.status = s[0]
+ break
+ elif s[1].startswith("eval:"):
+ if eval(s[1][5:]):
+ self.doc.status = s[0]
+ break
+ elif getattr(self, s[1])():
+ self.doc.status = s[0]
+ break
+
+ if update:
+ webnotes.conn.set_value(self.doc.doctype, self.doc.name, "status", self.doc.status)
+
+ def on_communication(self):
+ self.communication_set = True
+ self.set_status(update=True)
+ del self.communication_set
+
+ def communication_received(self):
+ if getattr(self, "communication_set", False):
+ last_comm = self.doclist.get({"doctype":"Communication"})
+ if last_comm:
+ return last_comm[-1].sent_or_received == "Received"
+
+ def communication_sent(self):
+ if getattr(self, "communication_set", False):
+ last_comm = self.doclist.get({"doctype":"Communication"})
+ if last_comm:
+ return last_comm[-1].sent_or_received == "Sent"
+
def validate_qty(self):
"""
Validates qty at row level
diff --git a/docs/user/accounts/docs.user.accounts.pos.md b/docs/user/accounts/docs.user.accounts.pos.md
index 654a5d6..7bd668e 100644
--- a/docs/user/accounts/docs.user.accounts.pos.md
+++ b/docs/user/accounts/docs.user.accounts.pos.md
@@ -13,33 +13,34 @@
- Update Stock: If this is checked, Stock Ledger Entries will be made when you “Submit” this Sales Invoice thereby eliminating the need for a separate Delivery Note.
- In your Items table, update inventory information like Warehouse (saved as default), Serial Number, or Batch Number if applicable.
-- Update Payment Details like your Bank / Cash Account, paid amount etc.
+- Update Payment Details like your Bank / Cash Account, Paid amount etc.
- If you are writing off certain amount. For example when you receive extra cash as a result of not having exact denomination of change, check on ‘Write off Outstanding Amount’ and set the Account.
-Setup [POS Setting](docs.user.setup.pos_setting.html)
#### Enable POS View
-Sales Invoice has 2 different interfaces, Invoice View and POS View. The current view used by most users is the Invoice View. This view is preferred by non-retailing companies.The POS view is used by retailing companies. For retailers it is very important to provide bill or sales invoice at the point of sale. Their customers cannot wait to receive the bill by post. The customers want an immediate bill for the payment which they make. In such cases, the POS View is preferred.
+- Every Sales & Purchase documents has 2 different interfaces, Invoice View and POS View. The current view used by most users is the Invoice View. This view is preferred by non-retailing companies.The POS view is used by retailing companies. For retailers it is very important to provide bill or sales invoice at the point of sale. Their customers cannot wait to receive the bill by post. The customers want an immediate bill for the payment which they make. In such cases, the POS View is preferred.
> Setup > Show/Hide Features

+- Setup [POS Setting](docs.user.setup.pos_setting.html)
+
### Adding an Item
-At the billing counter, the retailer needs to select Items which the consumer buys. In the POS interface you can select an Item by two methods. One, is by clicking on the Item image and the other, is through the Barcode.
+At the billing counter, the retailer needs to select Items which the consumer buys. In the POS interface you can select an Item by two methods. One, is by clicking on the Item image and the other, is through the Barcode / Serial No.
**Select Item** - To select a product click on the Item image and add it into the cart. A cart is an area that prepares a customer for checkout by allowing to edit product information, adjust taxes and add discounts.
-**Barcode** - A Barcode is an optical machine-readable representation of data relating to the object to which it is attached. Enter Barcode in the barcode box and pause for a second. The Item will be automatically added to the cart.
+**Barcode / Serial No** - A Barcode / Serial No is an optical machine-readable representation of data relating to the object to which it is attached. Enter Barcode / Serial No in the box as shown in the image below and pause for a second, the item will be automatically added to the cart.

> Tip: To change the quantity of an Item, enter your desired quantity in the quantity box. These are mostly used if the same Item is purchased in bulk.
-If your product list is very long use the universal search field, to type the product name and select faster.
+If your product list is very long use the Search field, type the product name in Search box.
### Removing an Item
@@ -48,7 +49,7 @@
- Select an Item by clicking on the row of that Item from Item cart. Then click on “Del” button. OR
-- Type 0 in the ‘select quantity’ field to delete the record.
+- Enter 0(zero) quantity of any item to delete that item.
To remove multiple Items together, select multiple rows & click on “Del” button.
@@ -62,11 +63,11 @@
1. Click on “Make Payment” to get the Payment window.
1. Select your “Mode of Payment”.
-1. Click on “Pay” button to Save the Sales Invoice.
+1. Click on “Pay” button to Save the document.

-Submit the document to finalise the record. After the Invoice is submitted, you can either print an invoice or email it directly to the customer.
+Submit the document to finalise the record. After the document is submitted, you can either print or email it directly to the customer.
#### Accounting entries (GL Entry) for a Point of Sale:
diff --git a/docs/user/buying/docs.user.buying.supplier.md b/docs/user/buying/docs.user.buying.supplier.md
index e477fa2..3707fbe 100644
--- a/docs/user/buying/docs.user.buying.supplier.md
+++ b/docs/user/buying/docs.user.buying.supplier.md
@@ -20,7 +20,15 @@
> Tip: When you select a Supplier in any transaction, one Contact and Address gets pre-selected. This is the “Default Contact or Address”. So make sure you set your defaults correctly!
+### Integration with Accounts
+In ERPNext, there is a separate Account record for each Supplier, of Each company.
+
+When you create a new Supplier, ERPNext will automatically create an Account Ledger for the Supplier under “Accounts Receivable” in the Company set in the Supplier record.
+
+> Advanced Tip: If you want to change the Account Group under which the Supplier Account is created, you can set it in the Company master.
+
+If you want to create an Account in another Company, just change the Company value and “Save” the Supplier again.
> Buying > Contact > New Contact
diff --git a/docs/user/buying/docs.user.buying.supplier_type.md b/docs/user/buying/docs.user.buying.supplier_type.md
index deaf01f..3871991 100644
--- a/docs/user/buying/docs.user.buying.supplier_type.md
+++ b/docs/user/buying/docs.user.buying.supplier_type.md
@@ -3,9 +3,11 @@
"_label": "Supplier Type"
}
---
+A supplier may be distinguished from a contractor or subcontractor, who commonly adds specialized input to deliverables. A supplier is also known as a vendor. There are different types of suppliers based on their goods and products.
-Based on what the suppliers supply, they are classified into different categories called Supplier Type.
-There can be different types of suppliers. You can create your own category of Supplier Type.
+ERPNext allows you to create your own categories of suppliers. These categories are known as Supplier Type. For Example, if your suppliers are mainly pharmaceutical companies and FMCG distributors, You can create a new Type for them and name them accordingly.
+
+Based on what the suppliers supply, they are classified into different categories called Supplier Type. There can be different types of suppliers. You can create your own category of Supplier Type.
> Buying > Supplier Type > New Supplier Type
diff --git a/docs/user/docs.user.md b/docs/user/docs.user.md
index d9e7f2a..5c65882 100644
--- a/docs/user/docs.user.md
+++ b/docs/user/docs.user.md
@@ -3,6 +3,7 @@
"_label": "User Guide",
"_toc": [
"docs.user.intro",
+ "docs.user.five_day_setup",
"docs.user.implement",
"docs.user.setup",
"docs.user.selling",
@@ -17,7 +18,7 @@
"docs.user.tools",
"docs.user.customize",
"docs.user.knowledge"
- ],
+ ],
"_no_toc": 1
}
---
@@ -29,6 +30,12 @@
1. [Open Source](docs.user.intro.open_source.html)
1. [Ways to get started](docs.user.intro.try.html)
1. [Getting Help](docs.user.help.html)
+1. [Five-Day-Setup](docs.user.five_day_setup.html)
+ 1. [Day-1: Setup Customer,Item, and Supplier](docs.user.five_day_setup.day_1.html)
+ 1. [Day-2: Setup Chart of Accounts, Opening Accounts, and HR](docs.user.five_day_setup.day_2.html)
+ 1. [Day-3: Sales Cycle and Purchase Cycle](docs.user.five_day_setup.day_3.html)
+ 1. [Day-4: Manufacturing Cycle and Accounting Reports](docs.user.five_day_setup.day_4.html)
+ 1. [Day-5: Projects, Calendar, and Website](docs.user.five_day_setup.day_5.html)
1. [Implementation](docs.user.implement.html)
1. [Implementation Strategy](docs.user.implement.strategy.html)
1. [Concepts](docs.user.implement.concepts.html)
@@ -151,3 +158,6 @@
1. [Fiscal Year](docs.user.knowledge.fiscal_year.html)
1. [Accounting Knowledge](docs.user.knowledge.accounting.html)
1. [Accounting Entries](docs.user.knowledge.accounting_entries.html)
+ 1. [DocType Definitions](docs.user.knowledge.doctype.html)
+ 1. [Attachment and CSV Files](docs.user.knowledge.attachment_csv.html)
+ 1. [Format using Markdown](docs.user.knowledge.markdown.html)
diff --git a/docs/user/five_day_setup/docs.user.five_day_setup.day_1.md b/docs/user/five_day_setup/docs.user.five_day_setup.day_1.md
new file mode 100644
index 0000000..bbb8b86
--- /dev/null
+++ b/docs/user/five_day_setup/docs.user.five_day_setup.day_1.md
@@ -0,0 +1,81 @@
+---
+{
+ "_label": "Day-1: Setup Customer, Item, and Supplier"
+}
+---
+Login to your ERPNext account with the User ID and Password sent through the mail.
+
+After logging into your account you will receive a pop-up form to fill. Please fill this form. Select the abbreviation you would wish to have for your company. The abbreviation selected here will be used in naming most of the business documents.
+
+#### Form Part I
+
+
+<br><br>
+#### Form Part II
+To understand about Company Financial Year or Fiscal Year visit [Fiscal Year](docs.user.knowledge.fiscal_year.html)
+
+
+
+
+After filling this form, you will get a pop-up message for completing the set-up process. Click on the Setup button. The Setup page will appear.
+
+<br>
+#### Setup Page - Customer
+
+The Organisation details are updated in ERPNext, after filling the first form. Go directly to the Customer Icon.
+
+
+<br>
+
+
+After clicking on Customer, a new form will appear.
+<br>
+
+
+
+To see how customer details are added, visit [Customer](docs.user.selling.customer.html). Create 5 new customer records in the system.
+
+Now proceed to make an Item. To go to the main menu, click on erpnext icon which is on the left hand corner of the page. On the main menu page, click on Setup
+
+
+
+<br>
+
+#### Setup Page - Item
+
+On the setup page go to Item.
+
+
+
+
+
+Create a new Item. An Item is your company's product or a service.The term Item is applicable to your core products as well as your raw materials. It can be a product or service that you buy/sell from your customers/ suppliers.
+
+Filling Item details is an important step in ERPNext. Do not postpone this step. After clicking on Item, make a new Item.
+
+
+
+To understand how to fill an Item in detail, visit [Item](docs.user.stock.item.html). Add 5 item records to ERPnext. After adding these records, go back to the Setup Page and add Suppliers.
+
+<br>
+#### Setup Page - Suppliers
+On the Setup page go to Supplier.
+
+
+<br>
+
+Suppliers are companies or individuals who provide you with products or services. They are treated in exactly the same manner as Customers in ERPNext. Create a new Supplier record.
+
+
+
+
+To understand how to fill Supplier details, visit [Supplier](docs.user.buying.supplier.html).
+
+If you wish to import your list of customers and suppliers directly to ERPNext, you can do that via the Data Import Tool.
+
+To upload Customers or suppliers in bulk, go to the Data Import Tool.
+
+> Note: The data import format is case-sensitive. The file will not be processed if there are any spelling mistakes or deviations from the default values.
+
+To understand how to import data, visit [Importing Data](docs.user.setup.data_import.html).
+
diff --git a/docs/user/five_day_setup/docs.user.five_day_setup.day_2.md b/docs/user/five_day_setup/docs.user.five_day_setup.day_2.md
new file mode 100644
index 0000000..f016623
--- /dev/null
+++ b/docs/user/five_day_setup/docs.user.five_day_setup.day_2.md
@@ -0,0 +1,54 @@
+---
+{
+ "_label": "Day-2: Setup Chart of Accounts, Opening Accounts, and HR"
+}
+---
+
+#### Setup Page - Accounts
+<br>
+Go to the Accounts icon and make ledgers under Chart of Accounts.
+
+
+
+<br>
+
+
+Create acccounting ledgers.
+
+
+
+<br>
+
+To begin Opening Entries, go to 'Opening Accounts and Stock' on the Setup Page.
+
+
+
+<br>
+
+
+To understand how to create opening entries in detail visit [Opening Entry](docs.user.setup.opening.html).
+
+<br>
+
+#### Opening Stock
+
+You can upload your opening stock in the system using Stock Reconciliation. Stock Reconciliation will update your stock for any given Item.
+
+
+<br>
+
+
+To understand Stock Opening in detail visit [Opening Stock](docs.user.accounts.opening_stock.html).
+
+<br>
+
+#### Setup Page - HR Setup
+
+To setup HR, begin by creating individual employee records.
+
+
+
+
+To fill the Employee Form, refer to [Employee](docs.user.hr.employee.html)
+
+To complete the remaining HR-Setup, see [Human Resources](docs.user.hr.html)
\ No newline at end of file
diff --git a/docs/user/five_day_setup/docs.user.five_day_setup.day_3.md b/docs/user/five_day_setup/docs.user.five_day_setup.day_3.md
new file mode 100644
index 0000000..e0c4114
--- /dev/null
+++ b/docs/user/five_day_setup/docs.user.five_day_setup.day_3.md
@@ -0,0 +1,139 @@
+---
+{
+ "_label": "Day-3: Sales Cycle and Purchase Cycle"
+}
+---
+After completing the set-up and account opening process, it is advisable to complete a few cycles of sales, purchase, and manufacturing process.
+
+
+### Sales Cycle
+
+Complete a standard Sales Cycle.
+> Lead > Opportunity > Quotation > Sales Order > Delivery Note > Sales Invoice > Payment (Journal Voucher)
+
+<br>
+#### Lead
+
+To begin the sales cycle, go to the Selling Icon. On the selling page, click on Lead.
+
+
+
+Fill the Lead form.
+
+> To understand Lead in detail, visit [Lead](docs.user.selling.lead.html)
+
+<br>
+#### Opportunity
+
+After completing the Lead form, assume that, this same lead is getting converted into an Opportunity. Thus, to create an Opportunity from the existing lead, click on Create Opportunity, on the Lead page.
+
+##### Step 1: Go to 'Lead List' Page and open the Lead that shows interested status.
+
+
+
+<br>
+
+##### Step 2: Generate Opportunity from the selected Lead
+
+
+
+You can also generate an Opportunity directly from the Selling Page.
+
+> To understand Opportunity in detail visit [Opportunity](docs.user.selling.opportunity.html).
+
+<br>
+#### Quotation
+
+Imagine that your Opportunity has shown interest and asked for a Quotation. To generate a Quotation from the same Opportunity, open the submitted Opportunity and click on Create Quotation.
+
+
+
+You can also generate a Quotation directly from the Selling Page.
+
+> To understand Quotation in detail visit [Quotation](docs.user.selling.quotation.html)
+
+<br>
+#### Sales Order
+
+Imagine that the Quotation which you sent was accepted by the prospect. You are now reequired to send him a Sales Order. To make a sales order from this same Quotation, go to that Quotation page and click on Make Sales Order.
+
+
+
+You can also generate a Sales Order directly from the Selling Page.
+
+> To understand Sales Order in detail visit [Sales Order](docs.user.selling.sales_order.html).
+
+<br>
+#### Delivery Note
+
+If your organisation has the practice of sending Delivery Note, this section will be helpful. To create a Delivery Note from the a Sales Order, go to that Sales Order and click on Make Delivery.
+
+
+
+
+> To understand Delivery Note in detail, visit [Delivery Note](docs.user.stock.delivery_note.html)
+
+<br>
+#### Sales Invoice
+
+Save and Submit your Delivery Note to generate a Sales Invoice. You can also generate an Invoice from Sales Order.
+
+
+
+<br>
+
+#### Payment (Journal Voucher)
+
+
+A Journal Voucher or a payment entry can be generated directly from the Sales Invoice.
+
+
+
+> To understand a Journal Voucher in detail, visit [Journal Voucher](docs.user.accounts.journal_voucher.html)
+
+<br>
+
+### Purchase Cycle
+
+Complete a standard Purchase Cycle.
+> Material Request > Purchase Order > Purchase Receipt > Payment (Journal Voucher).
+
+<br>
+#### Material Request
+
+To create a Material Request, go to Stock/Buying and Click on Material Request.
+
+
+
+> To understand Material Request in detail, visit [Material Request](docs.user.buying.material_request.html)
+
+<br>
+#### Purchase Order
+
+To create a Purchase Order go to Buying and click on Purchase Order
+
+
+
+> To understand Purchase Order in detail, visit [Purchase Order](docs.user.buying.purchase_order.html)
+
+<br>
+#### Purchase Receipt
+
+To create a Purchase Receipt from an existing Purchase Order, open that purchase order and click on Make Purchase Receipt.
+
+
+<br>
+
+>To understand Purchase Receipt in detail, visit [Purchase Receipt](docs.user.stock.purchase_receipt.html)
+
+<br>
+
+#### Payment (Journal Voucher)
+
+Payments made against Sales Invoices or Purchase Invoices can be made by clicking on “Make Payment Entry” button on “Submitted” invoices.
+
+
+
+<br>
+
+> To understand Payment Entry in detail, visit [Payment Entry](docs.user.accounts.payments.html).
diff --git a/docs/user/five_day_setup/docs.user.five_day_setup.day_4.md b/docs/user/five_day_setup/docs.user.five_day_setup.day_4.md
new file mode 100644
index 0000000..af6e636
--- /dev/null
+++ b/docs/user/five_day_setup/docs.user.five_day_setup.day_4.md
@@ -0,0 +1,86 @@
+---
+{
+ "_label": "Day-4: Manufacturing Cycle and Accounting Reports"
+}
+---
+
+### Manufacturing Cycle
+
+Complete a manufacturing cycle (if applicable).
+> BOM > Production Planning Tool > Production Order > Stock Entry (Material Issue) > Stock Entry (Sales Return)
+
+<br>
+#### Bill of Materials
+
+To go to Bill of Materials, Click on Manufacturing. On the Manufacturing page, click on Bill of Materials.
+
+
+
+> To understand BOM in detail, visit [Bill of Materials](docs.user.mfg.bom.html)
+
+<br>
+#### Production Planning Tool
+
+To go to Production Planning Tool, click on the Manufacturing Icon. On the Manufacturing Page, click on Production Planning Tool to go to that page.
+
+
+
+> To understand Production Planning Tool in detail, visit [Production Planning](docs.user.mfg.planning.html)
+
+<br>
+#### Production Order
+
+To go to Production Order click on the Manufacturing Icon. On the Manufacturing Page, click on Production Order.
+
+
+
+> To understand Production Order in detail, visit [Production Order](docs.user.mfg.production_order.html)
+
+<br>
+#### Stock Entry
+
+To go to Stock Entry, click on the Stock Icon and go to Stock Entry.
+
+
+
+> To understand Material Issue, visit [Material Issue](docs.user.stock.material_issue.html).
+
+> To understand Sales Return, visit [Sales Return](docs.user.stock.sales_return.html).
+
+<br>
+#### Delivery Note
+
+To go to Delivery Note, click on Stock. On the Stock Page, click on Delivery Note.
+
+
+
+> To understand Delivery Note in detail, visit [Delivery Note](docs.user.stock.delivery_note.html)
+
+<br>
+#### Warehouse
+
+To go to Warehouse, Click on Stock. On the Stock Page, go to Warehouse.
+
+
+
+> To understand Warehouse in detail, visit [Warehouse](docs.user.stock.warehouse.html)
+
+<br>
+#### Accounts
+
+Make a few Journal Vouchers. Generate some Accounting Reports.
+
+#### Journal Voucher
+
+To go to a Journal Voucher, click on Accounts. On the Accounts page, click on Journal Voucher.
+
+
+
+> To understand Journal Voucher in detail, visit [Journal Voucher](docs.user.accounts.journal_voucher.html)
+
+<br>
+### Accounting Reports
+
+Some of the major Accounting Reports are General Ledger, Trial Balance, Accounts Payable and Accounts Receivables, and Sales and Purchase Register.
+
+> To be able to generate these accounts, visti [Accounting Reports](docs.user.accounts.report.html)
diff --git a/docs/user/five_day_setup/docs.user.five_day_setup.day_5.md b/docs/user/five_day_setup/docs.user.five_day_setup.day_5.md
new file mode 100644
index 0000000..db386a0
--- /dev/null
+++ b/docs/user/five_day_setup/docs.user.five_day_setup.day_5.md
@@ -0,0 +1,32 @@
+---
+{
+ "_label": "Day-5: Projects, Calendar, and Website"
+}
+---
+
+#### Projects
+
+ERPNext helps you to manage your Projects by breaking them into Tasks and allocating them to different people.
+
+<br>
+#### Tasks
+
+Project is divided into Tasks and each Task is allocated to a resource. In ERPNext, you can also create and allocate a Task independently of a Project.
+
+To create a Task, go to Project and click on Task.
+
+
+
+> To understand Projects in detail, visit [Projects](docs.user.projects.html).
+
+> To understand Task in detail, visit [Tasks](docs.user.projects.tasks.html)
+
+<br>
+#### Calendar
+
+> To understand Calendar in detail, visit [Calendar](docs.user.tools.calendar.html)
+
+<br>
+#### Website
+
+> To understand Website in detail, visit [Website](docs.user.website.html)
diff --git a/docs/user/five_day_setup/docs.user.five_day_setup.md b/docs/user/five_day_setup/docs.user.five_day_setup.md
new file mode 100644
index 0000000..90aeb56
--- /dev/null
+++ b/docs/user/five_day_setup/docs.user.five_day_setup.md
@@ -0,0 +1,48 @@
+---
+{
+ "_label": "Five-Day Setup",
+ "_toc": [
+ "docs.user.five_day_setup.day_1",
+ "docs.user.five_day_setup.day_2",
+ "docs.user.five_day_setup.day_3",
+ "docs.user.five_day_setup.day_4",
+ "docs.user.five_day_setup.day_5"
+
+ ]
+}
+---
+Welcome to ERPNext. To be able to setup ERPNext account successfully, a five-day-setup process is recommended. Perform the 5-days-setup instructions and sail through the ERPNext implementation.
+
+The setup help is divided into day-wise instructions for 5 consecutive days.
+
+#### Day 1
+
+- Company Setup
+- Customer Setup
+- Item Setup
+- Supplier Setup
+- Data Import Tool
+
+#### Day 2
+
+- Opening Accounts
+- Opening Stock
+- HR Setup
+
+#### Day 3
+
+- Sales Cycle
+- Purchase Cycle
+
+
+#### Day 4
+
+- Manufacturing Cycle
+- Delivery Note and Warehouse
+- Accounts
+
+#### Day 5
+
+- Projects
+- Calendar
+- Website
\ No newline at end of file
diff --git a/docs/user/intro/docs.user.implement.strategy.md b/docs/user/intro/docs.user.implement.strategy.md
index 9ec58e8..e077a90 100644
--- a/docs/user/intro/docs.user.implement.strategy.md
+++ b/docs/user/intro/docs.user.implement.strategy.md
@@ -10,11 +10,12 @@
### Test Phase
- Read the Manual
+- Follow the Five-Day-Setup Module or the instructions given below.
- Create your first Customer, Supplier and Item. Add a few more so you get familiar with them.
- Create Customer Groups, Item Groups, Warehouses, Supplier Groups, so that you can classify your Items.
- Complete a standard sales cycle - Lead > Opportunity > Quotation > Sales Order > Delivery Note > Sales Invoice > Payment (Journal Voucher)
-- Complete a standard purchase cycle - Purchase Request > Purchase Order > Purchase Receipt > Payment (Journal Voucher).
-- Complete a manufacturing cycle (if applicable) - BOM > Production Planning Tool > Production Order > Stock Entry (issue) > Stock Entry (back-flush)
+- Complete a standard purchase cycle - Material Request > Purchase Order > Purchase Receipt > Payment (Journal Voucher).
+- Complete a manufacturing cycle (if applicable) - BOM > Production Planning Tool > Production Order > Material Issue > Sales Return
> Tip: Use the 30-day free trial at [erpnext.com](https://erpnext.com) to take your test drive.
diff --git a/docs/user/knowledge/docs.user.knowledge.accounting.md b/docs/user/knowledge/docs.user.knowledge.accounting.md
index 1730498..995d38c 100644
--- a/docs/user/knowledge/docs.user.knowledge.accounting.md
+++ b/docs/user/knowledge/docs.user.knowledge.accounting.md
@@ -22,34 +22,34 @@
The balance of account can be increased / decreased, depending on account type and transaction type.
-<table class="table table-bordered">
+<table class="table table-bordered text-center">
<thead>
<tr class="active">
- <td style="text-align: center;">Account Type</td>
- <td style="text-align: center;">Transaction Type</td>
- <td style="text-align: center;">Effect on account balance</td>
+ <td>Account Type</td>
+ <td>Transaction Type</td>
+ <td>Effect on account balance</td>
</tr>
</thead>
<tbody>
<tr>
- <td style="text-align: center;">Debit</td>
- <td style="text-align: center;">Debit</td>
- <td style="text-align: center;">Increases</td>
+ <td>Debit</td>
+ <td>Debit</td>
+ <td>Increases</td>
</tr>
<tr>
- <td style="text-align: center;">Debit</td>
- <td style="text-align: center;">Credit</td>
- <td style="text-align: center;">Decreases</td>
+ <td>Debit</td>
+ <td>Credit</td>
+ <td>Decreases</td>
</tr>
<tr>
- <td style="text-align: center;">Credit</td>
- <td style="text-align: center;">Credit</td>
- <td style="text-align: center;">Increases</td>
+ <td>Credit</td>
+ <td>Credit</td>
+ <td>Increases</td>
</tr>
<tr>
- <td style="text-align: center;">Credit</td>
- <td style="text-align: center;">Debit</td>
- <td style="text-align: center;">Decreases</td>
+ <td>Credit</td>
+ <td>Debit</td>
+ <td>Decreases</td>
</tr>
</tbody>
</table>
@@ -62,48 +62,48 @@
As the company will receive a payment from customer, the customer is considered as an asset account. For booking income, company maintains an account called "Sales of Laptop". So, entries will be done in the following manner:
-<table class="table table-bordered">
+<table class="table table-bordered text-center">
<thead>
<tr class="active">
- <td style="text-align: center;">Account</td>
- <td style="text-align: center;">Debit</td>
- <td style="text-align: center;">Credit</td>
+ <td>Account</td>
+ <td>Debit</td>
+ <td>Credit</td>
</tr>
</thead>
<tbody>
<tr>
- <td style="text-align: center;">Customer A</td>
- <td style="text-align: center;">50000</td>
- <td style="text-align: center;"></td>
+ <td>Customer A</td>
+ <td>50000</td>
+ <td></td>
</tr>
<tr>
- <td style="text-align: center;">Sales of Laptop</td>
- <td style="text-align: center;"></td>
- <td style="text-align: center;">50000</td>
+ <td>Sales of Laptop</td>
+ <td></td>
+ <td>50000</td>
</tr>
</tbody>
</table>
Customer A has made the payment, so customer balance should decreased based on the paid amount, which will increase "Cash" balance.
-<table class="table table-bordered">
+<table class="table table-bordered text-center">
<thead>
<tr class="active">
- <td style="text-align: center;">Account</td>
- <td style="text-align: center;">Debit</td>
- <td style="text-align: center;">Credit</td>
+ <td>Account</td>
+ <td>Debit</td>
+ <td>Credit</td>
</tr>
</thead>
<tbody>
<tr>
- <td style="text-align: center;">Customer A</td>
- <td style="text-align: center;"></td>
- <td style="text-align: center;">50000</td>
+ <td>Customer A</td>
+ <td></td>
+ <td>50000</td>
</tr>
<tr>
- <td style="text-align: center;">Cash</td>
- <td style="text-align: center;">50000</td>
- <td style="text-align: center;"></td>
+ <td>Cash</td>
+ <td>50000</td>
+ <td></td>
</tr>
</tbody>
</table>
diff --git a/docs/user/knowledge/docs.user.knowledge.attachment_csv.md b/docs/user/knowledge/docs.user.knowledge.attachment_csv.md
new file mode 100644
index 0000000..eecc42d
--- /dev/null
+++ b/docs/user/knowledge/docs.user.knowledge.attachment_csv.md
@@ -0,0 +1,16 @@
+---
+{
+ "_label": "Attachment and CSV files"
+}
+---
+
+#### How to Attach files?
+
+When you open a form, on the right sidebar, you will see a section to attach files. Click on “Add” and select the file you want to attach. Click on “Upload” and you are set.
+
+#### What is a CSV file?
+
+A CSV (Comma Separated Value) file is a data file that you can upload into ERPNext to update various data. Any spreadsheet file from popular spreadsheet applications like MS Excel or Open Office Spreadsheet can be saved as a CSV file.
+
+If you are using Microsoft Excel and using non-English characters, make sure to save your file encoded as UTF-8. For older versions of Excel, there is no clear way of saving as UTF-8. So save your file as a CSV, then open it in Notepad, and save as “UTF-8”. (Sorry blame Microsoft for this!)
+
diff --git a/docs/user/knowledge/docs.user.knowledge.doctype.md b/docs/user/knowledge/docs.user.knowledge.doctype.md
new file mode 100644
index 0000000..a2550d4
--- /dev/null
+++ b/docs/user/knowledge/docs.user.knowledge.doctype.md
@@ -0,0 +1,267 @@
+---
+{
+ "_label": "DocType"
+}
+---
+
+
+
+ERPNext is a based on a “metadata” (data about data) framework that helps define all the different types of documents in the system. The basic building block of ERPNext is a DocType.
+
+A DocType represents both a table in the database and a form from which a user can enter data.
+
+Many DocTypes are single tables, but some work in groups. For example, Quotation has a “Quotation” DocType and a “Quotation Item” doctype for the Items table, among others. DocTypes contain a collection of fields called DocFields that form the basis of the columns in the database and the layout of the form.
+
+<table class="table table-bordered text-left">
+ <thead>
+ <tr class="active">
+ <td width="30%">Column</td>
+ <td>Description</td>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>Name</td>
+ <td>Name of the record</td>
+
+ </tr>
+ <tr>
+ <td>Owner</td>
+ <td>Creator and Owner of the record</td>
+
+ </tr>
+ <tr>
+ <td>Created on</td>
+ <td>Date and Time of Creation</td>
+
+ </tr>
+ <tr>
+ <td>Modified On </td>
+ <td>Date and Time of Modification</td>
+ </tr>
+ <tr>
+ <td>Docstatus</td>
+ <td>Status of the record<br>
+ 0 = Saved/Draft<br>
+ 1 = Submitted<br>
+ 2 = Cancelled/Deleted
+ </td>
+ </tr>
+ <tr>
+ <td>Parent</td>
+ <td>Name of the Parent</td>
+ </tr>
+ <tr>
+ <td>Parent Type</td>
+ <td>Type of Parent</td>
+ </tr>
+ <tr>
+ <td>Parent Field</td>
+ <td>Specifying the relationship with the parent (there can be multiple child relationships with the same DocType).</td>
+ </tr>
+ <tr>
+ <td>Index(idx)</td>
+ <td>Index (sequence) of the record in the child table.</td>
+
+ </tr>
+ </tbody>
+</table>
+
+#### Single DocType
+
+There are a certain type of DocTypes that are “Single”, i.e. they have no table associated and have only one record of its fields. DocTypes such as Global Defaults, Production Planning Tool are “Single” DocTypes.
+
+#### Field Columns
+
+In the fields table, there are many columns, here is an explanation of the columns of the field table.
+
+<table class="table table-bordered text-left">
+ <thead>
+ <tr class="active">
+ <td width="30%">Column</td>
+ <td>Description</td>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>Label</td>
+ <td>Field Label (that appears in the form).</td>
+ </tr>
+ <tr>
+ <td>Type</td>
+ <td>Field Type</td>
+ </tr>
+ <tr>
+ <td>Name</td>
+ <td>Column name in the database, must be code friendly with no white spaces, special characters and capital letters.</td>
+ </tr>
+ <tr>
+ <td>options</td>
+ <td>Field settings:<br>
+ For Select: List of options (each on a new line).<br>
+ For Link: DocType that is “linked”.<br>
+ For HTML: HTML Content
+ </tr>
+ <tr>
+ <td>Perm Level</td>
+ <td>Permission level (number) of the field. You can group fields by numbers, called levels, and apply rules on the levels.</td>
+ </tr>
+ <tr>
+ <td>Width</td>
+ <td>Width of the field (in pixels) - useful for “Table” types.</td>
+ </tr>
+ <tr>
+ <td>Reqd</td>
+ <td>Checked if field is mandatory (required).</td>
+ </tr>
+ <tr>
+ <td>In Filter</td>
+ <td>Checked if field appears as a standard filter in old style reports.</td>
+ </tr>
+ <tr>
+ <td>Hidden</td>
+ <td>Checked if field is hidden.</td>
+ </tr>
+ <tr>
+ <td>Print Hide</td>
+ <td>Checked if field is hidden in Print Formats.</td>
+ </tr>
+ <tr>
+ <td>Report Hide</td>
+ <td>Checked if field is hidden in old style reports.</td>
+ </tr>
+ <tr>
+ <td>Allow on Submit</td>
+ <td>Checked if this field can be edited after the document is “Submitted”.</td>
+ </tr>
+ <tr>
+ <td>Depends On</td>
+ <td>The fieldname of the field that will decide whether this field will be shown or hidden. It is useful to hide un-necessary fields.</td>
+ </tr>
+ <tr>
+ <td>Description</td>
+ <td>Description of the field</td>
+ </tr>
+ <tr>
+ <td>Default</td>
+ <td>Default value when a new record is created.<br>
+ Note: “user” will set the current user as default and “today” will set today’s date (if the field is a Date field).</td>
+ </tr>
+ <tbody>
+<table>
+
+#### Field Types and Options
+
+Here is a list of the different types of fields used to make / customize forms in ERPNext.
+
+<table class="table table-bordered text-left">
+ <thead>
+ <tr class="active">
+ <td width="30%">Type</td>
+ <td>Description</td>
+ <td>Options/Setting</td>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>Data</td>
+ <td>Single line text field with 180 characters</td>
+ <td> </td>
+ </tr>
+ <tr>
+ <td>Select</td>
+ <td>Select from a pre-determined items in a drop-down.</td>
+ <td>The “Options” contains the drop-down items, each on a new row</td>
+ </tr>
+ <tr>
+ <td>Link</td>
+ <td>Link an existing document / record</td>
+ <td>Options contains the name of the type of document (DocType)</td>
+ </tr>
+ <tr>
+ <td>Currency</td>
+ <td>Number with 2 decimal places, that will be shown separated by commas for thousands etc. in Print.</td>
+ <td>e.g. 1,000,000.00</td>
+ </tr>
+ <tr>
+ <td>Float</td>
+ <td>Number with 6 decimal places.</td>
+ <td>e.g. 3.141593</td>
+ </tr>
+ <tr>
+ <td>Int</td>
+ <td>Integer (no decimals)</td>
+ <td>e.g. 100</td>
+ </tr>
+ <tr>
+ <td>Date</td>
+ <td>Date</td>
+ <td>Format can be selected in Global Defaults</td>
+ </tr>
+ <tr>
+ <td>Time</td>
+ <td>Time</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td colspan="3" class="active">Text</td>
+ </tr>
+ <tr>
+ <td>Text</td>
+ <td>Multi-line text box without formatting features</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>Text editor</td>
+ <td>Multi-line text box with formatting toolbar etc</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>Code</td>
+ <td>Code Editor</td>
+ <td>Options can include the type of language for syntax formatting.
+ Eg JS / Python / HTML</td>
+ </tr>
+ <tr>
+ <td colspan="3" class="active">Table (Grid)</td>
+ </tr>
+ <tr>
+ <td>Table</td>
+ <td>Table of child items linked to the record.</td>
+ <td>Options contains the name of the DocType of the child table. For example “Sales Invoice Item” for “Sales Invoice”</td>
+ </tr>
+ <tr>
+ <td colspan="3" class="active">Layout</td>
+ </tr>
+ <tr>
+ <td>Section Break</td>
+ <td>Break into a new horizontal section.</td>
+ <td>The layout in ERPNext is evaluated from top to bottom.</td>
+ </tr>
+ <tr>
+ <td>Column Break</td>
+ <td>Break into a new vertical column.</td>
+ <td></td>
+ </tr>
+ <tr>
+ <td>HTML</td>
+ <td>Add a static text / help / link etc in HTML</td>
+ <td>Options contains the HTML.</td>
+ </tr>
+ <tr>
+ <td colspan="3" class="active">Action</td>
+ </tr>
+ <tr>
+ <td>Button</td>
+ <td>Button</td>
+ <td>[for developers only]</td>
+ </tr>
+ <tbody>
+ <table>
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/user/knowledge/docs.user.knowledge.markdown.md b/docs/user/knowledge/docs.user.knowledge.markdown.md
new file mode 100644
index 0000000..b2f76be
--- /dev/null
+++ b/docs/user/knowledge/docs.user.knowledge.markdown.md
@@ -0,0 +1,84 @@
+---
+{
+ "_label": "Format Using Markdown"
+}
+---
+
+Markdown is a simple way of writing text to format your content. Markdown allows you easy ways to format.
+
+1. Headings (h1 (largest), h2, h3, h4 and so on)
+1. Paragraphs
+1. Lists (numbered or bulleted)
+1. Hyper links (links to other pages)
+1. Images
+1. Code
+1. Embed HTML (HTML tags within your text)
+
+#### Headings
+
+Headings are specified by adding a `#` (hash) at the beginning of the line. The more the number of hashes, the smaller the heading:
+
+ # This is a large heading.
+
+ ### This is a smaller heading.
+
+#### Paragraphs
+
+To start a new paragraph, just make sure that there is an empty line at the beginning and end of the paragraph.
+
+To format text as **bold** or with _italics_ format as follows:
+
+ **This text** is **bold** and _this one_ is with _italics_
+
+#### Lists
+
+To define numbered lists, start your link with a number and a dot (.) and ensure there is a blank line before and after the list. The numbers are automatically generated so it does not matter what number you put:
+
+ 1. list 1st item
+ 1. second item
+ 1. and so on
+ 1. and so forth
+
+To define bulleted lists, start your items with a hyphen (-)
+
+ - item 1
+ - item 2
+ - item 3
+
+To nest lists within one another, put four spaces to indent your inner list as follows:
+
+ 1. item 1
+ 1. item 2
+ - sub item 1
+ - sub item 2
+ 1. item 3
+
+#### Links (to other pages)
+
+Links to other pages can be defined by adding your text in box brackets [] followed by the link in round brackets ()
+
+ [This is an external link](http://example.com)
+ [A link within the site](my-page.html)
+
+#### Images
+
+Images can be added by adding an exclamation ! before the link.
+
+ 
+
+
+#### Code
+
+To add a code block, just leave a blank line before and after the block and make sure all code line are indented by four spaces:
+
+ This is normal text
+
+ This is a code block
+
+#### HTML
+
+You can embed any kind of HTML tags within your code. Any content written within HTML tags will not be formatted.
+
+[Detailed description of the markdown format](http://daringfireball.net/projects/markdown/syntax)
+
+
diff --git a/docs/user/knowledge/docs.user.knowledge.md b/docs/user/knowledge/docs.user.knowledge.md
index 5d98cdd..11dc995 100644
--- a/docs/user/knowledge/docs.user.knowledge.md
+++ b/docs/user/knowledge/docs.user.knowledge.md
@@ -4,8 +4,12 @@
"_toc": [
"docs.user.knowledge.fiscal_year",
"docs.user.knowledge.accounting",
- "docs.user.knowledge.accounting_entries"
+ "docs.user.knowledge.accounting_entries",
+ "docs.user.knowledge.doctype",
+ "docs.user.knowledge.attachment_csv",
+ "docs.user.knowledge.markdown"
]
}
---
-Knowledge Library contains definitions and explanations of various management concepts. This page is created for users who wish to elaborate their conceptual knowledge.
\ No newline at end of file
+Knowledge Library contains definitions and explanations of various management concepts. This page is created for users who wish to elaborate their conceptual knowledge.
+
diff --git a/docs/user/selling/docs.user.selling.customer.md b/docs/user/selling/docs.user.selling.customer.md
index 108f20d..5336ac6 100644
--- a/docs/user/selling/docs.user.selling.customer.md
+++ b/docs/user/selling/docs.user.selling.customer.md
@@ -4,7 +4,9 @@
"_title_image": "img/customers.png"
}
---
-You can either directly create your Customers via
+A customer, who is sometimes known as a client, buyer, or purchaser is the one who receives goods, services, products, or ideas, from a seller for a monetary consideration. A customer can also receive goods or services from a vendor or a supplier for other valuable considerations.
+
+ You can either directly create your Customers via
> Selling > Customer
diff --git a/docs/user/setup/docs.user.setup.cost_centers.md b/docs/user/setup/docs.user.setup.cost_centers.md
index 3df26e1..d666e41 100644
--- a/docs/user/setup/docs.user.setup.cost_centers.md
+++ b/docs/user/setup/docs.user.setup.cost_centers.md
@@ -45,6 +45,14 @@
Budgets are also great for planning purposes. When you are making plans for the next financial year, you would typically target a revenue based on which you would set your expenses. Setting a budget will ensure that your expenses do not get out of hand, at any point, as per your plans.
You can define it in the Cost Center. If you have seasonal sales you can also define a budget distribution that the budget will follow.
+
+> Accounts > Budget Distribution > New Budget Distribution
+
+
+
+
+
+

#### Budget Actions
diff --git a/docs/user/setup/docs.user.setup.data_import.md b/docs/user/setup/docs.user.setup.data_import.md
index eec6b65..b40573e 100644
--- a/docs/user/setup/docs.user.setup.data_import.md
+++ b/docs/user/setup/docs.user.setup.data_import.md
@@ -3,7 +3,7 @@
"_label": "Data Import Tool"
}
---
-The Data Import Tool is a great way to upload (or edit) bulk data, specially master data, into the system. To start the tool go to:
+The Data Import Tool is a great way to upload (or edit) bulk data, specially master data, into the system.
To Open the data import tool, you either go to Setup or go to the Transaction you want to Import. If Data Import is allowed, you will see an Import Button:
@@ -15,7 +15,7 @@
### 1. Downloading The Template
-Data in ERPNext is stored in tables, much like a spreadsheet with columns and rows of data. Each entity in ERPNext can have multiple child tables associated with it too. The child tables are linked to the parent tables and are implemented where are multiple values for any property. For example an Item can have multiple prices, An Invoice has multiple Items and so on.
+Data in ERPNext is stored in tables, much like a spreadsheet with columns and rows of data. Each entity in ERPNext can have multiple child tables associated with it too. The child tables are linked to the parent tables and are implemented where there are multiple values for any property. For example an Item can have multiple prices, An Invoice has multiple Items and so on.
You can import each table separately, or all at a time. In the child table, you must mention the parent of the row in the “parent” column so that ERPNext knows which Item’s price or tax you are trying to set if you are importing separately.
@@ -36,7 +36,7 @@
### 3. Upload the .csv File
-Finally attach the .csv file in the section section click on the "Upload and Import" button.
+Finally attach the .csv file in the section. Click on the "Upload and Import" button.

diff --git a/docs/user/setup/docs.user.setup.md b/docs/user/setup/docs.user.setup.md
index fa9680a..585811f 100644
--- a/docs/user/setup/docs.user.setup.md
+++ b/docs/user/setup/docs.user.setup.md
@@ -18,7 +18,7 @@
"docs.user.setup.email",
"docs.user.setup.sms",
"docs.user.setup.taxes",
- "docs.user.setup.price_lists",
+ "docs.user.setup.price_list",
"docs.user.setup.opening",
"docs.user.setup.pos_setting",
"docs.user.setup.third_party_backups"
diff --git a/docs/user/setup/docs.user.setup.price_list.md b/docs/user/setup/docs.user.setup.price_list.md
index 021df09..7214fff 100644
--- a/docs/user/setup/docs.user.setup.price_list.md
+++ b/docs/user/setup/docs.user.setup.price_list.md
@@ -7,7 +7,7 @@
An Item can have multiple prices based on customer, currency, region, shipping cost etc, which can be stored as different rate plans. In ERPNext, you are required to store all the lists seperately. Buying Price List is different from Selling Price List and thus is stored separately.
-> Selling > Price List
+> Setup > Price List

@@ -15,7 +15,14 @@
> For multiple currencies, maintain multiple Price Lists.
+<br>
+### Add Item in Price List
-To add a new Item to the Price List, add the Item Code and its rate in the Item Prices table.
+> Setup > Item Price
-You can also import Item Prices via [Data Import Tool](docs.user.setup.data_import.html)
\ No newline at end of file
+- Enter Price List and Item Code, Valid for Buying or Selling, Item Name & Item Description will be automatically fetched.
+- Enter Rate and save the document.
+
+
+
+For bulk upload of Item Prices, use [Data Import Tool](docs.user.setup.data_import.html)
\ No newline at end of file
diff --git a/docs/user/setup/docs.user.setup.users.md b/docs/user/setup/docs.user.setup.users.md
index 82d79d3..06ac910 100644
--- a/docs/user/setup/docs.user.setup.users.md
+++ b/docs/user/setup/docs.user.setup.users.md
@@ -6,7 +6,7 @@
ERPNext has a role-based permission system, which means that you can assign Roles to Users, and permissions on Roles.Each ERPNext user has a Profile. The Profile contains the user’s email and authentication and can be set from:
-> Setup > Users and Permissions > Users
+> Setup > Profile
#### Step 1: Adding a new User
diff --git a/docs/user/stock/docs.user.stock.item.md b/docs/user/stock/docs.user.stock.item.md
index c98afd2..f7746c4 100644
--- a/docs/user/stock/docs.user.stock.item.md
+++ b/docs/user/stock/docs.user.stock.item.md
@@ -15,7 +15,7 @@
- **Item Name:** Item name is the actual name of your product or service.
- **Item Code:** Item Code is a short-form to denote your Item. If you have very few Items, it is advisable to keep the Item Name and the Item Code same. This helps new users to recognise and update Item details in all transactions. In case you have lot of Items with long names and the list runs in hundreds, it is advisable to code. To understand naming Item codes see [Item Codification](docs.user.setup.codification.html)
-- **Item Group:** Item Group is used to categorize an Item under various criterias like products, raw materials, services, sub-assemblies, consumables or all Item groups. Create your default Item Group list under Setup> Item Group and pre-select the option while filling your New Item details under Item Group.
+- **Item Group:** Item Group is used to categorize an Item under various criterias like products, raw materials, services, sub-assemblies, consumables or all Item groups. Create your default Item Group list under Setup> Item Group and pre-select the option while filling your New Item details under [Item Group](docs.user.stock.item_group.html)
- **Default Unit of Measure:** This is the default measuring unit that you will use for your product. It could be in nos, kgs, meters, etc. You can store all the UOM’s that your product will require under Set Up> Master Data > UOM. These can be preselected while filling New Item by using % sign to get a pop up of the UOM list.
- **Brand:** If you have more than one brand save them under Set Up> Master Data> Brand and pre-select them while filling a New Item.
@@ -27,11 +27,7 @@

-### Item Pricing
-
-Item Price and Price Lists: ERPNext lets you maintain multiple selling prices for an Item using Price Lists. A Price List is a place where different rate plans can be stored. It’s a name you can give to a set of Item prices. In case you have different zones (based on the shipping costs), for different currencies etc, you can maintain different Price Lists. A Price List is formed when you create different Item Prices. To import Item Price see [Importing Data](docs.user.data_import.md).
-
-## Inventory : Warehouse and Stock Setting
+### Inventory : Warehouse and Stock Setting
In ERPNext, you can select different type of Warehouses to stock your different Items. This can be selected based on Item types. It could be Fixed Asset Item, Stock Item or even Manufacturing Item.
@@ -95,3 +91,59 @@
Visit [Manufacturing](docs.user.mfg.html) and [Website](docs.user.website.html) to understand these topics in detail.
+
+### Listing Item on Website
+
+To list your Item on the Website, fill the Item details and save the file. Once the file is saved, a plus (+) button will appear next to the Image icon. Click on the plus button and add your Item image. The html code will be generated automatically.
+
+##### Step 1: Save Image
+
+
+
+<br>
+
+##### Step 2: Check the 'Show in Website' box.
+
+Under the Website section, please check the box that says 'show in Website'. Once the box is checked, the page will display other fields for entering information.
+
+
+
+<br>
+
+
+##### Step 3: Enter Website Details
+
+
+
+
+The page name will be generated automatically. Mention the Item-Group under which the Item will be displayed.
+
+#### Item Groups
+
+Mention the Item Group under this column. If you wish to list your Item under the broad category products, name your Item Group as Products. In case you have various varieties of Item and want to classify them under different names, make Item Groups with those names and check the box that says 'show in Website'. For Example, if you wish to create a category called 'Bags', create a Item Group named Bags.
+
+
+
+
+Once the Item Group is created go to the Website Settings page under Website. Enter the Label, Url, and Parent Label.
+
+
+
+
+<br>
+
+#### Webpage labels
+
+
+
+Add more Items under a particular Item Group.
+
+To add more Items under a certain Label, mention the Item Group on the Item Page. The Items will be added automatically on the Webpage, under the Item Group Label. For Example, To add Item-Kiddies Bag and Butterfly Print Bag, check the 'Show in Website'box. The Items will be placed under the Label Bags on the Webpage.
+
+
+
+<br>
+
+Item Group Display
+
+
\ No newline at end of file
diff --git a/docs/user/website/docs.user.website.blog.md b/docs/user/website/docs.user.website.blog.md
index ec035a0..b71836e 100644
--- a/docs/user/website/docs.user.website.blog.md
+++ b/docs/user/website/docs.user.website.blog.md
@@ -15,6 +15,11 @@

-You can format the blog using the same Markdown format
+You can format a blog using the Markdown format.You can also access your blog by going to the page “blog.html”.
-You can access your blog by going to the page “blog.html”
+
+#### A sample blog-page.
+
+
+
+
diff --git a/docs/user/website/docs.user.website.setup.md b/docs/user/website/docs.user.website.setup.md
index 3249637..4d1d7c7 100644
--- a/docs/user/website/docs.user.website.setup.md
+++ b/docs/user/website/docs.user.website.setup.md
@@ -29,11 +29,36 @@
> Website > Website Settings
+#### Step 1: Landing Page Details

<br>
+#### Step 2: Banner Details
+
+
+
+<br>
+
+#### Step 3: Top Bar Labels
+
+
+
+> Note: Create seperate web pages which will be linked to the main web-icons like company, contact, products etc.
+
+<br>
+#### Step 4: Footer Details
+
+
+
+A website can be generated once all the settings and style requirements are added.
+
+A sample website generated by ERPNext would look like this.
+
+
+
+<br>
#### Top Menu
diff --git a/home/doctype/feed/feed.py b/home/doctype/feed/feed.py
index 25abf57..ebee5c4 100644
--- a/home/doctype/feed/feed.py
+++ b/home/doctype/feed/feed.py
@@ -7,7 +7,6 @@
from webnotes.model import db_exists
from webnotes.model.bean import copy_doclist
-sql = webnotes.conn.sql
diff --git a/hr/doctype/attendance/attendance.py b/hr/doctype/attendance/attendance.py
index 7b82bc5..6d52b57 100644
--- a/hr/doctype/attendance/attendance.py
+++ b/hr/doctype/attendance/attendance.py
@@ -7,7 +7,6 @@
from webnotes.utils import getdate, nowdate
from webnotes import msgprint, _
-sql = webnotes.conn.sql
class DocType:
def __init__(self, doc, doclist=[]):
@@ -15,7 +14,7 @@
self.doclist = doclist
def validate_duplicate_record(self):
- res = sql("""select name from `tabAttendance` where employee = %s and att_date = %s
+ res = webnotes.conn.sql("""select name from `tabAttendance` where employee = %s and att_date = %s
and name != %s and docstatus = 1""",
(self.doc.employee, self.doc.att_date, self.doc.name))
if res:
@@ -24,7 +23,7 @@
def check_leave_record(self):
if self.doc.status == 'Present':
- leave = sql("""select name from `tabLeave Application`
+ leave = webnotes.conn.sql("""select name from `tabLeave Application`
where employee = %s and %s between from_date and to_date and status = 'Approved'
and docstatus = 1""", (self.doc.employee, self.doc.att_date))
@@ -42,7 +41,7 @@
msgprint(_("Attendance can not be marked for future dates"), raise_exception=1)
def validate_employee(self):
- emp = sql("select name from `tabEmployee` where name = %s and status = 'Active'",
+ emp = webnotes.conn.sql("select name from `tabEmployee` where name = %s and status = 'Active'",
self.doc.employee)
if not emp:
msgprint(_("Employee: ") + self.doc.employee +
diff --git a/hr/doctype/employee/employee.js b/hr/doctype/employee/employee.js
index cfedfc6..3814dd9 100644
--- a/hr/doctype/employee/employee.js
+++ b/hr/doctype/employee/employee.js
@@ -4,7 +4,6 @@
wn.provide("erpnext.hr");
erpnext.hr.EmployeeController = wn.ui.form.Controller.extend({
setup: function() {
- this.setup_leave_approver_select();
this.frm.fields_dict.user_id.get_query = function(doc,cdt,cdn) {
return { query:"core.doctype.profile.profile.profile_query"} }
this.frm.fields_dict.reports_to.get_query = function(doc,cdt,cdn) {
@@ -12,6 +11,7 @@
},
onload: function() {
+ this.setup_leave_approver_select();
this.frm.toggle_display(["esic_card_no", "gratuity_lic_id", "pan_number", "pf_number"],
wn.control_panel.country==="India");
if(this.frm.doc.__islocal) this.frm.set_value("employee_name", "");
diff --git a/hr/doctype/employee/employee.py b/hr/doctype/employee/employee.py
index e28e02d..b46123a 100644
--- a/hr/doctype/employee/employee.py
+++ b/hr/doctype/employee/employee.py
@@ -4,11 +4,10 @@
from __future__ import unicode_literals
import webnotes
-from webnotes.utils import getdate, validate_email_add, cstr
+from webnotes.utils import getdate, validate_email_add, cstr, cint
from webnotes.model.doc import make_autoname
from webnotes import msgprint, _
-sql = webnotes.conn.sql
class DocType:
def __init__(self,doc,doclist=[]):
@@ -40,12 +39,12 @@
self.validate_email()
self.validate_status()
self.validate_employee_leave_approver()
+ self.update_dob_event()
def on_update(self):
if self.doc.user_id:
self.update_user_default()
self.update_profile()
- self.update_dob_event()
def update_user_default(self):
webnotes.conn.set_default("employee", self.doc.name, self.doc.user_id)
@@ -156,10 +155,11 @@
raise_exception=InvalidLeaveApproverError)
def update_dob_event(self):
- if self.doc.status == "Active" and self.doc.date_of_birth:
+ if self.doc.status == "Active" and self.doc.date_of_birth \
+ and not cint(webnotes.conn.get_value("HR Settings", None, "stop_birthday_reminders")):
birthday_event = webnotes.conn.sql("""select name from `tabEvent` where repeat_on='Every Year'
and ref_type='Employee' and ref_name=%s""", self.doc.name)
-
+
starts_on = self.doc.date_of_birth + " 00:00:00"
ends_on = self.doc.date_of_birth + " 00:15:00"
diff --git a/hr/doctype/employee/employee.txt b/hr/doctype/employee/employee.txt
index bbe87ad..4ed8732 100644
--- a/hr/doctype/employee/employee.txt
+++ b/hr/doctype/employee/employee.txt
@@ -2,12 +2,13 @@
{
"creation": "2013-03-07 09:04:18",
"docstatus": 0,
- "modified": "2013-08-08 14:22:11",
+ "modified": "2013-10-11 10:52:53",
"modified_by": "Administrator",
"owner": "Administrator"
},
{
"allow_attach": 1,
+ "allow_rename": 1,
"autoname": "naming_series:",
"doctype": "DocType",
"document_type": "Master",
diff --git a/hr/doctype/holiday_list/holiday_list.py b/hr/doctype/holiday_list/holiday_list.py
index 81d18f3..100c140 100644
--- a/hr/doctype/holiday_list/holiday_list.py
+++ b/hr/doctype/holiday_list/holiday_list.py
@@ -10,7 +10,6 @@
from webnotes.model.bean import copy_doclist
from webnotes import msgprint
-sql = webnotes.conn.sql
import datetime
diff --git a/hr/doctype/hr_settings/hr_settings.py b/hr/doctype/hr_settings/hr_settings.py
index 784339d..101905c 100644
--- a/hr/doctype/hr_settings/hr_settings.py
+++ b/hr/doctype/hr_settings/hr_settings.py
@@ -6,6 +6,24 @@
from __future__ import unicode_literals
import webnotes
+from webnotes.utils import cint
+
class DocType:
def __init__(self, d, dl):
- self.doc, self.doclist = d, dl
\ No newline at end of file
+ self.doc, self.doclist = d, dl
+
+ def validate(self):
+ self.original_stop_birthday_reminders = cint(webnotes.conn.get_value("HR Settings",
+ None, "stop_birthday_reminders"))
+
+ def on_update(self):
+ # reset birthday reminders
+ if cint(self.doc.stop_birthday_reminders) != self.original_stop_birthday_reminders:
+ webnotes.conn.sql("""delete from `tabEvent` where repeat_on='Every Year' and ref_type='Employee'""")
+
+ if not self.doc.stop_birthday_reminders:
+ for employee in webnotes.conn.sql_list("""select name from `tabEmployee` where status='Active' and
+ ifnull(date_of_birth, '')!=''"""):
+ webnotes.get_obj("Employee", employee).update_dob_event()
+
+ webnotes.msgprint(webnotes._("Updated Birthday Reminders"))
\ No newline at end of file
diff --git a/hr/doctype/hr_settings/hr_settings.txt b/hr/doctype/hr_settings/hr_settings.txt
index e3694d0..bf4b011 100644
--- a/hr/doctype/hr_settings/hr_settings.txt
+++ b/hr/doctype/hr_settings/hr_settings.txt
@@ -2,7 +2,7 @@
{
"creation": "2013-08-02 13:45:23",
"docstatus": 0,
- "modified": "2013-08-02 14:22:26",
+ "modified": "2013-10-02 15:44:38",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -39,6 +39,12 @@
"name": "HR Settings"
},
{
+ "doctype": "DocField",
+ "fieldname": "employee_settings",
+ "fieldtype": "Section Break",
+ "label": "Employee Settings"
+ },
+ {
"description": "Employee record is created using selected field. ",
"doctype": "DocField",
"fieldname": "emp_created_by",
@@ -47,6 +53,19 @@
"options": "Naming Series\nEmployee Number"
},
{
+ "description": "Don't send Employee Birthday Reminders",
+ "doctype": "DocField",
+ "fieldname": "stop_birthday_reminders",
+ "fieldtype": "Check",
+ "label": "Stop Birthday Reminders"
+ },
+ {
+ "doctype": "DocField",
+ "fieldname": "payroll_settings",
+ "fieldtype": "Section Break",
+ "label": "Payroll Settings"
+ },
+ {
"description": "If checked, Total no. of Working Days will include holidays, and this will reduce the value of Salary Per Day",
"doctype": "DocField",
"fieldname": "include_holidays_in_total_working_days",
diff --git a/hr/doctype/job_applicant/get_job_applications.py b/hr/doctype/job_applicant/get_job_applications.py
index 2e01328..a929781 100644
--- a/hr/doctype/job_applicant/get_job_applications.py
+++ b/hr/doctype/job_applicant/get_job_applications.py
@@ -37,7 +37,7 @@
mail.save_attachments_in_doc(applicant.doc)
make(content=mail.content, sender=mail.from_email,
- doctype="Job Applicant", name=applicant.doc.name)
+ doctype="Job Applicant", name=applicant.doc.name, sent_or_received="Received")
def get_job_applications():
if cint(webnotes.conn.get_value('Jobs Email Settings', None, 'extract_emails')):
diff --git a/hr/doctype/job_applicant/job_applicant.py b/hr/doctype/job_applicant/job_applicant.py
index 9bf1b96..0ab4ba8 100644
--- a/hr/doctype/job_applicant/job_applicant.py
+++ b/hr/doctype/job_applicant/job_applicant.py
@@ -11,14 +11,9 @@
class DocType(TransactionBase):
def __init__(self, d, dl):
self.doc, self.doclist = d, dl
-
+
def get_sender(self, comm):
- return webnotes.conn.get_value('Jobs Email Settings',None,'email_id')
-
- def on_communication(self, comm):
- if webnotes.conn.get_value("Profile", extract_email_id(comm.sender), "user_type")=="System User":
- status = "Replied"
- else:
- status = "Open"
-
- webnotes.conn.set(self.doc, 'status', status)
\ No newline at end of file
+ return webnotes.conn.get_value('Jobs Email Settings',None,'email_id')
+
+ def validate(self):
+ self.set_status()
\ No newline at end of file
diff --git a/hr/doctype/leave_allocation/leave_allocation.py b/hr/doctype/leave_allocation/leave_allocation.py
index 1c856fa..a058e1d 100755
--- a/hr/doctype/leave_allocation/leave_allocation.py
+++ b/hr/doctype/leave_allocation/leave_allocation.py
@@ -5,7 +5,6 @@
import webnotes
from webnotes.utils import cint, flt
from webnotes import msgprint
-sql = webnotes.conn.sql
class DocType:
def __init__(self, doc, doclist):
@@ -37,7 +36,7 @@
def check_existing_leave_allocation(self):
"""check whether leave for same type is already allocated or not"""
- leave_allocation = sql("""select name from `tabLeave Allocation`
+ leave_allocation = webnotes.conn.sql("""select name from `tabLeave Allocation`
where employee=%s and leave_type=%s and fiscal_year=%s and docstatus=1""",
(self.doc.employee, self.doc.leave_type, self.doc.fiscal_year))
if leave_allocation:
@@ -64,14 +63,14 @@
return self.get_leaves_allocated(prev_fyear) - self.get_leaves_applied(prev_fyear)
def get_leaves_applied(self, fiscal_year):
- leaves_applied = sql("""select SUM(ifnull(total_leave_days, 0))
+ leaves_applied = webnotes.conn.sql("""select SUM(ifnull(total_leave_days, 0))
from `tabLeave Application` where employee=%s and leave_type=%s
and fiscal_year=%s and docstatus=1""",
(self.doc.employee, self.doc.leave_type, fiscal_year))
return leaves_applied and flt(leaves_applied[0][0]) or 0
def get_leaves_allocated(self, fiscal_year):
- leaves_allocated = sql("""select SUM(ifnull(total_leaves_allocated, 0))
+ leaves_allocated = webnotes.conn.sql("""select SUM(ifnull(total_leaves_allocated, 0))
from `tabLeave Allocation` where employee=%s and leave_type=%s
and fiscal_year=%s and docstatus=1 and name!=%s""",
(self.doc.employee, self.doc.leave_type, fiscal_year, self.doc.name))
@@ -79,7 +78,7 @@
def allow_carry_forward(self):
"""check whether carry forward is allowed or not for this leave type"""
- cf = sql("""select is_carry_forward from `tabLeave Type` where name = %s""",
+ cf = webnotes.conn.sql("""select is_carry_forward from `tabLeave Type` where name = %s""",
self.doc.leave_type)
cf = cf and cint(cf[0][0]) or 0
if not cf:
@@ -110,7 +109,7 @@
webnotes.conn.set(self.doc,'total_leaves_allocated',flt(leave_det['total_leaves_allocated']))
def check_for_leave_application(self):
- exists = sql("""select name from `tabLeave Application`
+ exists = webnotes.conn.sql("""select name from `tabLeave Application`
where employee=%s and leave_type=%s and fiscal_year=%s and docstatus=1""",
(self.doc.employee, self.doc.leave_type, self.doc.fiscal_year))
if exists:
diff --git a/hr/doctype/leave_application/test_leave_application.py b/hr/doctype/leave_application/test_leave_application.py
index c89f7c4..7a900e3 100644
--- a/hr/doctype/leave_application/test_leave_application.py
+++ b/hr/doctype/leave_application/test_leave_application.py
@@ -7,6 +7,9 @@
from hr.doctype.leave_application.leave_application import LeaveDayBlockedError, OverlapError
class TestLeaveApplication(unittest.TestCase):
+ def tearDown(self):
+ webnotes.session.user = "Administrator"
+
def _clear_roles(self):
webnotes.conn.sql("""delete from `tabUserRole` where parent in
("test@example.com", "test1@example.com", "test2@example.com")""")
@@ -15,6 +18,7 @@
webnotes.conn.sql("""delete from `tabLeave Application`""")
def _add_employee_leave_approver(self, employee, leave_approver):
+ temp_session_user = webnotes.session.user
webnotes.session.user = "Administrator"
employee = webnotes.bean("Employee", employee)
employee.doclist.append({
@@ -23,6 +27,7 @@
"leave_approver": leave_approver
})
employee.save()
+ webnotes.session.user = temp_session_user
def get_application(self, doclist):
application = webnotes.bean(copy=doclist)
@@ -31,7 +36,6 @@
return application
def test_block_list(self):
- webnotes.session.user = "Administrator"
self._clear_roles()
from webnotes.profile import add_role
@@ -54,7 +58,6 @@
self.assertTrue(application.insert())
def test_overlap(self):
- webnotes.session.user = "Administrator"
self._clear_roles()
self._clear_applications()
@@ -72,7 +75,6 @@
self.assertRaises(OverlapError, application.insert)
def test_global_block_list(self):
- webnotes.session.user = "Administrator"
self._clear_roles()
from webnotes.profile import add_role
@@ -98,7 +100,6 @@
"applies_to_all_departments", 0)
def test_leave_approval(self):
- webnotes.session.user = "Administrator"
self._clear_roles()
from webnotes.profile import add_role
diff --git a/hr/doctype/leave_block_list/test_leave_block_list.py b/hr/doctype/leave_block_list/test_leave_block_list.py
index e266cd8..34814d9 100644
--- a/hr/doctype/leave_block_list/test_leave_block_list.py
+++ b/hr/doctype/leave_block_list/test_leave_block_list.py
@@ -7,6 +7,9 @@
from hr.doctype.leave_block_list.leave_block_list import get_applicable_block_dates
class TestLeaveBlockList(unittest.TestCase):
+ def tearDown(self):
+ webnotes.session.user = "Administrator"
+
def test_get_applicable_block_dates(self):
webnotes.session.user = "test@example.com"
webnotes.conn.set_value("Department", "_Test Department", "leave_block_list",
diff --git a/hr/doctype/leave_control_panel/leave_control_panel.py b/hr/doctype/leave_control_panel/leave_control_panel.py
index 30b52ba..294701c 100644
--- a/hr/doctype/leave_control_panel/leave_control_panel.py
+++ b/hr/doctype/leave_control_panel/leave_control_panel.py
@@ -9,7 +9,6 @@
from webnotes.model.code import get_obj
from webnotes import msgprint
-sql = webnotes.conn.sql
@@ -34,7 +33,7 @@
emp_query = "select name from `tabEmployee` "
if flag == 1:
emp_query += condition
- e = sql(emp_query)
+ e = webnotes.conn.sql(emp_query)
return e
# ----------------
diff --git a/hr/doctype/salary_manager/salary_manager.py b/hr/doctype/salary_manager/salary_manager.py
index 0eadca1..48dcab1 100644
--- a/hr/doctype/salary_manager/salary_manager.py
+++ b/hr/doctype/salary_manager/salary_manager.py
@@ -11,7 +11,6 @@
from webnotes.model.code import get_obj
from webnotes import msgprint
-sql = webnotes.conn.sql
@@ -30,7 +29,7 @@
cond = self.get_filter_condition()
cond += self.get_joining_releiving_condition()
- emp_list = sql("""
+ emp_list = webnotes.conn.sql("""
select t1.name
from `tabEmployee` t1, `tabSalary Structure` t2
where t1.docstatus!=2 and t2.docstatus != 2
@@ -68,7 +67,7 @@
def get_month_details(self, year, month):
- ysd = sql("select year_start_date from `tabFiscal Year` where name ='%s'"%year)[0][0]
+ ysd = webnotes.conn.sql("select year_start_date from `tabFiscal Year` where name ='%s'"%year)[0][0]
if ysd:
from dateutil.relativedelta import relativedelta
import calendar, datetime
@@ -96,7 +95,7 @@
emp_list = self.get_emp_list()
ss_list = []
for emp in emp_list:
- if not sql("""select name from `tabSalary Slip`
+ if not webnotes.conn.sql("""select name from `tabSalary Slip`
where docstatus!= 2 and employee = %s and month = %s and fiscal_year = %s and company = %s
""", (emp[0], self.doc.month, self.doc.fiscal_year, self.doc.company)):
ss = webnotes.bean({
@@ -127,7 +126,7 @@
which are not submitted
"""
cond = self.get_filter_condition()
- ss_list = sql("""
+ ss_list = webnotes.conn.sql("""
select t1.name from `tabSalary Slip` t1
where t1.docstatus = 0 and month = '%s' and fiscal_year = '%s' %s
""" % (self.doc.month, self.doc.fiscal_year, cond))
@@ -189,7 +188,7 @@
Get total salary amount from submitted salary slip based on selected criteria
"""
cond = self.get_filter_condition()
- tot = sql("""
+ tot = webnotes.conn.sql("""
select sum(rounded_total) from `tabSalary Slip` t1
where t1.docstatus = 1 and month = '%s' and fiscal_year = '%s' %s
""" % (self.doc.month, self.doc.fiscal_year, cond))
@@ -202,7 +201,7 @@
get default bank account,default salary acount from company
"""
amt = self.get_total_salary()
- com = sql("select default_bank_account from `tabCompany` where name = '%s'" % self.doc.company)
+ com = webnotes.conn.sql("select default_bank_account from `tabCompany` where name = '%s'" % self.doc.company)
if not com[0][0] or not com[0][1]:
msgprint("You can set Default Bank Account in Company master.")
diff --git a/hr/doctype/salary_manager/test_salary_manager.py b/hr/doctype/salary_manager/test_salary_manager.py
index 04000f0..13815db 100644
--- a/hr/doctype/salary_manager/test_salary_manager.py
+++ b/hr/doctype/salary_manager/test_salary_manager.py
@@ -9,7 +9,7 @@
# from webnotes.model.doc import Document
# from webnotes.model.code import get_obj
-# sql = webnotes.conn.sql
+# webnotes.conn.sql = webnotes.conn.sql
#
# class TestSalaryManager(unittest.TestCase):
# def setUp(self):
@@ -20,15 +20,15 @@
# ss1[0].employee = emp1.name
# for s in ss1: s.save(1)
# for s in ss1[1:]:
-# sql("update `tabSalary Structure Earning` set parent = '%s' where name = '%s'" % (ss1[0].name, s.name))
-# sql("update `tabSalary Structure Deduction` set parent = '%s' where name = '%s'" % (ss1[0].name, s.name))
+# webnotes.conn.sql("update `tabSalary Structure Earning` set parent = '%s' where name = '%s'" % (ss1[0].name, s.name))
+# webnotes.conn.sql("update `tabSalary Structure Deduction` set parent = '%s' where name = '%s'" % (ss1[0].name, s.name))
#
#
# ss2[0].employee = emp2.name
# for s in ss2: s.save(1)
# for s in ss2[1:]:
-# sql("update `tabSalary Structure Earning` set parent = '%s' where name = '%s'" % (ss2[0].name, s.name))
-# sql("update `tabSalary Structure Deduction` set parent = '%s' where name = '%s'" % (ss2[0].name, s.name))
+# webnotes.conn.sql("update `tabSalary Structure Earning` set parent = '%s' where name = '%s'" % (ss2[0].name, s.name))
+# webnotes.conn.sql("update `tabSalary Structure Deduction` set parent = '%s' where name = '%s'" % (ss2[0].name, s.name))
#
# sman.save()
# self.sm = get_obj('Salary Manager')
@@ -36,7 +36,7 @@
# self.sm.create_sal_slip()
#
# def test_creation(self):
-# ssid = sql("""
+# ssid = webnotes.conn.sql("""
# select name, department
# from `tabSalary Slip`
# where month = '08' and fiscal_year='2011-2012'""")
@@ -46,7 +46,7 @@
#
#
# def test_lwp_calc(self):
-# ss = sql("""
+# ss = webnotes.conn.sql("""
# select payment_days
# from `tabSalary Slip`
# where month = '08' and fiscal_year='2011-2012' and employee = '%s'
diff --git a/hr/doctype/salary_slip/salary_slip.py b/hr/doctype/salary_slip/salary_slip.py
index 36d7ceb..dab026e 100644
--- a/hr/doctype/salary_slip/salary_slip.py
+++ b/hr/doctype/salary_slip/salary_slip.py
@@ -11,7 +11,6 @@
from webnotes import msgprint, _
from setup.utils import get_company_currency
-sql = webnotes.conn.sql
from utilities.transaction_base import TransactionBase
@@ -32,7 +31,7 @@
def check_sal_struct(self):
- struct = sql("select name from `tabSalary Structure` where employee ='%s' and is_active = 'Yes' "%self.doc.employee)
+ struct = webnotes.conn.sql("select name from `tabSalary Structure` where employee ='%s' and is_active = 'Yes' "%self.doc.employee)
if not struct:
msgprint("Please create Salary Structure for employee '%s'"%self.doc.employee)
self.doc.employee = ''
@@ -100,13 +99,13 @@
return payment_days
def get_holidays_for_employee(self, m):
- holidays = sql("""select t1.holiday_date
+ holidays = webnotes.conn.sql("""select t1.holiday_date
from `tabHoliday` t1, tabEmployee t2
where t1.parent = t2.holiday_list and t2.name = %s
and t1.holiday_date between %s and %s""",
(self.doc.employee, m['month_start_date'], m['month_end_date']))
if not holidays:
- holidays = sql("""select t1.holiday_date
+ holidays = webnotes.conn.sql("""select t1.holiday_date
from `tabHoliday` t1, `tabHoliday List` t2
where t1.parent = t2.name and ifnull(t2.is_default, 0) = 1
and t2.fiscal_year = %s
@@ -120,7 +119,7 @@
for d in range(m['month_days']):
dt = add_days(cstr(m['month_start_date']), d)
if dt not in holidays:
- leave = sql("""
+ leave = webnotes.conn.sql("""
select t1.name, t1.half_day
from `tabLeave Application` t1, `tabLeave Type` t2
where t2.name = t1.leave_type
@@ -134,7 +133,7 @@
return lwp
def check_existing(self):
- ret_exist = sql("""select name from `tabSalary Slip`
+ ret_exist = webnotes.conn.sql("""select name from `tabSalary Slip`
where month = %s and fiscal_year = %s and docstatus != 2
and employee = %s and name != %s""",
(self.doc.month, self.doc.fiscal_year, self.doc.employee, self.doc.name))
@@ -201,9 +200,9 @@
receiver = webnotes.conn.get_value("Employee", self.doc.employee, "company_email")
if receiver:
subj = 'Salary Slip - ' + cstr(self.doc.month) +'/'+cstr(self.doc.fiscal_year)
- earn_ret=sql("""select e_type, e_modified_amount from `tabSalary Slip Earning`
+ earn_ret=webnotes.conn.sql("""select e_type, e_modified_amount from `tabSalary Slip Earning`
where parent = %s""", self.doc.name)
- ded_ret=sql("""select d_type, d_modified_amount from `tabSalary Slip Deduction`
+ ded_ret=webnotes.conn.sql("""select d_type, d_modified_amount from `tabSalary Slip Deduction`
where parent = %s""", self.doc.name)
earn_table = ''
diff --git a/hr/doctype/salary_structure/salary_structure.py b/hr/doctype/salary_structure/salary_structure.py
index 50b0160..bfa7850 100644
--- a/hr/doctype/salary_structure/salary_structure.py
+++ b/hr/doctype/salary_structure/salary_structure.py
@@ -8,7 +8,6 @@
from webnotes.model.doc import addchild, make_autoname
from webnotes import msgprint, _
-sql = webnotes.conn.sql
class DocType:
def __init__(self,doc,doclist=[]):
@@ -20,7 +19,7 @@
def get_employee_details(self):
ret = {}
- det = sql("""select employee_name, branch, designation, department, grade
+ det = webnotes.conn.sql("""select employee_name, branch, designation, department, grade
from `tabEmployee` where name = %s""", self.doc.employee)
if det:
ret = {
@@ -34,7 +33,7 @@
return ret
def get_ss_values(self,employee):
- basic_info = sql("""select bank_name, bank_ac_no, esic_card_no, pf_number
+ basic_info = webnotes.conn.sql("""select bank_name, bank_ac_no, esic_card_no, pf_number
from `tabEmployee` where name =%s""", employee)
ret = {'bank_name': basic_info and basic_info[0][0] or '',
'bank_ac_no': basic_info and basic_info[0][1] or '',
@@ -43,7 +42,7 @@
return ret
def make_table(self, doct_name, tab_fname, tab_name):
- list1 = sql("select name from `tab%s` where docstatus != 2" % doct_name)
+ list1 = webnotes.conn.sql("select name from `tab%s` where docstatus != 2" % doct_name)
for li in list1:
child = addchild(self.doc, tab_fname, tab_name, self.doclist)
if(tab_fname == 'earning_details'):
@@ -58,7 +57,7 @@
self.make_table('Deduction Type','deduction_details', 'Salary Structure Deduction')
def check_existing(self):
- ret = sql("""select name from `tabSalary Structure` where is_active = 'Yes'
+ ret = webnotes.conn.sql("""select name from `tabSalary Structure` where is_active = 'Yes'
and employee = %s and name!=%s""", (self.doc.employee,self.doc.name))
if ret and self.doc.is_active=='Yes':
msgprint(_("""Another Salary Structure '%s' is active for employee '%s'.
diff --git a/hr/doctype/upload_attendance/upload_attendance.py b/hr/doctype/upload_attendance/upload_attendance.py
index c1344b9..56c8eed 100644
--- a/hr/doctype/upload_attendance/upload_attendance.py
+++ b/hr/doctype/upload_attendance/upload_attendance.py
@@ -9,7 +9,8 @@
from webnotes import msgprint, _
from webnotes.utils.datautils import UnicodeWriter
-doclist = None
+# doclist = None
+doclist = webnotes.local('uploadattendance_doclist')
class DocType():
def __init__(self, doc, doclist=[]):
@@ -21,9 +22,8 @@
if not webnotes.has_permission("Attendance", "create"):
raise webnotes.PermissionError
- args = webnotes.form_dict
- global doclist
- doclist = webnotes.model.doctype.get("Attendance")
+ args = webnotes.local.form_dict
+ webnotes.local.uploadattendance_doclist = webnotes.model.doctype.get("Attendance")
w = UnicodeWriter()
w = add_header(w)
@@ -144,4 +144,4 @@
webnotes.conn.rollback()
else:
webnotes.conn.commit()
- return {"messages": ret, "error": error}
\ No newline at end of file
+ return {"messages": ret, "error": error}
diff --git a/manufacturing/doctype/bom/bom.py b/manufacturing/doctype/bom/bom.py
index 9a56612..97a2f96 100644
--- a/manufacturing/doctype/bom/bom.py
+++ b/manufacturing/doctype/bom/bom.py
@@ -9,7 +9,6 @@
from webnotes.model.code import get_obj
from webnotes import msgprint, _
-sql = webnotes.conn.sql
class DocType:
@@ -18,7 +17,7 @@
self.doclist = doclist
def autoname(self):
- last_name = sql("""select max(name) from `tabBOM`
+ last_name = webnotes.conn.sql("""select max(name) from `tabBOM`
where name like "BOM/%s/%%" """ % cstr(self.doc.item).replace('"', '\\"'))
if last_name:
idx = cint(cstr(last_name[0][0]).split('/')[-1].split('-')[0]) + 1
@@ -144,7 +143,7 @@
webnotes.bean(self.doclist).update_after_submit()
def get_bom_unitcost(self, bom_no):
- bom = sql("""select name, total_cost/quantity as unit_cost from `tabBOM`
+ bom = webnotes.conn.sql("""select name, total_cost/quantity as unit_cost from `tabBOM`
where is_active = 1 and name = %s""", bom_no, as_dict=1)
return bom and bom[0]['unit_cost'] or 0
@@ -156,7 +155,7 @@
from stock.utils import get_incoming_rate
dt = self.doc.costing_date or nowdate()
time = self.doc.costing_date == nowdate() and now().split()[1] or '23:59'
- warehouse = sql("select warehouse from `tabBin` where item_code = %s", args['item_code'])
+ warehouse = webnotes.conn.sql("select warehouse from `tabBin` where item_code = %s", args['item_code'])
rate = []
for wh in warehouse:
r = get_incoming_rate({
@@ -184,7 +183,7 @@
if not self.doc.is_active:
webnotes.conn.set(self.doc, "is_default", 0)
- sql("update `tabItem` set default_bom = null where name = %s and default_bom = %s",
+ webnotes.conn.sql("update `tabItem` set default_bom = null where name = %s and default_bom = %s",
(self.doc.item, self.doc.name))
def clear_operations(self):
@@ -250,7 +249,7 @@
def validate_bom_no(self, item, bom_no, idx):
"""Validate BOM No of sub-contracted items"""
- bom = sql("""select name from `tabBOM` where name = %s and item = %s
+ bom = webnotes.conn.sql("""select name from `tabBOM` where name = %s and item = %s
and is_active=1 and docstatus=1""",
(bom_no, item), as_dict =1)
if not bom:
@@ -272,7 +271,7 @@
for d in check_list:
bom_list, count = [self.doc.name], 0
while (len(bom_list) > count ):
- boms = sql(" select %s from `tabBOM Item` where %s = '%s' " %
+ boms = webnotes.conn.sql(" select %s from `tabBOM Item` where %s = '%s' " %
(d[0], d[1], cstr(bom_list[count])))
count = count + 1
for b in boms:
@@ -333,6 +332,7 @@
d.amount = flt(d.rate) * flt(d.qty)
d.qty_consumed_per_unit = flt(d.qty) / flt(self.doc.quantity)
total_rm_cost += d.amount
+
self.doc.raw_material_cost = total_rm_cost
def update_exploded_items(self):
@@ -364,7 +364,7 @@
def get_child_exploded_items(self, bom_no, qty):
""" Add all items from Flat BOM of child BOM"""
- child_fb_items = sql("""select item_code, description, stock_uom, qty, rate,
+ child_fb_items = webnotes.conn.sql("""select item_code, description, stock_uom, qty, rate,
qty_consumed_per_unit from `tabBOM Explosion Item`
where parent = %s and docstatus = 1""", bom_no, as_dict = 1)
@@ -390,12 +390,12 @@
ch.save(1)
def get_parent_bom_list(self, bom_no):
- p_bom = sql("select parent from `tabBOM Item` where bom_no = '%s'" % bom_no)
+ p_bom = webnotes.conn.sql("select parent from `tabBOM Item` where bom_no = '%s'" % bom_no)
return p_bom and [i[0] for i in p_bom] or []
def validate_bom_links(self):
if not self.doc.is_active:
- act_pbom = sql("""select distinct bom_item.parent from `tabBOM Item` bom_item
+ act_pbom = webnotes.conn.sql("""select distinct bom_item.parent from `tabBOM Item` bom_item
where bom_item.bom_no = %s and bom_item.docstatus = 1
and exists (select * from `tabBOM` where name = bom_item.parent
and docstatus = 1 and is_active = 1)""", self.doc.name)
@@ -403,4 +403,53 @@
if act_pbom and act_pbom[0][0]:
action = self.doc.docstatus < 2 and _("deactivate") or _("cancel")
msgprint(_("Cannot ") + action + _(": It is linked to other active BOM(s)"),
- raise_exception=1)
\ No newline at end of file
+ raise_exception=1)
+
+def get_bom_items_as_dict(bom, qty=1, fetch_exploded=1):
+ item_dict = {}
+
+ query = """select
+ bom_item.item_code,
+ ifnull(sum(bom_item.qty_consumed_per_unit),0) * %(qty)s as qty,
+ item.description,
+ item.stock_uom,
+ item.default_warehouse
+ from
+ `tab%(table)s` bom_item, `tabItem` item
+ where
+ bom_item.docstatus < 2
+ and bom_item.parent = "%(bom)s"
+ and item.name = bom_item.item_code
+ %(conditions)s
+ group by item_code, stock_uom"""
+
+ if fetch_exploded:
+ items = webnotes.conn.sql(query % {
+ "qty": qty,
+ "table": "BOM Explosion Item",
+ "bom": bom,
+ "conditions": """and ifnull(item.is_pro_applicable, 'No') = 'No'
+ and ifnull(item.is_sub_contracted_item, 'No') = 'No' """
+ }, as_dict=True)
+ else:
+ items = webnotes.conn.sql(query % {
+ "qty": qty,
+ "table": "BOM Item",
+ "bom": bom,
+ "conditions": ""
+ }, as_dict=True)
+
+ # make unique
+ for item in items:
+ if item_dict.has_key(item.item_code):
+ item_dict[item.item_code]["qty"] += flt(item.qty)
+ else:
+ item_dict[item.item_code] = item
+
+ return item_dict
+
+@webnotes.whitelist()
+def get_bom_items(bom, qty=1, fetch_exploded=1):
+ items = get_bom_items_as_dict(bom, qty, fetch_exploded).values()
+ items.sort(lambda a, b: a.item_code > b.item_code and 1 or -1)
+ return items
\ No newline at end of file
diff --git a/manufacturing/doctype/bom/test_bom.py b/manufacturing/doctype/bom/test_bom.py
index d0b394a..2f4424e 100644
--- a/manufacturing/doctype/bom/test_bom.py
+++ b/manufacturing/doctype/bom/test_bom.py
@@ -10,6 +10,35 @@
[
{
"doctype": "BOM",
+ "item": "_Test Item Home Desktop Manufactured",
+ "quantity": 1.0,
+ "is_active": 1,
+ "is_default": 1,
+ "docstatus": 1
+ },
+ {
+ "doctype": "BOM Item",
+ "item_code": "_Test Serialized Item With Series",
+ "parentfield": "bom_materials",
+ "qty": 1.0,
+ "rate": 5000.0,
+ "amount": 5000.0,
+ "stock_uom": "_Test UOM"
+ },
+ {
+ "doctype": "BOM Item",
+ "item_code": "_Test Item 2",
+ "parentfield": "bom_materials",
+ "qty": 2.0,
+ "rate": 1000.0,
+ "amount": 2000.0,
+ "stock_uom": "_Test UOM"
+ }
+ ],
+
+ [
+ {
+ "doctype": "BOM",
"item": "_Test FG Item",
"quantity": 1.0,
"is_active": 1,
@@ -34,5 +63,57 @@
"amount": 2000.0,
"stock_uom": "_Test UOM"
}
- ]
-]
\ No newline at end of file
+ ],
+
+ [
+ {
+ "doctype": "BOM",
+ "item": "_Test FG Item 2",
+ "quantity": 1.0,
+ "is_active": 1,
+ "is_default": 1,
+ "docstatus": 1
+ },
+ {
+ "doctype": "BOM Item",
+ "item_code": "_Test Item",
+ "parentfield": "bom_materials",
+ "qty": 1.0,
+ "rate": 5000.0,
+ "amount": 5000.0,
+ "stock_uom": "_Test UOM"
+ },
+ {
+ "doctype": "BOM Item",
+ "item_code": "_Test Item Home Desktop Manufactured",
+ "bom_no": "BOM/_Test Item Home Desktop Manufactured/001",
+ "parentfield": "bom_materials",
+ "qty": 2.0,
+ "rate": 1000.0,
+ "amount": 2000.0,
+ "stock_uom": "_Test UOM"
+ }
+ ],
+]
+
+class TestBOM(unittest.TestCase):
+ def test_get_items(self):
+ from manufacturing.doctype.bom.bom import get_bom_items_as_dict
+ items_dict = get_bom_items_as_dict(bom="BOM/_Test FG Item 2/001", qty=1, fetch_exploded=0)
+ self.assertTrue(test_records[2][1]["item_code"] in items_dict)
+ self.assertTrue(test_records[2][2]["item_code"] in items_dict)
+ self.assertEquals(len(items_dict.values()), 2)
+
+ def test_get_items_exploded(self):
+ from manufacturing.doctype.bom.bom import get_bom_items_as_dict
+ items_dict = get_bom_items_as_dict(bom="BOM/_Test FG Item 2/001", qty=1, fetch_exploded=1)
+ self.assertTrue(test_records[2][1]["item_code"] in items_dict)
+ self.assertFalse(test_records[2][2]["item_code"] in items_dict)
+ self.assertTrue(test_records[0][1]["item_code"] in items_dict)
+ self.assertTrue(test_records[0][2]["item_code"] in items_dict)
+ self.assertEquals(len(items_dict.values()), 3)
+
+ def test_get_items_list(self):
+ from manufacturing.doctype.bom.bom import get_bom_items
+ self.assertEquals(len(get_bom_items(bom="BOM/_Test FG Item 2/001", qty=1, fetch_exploded=1)), 3)
+
diff --git a/manufacturing/doctype/production_order/production_order.py b/manufacturing/doctype/production_order/production_order.py
index 6447c0a..36cbc64 100644
--- a/manufacturing/doctype/production_order/production_order.py
+++ b/manufacturing/doctype/production_order/production_order.py
@@ -8,7 +8,6 @@
from webnotes.model.code import get_obj
from webnotes import msgprint, _
-sql = webnotes.conn.sql
class OverProductionError(webnotes.ValidationError): pass
@@ -18,19 +17,23 @@
self.doclist = doclist
def validate(self):
+ if self.doc.docstatus == 0:
+ self.doc.status = "Draft"
+
import utilities
utilities.validate_status(self.doc.status, ["Draft", "Submitted", "Stopped",
"In Process", "Completed", "Cancelled"])
- if self.doc.production_item :
- item_detail = sql("select name from `tabItem` where name = '%s' and docstatus != 2"
- % self.doc.production_item, as_dict = 1)
- if not item_detail:
- msgprint("Item '%s' does not exist or cancelled in the system."
- % cstr(self.doc.production_item), raise_exception=1)
-
+ self.validate_bom_no()
+ self.validate_sales_order()
+ self.validate_warehouse()
+
+ from utilities.transaction_base import validate_uom_is_integer
+ validate_uom_is_integer(self.doclist, "stock_uom", ["qty", "produced_qty"])
+
+ def validate_bom_no(self):
if self.doc.bom_no:
- bom = sql("""select name from `tabBOM` where name=%s and docstatus=1
+ bom = webnotes.conn.sql("""select name from `tabBOM` where name=%s and docstatus=1
and is_active=1 and item=%s"""
, (self.doc.bom_no, self.doc.production_item), as_dict =1)
if not bom:
@@ -38,16 +41,20 @@
May be BOM not exists or inactive or not submitted
or for some other item.""" % cstr(self.doc.bom_no), raise_exception=1)
+ def validate_sales_order(self):
if self.doc.sales_order:
if not webnotes.conn.sql("""select name from `tabSales Order`
where name=%s and docstatus = 1""", self.doc.sales_order):
msgprint("Sales Order: %s is not valid" % self.doc.sales_order, raise_exception=1)
-
+
self.validate_production_order_against_so()
-
- from utilities.transaction_base import validate_uom_is_integer
- validate_uom_is_integer(self.doclist, "stock_uom", ["qty", "produced_qty"])
-
+
+ def validate_warehouse(self):
+ from stock.utils import validate_warehouse_user, validate_warehouse_company
+
+ for w in [self.doc.fg_warehouse, self.doc.wip_warehouse]:
+ validate_warehouse_user(w)
+ validate_warehouse_company(w, self.doc.company)
def validate_production_order_against_so(self):
# already ordered qty
@@ -104,7 +111,7 @@
def on_cancel(self):
# Check whether any stock entry exists against this Production Order
- stock_entry = sql("""select name from `tabStock Entry`
+ stock_entry = webnotes.conn.sql("""select name from `tabStock Entry`
where production_order = %s and docstatus = 1""", self.doc.name)
if stock_entry:
msgprint("""Submitted Stock Entry %s exists against this production order.
@@ -144,12 +151,6 @@
@webnotes.whitelist()
def make_stock_entry(production_order_id, purpose):
production_order = webnotes.bean("Production Order", production_order_id)
-
- # validate already existing
- ste = webnotes.conn.get_value("Stock Entry", {
- "production_order":production_order_id,
- "purpose": purpose
- }, "name")
stock_entry = webnotes.new_bean("Stock Entry")
stock_entry.doc.purpose = purpose
diff --git a/manufacturing/doctype/production_order/production_order.txt b/manufacturing/doctype/production_order/production_order.txt
index 782c99f..81821f6 100644
--- a/manufacturing/doctype/production_order/production_order.txt
+++ b/manufacturing/doctype/production_order/production_order.txt
@@ -2,11 +2,12 @@
{
"creation": "2013-01-10 16:34:16",
"docstatus": 0,
- "modified": "2013-08-08 14:22:12",
+ "modified": "2013-10-02 14:25:03",
"modified_by": "Administrator",
"owner": "Administrator"
},
{
+ "allow_import": 1,
"autoname": "naming_series:",
"doctype": "DocType",
"icon": "icon-cogs",
diff --git a/manufacturing/doctype/production_order/test_production_order.py b/manufacturing/doctype/production_order/test_production_order.py
new file mode 100644
index 0000000..9a75762
--- /dev/null
+++ b/manufacturing/doctype/production_order/test_production_order.py
@@ -0,0 +1,75 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd.
+# License: GNU General Public License v3. See license.txt
+
+
+from __future__ import unicode_literals
+import unittest
+import webnotes
+from stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory
+from manufacturing.doctype.production_order.production_order import make_stock_entry
+
+
+class TestProductionOrder(unittest.TestCase):
+ def test_planned_qty(self):
+ set_perpetual_inventory(0)
+ webnotes.conn.sql("delete from `tabStock Ledger Entry`")
+ webnotes.conn.sql("""delete from `tabBin`""")
+ webnotes.conn.sql("""delete from `tabGL Entry`""")
+
+ pro_bean = webnotes.bean(copy = test_records[0])
+ pro_bean.insert()
+ pro_bean.submit()
+
+ from stock.doctype.stock_entry.test_stock_entry import test_records as se_test_records
+ mr1 = webnotes.bean(copy = se_test_records[0])
+ mr1.insert()
+ mr1.submit()
+
+ mr2 = webnotes.bean(copy = se_test_records[0])
+ mr2.doclist[1].item_code = "_Test Item Home Desktop 100"
+ mr2.insert()
+ mr2.submit()
+
+ stock_entry = make_stock_entry(pro_bean.doc.name, "Manufacture/Repack")
+ stock_entry = webnotes.bean(stock_entry)
+
+ stock_entry.doc.fg_completed_qty = 4
+ stock_entry.run_method("get_items")
+ stock_entry.submit()
+
+ self.assertEqual(webnotes.conn.get_value("Production Order", pro_bean.doc.name,
+ "produced_qty"), 4)
+ self.assertEqual(webnotes.conn.get_value("Bin", {"item_code": "_Test FG Item",
+ "warehouse": "_Test Warehouse 1 - _TC"}, "planned_qty"), 6)
+
+ return pro_bean.doc.name
+
+ def test_over_production(self):
+ from stock.doctype.stock_entry.stock_entry import StockOverProductionError
+ pro_order = self.test_planned_qty()
+
+ stock_entry = make_stock_entry(pro_order, "Manufacture/Repack")
+ stock_entry = webnotes.bean(stock_entry)
+
+ stock_entry.doc.fg_completed_qty = 15
+ stock_entry.run_method("get_items")
+ stock_entry.insert()
+
+ self.assertRaises(StockOverProductionError, stock_entry.submit)
+
+
+
+test_records = [
+ [
+ {
+ "bom_no": "BOM/_Test FG Item/001",
+ "company": "_Test Company",
+ "doctype": "Production Order",
+ "production_item": "_Test FG Item",
+ "qty": 10.0,
+ "fg_warehouse": "_Test Warehouse 1 - _TC",
+ "wip_warehouse": "_Test Warehouse - _TC",
+ "stock_uom": "Nos"
+ }
+ ]
+]
\ No newline at end of file
diff --git a/manufacturing/doctype/production_planning_tool/production_planning_tool.py b/manufacturing/doctype/production_planning_tool/production_planning_tool.py
index 766f2ac..6f15d4c 100644
--- a/manufacturing/doctype/production_planning_tool/production_planning_tool.py
+++ b/manufacturing/doctype/production_planning_tool/production_planning_tool.py
@@ -9,7 +9,6 @@
from webnotes.model.code import get_obj
from webnotes import msgprint, _
-sql = webnotes.conn.sql
class DocType:
def __init__(self, doc, doclist=[]):
@@ -19,7 +18,7 @@
def get_so_details(self, so):
"""Pull other details from so"""
- so = sql("""select transaction_date, customer, grand_total
+ so = webnotes.conn.sql("""select transaction_date, customer, grand_total
from `tabSales Order` where name = %s""", so, as_dict = 1)
ret = {
'sales_order_date': so and so[0]['transaction_date'] or '',
@@ -31,7 +30,7 @@
def get_item_details(self, item_code):
""" Pull other item details from item master"""
- item = sql("""select description, stock_uom, default_bom
+ item = webnotes.conn.sql("""select description, stock_uom, default_bom
from `tabItem` where name = %s""", item_code, as_dict =1)
ret = {
'description' : item and item[0]['description'],
@@ -63,7 +62,7 @@
if self.doc.fg_item:
item_filter += ' and item.name = "' + self.doc.fg_item + '"'
- open_so = sql("""
+ open_so = webnotes.conn.sql("""
select distinct so.name, so.transaction_date, so.customer, so.grand_total
from `tabSales Order` so, `tabSales Order Item` so_item
where so_item.parent = so.name
@@ -108,7 +107,7 @@
msgprint("Please enter sales order in the above table")
return []
- items = sql("""select distinct parent, item_code, reserved_warehouse,
+ items = webnotes.conn.sql("""select distinct parent, item_code, reserved_warehouse,
(qty - ifnull(delivered_qty, 0)) as pending_qty
from `tabSales Order Item` so_item
where parent in (%s) and docstatus = 1 and ifnull(qty, 0) > ifnull(delivered_qty, 0)
@@ -117,7 +116,7 @@
or ifnull(item.is_sub_contracted_item, 'No') = 'Yes'))""" % \
(", ".join(["%s"] * len(so_list))), tuple(so_list), as_dict=1)
- dnpi_items = sql("""select distinct dnpi.parent, dnpi.item_code, dnpi.warehouse as reserved_warhouse,
+ dnpi_items = webnotes.conn.sql("""select distinct dnpi.parent, dnpi.item_code, dnpi.warehouse as reserved_warhouse,
(((so_item.qty - ifnull(so_item.delivered_qty, 0)) * dnpi.qty) / so_item.qty)
as pending_qty
from `tabSales Order Item` so_item, `tabDelivery Note Packing Item` dnpi
@@ -136,7 +135,7 @@
self.clear_item_table()
for p in items:
- item_details = sql("""select description, stock_uom, default_bom
+ item_details = webnotes.conn.sql("""select description, stock_uom, default_bom
from tabItem where name=%s""", p['item_code'])
pi = addchild(self.doc, 'pp_details', 'Production Plan Item', self.doclist)
pi.sales_order = p['parent']
@@ -162,7 +161,7 @@
msgprint("Please enter bom no for item: %s at row no: %s" %
(d.item_code, d.idx), raise_exception=1)
else:
- bom = sql("""select name from `tabBOM` where name = %s and item = %s
+ bom = webnotes.conn.sql("""select name from `tabBOM` where name = %s and item = %s
and docstatus = 1 and is_active = 1""",
(d.bom_no, d.item_code), as_dict = 1)
if not bom:
@@ -216,14 +215,14 @@
pro = webnotes.new_bean("Production Order")
pro.doc.fields.update(items[key])
- webnotes.mute_messages = True
+ webnotes.flags.mute_messages = True
try:
pro.insert()
pro_list.append(pro.doc.name)
except OverProductionError, e:
pass
- webnotes.mute_messages = False
+ webnotes.flags.mute_messages = False
return pro_list
@@ -243,7 +242,7 @@
for bom in bom_dict:
if self.doc.use_multi_level_bom:
# get all raw materials with sub assembly childs
- fl_bom_items = sql("""select fb.item_code,
+ fl_bom_items = webnotes.conn.sql("""select fb.item_code,
ifnull(sum(fb.qty_consumed_per_unit), 0)*%s as qty,
fb.description, fb.stock_uom, it.min_order_qty
from `tabBOM Explosion Item` fb,`tabItem` it
@@ -254,7 +253,7 @@
else:
# Get all raw materials considering SA items as raw materials,
# so no childs of SA items
- fl_bom_items = sql("""select bom_item.item_code,
+ fl_bom_items = webnotes.conn.sql("""select bom_item.item_code,
ifnull(sum(bom_item.qty_consumed_per_unit), 0) * %s,
bom_item.description, bom_item.stock_uom, item.min_order_qty
from `tabBOM Item` bom_item, tabItem item
@@ -274,7 +273,7 @@
'Quantity Requested for Purchase', 'Ordered Qty', 'Actual Qty']]
for d in self.item_dict:
item_list.append([d, self.item_dict[d][1], self.item_dict[d][2], self.item_dict[d][0]])
- item_qty= sql("""select warehouse, indented_qty, ordered_qty, actual_qty
+ item_qty= webnotes.conn.sql("""select warehouse, indented_qty, ordered_qty, actual_qty
from `tabBin` where item_code = %s""", d)
i_qty, o_qty, a_qty = 0, 0, 0
for w in item_qty:
diff --git a/manufacturing/doctype/workstation/workstation.py b/manufacturing/doctype/workstation/workstation.py
index 35e2c1f..cc12934 100644
--- a/manufacturing/doctype/workstation/workstation.py
+++ b/manufacturing/doctype/workstation/workstation.py
@@ -8,7 +8,6 @@
from webnotes.model import db_exists
from webnotes.model.bean import copy_doclist
-sql = webnotes.conn.sql
@@ -18,9 +17,9 @@
self.doclist = doclist
def update_bom_operation(self):
- bom_list = sql(" select DISTINCT parent from `tabBOM Operation` where workstation = '%s'" % self.doc.name)
+ bom_list = webnotes.conn.sql(" select DISTINCT parent from `tabBOM Operation` where workstation = '%s'" % self.doc.name)
for bom_no in bom_list:
- sql("update `tabBOM Operation` set hour_rate = '%s' where parent = '%s' and workstation = '%s'"%( self.doc.hour_rate, bom_no[0], self.doc.name))
+ webnotes.conn.sql("update `tabBOM Operation` set hour_rate = '%s' where parent = '%s' and workstation = '%s'"%( self.doc.hour_rate, bom_no[0], self.doc.name))
def on_update(self):
webnotes.conn.set(self.doc, 'overhead', flt(self.doc.hour_rate_electricity) + flt(self.doc.hour_rate_consumable) + flt(self.doc.hour_rate_rent))
diff --git a/patches/april_2013/p05_update_file_data.py b/patches/april_2013/p05_update_file_data.py
index e03abc6..47d6de9 100644
--- a/patches/april_2013/p05_update_file_data.py
+++ b/patches/april_2013/p05_update_file_data.py
@@ -52,7 +52,7 @@
exists = True
if not (filename.startswith("http://") or filename.startswith("https://")):
- if not os.path.exists(webnotes.utils.get_path("public", "files", filename)):
+ if not os.path.exists(webnotes.utils.get_site_path(webnotes.conf.files_path, filename)):
exists = False
if exists:
diff --git a/patches/april_2013/p06_update_file_size.py b/patches/april_2013/p06_update_file_size.py
index 6879625..973cea9 100644
--- a/patches/april_2013/p06_update_file_size.py
+++ b/patches/april_2013/p06_update_file_size.py
@@ -4,7 +4,7 @@
import webnotes, os, webnotes.utils
def execute():
- files_path = webnotes.utils.get_path("public", "files")
+ files_path = webnotes.utils.get_site_path(webnotes.conf.files_path)
webnotes.conn.auto_commit_on_many_writes = 1
for f in webnotes.conn.sql("""select name, file_name from
@@ -14,4 +14,4 @@
if os.path.exists(filepath):
webnotes.conn.set_value("File Data", f.name, "file_size", os.stat(filepath).st_size)
- webnotes.conn.auto_commit_on_many_writes = 0
\ No newline at end of file
+ webnotes.conn.auto_commit_on_many_writes = 0
diff --git a/patches/august_2013/p02_rename_price_list.py b/patches/august_2013/p02_rename_price_list.py
index 41efb27..0a19299 100644
--- a/patches/august_2013/p02_rename_price_list.py
+++ b/patches/august_2013/p02_rename_price_list.py
@@ -6,6 +6,7 @@
def execute():
webnotes.reload_doc("selling", "doctype", "shopping_cart_price_list")
+ webnotes.reload_doc("setup", "doctype", "item_price")
for t in [
("Supplier Quotation", "price_list_name", "buying_price_list"),
diff --git a/patches/august_2013/p06_fix_sle_against_stock_entry.py b/patches/august_2013/p06_fix_sle_against_stock_entry.py
index 02588be..2e6c383 100644
--- a/patches/august_2013/p06_fix_sle_against_stock_entry.py
+++ b/patches/august_2013/p06_fix_sle_against_stock_entry.py
@@ -1,10 +1,9 @@
import webnotes
-cancelled = []
-uncancelled = []
-
def execute():
- global cancelled, uncancelled
+ cancelled = []
+ uncancelled = []
+
stock_entries = webnotes.conn.sql("""select * from `tabStock Entry`
where docstatus >= 1 and date(modified) >= "2013-08-16"
and ifnull(production_order, '') != '' and ifnull(bom_no, '') != ''
@@ -17,14 +16,12 @@
where voucher_type='Stock Entry' and voucher_no=%s
and is_cancelled='No'""", entry.name, as_dict=True)
if res:
- make_stock_entry_detail(entry, res)
+ make_stock_entry_detail(entry, res, cancelled, uncancelled)
if cancelled or uncancelled:
- send_email()
+ send_email(cancelled, uncancelled)
-def make_stock_entry_detail(entry, res):
- global cancelled, uncancelled
-
+def make_stock_entry_detail(entry, res, cancelled, uncancelled):
fg_item = webnotes.conn.get_value("Production Order", entry.production_order,
"production_item")
voucher_detail_entries_map = {}
@@ -87,9 +84,8 @@
uncancelled.append(se.doc.name)
-def send_email():
+def send_email(cancelled, uncancelled):
from webnotes.utils.email_lib import sendmail_to_system_managers
- global cancelled, uncancelled
uncancelled = "we have undone the cancellation of the following Stock Entries through a patch:\n" + \
"\n".join(uncancelled) if uncancelled else ""
cancelled = "and cancelled the following Stock Entries:\n" + "\n".join(cancelled) \
diff --git a/patches/december_2012/repost_ordered_qty.py b/patches/december_2012/repost_ordered_qty.py
index 2e3c690..4c1d11d 100644
--- a/patches/december_2012/repost_ordered_qty.py
+++ b/patches/december_2012/repost_ordered_qty.py
@@ -3,16 +3,9 @@
def execute():
import webnotes
- from webnotes.utils import flt
- bins = webnotes.conn.sql("select item_code, warehouse, name, ordered_qty from `tabBin`")
- for d in bins:
- ordered_qty = webnotes.conn.sql("""
- select sum(ifnull(po_item.qty, 0) - ifnull(po_item.received_qty, 0))
- from `tabPurchase Order Item` po_item, `tabPurchase Order` po
- where po_item.parent = po.name and po.docstatus = 1 and po.status != 'Stopped'
- and po_item.item_code = %s and po_item.warehouse = %s
- """, (d[0], d[1]))
-
- if flt(d[3]) != flt(ordered_qty[0][0]):
- webnotes.conn.sql("""update `tabBin` set ordered_qty = %s where name = %s""",
- (ordered_qty and ordered_qty[0][0] or 0, d[2]))
\ No newline at end of file
+ from utilities.repost_stock import get_ordered_qty, update_bin
+
+ for d in webnotes.conn.sql("select item_code, warehouse from tabBin"):
+ update_bin(d[0], d[1], {
+ "ordered_qty": get_ordered_qty(d[0], d[1])
+ })
\ No newline at end of file
diff --git a/patches/february_2013/repost_reserved_qty.py b/patches/february_2013/repost_reserved_qty.py
index 3a3353f..5c41266 100644
--- a/patches/february_2013/repost_reserved_qty.py
+++ b/patches/february_2013/repost_reserved_qty.py
@@ -4,54 +4,10 @@
import webnotes
def execute():
webnotes.conn.auto_commit_on_many_writes = 1
- repost_reserved_qty()
- webnotes.conn.auto_commit_on_many_writes = 0
+ from utilities.repost_stock import get_reserved_qty, update_bin
-def repost_reserved_qty():
- from webnotes.utils import flt
- bins = webnotes.conn.sql("select item_code, warehouse, name, reserved_qty from `tabBin`")
- i = 0
- for d in bins:
- i += 1
- reserved_qty = webnotes.conn.sql("""
- select
- sum((dnpi_qty / so_item_qty) * (so_item_qty - so_item_delivered_qty))
- from
- (
- (select
- qty as dnpi_qty,
- (
- select qty from `tabSales Order Item`
- where name = dnpi.parent_detail_docname
- ) as so_item_qty,
- (
- select ifnull(delivered_qty, 0) from `tabSales Order Item`
- where name = dnpi.parent_detail_docname
- ) as so_item_delivered_qty,
- parent, name
- from
- (
- select qty, parent_detail_docname, parent, name
- from `tabDelivery Note Packing Item` dnpi_in
- where item_code = %s and warehouse = %s
- and parenttype="Sales Order"
- and item_code != parent_item
- and exists (select * from `tabSales Order` so
- where name = dnpi_in.parent and docstatus = 1 and status != 'Stopped')
- ) dnpi)
- union
- (select qty as dnpi_qty, qty as so_item_qty,
- ifnull(delivered_qty, 0) as so_item_delivered_qty, parent, name
- from `tabSales Order Item` so_item
- where item_code = %s and reserved_warehouse = %s
- and exists(select * from `tabSales Order` so
- where so.name = so_item.parent and so.docstatus = 1
- and so.status != 'Stopped'))
- ) tab
- where
- so_item_qty >= so_item_delivered_qty
- """, (d[0], d[1], d[0], d[1]))
-
- if flt(d[3]) != flt(reserved_qty[0][0]):
- webnotes.conn.sql("""update `tabBin` set reserved_qty = %s where name = %s""",
- (reserved_qty and reserved_qty[0][0] or 0, d[2]))
\ No newline at end of file
+ for d in webnotes.conn.sql("select item_code, warehouse from tabBin"):
+ update_bin(d[0], d[1], {
+ "reserved_qty": get_reserved_qty(d[0], d[1])
+ })
+ webnotes.conn.auto_commit_on_many_writes = 0
\ No newline at end of file
diff --git a/patches/june_2013/p08_shopping_cart_settings.py b/patches/june_2013/p08_shopping_cart_settings.py
index 479a696..677b62a 100644
--- a/patches/june_2013/p08_shopping_cart_settings.py
+++ b/patches/june_2013/p08_shopping_cart_settings.py
@@ -7,7 +7,7 @@
webnotes.reload_doc("selling", "doctype", "shopping_cart_settings")
# create two default territories, one for home country and one named Rest of the World
- from setup.doctype.setup_control.setup_control import create_territories
+ from setup.page.setup_wizard.setup_wizard import create_territories
create_territories()
webnotes.conn.set_value("Shopping Cart Settings", None, "default_territory", "Rest of the World")
diff --git a/patches/october_2013/__init__.py b/patches/october_2013/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/patches/october_2013/__init__.py
diff --git a/patches/october_2013/fix_is_cancelled_in_sle.py b/patches/october_2013/fix_is_cancelled_in_sle.py
new file mode 100644
index 0000000..cb51b5d
--- /dev/null
+++ b/patches/october_2013/fix_is_cancelled_in_sle.py
@@ -0,0 +1,13 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd.
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import webnotes
+
+def execute():
+ webnotes.conn.sql("""update `tabStock Ledger Entry` set is_cancelled = 'No'
+ where ifnull(is_cancelled, '') = ''""")
+
+ webnotes.conn.sql("""update tabBin b set b.stock_uom =
+ (select i.stock_uom from tabItem i where i.name = b.item_code)
+ where b.creation>='2013-09-01'""")
\ No newline at end of file
diff --git a/patches/october_2013/p01_fix_serial_no_status.py b/patches/october_2013/p01_fix_serial_no_status.py
new file mode 100644
index 0000000..0bfc400
--- /dev/null
+++ b/patches/october_2013/p01_fix_serial_no_status.py
@@ -0,0 +1,18 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd.
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import webnotes
+from webnotes.utils import flt
+
+def execute():
+ serial_nos = webnotes.conn.sql("""select name from `tabSerial No` where status!='Not in Use'
+ and docstatus=0""")
+ for sr in serial_nos:
+ sr_bean = webnotes.bean("Serial No", sr[0])
+ sr_bean.make_controller().via_stock_ledger = True
+ sr_bean.run_method("validate")
+ sr_bean.save()
+
+ webnotes.conn.sql("""update `tabSerial No` set warehouse='' where status in
+ ('Delivered', 'Purchase Returned')""")
\ No newline at end of file
diff --git a/patches/october_2013/p01_update_delivery_note_prevdocs.py b/patches/october_2013/p01_update_delivery_note_prevdocs.py
new file mode 100644
index 0000000..75ac53f
--- /dev/null
+++ b/patches/october_2013/p01_update_delivery_note_prevdocs.py
@@ -0,0 +1,13 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd.
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import webnotes
+
+def execute():
+ webnotes.reload_doc("stock", "doctype", "delivery_note_item")
+ webnotes.conn.sql("""update `tabDelivery Note Item` set against_sales_order=prevdoc_docname
+ where prevdoc_doctype='Sales Order' """)
+
+ webnotes.conn.sql("""update `tabDelivery Note Item` set against_sales_invoice=prevdoc_docname
+ where prevdoc_doctype='Sales Invoice' """)
\ No newline at end of file
diff --git a/patches/october_2013/p02_set_communication_status.py b/patches/october_2013/p02_set_communication_status.py
new file mode 100644
index 0000000..d67d08d
--- /dev/null
+++ b/patches/october_2013/p02_set_communication_status.py
@@ -0,0 +1,11 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd.
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import webnotes
+
+def execute():
+ webnotes.reload_doc("core", "doctype", "communication")
+
+ webnotes.conn.sql("""update tabCommunication
+ set sent_or_received= if(ifnull(recipients, '')='', "Received", "Sent")""")
\ No newline at end of file
diff --git a/patches/october_2013/p03_crm_update_status.py b/patches/october_2013/p03_crm_update_status.py
new file mode 100644
index 0000000..07a70c6
--- /dev/null
+++ b/patches/october_2013/p03_crm_update_status.py
@@ -0,0 +1,47 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd.
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import webnotes
+
+# reason field
+
+def execute():
+ change_map = {
+ "Lead": [
+ ["Lead Lost", "Lead"],
+ ["Not interested", "Do Not Contact"],
+ ["Opportunity Made", "Opportunity"],
+ ["Contacted", "Replied"],
+ ["Attempted to Contact", "Replied"],
+ ["Contact in Future", "Interested"],
+ ],
+ "Opportunity": [
+ ["Quotation Sent", "Quotation"],
+ ["Order Confirmed", "Quotation"],
+ ["Opportunity Lost", "Lost"],
+ ],
+ "Quotation": [
+ ["Order Confirmed", "Ordered"],
+ ["Order Lost", "Lost"]
+ ],
+ "Support Ticket": [
+ ["Waiting for Customer", "Replied"],
+ ["To Reply", "Open"],
+ ]
+ }
+
+ for dt, opts in change_map.items():
+ for status in opts:
+ webnotes.conn.sql("""update `tab%s` set status=%s where status=%s""" % \
+ (dt, "%s", "%s"), (status[1], status[0]))
+
+ for dt in ["Lead", "Opportunity"]:
+ for name in webnotes.conn.sql_list("""select name from `tab%s`""" % dt):
+ bean = webnotes.bean(dt, name)
+ before_status = bean.doc.status
+ bean.get_controller().set_status()
+
+ if bean.doc.status != before_status:
+ webnotes.conn.sql("""update `tab%s` set status=%s where name=%s""" % (dt, "%s", "%s"),
+ (bean.doc.status, name))
diff --git a/patches/october_2013/p04_wsgi_migration.py b/patches/october_2013/p04_wsgi_migration.py
new file mode 100644
index 0000000..ca73516
--- /dev/null
+++ b/patches/october_2013/p04_wsgi_migration.py
@@ -0,0 +1,30 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd.
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import webnotes
+import webnotes.utils
+import os
+
+def execute():
+ base_path = webnotes.utils.get_base_path()
+
+ # Remove symlinks from public folder:
+ # - server.py
+ # - web.py
+ # - unsupported.html
+ # - blank.html
+ # - rss.xml
+ # - sitemap.xml
+ for file in ("server.py", "web.py", "unsupported.html", "blank.html", "rss.xml", "sitemap.xml"):
+ file_path = os.path.join(base_path, "public", file)
+ if os.path.exists(file_path):
+ os.remove(file_path)
+
+ # Remove wn-web files
+ # - js/wn-web.js
+ # - css/wn-web.css
+ for file_path in (("js", "wn-web.js"), ("css", "wn-web.css")):
+ file_path = os.path.join(base_path, "public", *file_path)
+ if os.path.exists(file_path):
+ os.remove(file_path)
\ No newline at end of file
diff --git a/patches/october_2013/p05_server_custom_script_to_file.py b/patches/october_2013/p05_server_custom_script_to_file.py
new file mode 100644
index 0000000..3a7e770
--- /dev/null
+++ b/patches/october_2013/p05_server_custom_script_to_file.py
@@ -0,0 +1,36 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd.
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import webnotes
+
+def execute():
+ """
+ Assuming that some kind of indentation exists:
+ - Find indentation of server custom script
+ - replace indentation with tabs
+ - Add line:
+ class CustomDocType(DocType):
+ - Add tab indented code after this line
+ - Write to file
+ - Delete custom script record
+ """
+ import os
+ from webnotes.utils import get_site_base_path
+ from core.doctype.custom_script.custom_script import make_custom_server_script_file
+ for name, dt, script in webnotes.conn.sql("""select name, dt, script from `tabCustom Script`
+ where script_type='Server'"""):
+ if script.strip():
+ script = indent_using_tabs(script)
+ make_custom_server_script_file(dt, script)
+
+def indent_using_tabs(script):
+ for line in script.split("\n"):
+ try:
+ indentation_used = line[:line.index("def ")]
+ script = script.replace(indentation_used, "\t")
+ break
+ except ValueError:
+ pass
+
+ return script
\ No newline at end of file
diff --git a/patches/october_2013/perpetual_inventory_stock_transfer_utility.py b/patches/october_2013/perpetual_inventory_stock_transfer_utility.py
new file mode 100644
index 0000000..d8cade7
--- /dev/null
+++ b/patches/october_2013/perpetual_inventory_stock_transfer_utility.py
@@ -0,0 +1,86 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd.
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import webnotes
+from webnotes.utils import nowdate, nowtime, cstr
+from accounts.utils import get_fiscal_year
+
+def execute():
+ item_map = {}
+ for item in webnotes.conn.sql("""select * from tabItem""", as_dict=1):
+ item_map.setdefault(item.name, item)
+
+ warehouse_map = get_warehosue_map()
+ naming_series = "STE/13/"
+
+ for company in webnotes.conn.sql("select name from tabCompany"):
+ stock_entry = [{
+ "doctype": "Stock Entry",
+ "naming_series": naming_series,
+ "posting_date": nowdate(),
+ "posting_time": nowtime(),
+ "purpose": "Material Transfer",
+ "company": company[0],
+ "remarks": "Material Transfer to activate perpetual inventory",
+ "fiscal_year": get_fiscal_year(nowdate())[0]
+ }]
+ expense_account = "Cost of Goods Sold - NISL"
+ cost_center = "Default CC Ledger - NISL"
+
+ for bin in webnotes.conn.sql("""select * from tabBin bin where ifnull(item_code, '')!=''
+ and ifnull(warehouse, '') in (%s) and ifnull(actual_qty, 0) != 0
+ and (select company from tabWarehouse where name=bin.warehouse)=%s""" %
+ (', '.join(['%s']*len(warehouse_map)), '%s'),
+ (warehouse_map.keys() + [company[0]]), as_dict=1):
+ item_details = item_map[bin.item_code]
+ new_warehouse = warehouse_map[bin.warehouse].get("fixed_asset_warehouse") \
+ if cstr(item_details.is_asset_item) == "Yes" \
+ else warehouse_map[bin.warehouse].get("current_asset_warehouse")
+
+ if item_details.has_serial_no == "Yes":
+ serial_no = "\n".join([d[0] for d in webnotes.conn.sql("""select name
+ from `tabSerial No` where item_code = %s and warehouse = %s
+ and status in ('Available', 'Sales Returned')""",
+ (bin.item_code, bin.warehouse))])
+ else:
+ serial_no = None
+
+ stock_entry.append({
+ "doctype": "Stock Entry Detail",
+ "parentfield": "mtn_details",
+ "s_warehouse": bin.warehouse,
+ "t_warehouse": new_warehouse,
+ "item_code": bin.item_code,
+ "description": item_details.description,
+ "qty": bin.actual_qty,
+ "transfer_qty": bin.actual_qty,
+ "uom": item_details.stock_uom,
+ "stock_uom": item_details.stock_uom,
+ "conversion_factor": 1,
+ "expense_account": expense_account,
+ "cost_center": cost_center,
+ "serial_no": serial_no
+ })
+
+ webnotes.bean(stock_entry).insert()
+
+def get_warehosue_map():
+ return {
+ "MAHAPE": {
+ "current_asset_warehouse": "Mahape-New - NISL",
+ "fixed_asset_warehouse": ""
+ },
+ "DROP SHIPMENT": {
+ "current_asset_warehouse": "Drop Shipment-New - NISL",
+ "fixed_asset_warehouse": ""
+ },
+ "TRANSIT": {
+ "current_asset_warehouse": "Transit-New - NISL",
+ "fixed_asset_warehouse": ""
+ },
+ "ASSET - MAHAPE": {
+ "current_asset_warehouse": "",
+ "fixed_asset_warehouse": "Assets-New - NISL"
+ }
+ }
\ No newline at end of file
diff --git a/patches/october_2013/repost_ordered_qty.py b/patches/october_2013/repost_ordered_qty.py
new file mode 100644
index 0000000..5582794
--- /dev/null
+++ b/patches/october_2013/repost_ordered_qty.py
@@ -0,0 +1,6 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd.
+# License: GNU General Public License v3. See license.txt
+
+def execute():
+ from patches.december_2012 import repost_ordered_qty
+ repost_ordered_qty.execute()
\ No newline at end of file
diff --git a/patches/october_2013/repost_planned_qty.py b/patches/october_2013/repost_planned_qty.py
new file mode 100644
index 0000000..cfe47ca
--- /dev/null
+++ b/patches/october_2013/repost_planned_qty.py
@@ -0,0 +1,11 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd.
+# License: GNU General Public License v3. See license.txt
+
+def execute():
+ import webnotes
+ from utilities.repost_stock import get_planned_qty, update_bin
+
+ for d in webnotes.conn.sql("select item_code, warehouse from tabBin"):
+ update_bin(d[0], d[1], {
+ "planned_qty": get_planned_qty(d[0], d[1])
+ })
\ No newline at end of file
diff --git a/patches/october_2013/set_stock_value_diff_in_sle.py b/patches/october_2013/set_stock_value_diff_in_sle.py
new file mode 100644
index 0000000..25f95e0
--- /dev/null
+++ b/patches/october_2013/set_stock_value_diff_in_sle.py
@@ -0,0 +1,10 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd.
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import webnotes
+from webnotes.utils import cint
+
+def execute():
+ from patches.september_2012 import repost_stock
+ repost_stock.execute()
\ No newline at end of file
diff --git a/patches/patch_list.py b/patches/patch_list.py
index 7041ba8..9851986 100644
--- a/patches/patch_list.py
+++ b/patches/patch_list.py
@@ -3,10 +3,8 @@
from __future__ import unicode_literals
patch_list = [
- "execute:webnotes.reload_doc('core', 'doctype', 'doctype', force=True) #2013-07-15",
- "execute:webnotes.reload_doc('core', 'doctype', 'docfield', force=True) #2013-07-15",
- "execute:webnotes.reload_doc('core', 'doctype', 'doctype', force=True) #2013-07-16",
- "execute:webnotes.reload_doc('core', 'doctype', 'docfield', force=True) #2013-07-16",
+ "execute:webnotes.reload_doc('core', 'doctype', 'doctype', force=True) #2013-10-15",
+ "execute:webnotes.reload_doc('core', 'doctype', 'docfield', force=True) #2013-10-15",
"execute:webnotes.reload_doc('core', 'doctype', 'docperm') #2013-07-16",
"execute:webnotes.reload_doc('core', 'doctype', 'page') #2013-07-16",
"execute:webnotes.reload_doc('core', 'doctype', 'report') #2013-07-16",
@@ -218,4 +216,14 @@
"execute:webnotes.bean('Style Settings').save() #2013-09-19",
"execute:webnotes.conn.set_value('Accounts Settings', None, 'frozen_accounts_modifier', 'Accounts Manager') # 2013-09-24",
"patches.september_2013.p04_unsubmit_serial_nos",
+ "patches.september_2013.p05_fix_customer_in_pos",
+ "patches.october_2013.fix_is_cancelled_in_sle",
+ "patches.october_2013.p01_update_delivery_note_prevdocs",
+ "patches.october_2013.p02_set_communication_status",
+ "patches.october_2013.p03_crm_update_status",
+ "execute:webnotes.delete_doc('DocType', 'Setup Control')",
+ "patches.october_2013.p04_wsgi_migration",
+ "patches.october_2013.p05_server_custom_script_to_file",
+ "patches.october_2013.repost_ordered_qty",
+ "patches.october_2013.repost_planned_qty",
]
\ No newline at end of file
diff --git a/patches/september_2013/p05_fix_customer_in_pos.py b/patches/september_2013/p05_fix_customer_in_pos.py
new file mode 100644
index 0000000..60210da
--- /dev/null
+++ b/patches/september_2013/p05_fix_customer_in_pos.py
@@ -0,0 +1,22 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd.
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import webnotes
+def execute():
+ si_list = webnotes.conn.sql("""select name, debit_to from `tabSales Invoice`
+ where ifnull(is_pos, 1)=1 and docstatus=1 and modified > '2013-09-03'""", as_dict=1)
+
+ for si in si_list:
+ if not webnotes.conn.get_value("GL Entry", {"voucher_type": "Sales Invoice",
+ "voucher_no": si.name, "account": si.debit_to}):
+ debit_to = webnotes.conn.sql("""select account from `tabGL Entry` gle
+ where voucher_type='Sales Invoice' and voucher_no=%s
+ and (select master_type from tabAccount where name=gle.account)='Customer'
+ """, si.name)
+ if debit_to:
+ si_bean = webnotes.bean("Sales Invoice", si.name)
+ si_bean.doc.debit_to = debit_to[0][0]
+ si_bean.doc.customer = None
+ si_bean.run_method("set_customer_defaults")
+ si_bean.update_after_submit()
\ No newline at end of file
diff --git a/portal/templates/pages/cart.py b/portal/templates/pages/cart.py
index 24b474a..ecf3163 100644
--- a/portal/templates/pages/cart.py
+++ b/portal/templates/pages/cart.py
@@ -3,4 +3,5 @@
from __future__ import unicode_literals
-no_cache = True
\ No newline at end of file
+no_cache = True
+no_sitemap = True
\ No newline at end of file
diff --git a/portal/templates/pages/profile.py b/portal/templates/pages/profile.py
index 8edd830..3a75cfb 100644
--- a/portal/templates/pages/profile.py
+++ b/portal/templates/pages/profile.py
@@ -7,6 +7,7 @@
from webnotes.utils import cstr
no_cache = True
+no_sitemap = True
def get_context():
from selling.utils.cart import get_lead_or_customer
@@ -33,7 +34,7 @@
return _("Name is required")
webnotes.conn.set_value("Profile", webnotes.session.user, "first_name", fullname)
- webnotes.add_cookies["full_name"] = fullname
+ webnotes._response.set_cookie("full_name", fullname)
return _("Updated")
\ No newline at end of file
diff --git a/projects/doctype/project/project.txt b/projects/doctype/project/project.txt
index 91dcfa1..fc8accf 100644
--- a/projects/doctype/project/project.txt
+++ b/projects/doctype/project/project.txt
@@ -2,12 +2,13 @@
{
"creation": "2013-03-07 11:55:07",
"docstatus": 0,
- "modified": "2013-07-05 14:51:41",
+ "modified": "2013-10-02 14:25:02",
"modified_by": "Administrator",
"owner": "Administrator"
},
{
"allow_attach": 1,
+ "allow_import": 1,
"autoname": "field:project_name",
"doctype": "DocType",
"document_type": "Master",
diff --git a/projects/doctype/task/task.py b/projects/doctype/task/task.py
index 6cd1ba8..fb9b6ab 100644
--- a/projects/doctype/task/task.py
+++ b/projects/doctype/task/task.py
@@ -9,7 +9,6 @@
from webnotes.model.bean import copy_doclist
from webnotes import msgprint
-sql = webnotes.conn.sql
class DocType:
def __init__(self,doc,doclist=[]):
@@ -17,13 +16,13 @@
self.doclist = doclist
def get_project_details(self):
- cust = sql("select customer, customer_name from `tabProject` where name = %s", self.doc.project)
+ cust = webnotes.conn.sql("select customer, customer_name from `tabProject` where name = %s", self.doc.project)
if cust:
ret = {'customer': cust and cust[0][0] or '', 'customer_name': cust and cust[0][1] or ''}
return ret
def get_customer_details(self):
- cust = sql("select customer_name from `tabCustomer` where name=%s", self.doc.customer)
+ cust = webnotes.conn.sql("select customer_name from `tabCustomer` where name=%s", self.doc.customer)
if cust:
ret = {'customer_name': cust and cust[0][0] or ''}
return ret
diff --git a/projects/doctype/task/task.txt b/projects/doctype/task/task.txt
index d890bd6..1c12c8a 100644
--- a/projects/doctype/task/task.txt
+++ b/projects/doctype/task/task.txt
@@ -2,12 +2,13 @@
{
"creation": "2013-01-29 19:25:50",
"docstatus": 0,
- "modified": "2013-07-05 14:57:57",
+ "modified": "2013-10-02 14:25:00",
"modified_by": "Administrator",
"owner": "Administrator"
},
{
"allow_attach": 1,
+ "allow_import": 1,
"autoname": "TASK.#####",
"doctype": "DocType",
"document_type": "Master",
diff --git a/projects/doctype/time_log_batch/test_time_log_batch.py b/projects/doctype/time_log_batch/test_time_log_batch.py
index 34a0cc0..4976dfe 100644
--- a/projects/doctype/time_log_batch/test_time_log_batch.py
+++ b/projects/doctype/time_log_batch/test_time_log_batch.py
@@ -5,15 +5,31 @@
class TimeLogBatchTest(unittest.TestCase):
def test_time_log_status(self):
- self.assertEquals(webnotes.conn.get_value("Time Log", "_T-Time Log-00001", "status"), "Submitted")
- tlb = webnotes.bean("Time Log Batch", "_T-Time Log Batch-00001")
+ from projects.doctype.time_log.test_time_log import test_records as time_log_records
+ time_log = webnotes.bean(copy=time_log_records[0])
+ time_log.doc.fields.update({
+ "from_time": "2013-01-02 10:00:00",
+ "to_time": "2013-01-02 11:00:00",
+ "docstatus": 0
+ })
+ time_log.insert()
+ time_log.submit()
+
+ self.assertEquals(webnotes.conn.get_value("Time Log", time_log.doc.name, "status"), "Submitted")
+ tlb = webnotes.bean(copy=test_records[0])
+ tlb.doclist[1].time_log = time_log.doc.name
+ tlb.insert()
tlb.submit()
- self.assertEquals(webnotes.conn.get_value("Time Log", "_T-Time Log-00001", "status"), "Batched for Billing")
+
+ self.assertEquals(webnotes.conn.get_value("Time Log", time_log.doc.name, "status"), "Batched for Billing")
tlb.cancel()
- self.assertEquals(webnotes.conn.get_value("Time Log", "_T-Time Log-00001", "status"), "Submitted")
+ self.assertEquals(webnotes.conn.get_value("Time Log", time_log.doc.name, "status"), "Submitted")
test_records = [[
- {"rate": "500"},
+ {
+ "doctype": "Time Log Batch",
+ "rate": "500"
+ },
{
"doctype": "Time Log Batch Detail",
"parenttype": "Time Log Batch",
diff --git a/public/js/complete_setup.js b/public/js/complete_setup.js
deleted file mode 100644
index eeb39d6..0000000
--- a/public/js/complete_setup.js
+++ /dev/null
@@ -1,126 +0,0 @@
-// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd.
-// License: GNU General Public License v3. See license.txt
-
-// complete my company registration
-// --------------------------------
-wn.provide('erpnext.complete_setup');
-
-$.extend(erpnext.complete_setup, {
- show: function() {
- d = erpnext.complete_setup.prepare_dialog();
- d.show();
- },
-
- prepare_dialog: function() {
- var d = new wn.ui.Dialog({
- title: "Setup",
- fields: [
- {fieldname:'first_name', label:wn._('Your First Name'), fieldtype:'Data', reqd: 1},
- {fieldname:'last_name', label: wn._('Your Last Name'), fieldtype:'Data'},
- {fieldname:'company_name', label:wn._('Company Name'), fieldtype:'Data', reqd:1,
- description: wn._('e.g. "My Company LLC"')},
- {fieldname:'company_abbr', label:wn._('Company Abbreviation'), fieldtype:'Data',
- description:wn._('e.g. "MC"'),reqd:1},
- {fieldname:'fy_start', label:wn._('Financial Year Start Date'), fieldtype:'Select',
- description:wn._('Your financial year begins on"'), reqd:1,
- options: erpnext.complete_setup.fy_start_list.join('\n')},
- {fieldname:'country', label: wn._('Country'), reqd:1,
- options: "", fieldtype: 'Select'},
- {fieldname:'currency', label: wn._('Default Currency'), reqd:1,
- options: "", fieldtype: 'Select'},
- {fieldname:'timezone', label: wn._('Time Zone'), reqd:1,
- options: "", fieldtype: 'Select'},
- {fieldname:'industry', label: wn._('Industry'), reqd:1,
- options: erpnext.complete_setup.domains.join('\n'), fieldtype: 'Select'},
- {fieldname:'update', label:wn._('Setup'),fieldtype:'Button'},
- ],
- });
-
- if(user != 'Administrator'){
- d.$wrapper.find('.close').toggle(false); // Hide close image
- $('header').toggle(false); // hide toolbar
- }
-
- wn.call({
- method:"webnotes.country_info.get_country_timezone_info",
- callback: function(data) {
- erpnext.country_info = data.message.country_info;
- erpnext.all_timezones = data.message.all_timezones;
- d.get_input("country").empty()
- .add_options([""].concat(keys(erpnext.country_info).sort()));
- d.get_input("currency").empty()
- .add_options(wn.utils.unique([""].concat($.map(erpnext.country_info,
- function(opts, country) { return opts.currency; }))).sort());
- d.get_input("timezone").empty()
- .add_options([""].concat(erpnext.all_timezones));
- }
- })
-
- // on clicking update
- d.fields_dict.update.input.onclick = function() {
- var data = d.get_values();
- if(!data) return;
- $(this).set_working();
- return $c_obj('Setup Control','setup_account',data,function(r, rt){
- $(this).done_working();
- if(!r.exc) {
- sys_defaults = r.message;
- user_fullname = r.message.user_fullname;
- wn.boot.user_info[user].fullname = user_fullname;
- d.hide();
- $('header').toggle(true);
- wn.container.wntoolbar.set_user_name();
-
- setTimeout(function() { window.location.reload(); }, 3000);
- }
- });
- };
-
- d.fields_dict.company_name.input.onchange = function() {
- var parts = d.get_input("company_name").val().split(" ");
- var abbr = $.map(parts, function(p) { return p ? p.substr(0,1) : null }).join("");
- d.get_input("company_abbr").val(abbr.toUpperCase());
- }
-
- d.fields_dict.country.input.onchange = function() {
- var country = d.fields_dict.country.input.value;
- var $timezone = $(d.fields_dict.timezone.input);
- $timezone.empty();
- // add country specific timezones first
- if(country){
- var timezone_list = erpnext.country_info[country].timezones || [];
- $timezone.add_options(timezone_list.sort());
-
- d.get_input("currency").val(erpnext.country_info[country].currency);
- }
- // add all timezones at the end, so that user has the option to change it to any timezone
- $timezone.add_options([""].concat(erpnext.all_timezones));
-
- };
-
- // company name already set
- if(wn.control_panel.company_name) {
- var inp = d.fields_dict.company_name.input;
- inp.value = wn.control_panel.company_name;
- inp.disabled = true;
- d.fields_dict.company_name.$input.trigger("change");
- }
-
- // set first name, last name
- if(user_fullname) {
- u = user_fullname.split(' ');
- if(u[0]) {
- d.fields_dict.first_name.input.value = u[0];
- }
- if(u[1]) {
- d.fields_dict.last_name.input.value = u[1];
- }
- }
-
- return d;
- },
-
- fy_start_list: ['', '1st Jan', '1st Apr', '1st Jul', '1st Oct'],
-
- domains: ['', "Manufacturing", "Retail", "Distribution", "Services", "Other"],
-});
\ No newline at end of file
diff --git a/public/js/controllers/accounts.js b/public/js/controllers/accounts.js
new file mode 100644
index 0000000..201c47e
--- /dev/null
+++ b/public/js/controllers/accounts.js
@@ -0,0 +1,18 @@
+
+// get tax rate
+cur_frm.cscript.account_head = function(doc, cdt, cdn) {
+ var d = locals[cdt][cdn];
+ if(!d.charge_type && d.account_head){
+ msgprint("Please select Charge Type first");
+ wn.model.set_value(cdt, cdn, "account_head", "");
+ } else if(d.account_head && d.charge_type!=="Actual") {
+ wn.call({
+ type:"GET",
+ method: "controllers.accounts_controller.get_tax_rate",
+ args: {"account_head":d.account_head},
+ callback: function(r) {
+ wn.model.set_value(cdt, cdn, "rate", r.message || 0);
+ }
+ })
+ }
+}
diff --git a/public/js/startup.css b/public/js/startup.css
index ab70ee4..c3b7276 100644
--- a/public/js/startup.css
+++ b/public/js/startup.css
@@ -29,4 +29,21 @@
width: 32px;
height: 32px;
margin: -10px auto;
+}
+
+/* pos */
+.pos-item {
+ height: 200px;
+ overflow: hidden;
+ cursor: pointer;
+ padding-left: 5px !important;
+ padding-right: 5px !important;
+}
+
+.pos-bill {
+ padding: 20px 5px;
+ font-family: Monospace;
+ border: 1px solid #eee;
+ -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
+ box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
}
\ No newline at end of file
diff --git a/public/js/startup.js b/public/js/startup.js
index 7d5792a..76b7714 100644
--- a/public/js/startup.js
+++ b/public/js/startup.js
@@ -9,25 +9,10 @@
console.log(wn._('Starting up...'));
$('#startup_div').html('Starting up...').toggle(true);
- if(user != 'Guest'){
- // setup toolbar
- erpnext.toolbar.setup();
-
- // complete registration
- if(in_list(user_roles,'System Manager') && (wn.boot.setup_complete==='No')) {
- wn.require("app/js/complete_setup.js");
- erpnext.complete_setup.show();
- } else if(!wn.boot.customer_count) {
- if(wn.get_route()[0]!=="Setup") {
- msgprint("<a class='btn btn-success' href='#Setup'>"
- + wn._("Proceed to Setup") + "</a>\
- <br><br><p class='text-muted'>"+
- wn._("This message goes away after you create your first customer.")+
- "</p>", wn._("Welcome"));
- }
- } else if(wn.boot.expires_on && in_list(user_roles, 'System Manager')) {
- erpnext.startup.show_expiry_banner();
- }
+ erpnext.toolbar.setup();
+
+ if(wn.boot.expires_on && in_list(user_roles, 'System Manager')) {
+ erpnext.startup.show_expiry_banner();
}
}
diff --git a/public/js/stock_grid_report.js b/public/js/stock_grid_report.js
index 8b79b5e..495ea43 100644
--- a/public/js/stock_grid_report.js
+++ b/public/js/stock_grid_report.js
@@ -28,7 +28,7 @@
var value_diff = (rate * add_qty);
if(add_qty)
- wh.fifo_stack.push([add_qty, sl.incoming_rate, sl.posting_date]);
+ wh.fifo_stack.push([add_qty, sl.incoming_rate, sl.posting_date]);
} else {
// outgoing
if(sl.serial_no) {
@@ -98,7 +98,7 @@
$.each(sl.serial_no.trim().split("\n"), function(i, sr) {
if(sr) {
- value_diff += flt(me.serialized_buying_rates[sr.trim()]);
+ value_diff += flt(me.serialized_buying_rates[sr.trim().toLowerCase()]);
}
});
@@ -111,8 +111,9 @@
$.each(wn.report_dump.data["Stock Ledger Entry"], function(i, sle) {
if(sle.qty > 0 && sle.serial_no) {
$.each(sle.serial_no.trim().split("\n"), function(i, sr) {
- if(sr && sle.incoming_rate !== undefined) {
- serialized_buying_rates[sr.trim()] = flt(sle.incoming_rate);
+ if(sr && sle.incoming_rate !== undefined
+ && !serialized_buying_rates[sr.trim().toLowerCase()]) {
+ serialized_buying_rates[sr.trim().toLowerCase()] = flt(sle.incoming_rate);
}
});
}
diff --git a/public/js/transaction.js b/public/js/transaction.js
index 3080ba1..db93e9f 100644
--- a/public/js/transaction.js
+++ b/public/js/transaction.js
@@ -21,8 +21,6 @@
company: wn.defaults.get_default("company"),
fiscal_year: wn.defaults.get_default("fiscal_year"),
is_subcontracted: "No",
- conversion_rate: 1.0,
- plc_conversion_rate: 1.0
}, function(fieldname, value) {
if(me.frm.fields_dict[fieldname] && !me.frm.doc[fieldname])
me.frm.set_value(fieldname, value);
@@ -41,18 +39,19 @@
},
onload_post_render: function() {
- if(this.frm.doc.__islocal && this.frm.doc.company && !this.frm.doc.customer) {
- var me = this;
- return this.frm.call({
- doc: this.frm.doc,
- method: "onload_post_render",
- freeze: true,
- callback: function(r) {
- // remove this call when using client side mapper
- me.set_default_values();
- me.set_dynamic_labels();
- }
- });
+ if(this.frm.doc.__islocal && this.frm.doc.company &&
+ !this.frm.doc.customer && !this.frm.doc.is_pos) {
+ var me = this;
+ return this.frm.call({
+ doc: this.frm.doc,
+ method: "onload_post_render",
+ freeze: true,
+ callback: function(r) {
+ // remove this call when using client side mapper
+ me.set_default_values();
+ me.set_dynamic_labels();
+ }
+ });
}
},
@@ -62,6 +61,56 @@
erpnext.hide_company();
this.show_item_wise_taxes();
this.set_dynamic_labels();
+
+ // Show POS button only if it is enabled from features setup
+ if(cint(sys_defaults.fs_pos_view)===1 && this.frm.doctype!="Material Request")
+ this.pos_btn();
+ },
+
+ pos_btn: function() {
+ if(this.$pos_btn)
+ this.$pos_btn.remove();
+
+ if(!this.pos_active) {
+ var btn_label = wn._("POS View"),
+ icon = "icon-desktop";
+ } else {
+ var btn_label = wn._(this.frm.doctype) + wn._(" View"),
+ icon = "icon-file-text";
+ }
+ var me = this;
+
+ this.$pos_btn = this.frm.add_custom_button(btn_label, function() {
+ me.toggle_pos();
+ me.pos_btn();
+ }, icon);
+ },
+
+ toggle_pos: function(show) {
+ // Check whether it is Selling or Buying cycle
+ var price_list = wn.meta.has_field(cur_frm.doc.doctype, "selling_price_list") ?
+ this.frm.doc.selling_price_list : this.frm.doc.buying_price_list;
+
+ if (!price_list)
+ msgprint(wn._("Please select Price List"))
+ else {
+ if((show===true && this.pos_active) || (show===false && !this.pos_active)) return;
+
+ // make pos
+ if(!this.frm.pos) {
+ this.frm.layout.add_view("pos");
+ this.frm.pos = new erpnext.POS(this.frm.layout.views.pos, this.frm);
+ }
+
+ // toggle view
+ this.frm.layout.set_view(this.pos_active ? "" : "pos");
+ this.pos_active = !this.pos_active;
+
+ // refresh
+ if(this.pos_active)
+ this.frm.pos.refresh();
+ this.frm.refresh();
+ }
},
validate: function() {
@@ -81,10 +130,18 @@
company: function() {
if(this.frm.doc.company && this.frm.fields_dict.currency) {
- if(!this.frm.doc.currency) {
- this.frm.set_value("currency", this.get_company_currency());
+ var company_currency = this.get_company_currency();
+ if (!this.frm.doc.currency) {
+ this.frm.set_value("currency", company_currency);
}
+ if (this.frm.doc.currency == company_currency) {
+ this.frm.set_value("conversion_rate", 1.0);
+ }
+ if (this.frm.doc.price_list_currency == company_currency) {
+ this.frm.set_value('plc_conversion_rate', 1.0);
+ }
+
this.frm.script_manager.trigger("currency");
}
},
@@ -96,15 +153,13 @@
currency: function() {
var me = this;
this.set_dynamic_labels();
-
+
var company_currency = this.get_company_currency();
if(this.frm.doc.currency !== company_currency) {
this.get_exchange_rate(this.frm.doc.currency, company_currency,
function(exchange_rate) {
- if(exchange_rate) {
- me.frm.set_value("conversion_rate", exchange_rate);
- me.conversion_rate();
- }
+ me.frm.set_value("conversion_rate", exchange_rate);
+ me.conversion_rate();
});
} else {
this.conversion_rate();
@@ -118,7 +173,7 @@
this.frm.doc.plc_conversion_rate !== this.frm.doc.conversion_rate) {
this.frm.set_value("plc_conversion_rate", this.frm.doc.conversion_rate);
}
-
+
this.calculate_taxes_and_totals();
},
@@ -182,7 +237,7 @@
tax_rate: function(doc, cdt, cdn) {
this.calculate_taxes_and_totals();
},
-
+
row_id: function(doc, cdt, cdn) {
var tax = wn.model.get_doc(cdt, cdn);
try {
@@ -413,15 +468,11 @@
}
var company_currency = this.get_company_currency();
- var valid_conversion_rate = this.frm.doc.conversion_rate ?
- ((this.frm.doc.currency == company_currency && this.frm.doc.conversion_rate == 1.0) ||
- (this.frm.doc.currency != company_currency && this.frm.doc.conversion_rate != 1.0)) :
- false;
- // if(!valid_conversion_rate) {
- // wn.throw(wn._("Please enter valid") + " " + wn._(conversion_rate_label) +
- // " 1 " + this.frm.doc.currency + " = [?] " + company_currency);
- // }
+ if(!this.frm.doc.conversion_rate) {
+ wn.throw(wn._("Please enter valid") + " " + wn._(conversion_rate_label) +
+ " 1 " + this.frm.doc.currency + " = [?] " + company_currency);
+ }
},
calculate_taxes_and_totals: function() {
diff --git a/public/js/utils.js b/public/js/utils.js
index a8df11e..e14a54c 100644
--- a/public/js/utils.js
+++ b/public/js/utils.js
@@ -20,7 +20,7 @@
hide_company: function() {
if(cur_frm.fields_dict.company) {
- var companies = Object.keys(locals[":Company"]);
+ var companies = Object.keys(locals[":Company"] || {});
if(companies.length === 1) {
if(!cur_frm.doc.company) cur_frm.set_value("company", companies[0]);
cur_frm.toggle_display("company", false);
diff --git a/selling/doctype/customer/customer.js b/selling/doctype/customer/customer.js
index 2b0e877..ef02d46 100644
--- a/selling/doctype/customer/customer.js
+++ b/selling/doctype/customer/customer.js
@@ -34,7 +34,6 @@
cur_frm.cscript.make_contact(doc,dt,dn);
cur_frm.communication_view = new wn.views.CommunicationList({
- list: wn.model.get("Communication", {"customer": doc.name}),
parent: cur_frm.fields_dict.communication_html.wrapper,
doc: doc,
});
@@ -45,7 +44,7 @@
cur_frm.dashboard.reset(doc);
if(doc.__islocal)
return;
- cur_frm.dashboard.set_headline('<span class="text-muted">Loading...</span>')
+ cur_frm.dashboard.set_headline('<span class="text-muted">'+ wn._('Loading...')+ '</span>')
cur_frm.dashboard.add_doctype_badge("Opportunity", "customer");
cur_frm.dashboard.add_doctype_badge("Quotation", "customer");
@@ -119,4 +118,4 @@
return{
query:"controllers.queries.lead_query"
}
-}
\ No newline at end of file
+}
diff --git a/selling/doctype/customer/customer.py b/selling/doctype/customer/customer.py
index 2b57da9..18c8a34 100644
--- a/selling/doctype/customer/customer.py
+++ b/selling/doctype/customer/customer.py
@@ -9,7 +9,6 @@
from webnotes import msgprint, _
import webnotes.defaults
-sql = webnotes.conn.sql
from utilities.transaction_base import TransactionBase
@@ -31,7 +30,7 @@
return webnotes.conn.get_value('Company', self.doc.company, 'abbr')
def get_receivables_group(self):
- g = sql("select receivables_group from tabCompany where name=%s", self.doc.company)
+ g = webnotes.conn.sql("select receivables_group from tabCompany where name=%s", self.doc.company)
g = g and g[0][0] or ''
if not g:
msgprint("Update Company master, assign a default group for Receivables")
@@ -47,7 +46,7 @@
def update_lead_status(self):
if self.doc.lead_name:
- sql("update `tabLead` set status='Converted' where name = %s", self.doc.lead_name)
+ webnotes.conn.sql("update `tabLead` set status='Converted' where name = %s", self.doc.lead_name)
def create_account_head(self):
if self.doc.company :
@@ -132,7 +131,7 @@
def delete_customer_account(self):
"""delete customer's ledger if exist and check balance before deletion"""
- acc = sql("select name from `tabAccount` where master_type = 'Customer' \
+ acc = webnotes.conn.sql("select name from `tabAccount` where master_type = 'Customer' \
and master_name = %s and docstatus < 2", self.doc.name)
if acc:
from webnotes.model import delete_doc
@@ -143,7 +142,7 @@
self.delete_customer_contact()
self.delete_customer_account()
if self.doc.lead_name:
- sql("update `tabLead` set status='Interested' where name=%s",self.doc.lead_name)
+ webnotes.conn.sql("update `tabLead` set status='Interested' where name=%s",self.doc.lead_name)
def on_rename(self, new, old, merge=False):
#update customer_name if not naming series
diff --git a/selling/doctype/customer/test_customer.py b/selling/doctype/customer/test_customer.py
index 7c90f6a..adc5a54 100644
--- a/selling/doctype/customer/test_customer.py
+++ b/selling/doctype/customer/test_customer.py
@@ -17,6 +17,8 @@
(("_Test Customer 1 Renamed",),))
self.assertEqual(webnotes.conn.exists("Customer", "_Test Customer 1"), ())
+ webnotes.rename_doc("Customer", "_Test Customer 1 Renamed", "_Test Customer 1")
+
def test_merge(self):
from webnotes.test_runner import make_test_records
make_test_records("Sales Invoice")
@@ -57,6 +59,9 @@
# check that old name doesn't exist
self.assertEqual(webnotes.conn.exists("Customer", "_Test Customer"), ())
self.assertEqual(webnotes.conn.exists("Account", "_Test Customer - _TC"), ())
+
+ # create back _Test Customer
+ webnotes.bean(copy=test_records[0]).insert()
test_ignore = ["Price List"]
diff --git a/selling/doctype/installation_note/installation_note.py b/selling/doctype/installation_note/installation_note.py
index ca47043..02b7247 100644
--- a/selling/doctype/installation_note/installation_note.py
+++ b/selling/doctype/installation_note/installation_note.py
@@ -38,7 +38,6 @@
self.check_item_table()
sales_com_obj = get_obj(dt = 'Sales Common')
sales_com_obj.check_active_sales_items(self)
- sales_com_obj.get_prevdoc_date(self)
def validate_fiscal_year(self):
from accounts.utils import validate_fiscal_year
diff --git a/selling/doctype/installation_note_item/installation_note_item.txt b/selling/doctype/installation_note_item/installation_note_item.txt
index 02871ad..7435d46 100644
--- a/selling/doctype/installation_note_item/installation_note_item.txt
+++ b/selling/doctype/installation_note_item/installation_note_item.txt
@@ -2,7 +2,7 @@
{
"creation": "2013-02-22 01:27:51",
"docstatus": 0,
- "modified": "2013-07-10 14:54:09",
+ "modified": "2013-10-10 17:02:31",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -50,18 +50,6 @@
},
{
"doctype": "DocField",
- "fieldname": "prevdoc_date",
- "fieldtype": "Date",
- "hidden": 0,
- "in_list_view": 1,
- "label": "Delivery Date",
- "oldfieldname": "prevdoc_date",
- "oldfieldtype": "Date",
- "print_hide": 0,
- "read_only": 1
- },
- {
- "doctype": "DocField",
"fieldname": "serial_no",
"fieldtype": "Small Text",
"in_list_view": 1,
diff --git a/selling/doctype/lead/get_leads.py b/selling/doctype/lead/get_leads.py
index 3305a3b..eaf837b 100644
--- a/selling/doctype/lead/get_leads.py
+++ b/selling/doctype/lead/get_leads.py
@@ -29,7 +29,7 @@
parent_name = contact_name or lead_name
message = make(content=content, sender=sender, subject=subject,
- doctype = parent_doctype, name = parent_name, date=date)
+ doctype = parent_doctype, name = parent_name, date=date, sent_or_received="Received")
if mail:
# save attachments to parent if from mail
diff --git a/selling/doctype/lead/lead.js b/selling/doctype/lead/lead.js
index 8f5057d..9f6412c 100644
--- a/selling/doctype/lead/lead.js
+++ b/selling/doctype/lead/lead.js
@@ -33,7 +33,7 @@
var doc = this.frm.doc;
erpnext.hide_naming_series();
this.frm.clear_custom_buttons();
-
+
this.frm.__is_customer = this.frm.__is_customer || this.frm.doc.__is_customer;
if(!this.frm.doc.__islocal && !this.frm.__is_customer) {
this.frm.add_custom_button(wn._("Create Customer"), this.create_customer);
diff --git a/selling/doctype/lead/lead.py b/selling/doctype/lead/lead.py
index 316481c..a33ae35 100644
--- a/selling/doctype/lead/lead.py
+++ b/selling/doctype/lead/lead.py
@@ -7,7 +7,6 @@
from webnotes.utils import cstr, validate_email_add, cint, extract_email_id
from webnotes import session, msgprint
-sql = webnotes.conn.sql
from controllers.selling_controller import SellingController
@@ -27,24 +26,9 @@
customer = webnotes.conn.get_value("Customer", {"lead_name": self.doc.name})
if customer:
self.doc.fields["__is_customer"] = customer
-
- def on_communication(self, comm):
- if comm.sender == self.get_sender(comm) or \
- webnotes.conn.get_value("Profile", extract_email_id(comm.sender), "user_type")=="System User":
- status = "Replied"
- else:
- status = "Open"
-
- webnotes.conn.set(self.doc, 'status', status)
-
- def check_status(self):
- chk = sql("select status from `tabLead` where name=%s", self.doc.name)
- chk = chk and chk[0][0] or ''
- return cstr(chk)
def validate(self):
- if self.doc.status == 'Lead Lost' and not self.doc.order_lost_reason:
- webnotes.throw("Please Enter Lost Reason under More Info section")
+ self.set_status()
if self.doc.source == 'Campaign' and not self.doc.campaign_name and session['user'] != 'Guest':
webnotes.throw("Please specify campaign name")
@@ -76,14 +60,18 @@
webnotes.msgprint(_("""Email Id must be unique, already exists for: """) + \
", ".join(items), raise_exception=True)
- def get_sender(self, comm):
- return webnotes.conn.get_value('Sales Email Settings',None,'email_id')
-
def on_trash(self):
webnotes.conn.sql("""update `tabSupport Ticket` set lead='' where lead=%s""",
self.doc.name)
self.delete_events()
+
+ def has_customer(self):
+ return webnotes.conn.get_value("Customer", {"lead_name": self.doc.name})
+
+ def has_opportunity(self):
+ return webnotes.conn.get_value("Opportunity", {"lead": self.doc.name, "docstatus": 1,
+ "status": ["!=", "Lost"]})
@webnotes.whitelist()
def make_customer(source_name, target_doclist=None):
@@ -133,4 +121,4 @@
}
}}, target_doclist)
- return [d.fields for d in doclist]
\ No newline at end of file
+ return [d if isinstance(d, dict) else d.fields for d in doclist]
\ No newline at end of file
diff --git a/selling/doctype/lead/lead.txt b/selling/doctype/lead/lead.txt
index 408ad45..b700a2e 100644
--- a/selling/doctype/lead/lead.txt
+++ b/selling/doctype/lead/lead.txt
@@ -2,11 +2,12 @@
{
"creation": "2013-04-10 11:45:37",
"docstatus": 0,
- "modified": "2013-09-26 16:30:36",
+ "modified": "2013-10-09 15:27:54",
"modified_by": "Administrator",
"owner": "Administrator"
},
{
+ "allow_import": 1,
"autoname": "naming_series:",
"doctype": "DocType",
"document_type": "Master",
@@ -101,7 +102,7 @@
"fieldtype": "Column Break"
},
{
- "default": "Open",
+ "default": "Lead",
"doctype": "DocField",
"fieldname": "status",
"fieldtype": "Select",
@@ -111,7 +112,7 @@
"no_copy": 1,
"oldfieldname": "status",
"oldfieldtype": "Select",
- "options": "\nOpen\nReplied\nAttempted to Contact\nContact in Future\nContacted\nOpportunity Made\nInterested\nNot interested\nLead Lost\nConverted\nPassive",
+ "options": "Lead\nOpen\nReplied\nOpportunity\nInterested\nConverted\nDo Not Contact",
"reqd": 1,
"search_index": 1
},
@@ -175,19 +176,6 @@
"search_index": 1
},
{
- "depends_on": "eval:!doc.__islocal",
- "description": "Date on which the lead was last contacted",
- "doctype": "DocField",
- "fieldname": "last_contact_date",
- "fieldtype": "Date",
- "label": "Last Contact Date",
- "no_copy": 1,
- "oldfieldname": "last_contact_date",
- "oldfieldtype": "Date",
- "print_hide": 1,
- "read_only": 1
- },
- {
"doctype": "DocField",
"fieldname": "col_break123",
"fieldtype": "Column Break",
@@ -236,8 +224,7 @@
"fieldtype": "HTML",
"label": "Communication HTML",
"oldfieldname": "follow_up",
- "oldfieldtype": "Table",
- "print_hide": 1
+ "oldfieldtype": "Table"
},
{
"doctype": "DocField",
@@ -391,18 +378,6 @@
"width": "50%"
},
{
- "allow_on_submit": 0,
- "depends_on": "eval:doc.status == 'Lead Lost'",
- "doctype": "DocField",
- "fieldname": "order_lost_reason",
- "fieldtype": "Link",
- "hidden": 0,
- "label": "Lost Reason",
- "oldfieldname": "order_lost_reason",
- "oldfieldtype": "Link",
- "options": "Quotation Lost Reason"
- },
- {
"doctype": "DocField",
"fieldname": "company",
"fieldtype": "Link",
@@ -430,8 +405,7 @@
"fieldtype": "Table",
"hidden": 1,
"label": "Communications",
- "options": "Communication",
- "print_hide": 1
+ "options": "Communication"
},
{
"cancel": 1,
diff --git a/selling/doctype/opportunity/opportunity.js b/selling/doctype/opportunity/opportunity.js
index 8b8fbf3..e65ef50 100644
--- a/selling/doctype/opportunity/opportunity.js
+++ b/selling/doctype/opportunity/opportunity.js
@@ -101,21 +101,21 @@
cur_frm.cscript.refresh = function(doc, cdt, cdn){
erpnext.hide_naming_series();
-
- cur_frm.dashboard.reset(doc);
- if(!doc.__islocal) {
- if(doc.status=="Converted" || doc.status=="Order Confirmed") {
- cur_frm.dashboard.set_headline_alert(wn._(doc.status), "alert-success", "icon-ok-sign");
- } else if(doc.status=="Opportunity Lost") {
- cur_frm.dashboard.set_headline_alert(wn._(doc.status), "alert-danger", "icon-exclamation-sign");
- }
- }
cur_frm.clear_custom_buttons();
+<<<<<<< HEAD
if(doc.docstatus === 1 && doc.status!=="Opportunity Lost") {
cur_frm.add_custom_button( wn._('Create Quotation'), cur_frm.cscript.create_quotation);
cur_frm.add_custom_button(wn._('Opportunity Lost'), cur_frm.cscript['Declare Opportunity Lost']);
cur_frm.add_custom_button(wn._('Send SMS'), cur_frm.cscript.send_sms);
+=======
+ if(doc.docstatus === 1 && doc.status!=="Lost") {
+ cur_frm.add_custom_button('Create Quotation', cur_frm.cscript.create_quotation);
+ if(doc.status!=="Quotation") {
+ cur_frm.add_custom_button('Opportunity Lost', cur_frm.cscript['Declare Opportunity Lost']);
+ }
+ cur_frm.add_custom_button('Send SMS', cur_frm.cscript.send_sms);
+>>>>>>> f146e8b7f52a3e46e335c0fefd92c347717b370b
}
cur_frm.toggle_display("contact_info", doc.customer || doc.lead);
diff --git a/selling/doctype/opportunity/opportunity.py b/selling/doctype/opportunity/opportunity.py
index c8c41e3..37672f2 100644
--- a/selling/doctype/opportunity/opportunity.py
+++ b/selling/doctype/opportunity/opportunity.py
@@ -4,11 +4,10 @@
from __future__ import unicode_literals
import webnotes
-from webnotes.utils import cstr, getdate, cint
+from webnotes.utils import cstr, cint
from webnotes.model.bean import getlist
-from webnotes import msgprint
+from webnotes import msgprint, _
-sql = webnotes.conn.sql
from utilities.transaction_base import TransactionBase
@@ -18,7 +17,7 @@
self.doclist = doclist
self.fname = 'enq_details'
self.tname = 'Opportunity Item'
-
+
self._prev = webnotes._dict({
"contact_date": webnotes.conn.get_value("Opportunity", self.doc.name, "contact_date") if \
(not cint(self.doc.fields.get("__islocal"))) else None,
@@ -27,7 +26,7 @@
})
def get_item_details(self, item_code):
- item = sql("""select item_name, stock_uom, description_html, description, item_group, brand
+ item = webnotes.conn.sql("""select item_name, stock_uom, description_html, description, item_group, brand
from `tabItem` where name = %s""", item_code, as_dict=1)
ret = {
'item_name': item and item[0]['item_name'] or '',
@@ -39,7 +38,7 @@
return ret
def get_cust_address(self,name):
- details = sql("select customer_name, address, territory, customer_group from `tabCustomer` where name = '%s' and docstatus != 2" %(name), as_dict = 1)
+ details = webnotes.conn.sql("select customer_name, address, territory, customer_group from `tabCustomer` where name = '%s' and docstatus != 2" %(name), as_dict = 1)
if details:
ret = {
'customer_name': details and details[0]['customer_name'] or '',
@@ -49,7 +48,7 @@
}
# ********** get primary contact details (this is done separately coz. , in case there is no primary contact thn it would not be able to fetch customer details in case of join query)
- contact_det = sql("select contact_name, contact_no, email_id from `tabContact` where customer = '%s' and is_customer = 1 and is_primary_contact = 'Yes' and docstatus != 2" %(name), as_dict = 1)
+ contact_det = webnotes.conn.sql("select contact_name, contact_no, email_id from `tabContact` where customer = '%s' and is_customer = 1 and is_primary_contact = 'Yes' and docstatus != 2" %(name), as_dict = 1)
ret['contact_person'] = contact_det and contact_det[0]['contact_name'] or ''
ret['contact_no'] = contact_det and contact_det[0]['contact_no'] or ''
@@ -62,18 +61,15 @@
def get_contact_details(self, arg):
arg = eval(arg)
- contact = sql("select contact_no, email_id from `tabContact` where contact_name = '%s' and customer_name = '%s'" %(arg['contact_person'],arg['customer']), as_dict = 1)
+ contact = webnotes.conn.sql("select contact_no, email_id from `tabContact` where contact_name = '%s' and customer_name = '%s'" %(arg['contact_person'],arg['customer']), as_dict = 1)
ret = {
'contact_no' : contact and contact[0]['contact_no'] or '',
'email_id' : contact and contact[0]['email_id'] or ''
}
return ret
-
+
+
def on_update(self):
- # Add to calendar
- if self.doc.contact_date and self.doc.contact_date_ref != self.doc.contact_date:
- webnotes.conn.set(self.doc, 'contact_date_ref',self.doc.contact_date)
-
self.add_calendar_event()
def add_calendar_event(self, opts=None, force=False):
@@ -101,14 +97,6 @@
super(DocType, self).add_calendar_event(opts, force)
- def set_last_contact_date(self):
- if self.doc.contact_date_ref and self.doc.contact_date_ref != self.doc.contact_date:
- if getdate(self.doc.contact_date_ref) < getdate(self.doc.contact_date):
- self.doc.last_contact_date=self.doc.contact_date_ref
- else:
- msgprint("Contact Date Cannot be before Last Contact Date")
- raise Exception
-
def validate_item_details(self):
if not getlist(self.doclist, 'enquiry_details'):
msgprint("Please select items for which enquiry needs to be made")
@@ -121,49 +109,36 @@
msgprint("Customer is mandatory if 'Opportunity From' is selected as Customer", raise_exception=1)
def validate(self):
- self.set_last_contact_date()
+ self.set_status()
self.validate_item_details()
self.validate_uom_is_integer("uom", "qty")
self.validate_lead_cust()
from accounts.utils import validate_fiscal_year
validate_fiscal_year(self.doc.transaction_date, self.doc.fiscal_year, "Opportunity Date")
- self.doc.status = "Draft"
def on_submit(self):
- webnotes.conn.set(self.doc, 'status', 'Submitted')
- if self.doc.lead and webnotes.conn.get_value("Lead", self.doc.lead, "status")!="Converted":
- webnotes.conn.set_value("Lead", self.doc.lead, "status", "Opportunity Made")
+ if self.doc.lead:
+ webnotes.bean("Lead", self.doc.lead).get_controller().set_status(update=True)
def on_cancel(self):
- chk = sql("select t1.name from `tabQuotation` t1, `tabQuotation Item` t2 where t2.parent = t1.name and t1.docstatus=1 and (t1.status!='Order Lost' and t1.status!='Cancelled') and t2.prevdoc_docname = %s",self.doc.name)
- if chk:
- msgprint("Quotation No. "+cstr(chk[0][0])+" is submitted against this Opportunity. Thus can not be cancelled.")
- raise Exception
- else:
- webnotes.conn.set(self.doc, 'status', 'Cancelled')
- if self.doc.lead and webnotes.conn.get_value("Lead", self.doc.lead,
- "status")!="Converted":
- if webnotes.conn.get_value("Communication", {"parent": self.doc.lead}):
- status = "Contacted"
- else:
- status = "Open"
-
- webnotes.conn.set_value("Lead", self.doc.lead, "status", status)
+ if self.has_quotation():
+ webnotes.throw(_("Cannot Cancel Opportunity as Quotation Exists"))
+ self.set_status(update=True)
def declare_enquiry_lost(self,arg):
- chk = sql("select t1.name from `tabQuotation` t1, `tabQuotation Item` t2 where t2.parent = t1.name and t1.docstatus=1 and (t1.status!='Order Lost' and t1.status!='Cancelled') and t2.prevdoc_docname = %s",self.doc.name)
- if chk:
- msgprint("Quotation No. "+cstr(chk[0][0])+" is submitted against this Opportunity. Thus 'Opportunity Lost' can not be declared against it.")
- raise Exception
- else:
- webnotes.conn.set(self.doc, 'status', 'Opportunity Lost')
+ if not self.has_quotation():
+ webnotes.conn.set(self.doc, 'status', 'Lost')
webnotes.conn.set(self.doc, 'order_lost_reason', arg)
- return 'true'
+ else:
+ webnotes.throw(_("Cannot declare as lost, because Quotation has been made."))
def on_trash(self):
self.delete_events()
+ def has_quotation(self):
+ return webnotes.conn.get_value("Quotation Item", {"prevdoc_docname": self.doc.name, "docstatus": 1})
+
@webnotes.whitelist()
def make_quotation(source_name, target_doclist=None):
from webnotes.model.mapper import get_mapped_doclist
diff --git a/selling/doctype/opportunity/opportunity.txt b/selling/doctype/opportunity/opportunity.txt
index aeedd08..3c860ad 100644
--- a/selling/doctype/opportunity/opportunity.txt
+++ b/selling/doctype/opportunity/opportunity.txt
@@ -2,11 +2,12 @@
{
"creation": "2013-03-07 18:50:30",
"docstatus": 0,
- "modified": "2013-09-25 19:32:29",
+ "modified": "2013-10-09 15:26:29",
"modified_by": "Administrator",
"owner": "Administrator"
},
{
+ "allow_import": 1,
"autoname": "naming_series:",
"description": "Potential Sales Deal",
"doctype": "DocType",
@@ -125,7 +126,7 @@
"no_copy": 1,
"oldfieldname": "status",
"oldfieldtype": "Select",
- "options": "\nDraft\nSubmitted\nQuotation Sent\nOrder Confirmed\nOpportunity Lost\nCancelled",
+ "options": "Draft\nSubmitted\nQuotation\nLost\nCancelled\nReplied\nOpen",
"read_only": 1,
"reqd": 1
},
@@ -168,7 +169,6 @@
"label": "Communication History",
"oldfieldtype": "Section Break",
"options": "icon-comments",
- "print_hide": 1,
"read_only": 0
},
{
@@ -179,7 +179,6 @@
"label": "Communication HTML",
"oldfieldname": "follow_up",
"oldfieldtype": "Table",
- "print_hide": 1,
"read_only": 0
},
{
@@ -352,18 +351,6 @@
"read_only": 0
},
{
- "depends_on": "eval:!doc.__islocal",
- "doctype": "DocField",
- "fieldname": "order_lost_reason",
- "fieldtype": "Small Text",
- "label": "Quotation Lost Reason",
- "no_copy": 1,
- "oldfieldname": "order_lost_reason",
- "oldfieldtype": "Small Text",
- "read_only": 1,
- "report_hide": 0
- },
- {
"doctype": "DocField",
"fieldname": "company",
"fieldtype": "Link",
@@ -378,6 +365,15 @@
"search_index": 1
},
{
+ "depends_on": "eval:!doc.__islocal",
+ "doctype": "DocField",
+ "fieldname": "order_lost_reason",
+ "fieldtype": "Text",
+ "label": "Lost Reason",
+ "no_copy": 1,
+ "read_only": 1
+ },
+ {
"doctype": "DocField",
"fieldname": "column_break2",
"fieldtype": "Column Break",
@@ -409,20 +405,6 @@
"read_only": 0
},
{
- "allow_on_submit": 0,
- "depends_on": "eval:!doc.__islocal",
- "description": "Date on which the lead was last contacted",
- "doctype": "DocField",
- "fieldname": "last_contact_date",
- "fieldtype": "Date",
- "label": "Last Contact Date",
- "no_copy": 1,
- "oldfieldname": "last_contact_date",
- "oldfieldtype": "Date",
- "print_hide": 1,
- "read_only": 1
- },
- {
"doctype": "DocField",
"fieldname": "to_discuss",
"fieldtype": "Small Text",
@@ -450,8 +432,7 @@
"fieldtype": "Table",
"hidden": 1,
"label": "Communications",
- "options": "Communication",
- "print_hide": 1
+ "options": "Communication"
},
{
"doctype": "DocPerm",
diff --git a/selling/doctype/quotation/quotation.js b/selling/doctype/quotation/quotation.js
index 90be81a..a104f34 100644
--- a/selling/doctype/quotation/quotation.js
+++ b/selling/doctype/quotation/quotation.js
@@ -11,6 +11,7 @@
wn.require('app/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.js');
wn.require('app/utilities/doctype/sms_control/sms_control.js');
wn.require('app/selling/doctype/sales_common/sales_common.js');
+wn.require('app/accounts/doctype/sales_invoice/pos.js');
erpnext.selling.QuotationController = erpnext.selling.SellingController.extend({
onload: function(doc, dt, dn) {
@@ -23,20 +24,18 @@
},
refresh: function(doc, dt, dn) {
this._super(doc, dt, dn);
-
- cur_frm.dashboard.reset(doc);
- if(!doc.__islocal) {
- if(doc.status=="Converted" || doc.status=="Order Confirmed") {
- cur_frm.dashboard.set_headline_alert(wn._(doc.status), "alert-success", "icon-ok-sign");
- } else if(doc.status==="Order Lost") {
- cur_frm.dashboard.set_headline_alert(wn._(doc.status), "alert-danger", "icon-exclamation-sign");
- }
- }
+<<<<<<< HEAD
if(doc.docstatus == 1 && doc.status!=='Order Lost') {
cur_frm.add_custom_button(wn._('Make Sales Order'), cur_frm.cscript['Make Sales Order']);
if(doc.status!=="Order Confirmed") {
cur_frm.add_custom_button(wn._('Set as Lost'), cur_frm.cscript['Declare Order Lost']);
+=======
+ if(doc.docstatus == 1 && doc.status!=='Lost') {
+ cur_frm.add_custom_button('Make Sales Order', cur_frm.cscript['Make Sales Order']);
+ if(doc.status!=="Ordered") {
+ cur_frm.add_custom_button('Set as Lost', cur_frm.cscript['Declare Order Lost']);
+>>>>>>> f146e8b7f52a3e46e335c0fefd92c347717b370b
}
cur_frm.add_custom_button(wn._('Send SMS'), cur_frm.cscript.send_sms);
}
@@ -82,12 +81,12 @@
},
validate_company_and_party: function(party_field) {
- if(this.frm.doc.quotation_to == "Lead") {
- return true;
- } else if(!this.frm.doc.quotation_to) {
+ if(!this.frm.doc.quotation_to) {
msgprint(wn._("Please select a value for" + " " + wn.meta.get_label(this.frm.doc.doctype,
"quotation_to", this.frm.doc.name)));
return false;
+ } else if (this.frm.doc.quotation_to == "Lead") {
+ return true;
} else {
return this._super(party_field);
}
diff --git a/selling/doctype/quotation/quotation.py b/selling/doctype/quotation/quotation.py
index 44a67fa..78afb97 100644
--- a/selling/doctype/quotation/quotation.py
+++ b/selling/doctype/quotation/quotation.py
@@ -4,12 +4,11 @@
from __future__ import unicode_literals
import webnotes
-from webnotes.utils import cstr, getdate
+from webnotes.utils import cstr
from webnotes.model.bean import getlist
from webnotes.model.code import get_obj
from webnotes import _, msgprint
-sql = webnotes.conn.sql
from controllers.selling_controller import SellingController
@@ -48,17 +47,16 @@
if not doc.fields.get(r):
doc.fields[r] = res[r]
+
+ def has_sales_order(self):
+ return webnotes.conn.get_value("Sales Order Item", {"prevdoc_docname": self.doc.name, "docstatus": 1})
+
+
# Re-calculates Basic Rate & amount based on Price List Selected
# --------------------------------------------------------------
def get_adj_percent(self, arg=''):
get_obj('Sales Common').get_adj_percent(self)
-
- # Get Tax rate if account type is TAX
- # -----------------------------------
- def get_rate(self,arg):
- return get_obj('Sales Common').get_rate(arg)
-
# Does not allow same item code to be entered twice
# -------------------------------------------------
def validate_for_items(self):
@@ -78,7 +76,7 @@
if self.doc.order_type in ['Maintenance', 'Service']:
for d in getlist(self.doclist, 'quotation_details'):
- is_service_item = sql("select is_service_item from `tabItem` where name=%s", d.item_code)
+ is_service_item = webnotes.conn.sql("select is_service_item from `tabItem` where name=%s", d.item_code)
is_service_item = is_service_item and is_service_item[0][0] or 'No'
if is_service_item == 'No':
@@ -86,37 +84,16 @@
raise Exception
else:
for d in getlist(self.doclist, 'quotation_details'):
- is_sales_item = sql("select is_sales_item from `tabItem` where name=%s", d.item_code)
+ is_sales_item = webnotes.conn.sql("select is_sales_item from `tabItem` where name=%s", d.item_code)
is_sales_item = is_sales_item and is_sales_item[0][0] or 'No'
if is_sales_item == 'No':
msgprint("You can not select non sales item "+d.item_code+" in Sales Quotation")
raise Exception
- #--------------Validation For Last Contact Date-----------------
- # ====================================================================================================================
- def set_last_contact_date(self):
- #if not self.doc.contact_date_ref:
- #self.doc.contact_date_ref=self.doc.contact_date
- #self.doc.last_contact_date=self.doc.contact_date_ref
- if self.doc.contact_date_ref and self.doc.contact_date_ref != self.doc.contact_date:
- if getdate(self.doc.contact_date_ref) < getdate(self.doc.contact_date):
- self.doc.last_contact_date=self.doc.contact_date_ref
- else:
- msgprint("Contact Date Cannot be before Last Contact Date")
- raise Exception
-
def validate(self):
super(DocType, self).validate()
-
- import utilities
- if not self.doc.status:
- self.doc.status = "Draft"
- else:
- utilities.validate_status(self.doc.status, ["Draft", "Submitted",
- "Order Confirmed", "Order Lost", "Cancelled"])
-
- self.set_last_contact_date()
+ self.set_status()
self.validate_order_type()
self.validate_for_items()
@@ -126,42 +103,22 @@
sales_com_obj.check_active_sales_items(self)
sales_com_obj.validate_max_discount(self,'quotation_details')
sales_com_obj.check_conversion_rate(self)
-
-
- def on_update(self):
- # Set Quotation Status
- webnotes.conn.set(self.doc, 'status', 'Draft')
-
+
#update enquiry
#------------------
- def update_enquiry(self, flag):
- prevdoc=''
- for d in getlist(self.doclist, 'quotation_details'):
- if d.prevdoc_docname:
- prevdoc = d.prevdoc_docname
-
- if prevdoc:
- if flag == 'submit': #on submit
- sql("update `tabOpportunity` set status = 'Quotation Sent' where name = %s", prevdoc)
- elif flag == 'cancel': #on cancel
- sql("update `tabOpportunity` set status = 'Open' where name = %s", prevdoc)
- elif flag == 'order lost': #order lost
- sql("update `tabOpportunity` set status = 'Opportunity Lost' where name=%s", prevdoc)
- elif flag == 'order confirm': #order confirm
- sql("update `tabOpportunity` set status='Order Confirmed' where name=%s", prevdoc)
+ def update_opportunity(self):
+ for opportunity in self.doclist.get_distinct_values("prevdoc_docname"):
+ webnotes.bean("Opportunity", opportunity).get_controller().set_status(update=True)
# declare as order lost
#-------------------------
def declare_order_lost(self, arg):
- chk = sql("select t1.name from `tabSales Order` t1, `tabSales Order Item` t2 where t2.parent = t1.name and t1.docstatus=1 and t2.prevdoc_docname = %s",self.doc.name)
- if chk:
- msgprint("Sales Order No. "+cstr(chk[0][0])+" is submitted against this Quotation. Thus 'Order Lost' can not be declared against it.")
- raise Exception
- else:
- webnotes.conn.set(self.doc, 'status', 'Order Lost')
+ if not self.has_sales_order():
+ webnotes.conn.set(self.doc, 'status', 'Lost')
webnotes.conn.set(self.doc, 'order_lost_reason', arg)
- self.update_enquiry('order lost')
- return 'true'
+ self.update_opportunity()
+ else:
+ webnotes.throw(_("Cannot set as Lost as Sales Order is made."))
#check if value entered in item table
#--------------------------------------
@@ -177,21 +134,17 @@
# Check for Approving Authority
get_obj('Authorization Control').validate_approving_authority(self.doc.doctype, self.doc.company, self.doc.grand_total, self)
-
- # Set Quotation Status
- webnotes.conn.set(self.doc, 'status', 'Submitted')
-
+
#update enquiry status
- self.update_enquiry('submit')
+ self.update_opportunity()
# ON CANCEL
# ==========================================================================
def on_cancel(self):
#update enquiry status
- self.update_enquiry('cancel')
-
- webnotes.conn.set(self.doc,'status','Cancelled')
+ self.set_status()
+ self.update_opportunity()
# Print other charges
# ===========================================================================
@@ -203,6 +156,7 @@
lst1.append(d.total)
print_lst.append(lst1)
return print_lst
+
@webnotes.whitelist()
def make_sales_order(source_name, target_doclist=None):
diff --git a/selling/doctype/quotation/quotation.txt b/selling/doctype/quotation/quotation.txt
index 3f97c98..1620d91 100644
--- a/selling/doctype/quotation/quotation.txt
+++ b/selling/doctype/quotation/quotation.txt
@@ -2,13 +2,14 @@
{
"creation": "2013-05-24 19:29:08",
"docstatus": 0,
- "modified": "2013-09-10 10:46:33",
+ "modified": "2013-10-11 13:21:07",
"modified_by": "Administrator",
"owner": "Administrator"
},
{
"allow_attach": 1,
"allow_email": 0,
+ "allow_import": 1,
"autoname": "naming_series:",
"doctype": "DocType",
"document_type": "Transaction",
@@ -257,7 +258,6 @@
"width": "100px"
},
{
- "default": "1.00",
"description": "Rate at which customer's currency is converted to company's base currency",
"doctype": "DocField",
"fieldname": "conversion_rate",
@@ -733,7 +733,7 @@
"no_copy": 1,
"oldfieldname": "status",
"oldfieldtype": "Select",
- "options": "\nDraft\nSubmitted\nOrder Confirmed\nOrder Lost\nCancelled",
+ "options": "Draft\nSubmitted\nOrdered\nLost\nCancelled",
"print_hide": 1,
"read_only": 1,
"reqd": 1,
@@ -841,8 +841,7 @@
"fieldtype": "Table",
"hidden": 1,
"label": "Communications",
- "options": "Communication",
- "print_hide": 1
+ "options": "Communication"
},
{
"amend": 1,
diff --git a/selling/doctype/quotation/test_quotation.py b/selling/doctype/quotation/test_quotation.py
index cf3881d..366035e 100644
--- a/selling/doctype/quotation/test_quotation.py
+++ b/selling/doctype/quotation/test_quotation.py
@@ -11,17 +11,19 @@
def test_make_sales_order(self):
from selling.doctype.quotation.quotation import make_sales_order
- self.assertRaises(webnotes.ValidationError, make_sales_order, "_T-Quotation-00001")
+ quotation = webnotes.bean(copy=test_records[0])
+ quotation.insert()
- quotation = webnotes.bean("Quotation","_T-Quotation-00001")
+ self.assertRaises(webnotes.ValidationError, make_sales_order, quotation.doc.name)
+
quotation.submit()
- sales_order = make_sales_order("_T-Quotation-00001")
+ sales_order = make_sales_order(quotation.doc.name)
self.assertEquals(sales_order[0]["doctype"], "Sales Order")
self.assertEquals(len(sales_order), 2)
self.assertEquals(sales_order[1]["doctype"], "Sales Order Item")
- self.assertEquals(sales_order[1]["prevdoc_docname"], "_T-Quotation-00001")
+ self.assertEquals(sales_order[1]["prevdoc_docname"], quotation.doc.name)
self.assertEquals(sales_order[0]["customer"], "_Test Customer")
sales_order[0]["delivery_date"] = "2014-01-01"
diff --git a/selling/doctype/sales_common/sales_common.js b/selling/doctype/sales_common/sales_common.js
index 20ba92a..6a4b228 100644
--- a/selling/doctype/sales_common/sales_common.js
+++ b/selling/doctype/sales_common/sales_common.js
@@ -10,6 +10,7 @@
wn.provide("erpnext.selling");
wn.require("app/js/transaction.js");
+wn.require("app/js/controllers/accounts.js");
erpnext.selling.SellingController = erpnext.TransactionController.extend({
onload: function() {
@@ -162,8 +163,7 @@
var item = wn.model.get_doc(cdt, cdn);
if(item.item_code || item.barcode) {
if(!this.validate_company_and_party("customer")) {
- item.item_code = null;
- refresh_field("item_code", item.name, item.parentfield);
+ cur_frm.fields_dict[me.frm.cscript.fname].grid.grid_rows[item.idx - 1].remove();
} else {
return this.frm.call({
method: "selling.utils.get_item_details",
diff --git a/selling/doctype/sales_common/sales_common.py b/selling/doctype/sales_common/sales_common.py
index 8271c82..01718dd 100644
--- a/selling/doctype/sales_common/sales_common.py
+++ b/selling/doctype/sales_common/sales_common.py
@@ -86,26 +86,6 @@
if (obj.doc.price_list_currency == default_currency and flt(obj.doc.plc_conversion_rate) != 1.00) or not obj.doc.plc_conversion_rate or (obj.doc.price_list_currency != default_currency and flt(obj.doc.plc_conversion_rate) == 1.00):
msgprint("Please Enter Appropriate Conversion Rate for Price List Currency to Base Currency (%s --> %s)" % (obj.doc.price_list_currency, default_currency), raise_exception = 1)
-
-
-
- # Get Tax rate if account type is TAX
- # =========================================================================
- def get_rate(self, arg):
- arg = eval(arg)
- rate = webnotes.conn.sql("select account_type, tax_rate from `tabAccount` where name = '%s' and docstatus != 2" %(arg['account_head']), as_dict=1)
- ret = {'rate' : 0}
- if arg['charge_type'] == 'Actual' and rate[0]['account_type'] == 'Tax':
- msgprint("You cannot select ACCOUNT HEAD of type TAX as your CHARGE TYPE is 'ACTUAL'")
- ret = {
- 'account_head' : ''
- }
- elif rate[0]['account_type'] in ['Tax', 'Chargeable'] and not arg['charge_type'] == 'Actual':
- ret = {
- 'rate' : rate and flt(rate[0]['tax_rate']) or 0
- }
- return ret
-
def get_item_list(self, obj, is_stopped=0):
"""get item list"""
@@ -123,12 +103,12 @@
if flt(d.qty) > flt(d.delivered_qty):
reserved_qty_for_main_item = flt(d.qty) - flt(d.delivered_qty)
- if obj.doc.doctype == "Delivery Note" and d.prevdoc_doctype == 'Sales Order':
+ if obj.doc.doctype == "Delivery Note" and d.against_sales_order:
# if SO qty is 10 and there is tolerance of 20%, then it will allow DN of 12.
# But in this case reserved qty should only be reduced by 10 and not 12
already_delivered_qty = self.get_already_delivered_qty(obj.doc.name,
- d.prevdoc_docname, d.prevdoc_detail_docname)
+ d.against_sales_order, d.prevdoc_detail_docname)
so_qty, reserved_warehouse = self.get_so_qty_and_warehouse(d.prevdoc_detail_docname)
if already_delivered_qty + d.qty > so_qty:
@@ -168,7 +148,7 @@
def get_already_delivered_qty(self, dn, so, so_detail):
qty = webnotes.conn.sql("""select sum(qty) from `tabDelivery Note Item`
where prevdoc_detail_docname = %s and docstatus = 1
- and prevdoc_doctype = 'Sales Order' and prevdoc_docname = %s
+ and against_sales_order = %s
and parent != %s""", (so_detail, so, dn))
return qty and flt(qty[0][0]) or 0.0
@@ -218,7 +198,6 @@
pi.qty = flt(qty)
pi.actual_qty = bin and flt(bin['actual_qty']) or 0
pi.projected_qty = bin and flt(bin['projected_qty']) or 0
- pi.prevdoc_doctype = line.prevdoc_doctype
if not pi.warehouse:
pi.warehouse = warehouse
if not pi.batch_no:
@@ -283,8 +262,8 @@
def check_stop_sales_order(self,obj):
for d in getlist(obj.doclist,obj.fname):
ref_doc_name = ''
- if d.fields.has_key('prevdoc_docname') and d.prevdoc_docname and d.prevdoc_doctype == 'Sales Order':
- ref_doc_name = d.prevdoc_docname
+ if d.fields.has_key('against_sales_order') and d.against_sales_order:
+ ref_doc_name = d.against_sales_order
elif d.fields.has_key('sales_order') and d.sales_order and not d.delivery_note:
ref_doc_name = d.sales_order
if ref_doc_name:
@@ -319,17 +298,6 @@
exact_outstanding = flt(tot_outstanding) + flt(grand_total)
get_obj('Account',acc_head[0][0]).check_credit_limit(acc_head[0][0], obj.doc.company, exact_outstanding)
- def get_prevdoc_date(self, obj):
- for d in getlist(obj.doclist, obj.fname):
- if d.prevdoc_doctype and d.prevdoc_docname:
- if d.prevdoc_doctype in ["Sales Invoice", "Delivery Note"]:
- date_field = "posting_date"
- else:
- date_field = "transaction_date"
-
- d.prevdoc_date = webnotes.conn.get_value(d.prevdoc_doctype,
- d.prevdoc_docname, date_field)
-
def get_batch_no(doctype, txt, searchfield, start, page_len, filters):
from controllers.queries import get_match_cond
diff --git a/selling/doctype/sales_order/sales_order.js b/selling/doctype/sales_order/sales_order.js
index b121f15..bf23b9c 100644
--- a/selling/doctype/sales_order/sales_order.js
+++ b/selling/doctype/sales_order/sales_order.js
@@ -12,6 +12,7 @@
wn.require('app/selling/doctype/sales_common/sales_common.js');
wn.require('app/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.js');
wn.require('app/utilities/doctype/sms_control/sms_control.js');
+wn.require('app/accounts/doctype/sales_invoice/pos.js');
erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend({
refresh: function(doc, dt, dn) {
@@ -65,7 +66,7 @@
source_doctype: "Quotation",
get_query_filters: {
docstatus: 1,
- status: ["!=", "Order Lost"],
+ status: ["!=", "Lost"],
order_type: cur_frm.doc.order_type,
customer: cur_frm.doc.customer || undefined,
company: cur_frm.doc.company
@@ -188,4 +189,4 @@
if(cint(wn.boot.notification_settings.sales_order)) {
cur_frm.email_doc(wn.boot.notification_settings.sales_order_message);
}
-};
\ No newline at end of file
+};
diff --git a/selling/doctype/sales_order/sales_order.py b/selling/doctype/sales_order/sales_order.py
index 1ccccdd..1c22c08 100644
--- a/selling/doctype/sales_order/sales_order.py
+++ b/selling/doctype/sales_order/sales_order.py
@@ -11,7 +11,6 @@
from webnotes import msgprint
from webnotes.model.mapper import get_mapped_doclist
-sql = webnotes.conn.sql
from controllers.selling_controller import SellingController
@@ -39,9 +38,6 @@
def get_available_qty(self,args):
return get_obj('Sales Common').get_available_qty(eval(args))
- def get_rate(self,arg):
- return get_obj('Sales Common').get_rate(arg)
-
def validate_mandatory(self):
# validate transaction date v/s delivery date
if self.doc.delivery_date:
@@ -88,14 +84,14 @@
# used for production plan
d.transaction_date = self.doc.transaction_date
- tot_avail_qty = sql("select projected_qty from `tabBin` \
+ tot_avail_qty = webnotes.conn.sql("select projected_qty from `tabBin` \
where item_code = '%s' and warehouse = '%s'" % (d.item_code,d.reserved_warehouse))
d.projected_qty = tot_avail_qty and flt(tot_avail_qty[0][0]) or 0
def validate_sales_mntc_quotation(self):
for d in getlist(self.doclist, 'sales_order_details'):
if d.prevdoc_docname:
- res = sql("select name from `tabQuotation` where name=%s and order_type = %s", (d.prevdoc_docname, self.doc.order_type))
+ res = webnotes.conn.sql("select name from `tabQuotation` where name=%s and order_type = %s", (d.prevdoc_docname, self.doc.order_type))
if not res:
msgprint("""Order Type (%s) should be same in Quotation: %s \
and current Sales Order""" % (self.doc.order_type, d.prevdoc_docname))
@@ -112,7 +108,7 @@
def validate_proj_cust(self):
if self.doc.project_name and self.doc.customer_name:
- res = sql("select name from `tabProject` where name = '%s' and (customer = '%s' or ifnull(customer,'')='')"%(self.doc.project_name, self.doc.customer))
+ res = webnotes.conn.sql("select name from `tabProject` where name = '%s' and (customer = '%s' or ifnull(customer,'')='')"%(self.doc.project_name, self.doc.customer))
if not res:
msgprint("Customer - %s does not belong to project - %s. \n\nIf you want to use project for multiple customers then please make customer details blank in project - %s."%(self.doc.customer,self.doc.project_name,self.doc.project_name))
raise Exception
@@ -127,7 +123,7 @@
self.validate_po()
self.validate_uom_is_integer("stock_uom", "qty")
self.validate_for_items()
- self.validate_warehouse_user()
+ self.validate_warehouse()
sales_com_obj = get_obj(dt = 'Sales Common')
sales_com_obj.check_active_sales_items(self)
sales_com_obj.check_conversion_rate(self)
@@ -148,14 +144,15 @@
if not self.doc.delivery_status: self.doc.delivery_status = 'Not Delivered'
- def validate_warehouse_user(self):
- from stock.utils import validate_warehouse_user
+ def validate_warehouse(self):
+ from stock.utils import validate_warehouse_user, validate_warehouse_company
warehouses = list(set([d.reserved_warehouse for d in
self.doclist.get({"doctype": self.tname}) if d.reserved_warehouse]))
for w in warehouses:
validate_warehouse_user(w)
+ validate_warehouse_company(w, self.doc.company)
def validate_with_previous_doc(self):
super(DocType, self).validate_with_previous_doc(self.tname, {
@@ -166,36 +163,20 @@
})
- def check_prev_docstatus(self):
- for d in getlist(self.doclist, 'sales_order_details'):
- cancel_quo = sql("select name from `tabQuotation` where docstatus = 2 and name = '%s'" % d.prevdoc_docname)
- if cancel_quo:
- msgprint("Quotation :" + cstr(cancel_quo[0][0]) + " is already cancelled !")
- raise Exception , "Validation Error. "
-
def update_enquiry_status(self, prevdoc, flag):
- enq = sql("select t2.prevdoc_docname from `tabQuotation` t1, `tabQuotation Item` t2 where t2.parent = t1.name and t1.name=%s", prevdoc)
+ enq = webnotes.conn.sql("select t2.prevdoc_docname from `tabQuotation` t1, `tabQuotation Item` t2 where t2.parent = t1.name and t1.name=%s", prevdoc)
if enq:
- sql("update `tabOpportunity` set status = %s where name=%s",(flag,enq[0][0]))
+ webnotes.conn.sql("update `tabOpportunity` set status = %s where name=%s",(flag,enq[0][0]))
- def update_prevdoc_status(self, flag):
- for d in getlist(self.doclist, 'sales_order_details'):
- if d.prevdoc_docname:
- if flag=='submit':
- sql("update `tabQuotation` set status = 'Order Confirmed' where name=%s",d.prevdoc_docname)
-
- #update enquiry
- self.update_enquiry_status(d.prevdoc_docname, 'Order Confirmed')
- elif flag == 'cancel':
- chk = sql("select t1.name from `tabSales Order` t1, `tabSales Order Item` t2 where t2.parent = t1.name and t2.prevdoc_docname=%s and t1.name!=%s and t1.docstatus=1", (d.prevdoc_docname,self.doc.name))
- if not chk:
- sql("update `tabQuotation` set status = 'Submitted' where name=%s",d.prevdoc_docname)
-
- #update enquiry
- self.update_enquiry_status(d.prevdoc_docname, 'Quotation Sent')
+ def update_prevdoc_status(self, flag):
+ for quotation in self.doclist.get_distinct_values("prevdoc_docname"):
+ bean = webnotes.bean("Quotation", quotation)
+ if bean.doc.docstatus==2:
+ webnotes.throw(d.prevdoc_docname + ": " + webnotes._("Quotation is cancelled."))
+
+ bean.get_controller().set_status(update=True)
def on_submit(self):
- self.check_prev_docstatus()
self.update_stock_ledger(update_stock = 1)
get_obj('Sales Common').check_credit(self,self.doc.grand_total)
@@ -219,35 +200,35 @@
def check_nextdoc_docstatus(self):
# Checks Delivery Note
- submit_dn = sql("select t1.name from `tabDelivery Note` t1,`tabDelivery Note Item` t2 where t1.name = t2.parent and t2.prevdoc_docname = '%s' and t1.docstatus = 1" % (self.doc.name))
+ submit_dn = webnotes.conn.sql("select t1.name from `tabDelivery Note` t1,`tabDelivery Note Item` t2 where t1.name = t2.parent and t2.against_sales_order = %s and t1.docstatus = 1", self.doc.name)
if submit_dn:
msgprint("Delivery Note : " + cstr(submit_dn[0][0]) + " has been submitted against " + cstr(self.doc.doctype) + ". Please cancel Delivery Note : " + cstr(submit_dn[0][0]) + " first and then cancel "+ cstr(self.doc.doctype), raise_exception = 1)
# Checks Sales Invoice
- submit_rv = sql("select t1.name from `tabSales Invoice` t1,`tabSales Invoice Item` t2 where t1.name = t2.parent and t2.sales_order = '%s' and t1.docstatus = 1" % (self.doc.name))
+ submit_rv = webnotes.conn.sql("select t1.name from `tabSales Invoice` t1,`tabSales Invoice Item` t2 where t1.name = t2.parent and t2.sales_order = '%s' and t1.docstatus = 1" % (self.doc.name))
if submit_rv:
msgprint("Sales Invoice : " + cstr(submit_rv[0][0]) + " has already been submitted against " +cstr(self.doc.doctype)+ ". Please cancel Sales Invoice : "+ cstr(submit_rv[0][0]) + " first and then cancel "+ cstr(self.doc.doctype), raise_exception = 1)
#check maintenance schedule
- submit_ms = sql("select t1.name from `tabMaintenance Schedule` t1, `tabMaintenance Schedule Item` t2 where t2.parent=t1.name and t2.prevdoc_docname = %s and t1.docstatus = 1",self.doc.name)
+ submit_ms = webnotes.conn.sql("select t1.name from `tabMaintenance Schedule` t1, `tabMaintenance Schedule Item` t2 where t2.parent=t1.name and t2.prevdoc_docname = %s and t1.docstatus = 1",self.doc.name)
if submit_ms:
msgprint("Maintenance Schedule : " + cstr(submit_ms[0][0]) + " has already been submitted against " +cstr(self.doc.doctype)+ ". Please cancel Maintenance Schedule : "+ cstr(submit_ms[0][0]) + " first and then cancel "+ cstr(self.doc.doctype), raise_exception = 1)
# check maintenance visit
- submit_mv = sql("select t1.name from `tabMaintenance Visit` t1, `tabMaintenance Visit Purpose` t2 where t2.parent=t1.name and t2.prevdoc_docname = %s and t1.docstatus = 1",self.doc.name)
+ submit_mv = webnotes.conn.sql("select t1.name from `tabMaintenance Visit` t1, `tabMaintenance Visit Purpose` t2 where t2.parent=t1.name and t2.prevdoc_docname = %s and t1.docstatus = 1",self.doc.name)
if submit_mv:
msgprint("Maintenance Visit : " + cstr(submit_mv[0][0]) + " has already been submitted against " +cstr(self.doc.doctype)+ ". Please cancel Maintenance Visit : " + cstr(submit_mv[0][0]) + " first and then cancel "+ cstr(self.doc.doctype), raise_exception = 1)
# check production order
- pro_order = sql("""select name from `tabProduction Order` where sales_order = %s and docstatus = 1""", self.doc.name)
+ pro_order = webnotes.conn.sql("""select name from `tabProduction Order` where sales_order = %s and docstatus = 1""", self.doc.name)
if pro_order:
msgprint("""Production Order: %s exists against this sales order.
Please cancel production order first and then cancel this sales order""" %
pro_order[0][0], raise_exception=1)
def check_modified_date(self):
- mod_db = sql("select modified from `tabSales Order` where name = '%s'" % self.doc.name)
- date_diff = sql("select TIMEDIFF('%s', '%s')" % ( mod_db[0][0],cstr(self.doc.modified)))
+ mod_db = webnotes.conn.sql("select modified from `tabSales Order` where name = '%s'" % self.doc.name)
+ date_diff = webnotes.conn.sql("select TIMEDIFF('%s', '%s')" % ( mod_db[0][0],cstr(self.doc.modified)))
if date_diff and date_diff[0][0]:
msgprint("%s: %s has been modified after you have opened. Please Refresh"
% (self.doc.doctype, self.doc.name), raise_exception=1)
@@ -342,8 +323,7 @@
"field_map": {
"export_rate": "export_rate",
"name": "prevdoc_detail_docname",
- "parent": "prevdoc_docname",
- "parenttype": "prevdoc_doctype",
+ "parent": "against_sales_order",
"reserved_warehouse": "warehouse"
},
"postprocess": update_item,
diff --git a/selling/doctype/sales_order/sales_order.txt b/selling/doctype/sales_order/sales_order.txt
index 94e6388..c27f8ff 100644
--- a/selling/doctype/sales_order/sales_order.txt
+++ b/selling/doctype/sales_order/sales_order.txt
@@ -2,12 +2,13 @@
{
"creation": "2013-06-18 12:39:59",
"docstatus": 0,
- "modified": "2013-08-09 14:46:17",
+ "modified": "2013-10-11 13:18:47",
"modified_by": "Administrator",
"owner": "Administrator"
},
{
"allow_attach": 1,
+ "allow_import": 1,
"autoname": "naming_series:",
"doctype": "DocType",
"document_type": "Transaction",
@@ -272,7 +273,6 @@
"width": "100px"
},
{
- "default": "1.00",
"description": "Rate at which customer's currency is converted to company's base currency",
"doctype": "DocField",
"fieldname": "conversion_rate",
diff --git a/selling/doctype/sales_order/test_sales_order.py b/selling/doctype/sales_order/test_sales_order.py
index 7b72271..0ee58a7 100644
--- a/selling/doctype/sales_order/test_sales_order.py
+++ b/selling/doctype/sales_order/test_sales_order.py
@@ -72,10 +72,13 @@
def create_dn_against_so(self, so, delivered_qty=0):
from stock.doctype.delivery_note.test_delivery_note import test_records as dn_test_records
+ from stock.doctype.delivery_note.test_delivery_note import _insert_purchase_receipt
+
+ _insert_purchase_receipt(so.doclist[1].item_code)
+
dn = webnotes.bean(webnotes.copy_doclist(dn_test_records[0]))
dn.doclist[1].item_code = so.doclist[1].item_code
- dn.doclist[1].prevdoc_doctype = "Sales Order"
- dn.doclist[1].prevdoc_docname = so.doc.name
+ dn.doclist[1].against_sales_order = so.doc.name
dn.doclist[1].prevdoc_detail_docname = so.doclist[1].name
if delivered_qty:
dn.doclist[1].qty = delivered_qty
@@ -273,14 +276,13 @@
so.doclist[1].reserved_warehouse, 20.0)
def test_warehouse_user(self):
- webnotes.session.user = "test@example.com"
-
webnotes.bean("Profile", "test@example.com").get_controller()\
.add_roles("Sales User", "Sales Manager", "Material User", "Material Manager")
webnotes.bean("Profile", "test2@example.com").get_controller()\
.add_roles("Sales User", "Sales Manager", "Material User", "Material Manager")
-
+
+ webnotes.session.user = "test@example.com"
from stock.utils import UserNotAllowedForWarehouse
so = webnotes.bean(copy = test_records[0])
diff --git a/selling/doctype/sms_center/sms_center.py b/selling/doctype/sms_center/sms_center.py
index 29e793c..eb331c20 100644
--- a/selling/doctype/sms_center/sms_center.py
+++ b/selling/doctype/sms_center/sms_center.py
@@ -10,7 +10,6 @@
from webnotes.model.code import get_obj
from webnotes import msgprint
-sql = webnotes.conn.sql
# ----------
@@ -29,15 +28,15 @@
where_clause = self.doc.sales_partner and " and ifnull(is_sales_partner, 0) = 1 and sales_partner = '%s'" % self.doc.sales_partner or " and ifnull(sales_partner, '') != ''"
if self.doc.send_to in ['All Contact', 'All Customer Contact', 'All Supplier Contact', 'All Sales Partner Contact']:
- rec = sql("select CONCAT(ifnull(first_name,''),'',ifnull(last_name,'')), mobile_no from `tabContact` where ifnull(mobile_no,'')!='' and docstatus != 2 %s" % where_clause)
+ rec = webnotes.conn.sql("select CONCAT(ifnull(first_name,''),'',ifnull(last_name,'')), mobile_no from `tabContact` where ifnull(mobile_no,'')!='' and docstatus != 2 %s" % where_clause)
elif self.doc.send_to == 'All Lead (Open)':
- rec = sql("select lead_name, mobile_no from tabLead where ifnull(mobile_no,'')!='' and docstatus != 2 and status = 'Open'")
+ rec = webnotes.conn.sql("select lead_name, mobile_no from tabLead where ifnull(mobile_no,'')!='' and docstatus != 2 and status = 'Open'")
elif self.doc.send_to == 'All Employee (Active)':
where_clause = self.doc.department and " and department = '%s'" % self.doc.department or ""
where_clause += self.doc.branch and " and branch = '%s'" % self.doc.branch or ""
- rec = sql("select employee_name, cell_number from `tabEmployee` where status = 'Active' and docstatus < 2 and ifnull(cell_number,'')!='' %s" % where_clause)
+ rec = webnotes.conn.sql("select employee_name, cell_number from `tabEmployee` where status = 'Active' and docstatus < 2 and ifnull(cell_number,'')!='' %s" % where_clause)
elif self.doc.send_to == 'All Sales Person':
- rec = sql("select sales_person_name, mobile_no from `tabSales Person` where docstatus != 2 and ifnull(mobile_no,'')!=''")
+ rec = webnotes.conn.sql("select sales_person_name, mobile_no from `tabSales Person` where docstatus != 2 and ifnull(mobile_no,'')!=''")
rec_list = ''
for d in rec:
rec_list += d[0] + ' - ' + d[1] + '\n'
diff --git a/selling/page/sales_funnel/__init__.py b/selling/page/sales_funnel/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/selling/page/sales_funnel/__init__.py
diff --git a/selling/page/sales_funnel/sales_funnel.css b/selling/page/sales_funnel/sales_funnel.css
new file mode 100644
index 0000000..89e904f
--- /dev/null
+++ b/selling/page/sales_funnel/sales_funnel.css
@@ -0,0 +1,3 @@
+.funnel-wrapper {
+ margin: 15px;
+}
\ No newline at end of file
diff --git a/selling/page/sales_funnel/sales_funnel.js b/selling/page/sales_funnel/sales_funnel.js
new file mode 100644
index 0000000..e2c3a98
--- /dev/null
+++ b/selling/page/sales_funnel/sales_funnel.js
@@ -0,0 +1,189 @@
+// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd.
+// License: GNU General Public License v3. See license.txt
+
+wn.pages['sales-funnel'].onload = function(wrapper) {
+ wn.ui.make_app_page({
+ parent: wrapper,
+ title: 'Sales Funnel',
+ single_column: true
+ });
+
+ wrapper.crm_funnel = new erpnext.CRMFunnel(wrapper);
+
+ wrapper.appframe.add_module_icon("Selling", "sales-funnel", function() {
+ wn.set_route("selling-home");
+ });
+}
+
+erpnext.CRMFunnel = Class.extend({
+ init: function(wrapper) {
+ var me = this;
+ // 0 setTimeout hack - this gives time for canvas to get width and height
+ setTimeout(function() {
+ me.setup(wrapper);
+ me.get_data();
+ }, 0);
+ },
+
+ setup: function(wrapper) {
+ var me = this;
+
+ this.elements = {
+ layout: $(wrapper).find(".layout-main"),
+ from_date: wrapper.appframe.add_date("From Date"),
+ to_date: wrapper.appframe.add_date("To Date"),
+ refresh_btn: wrapper.appframe.add_button("Refresh",
+ function() { me.get_data(); }, "icon-refresh"),
+ };
+
+ this.elements.no_data = $('<div class="alert alert-warning">No Data</div>')
+ .toggle(false)
+ .appendTo(this.elements.layout);
+
+ this.elements.funnel_wrapper = $('<div class="funnel-wrapper text-center"></div>')
+ .appendTo(this.elements.layout);
+
+ this.options = {
+ from_date: wn.datetime.add_months(wn.datetime.get_today(), -1),
+ to_date: wn.datetime.get_today()
+ };
+
+ // set defaults and bind on change
+ $.each(this.options, function(k, v) {
+ me.elements[k].val(wn.datetime.str_to_user(v));
+ me.elements[k].on("change", function() {
+ me.options[k] = wn.datetime.user_to_str($(this).val());
+ me.get_data();
+ });
+ });
+
+ // bind refresh
+ this.elements.refresh_btn.on("click", function() {
+ me.get_data(this);
+ });
+
+ // bind resize
+ $(window).resize(function() {
+ me.render();
+ });
+ },
+
+ get_data: function(btn) {
+ var me = this;
+ wn.call({
+ module: "selling",
+ page: "crm_funnel",
+ method: "get_funnel_data",
+ args: {
+ from_date: this.options.from_date,
+ to_date: this.options.to_date
+ },
+ btn: btn,
+ callback: function(r) {
+ if(!r.exc) {
+ me.options.data = r.message;
+ me.render();
+ }
+ }
+ });
+ },
+
+ render: function() {
+ var me = this;
+ this.prepare();
+
+ var context = this.elements.context,
+ x_start = 0.0,
+ x_end = this.options.width,
+ x_mid = (x_end - x_start) / 2.0,
+ y = 0,
+ y_old = 0.0;
+
+ if(this.options.total_value === 0) {
+ this.elements.no_data.toggle(true);
+ return;
+ }
+
+ this.options.data.forEach(function(d) {
+ context.fillStyle = d.color;
+ context.strokeStyle = d.color;
+ me.draw_triangle(x_start, x_mid, x_end, y, me.options.height);
+
+ y_old = y;
+
+ // new y
+ y = y + d.height;
+
+ // new x
+ var half_side = (me.options.height - y) / Math.sqrt(3);
+ x_start = x_mid - half_side;
+ x_end = x_mid + half_side;
+
+ var y_mid = y_old + (y - y_old) / 2.0;
+
+ me.draw_legend(x_mid, y_mid, me.options.width, me.options.height, d.value + " - " + d.title);
+ });
+ },
+
+ prepare: function() {
+ var me = this;
+
+ this.elements.no_data.toggle(false);
+
+ // calculate width and height options
+ this.options.width = $(this.elements.funnel_wrapper).width() * 2.0 / 3.0;
+ this.options.height = (Math.sqrt(3) * this.options.width) / 2.0;
+
+ // calculate total weightage
+ // as height decreases, area decreases by the square of the reduction
+ // hence, compensating by squaring the index value
+ this.options.total_weightage = this.options.data.reduce(
+ function(prev, curr, i) { return prev + Math.pow(i+1, 2) * curr.value; }, 0.0);
+
+ // calculate height for each data
+ $.each(this.options.data, function(i, d) {
+ d.height = me.options.height * d.value * Math.pow(i+1, 2) / me.options.total_weightage;
+ });
+
+ this.elements.canvas = $('<canvas></canvas>')
+ .appendTo(this.elements.funnel_wrapper.empty())
+ .attr("width", $(this.elements.funnel_wrapper).width())
+ .attr("height", this.options.height);
+
+ this.elements.context = this.elements.canvas.get(0).getContext("2d");
+ },
+
+ draw_triangle: function(x_start, x_mid, x_end, y, height) {
+ var context = this.elements.context;
+ context.beginPath();
+ context.moveTo(x_start, y);
+ context.lineTo(x_end, y);
+ context.lineTo(x_mid, height);
+ context.lineTo(x_start, y);
+ context.closePath();
+ context.fill();
+ },
+
+ draw_legend: function(x_mid, y_mid, width, height, title) {
+ var context = this.elements.context;
+
+ // draw line
+ context.beginPath();
+ context.moveTo(x_mid, y_mid);
+ context.lineTo(width, y_mid);
+ context.closePath();
+ context.stroke();
+
+ // draw circle
+ context.beginPath();
+ context.arc(width, y_mid, 5, 0, Math.PI * 2, false);
+ context.closePath();
+ context.fill();
+
+ // draw text
+ context.fillStyle = "black";
+ context.textBaseline = "middle";
+ context.font = "1.1em sans-serif";
+ context.fillText(title, width + 20, y_mid);
+ }
+});
\ No newline at end of file
diff --git a/selling/page/sales_funnel/sales_funnel.py b/selling/page/sales_funnel/sales_funnel.py
new file mode 100644
index 0000000..be0aebe
--- /dev/null
+++ b/selling/page/sales_funnel/sales_funnel.py
@@ -0,0 +1,33 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd.
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import webnotes
+
+@webnotes.whitelist()
+def get_funnel_data(from_date, to_date):
+ active_leads = webnotes.conn.sql("""select count(*) from `tabLead`
+ where (`modified` between %s and %s)
+ and status != "Do Not Contact" """, (from_date, to_date))[0][0]
+
+ active_leads += webnotes.conn.sql("""select count(distinct customer) from `tabContact`
+ where (`modified` between %s and %s)
+ and status != "Passive" """, (from_date, to_date))[0][0]
+
+ opportunities = webnotes.conn.sql("""select count(*) from `tabOpportunity`
+ where docstatus = 1 and (`modified` between %s and %s)
+ and status != "Lost" """, (from_date, to_date))[0][0]
+
+ quotations = webnotes.conn.sql("""select count(*) from `tabQuotation`
+ where docstatus = 1 and (`modified` between %s and %s)
+ and status != "Lost" """, (from_date, to_date))[0][0]
+
+ sales_orders = webnotes.conn.sql("""select count(*) from `tabQuotation`
+ where docstatus = 1 and (`modified` between %s and %s)""", (from_date, to_date))[0][0]
+
+ return [
+ { "title": "Active Leads / Customers", "value": active_leads, "color": "#B03B46" },
+ { "title": "Opportunities", "value": opportunities, "color": "#F09C00" },
+ { "title": "Quotations", "value": quotations, "color": "#006685" },
+ { "title": "Sales Orders", "value": sales_orders, "color": "#00AD65" }
+ ]
diff --git a/selling/page/sales_funnel/sales_funnel.txt b/selling/page/sales_funnel/sales_funnel.txt
new file mode 100644
index 0000000..b841f20
--- /dev/null
+++ b/selling/page/sales_funnel/sales_funnel.txt
@@ -0,0 +1,33 @@
+[
+ {
+ "creation": "2013-10-04 13:17:18",
+ "docstatus": 0,
+ "modified": "2013-10-04 13:17:18",
+ "modified_by": "Administrator",
+ "owner": "Administrator"
+ },
+ {
+ "doctype": "Page",
+ "icon": "icon-filter",
+ "module": "Selling",
+ "name": "__common__",
+ "page_name": "sales-funnel",
+ "standard": "Yes",
+ "title": "Sales Funnel"
+ },
+ {
+ "doctype": "Page Role",
+ "name": "__common__",
+ "parent": "sales-funnel",
+ "parentfield": "roles",
+ "parenttype": "Page",
+ "role": "Sales Manager"
+ },
+ {
+ "doctype": "Page",
+ "name": "sales-funnel"
+ },
+ {
+ "doctype": "Page Role"
+ }
+]
\ No newline at end of file
diff --git a/selling/page/selling_home/selling_home.js b/selling/page/selling_home/selling_home.js
index b4e9305..6e6283d 100644
--- a/selling/page/selling_home/selling_home.js
+++ b/selling/page/selling_home/selling_home.js
@@ -155,6 +155,10 @@
"label":wn._("Sales Analytics"),
page: "sales-analytics"
},
+ {
+ "label":wn._("Sales Funnel"),
+ page: "sales-funnel"
+ },
]
},
{
diff --git a/selling/utils/__init__.py b/selling/utils/__init__.py
index 224944d..6e74ac4 100644
--- a/selling/utils/__init__.py
+++ b/selling/utils/__init__.py
@@ -34,6 +34,7 @@
"plc_conversion_rate": 1.0
}
"""
+
if isinstance(args, basestring):
args = json.loads(args)
args = webnotes._dict(args)
@@ -73,7 +74,7 @@
out.update(apply_pos_settings(pos_settings, out))
if args.doctype in ("Sales Invoice", "Delivery Note"):
- if item_bean.doc.has_serial_no and not args.serial_no:
+ if item_bean.doc.has_serial_no == "Yes" and not args.serial_no:
out.serial_no = _get_serial_nos_by_fifo(args, item_bean)
return out
diff --git a/selling/utils/cart.py b/selling/utils/cart.py
index 7e7fb2e..e48059d 100644
--- a/selling/utils/cart.py
+++ b/selling/utils/cart.py
@@ -12,8 +12,8 @@
def set_cart_count(quotation=None):
if not quotation:
quotation = _get_cart_quotation()
- webnotes.add_cookies["cart_count"] = cstr(len(quotation.doclist.get(
- {"parentfield": "quotation_details"})) or "")
+ cart_count = cstr(len(quotation.doclist.get({"parentfield": "quotation_details"})))
+ webnotes._response.set_cookie("cart_count", cart_count)
@webnotes.whitelist()
def get_cart_quotation(doclist=None):
@@ -47,7 +47,7 @@
sales_order.ignore_permissions = True
sales_order.insert()
sales_order.submit()
- webnotes.add_cookies["cart_count"] = ""
+ webnotes._response.set_cookie("cart_count", "")
return sales_order.doc.name
diff --git a/setup/doctype/authorization_control/authorization_control.py b/setup/doctype/authorization_control/authorization_control.py
index ddf0791..533f398 100644
--- a/setup/doctype/authorization_control/authorization_control.py
+++ b/setup/doctype/authorization_control/authorization_control.py
@@ -9,7 +9,6 @@
from webnotes import session, msgprint
from setup.utils import get_company_currency
-sql = webnotes.conn.sql
from utilities.transaction_base import TransactionBase
@@ -28,10 +27,10 @@
amt_list.append(flt(x[0]))
max_amount = max(amt_list)
- app_dtl = sql("select approving_user, approving_role from `tabAuthorization Rule` where transaction = %s and (value = %s or value > %s) and docstatus != 2 and based_on = %s and company = %s %s" % ('%s', '%s', '%s', '%s', '%s', condition), (doctype_name, flt(max_amount), total, based_on, company))
+ app_dtl = webnotes.conn.sql("select approving_user, approving_role from `tabAuthorization Rule` where transaction = %s and (value = %s or value > %s) and docstatus != 2 and based_on = %s and company = %s %s" % ('%s', '%s', '%s', '%s', '%s', condition), (doctype_name, flt(max_amount), total, based_on, company))
if not app_dtl:
- app_dtl = sql("select approving_user, approving_role from `tabAuthorization Rule` where transaction = %s and (value = %s or value > %s) and docstatus != 2 and based_on = %s and ifnull(company,'') = '' %s" % ('%s', '%s', '%s', '%s', condition), (doctype_name, flt(max_amount), total, based_on))
+ app_dtl = webnotes.conn.sql("select approving_user, approving_role from `tabAuthorization Rule` where transaction = %s and (value = %s or value > %s) and docstatus != 2 and based_on = %s and ifnull(company,'') = '' %s" % ('%s', '%s', '%s', '%s', condition), (doctype_name, flt(max_amount), total, based_on))
for d in app_dtl:
if(d[0]): appr_users.append(d[0])
if(d[1]): appr_roles.append(d[1])
@@ -58,18 +57,18 @@
add_cond1,add_cond2 = '',''
if based_on == 'Itemwise Discount':
add_cond1 += " and master_name = '"+cstr(item)+"'"
- itemwise_exists = sql("select value from `tabAuthorization Rule` where transaction = %s and value <= %s and based_on = %s and company = %s and docstatus != 2 %s %s" % ('%s', '%s', '%s', '%s', cond, add_cond1), (doctype_name, total, based_on, company))
+ itemwise_exists = webnotes.conn.sql("select value from `tabAuthorization Rule` where transaction = %s and value <= %s and based_on = %s and company = %s and docstatus != 2 %s %s" % ('%s', '%s', '%s', '%s', cond, add_cond1), (doctype_name, total, based_on, company))
if not itemwise_exists:
- itemwise_exists = sql("select value from `tabAuthorization Rule` where transaction = %s and value <= %s and based_on = %s and ifnull(company,'') = '' and docstatus != 2 %s %s" % ('%s', '%s', '%s', cond, add_cond1), (doctype_name, total, based_on))
+ itemwise_exists = webnotes.conn.sql("select value from `tabAuthorization Rule` where transaction = %s and value <= %s and based_on = %s and ifnull(company,'') = '' and docstatus != 2 %s %s" % ('%s', '%s', '%s', cond, add_cond1), (doctype_name, total, based_on))
if itemwise_exists:
self.get_appr_user_role(itemwise_exists, doctype_name, total, based_on, cond+add_cond1, item,company)
chk = 0
if chk == 1:
if based_on == 'Itemwise Discount': add_cond2 += " and ifnull(master_name,'') = ''"
- appr = sql("select value from `tabAuthorization Rule` where transaction = %s and value <= %s and based_on = %s and company = %s and docstatus != 2 %s %s" % ('%s', '%s', '%s', '%s', cond, add_cond2), (doctype_name, total, based_on, company))
+ appr = webnotes.conn.sql("select value from `tabAuthorization Rule` where transaction = %s and value <= %s and based_on = %s and company = %s and docstatus != 2 %s %s" % ('%s', '%s', '%s', '%s', cond, add_cond2), (doctype_name, total, based_on, company))
if not appr:
- appr = sql("select value from `tabAuthorization Rule` where transaction = %s and value <= %s and based_on = %s and ifnull(company,'') = '' and docstatus != 2 %s %s"% ('%s', '%s', '%s', cond, add_cond2), (doctype_name, total, based_on))
+ appr = webnotes.conn.sql("select value from `tabAuthorization Rule` where transaction = %s and value <= %s and based_on = %s and ifnull(company,'') = '' and docstatus != 2 %s %s"% ('%s', '%s', '%s', cond, add_cond2), (doctype_name, total, based_on))
self.get_appr_user_role(appr, doctype_name, total, based_on, cond+add_cond2, item, company)
@@ -112,7 +111,7 @@
# ================
# Check for authorization set for individual user
- based_on = [x[0] for x in sql("select distinct based_on from `tabAuthorization Rule` where transaction = %s and system_user = %s and (company = %s or ifnull(company,'')='') and docstatus != 2", (doctype_name, session['user'], company))]
+ based_on = [x[0] for x in webnotes.conn.sql("select distinct based_on from `tabAuthorization Rule` where transaction = %s and system_user = %s and (company = %s or ifnull(company,'')='') and docstatus != 2", (doctype_name, session['user'], company))]
for d in based_on:
self.bifurcate_based_on_type(doctype_name, total, av_dis, d, doc_obj, 1, company)
@@ -124,7 +123,7 @@
# Specific Role
# ===============
# Check for authorization set on particular roles
- based_on = [x[0] for x in sql("""select based_on
+ based_on = [x[0] for x in webnotes.conn.sql("""select based_on
from `tabAuthorization Rule`
where transaction = %s and system_role IN (%s) and based_on IN (%s)
and (company = %s or ifnull(company,'')='')
@@ -148,9 +147,9 @@
# payroll related check
def get_value_based_rule(self,doctype_name,employee,total_claimed_amount,company):
val_lst =[]
- val = sql("select value from `tabAuthorization Rule` where transaction=%s and (to_emp=%s or to_designation IN (select designation from `tabEmployee` where name=%s)) and ifnull(value,0)< %s and company = %s and docstatus!=2",(doctype_name,employee,employee,total_claimed_amount,company))
+ val = webnotes.conn.sql("select value from `tabAuthorization Rule` where transaction=%s and (to_emp=%s or to_designation IN (select designation from `tabEmployee` where name=%s)) and ifnull(value,0)< %s and company = %s and docstatus!=2",(doctype_name,employee,employee,total_claimed_amount,company))
if not val:
- val = sql("select value from `tabAuthorization Rule` where transaction=%s and (to_emp=%s or to_designation IN (select designation from `tabEmployee` where name=%s)) and ifnull(value,0)< %s and ifnull(company,'') = '' and docstatus!=2",(doctype_name, employee, employee, total_claimed_amount))
+ val = webnotes.conn.sql("select value from `tabAuthorization Rule` where transaction=%s and (to_emp=%s or to_designation IN (select designation from `tabEmployee` where name=%s)) and ifnull(value,0)< %s and ifnull(company,'') = '' and docstatus!=2",(doctype_name, employee, employee, total_claimed_amount))
if val:
val_lst = [y[0] for y in val]
@@ -158,9 +157,9 @@
val_lst.append(0)
max_val = max(val_lst)
- rule = sql("select name, to_emp, to_designation, approving_role, approving_user from `tabAuthorization Rule` where transaction=%s and company = %s and (to_emp=%s or to_designation IN (select designation from `tabEmployee` where name=%s)) and ifnull(value,0)= %s and docstatus!=2",(doctype_name,company,employee,employee,flt(max_val)), as_dict=1)
+ rule = webnotes.conn.sql("select name, to_emp, to_designation, approving_role, approving_user from `tabAuthorization Rule` where transaction=%s and company = %s and (to_emp=%s or to_designation IN (select designation from `tabEmployee` where name=%s)) and ifnull(value,0)= %s and docstatus!=2",(doctype_name,company,employee,employee,flt(max_val)), as_dict=1)
if not rule:
- rule = sql("select name, to_emp, to_designation, approving_role, approving_user from `tabAuthorization Rule` where transaction=%s and ifnull(company,'') = '' and (to_emp=%s or to_designation IN (select designation from `tabEmployee` where name=%s)) and ifnull(value,0)= %s and docstatus!=2",(doctype_name,employee,employee,flt(max_val)), as_dict=1)
+ rule = webnotes.conn.sql("select name, to_emp, to_designation, approving_role, approving_user from `tabAuthorization Rule` where transaction=%s and ifnull(company,'') = '' and (to_emp=%s or to_designation IN (select designation from `tabEmployee` where name=%s)) and ifnull(value,0)= %s and docstatus!=2",(doctype_name,employee,employee,flt(max_val)), as_dict=1)
return rule
@@ -175,9 +174,9 @@
if doctype_name == 'Expense Claim':
rule = self.get_value_based_rule(doctype_name,doc_obj.doc.employee,doc_obj.doc.total_claimed_amount, doc_obj.doc.company)
elif doctype_name == 'Appraisal':
- rule = sql("select name, to_emp, to_designation, approving_role, approving_user from `tabAuthorization Rule` where transaction=%s and (to_emp=%s or to_designation IN (select designation from `tabEmployee` where name=%s)) and company = %s and docstatus!=2",(doctype_name,doc_obj.doc.employee, doc_obj.doc.employee, doc_obj.doc.company),as_dict=1)
+ rule = webnotes.conn.sql("select name, to_emp, to_designation, approving_role, approving_user from `tabAuthorization Rule` where transaction=%s and (to_emp=%s or to_designation IN (select designation from `tabEmployee` where name=%s)) and company = %s and docstatus!=2",(doctype_name,doc_obj.doc.employee, doc_obj.doc.employee, doc_obj.doc.company),as_dict=1)
if not rule:
- rule = sql("select name, to_emp, to_designation, approving_role, approving_user from `tabAuthorization Rule` where transaction=%s and (to_emp=%s or to_designation IN (select designation from `tabEmployee` where name=%s)) and ifnull(company,'') = '' and docstatus!=2",(doctype_name,doc_obj.doc.employee, doc_obj.doc.employee),as_dict=1)
+ rule = webnotes.conn.sql("select name, to_emp, to_designation, approving_role, approving_user from `tabAuthorization Rule` where transaction=%s and (to_emp=%s or to_designation IN (select designation from `tabEmployee` where name=%s)) and ifnull(company,'') = '' and docstatus!=2",(doctype_name,doc_obj.doc.employee, doc_obj.doc.employee),as_dict=1)
if rule:
for m in rule:
@@ -185,7 +184,7 @@
if m['approving_user']:
app_specific_user.append(m['approving_user'])
elif m['approving_role']:
- user_lst = [z[0] for z in sql("select distinct t1.name from `tabProfile` t1, `tabUserRole` t2 where t2.role=%s and t2.parent=t1.name and t1.name !='Administrator' and t1.name != 'Guest' and t1.docstatus !=2",m['approving_role'])]
+ user_lst = [z[0] for z in webnotes.conn.sql("select distinct t1.name from `tabProfile` t1, `tabUserRole` t2 where t2.role=%s and t2.parent=t1.name and t1.name !='Administrator' and t1.name != 'Guest' and t1.docstatus !=2",m['approving_role'])]
for x in user_lst:
if not x in app_user:
app_user.append(x)
diff --git a/setup/doctype/authorization_rule/authorization_rule.py b/setup/doctype/authorization_rule/authorization_rule.py
index 3d2de47..ea22b15 100644
--- a/setup/doctype/authorization_rule/authorization_rule.py
+++ b/setup/doctype/authorization_rule/authorization_rule.py
@@ -9,7 +9,6 @@
from webnotes.model.bean import copy_doclist
from webnotes import msgprint
-sql = webnotes.conn.sql
@@ -19,7 +18,7 @@
def check_duplicate_entry(self):
- exists = sql("""select name, docstatus from `tabAuthorization Rule`
+ exists = webnotes.conn.sql("""select name, docstatus from `tabAuthorization Rule`
where transaction = %s and based_on = %s and system_user = %s
and system_role = %s and approving_user = %s and approving_role = %s
and to_emp =%s and to_designation=%s and name != %s""",
@@ -39,12 +38,12 @@
def validate_master_name(self):
if self.doc.based_on == 'Customerwise Discount' and \
- not sql("select name from tabCustomer where name = '%s' and docstatus != 2" % \
+ not webnotes.conn.sql("select name from tabCustomer where name = '%s' and docstatus != 2" % \
(self.doc.master_name)):
msgprint("Please select valid Customer Name for Customerwise Discount",
raise_exception=1)
elif self.doc.based_on == 'Itemwise Discount' and \
- not sql("select name from tabItem where name = '%s' and docstatus != 2" % \
+ not webnotes.conn.sql("select name from tabItem where name = '%s' and docstatus != 2" % \
(self.doc.master_name)):
msgprint("Please select valid Item Name for Itemwise Discount", raise_exception=1)
elif (self.doc.based_on == 'Grand Total' or \
@@ -65,7 +64,7 @@
Applicable To (Role).", raise_exception=1)
elif self.doc.system_user and self.doc.approving_role and \
has_common([self.doc.approving_role], [x[0] for x in \
- sql("select role from `tabUserRole` where parent = '%s'" % \
+ webnotes.conn.sql("select role from `tabUserRole` where parent = '%s'" % \
(self.doc.system_user))]):
msgprint("System User : %s is assigned role : %s. So rule does not make sense" %
(self.doc.system_user,self.doc.approving_role), raise_exception=1)
diff --git a/setup/doctype/backup_manager/backup_dropbox.py b/setup/doctype/backup_manager/backup_dropbox.py
index 8d16353..3d3f428 100644
--- a/setup/doctype/backup_manager/backup_dropbox.py
+++ b/setup/doctype/backup_manager/backup_dropbox.py
@@ -61,8 +61,8 @@
allowed = 0
message = "Dropbox Access not approved."
- webnotes.message_title = "Dropbox Approval"
- webnotes.message = "<h3>%s</h3><p>Please close this window.</p>" % message
+ webnotes.local.message_title = "Dropbox Approval"
+ webnotes.local.message = "<h3>%s</h3><p>Please close this window.</p>" % message
webnotes.conn.commit()
webnotes.response['type'] = 'page'
@@ -96,6 +96,7 @@
error_log = []
path = os.path.join(get_base_path(), "public", "files")
for filename in os.listdir(path):
+ filename = cstr(filename)
if filename in ignore_list:
continue
diff --git a/setup/doctype/backup_manager/backup_googledrive.py b/setup/doctype/backup_manager/backup_googledrive.py
index 5d7b6ad..c72295a 100644
--- a/setup/doctype/backup_manager/backup_googledrive.py
+++ b/setup/doctype/backup_manager/backup_googledrive.py
@@ -85,6 +85,7 @@
webnotes.conn.close()
path = os.path.join(get_base_path(), "public", "files")
for filename in os.listdir(path):
+ filename = cstr(filename)
found = False
filepath = os.path.join(path, filename)
ext = filename.split('.')[-1]
@@ -113,9 +114,9 @@
def get_gdrive_flow():
from oauth2client.client import OAuth2WebServerFlow
- import conf
+ from webnotes import conf
- if not hasattr(conf, "gdrive_client_id"):
+ if not "gdrive_client_id" in conf:
webnotes.msgprint(_("Please set Google Drive access keys in") + " conf.py",
raise_exception=True)
@@ -169,4 +170,4 @@
return database['id']
if __name__=="__main__":
- backup_to_gdrive()
\ No newline at end of file
+ backup_to_gdrive()
diff --git a/setup/doctype/company/charts/import_from_openerp.py b/setup/doctype/company/charts/import_from_openerp.py
index 894c2d7..ef80008 100644
--- a/setup/doctype/company/charts/import_from_openerp.py
+++ b/setup/doctype/company/charts/import_from_openerp.py
@@ -9,6 +9,7 @@
import os, json
from xml.etree import ElementTree as ET
from webnotes.utils.datautils import read_csv_content
+from webnotes.utils import cstr
path = "/Users/rmehta/Downloads/openerp/openerp/addons"
chart_roots = []
@@ -108,6 +109,7 @@
basename = os.path.basename(basepath)
if basename.startswith("l10n"):
for fname in files:
+ fname = cstr(fname)
filepath = os.path.join(basepath, fname)
if fname.endswith(".xml"):
tree = ET.parse(filepath)
diff --git a/setup/doctype/company/company.py b/setup/doctype/company/company.py
index 9746eb2..da220cc 100644
--- a/setup/doctype/company/company.py
+++ b/setup/doctype/company/company.py
@@ -10,7 +10,6 @@
from webnotes.model.code import get_obj
import webnotes.defaults
-sql = webnotes.conn.sql
class DocType:
def __init__(self,d,dl):
diff --git a/setup/doctype/company/company.txt b/setup/doctype/company/company.txt
index 1ba1dde..eb6b9ad 100644
--- a/setup/doctype/company/company.txt
+++ b/setup/doctype/company/company.txt
@@ -2,7 +2,7 @@
{
"creation": "2013-04-10 08:35:39",
"docstatus": 0,
- "modified": "2013-08-28 19:15:04",
+ "modified": "2013-10-08 15:18:34",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -80,7 +80,7 @@
"fieldtype": "Select",
"label": "Domain",
"options": "Distribution\nManufacturing\nRetail\nServices",
- "reqd": 1
+ "reqd": 0
},
{
"doctype": "DocField",
diff --git a/setup/doctype/customer_group/customer_group.py b/setup/doctype/customer_group/customer_group.py
index 0940e1f..1264c1b 100644
--- a/setup/doctype/customer_group/customer_group.py
+++ b/setup/doctype/customer_group/customer_group.py
@@ -5,7 +5,6 @@
import webnotes
from webnotes import msgprint
-sql = webnotes.conn.sql
from webnotes.utils.nestedset import DocTypeNestedSet
class DocType(DocTypeNestedSet):
@@ -15,7 +14,7 @@
self.nsm_parent_field = 'parent_customer_group';
def validate(self):
- if sql("select name from `tabCustomer Group` where name = %s and docstatus = 2",
+ if webnotes.conn.sql("select name from `tabCustomer Group` where name = %s and docstatus = 2",
(self.doc.customer_group_name)):
msgprint("""Another %s record is trashed.
To untrash please go to Setup -> Recycle Bin.""" %
@@ -33,7 +32,7 @@
self.doc.name, raise_exception=1)
def on_trash(self):
- cust = sql("select name from `tabCustomer` where ifnull(customer_group, '') = %s",
+ cust = webnotes.conn.sql("select name from `tabCustomer` where ifnull(customer_group, '') = %s",
self.doc.name)
cust = [d[0] for d in cust]
if cust:
@@ -42,7 +41,7 @@
To trash/delete this, remove/change customer group in customer master""" %
(self.doc.name, cust or ''), raise_exception=1)
- if sql("select name from `tabCustomer Group` where parent_customer_group = %s \
+ if webnotes.conn.sql("select name from `tabCustomer Group` where parent_customer_group = %s \
and docstatus != 2", self.doc.name):
msgprint("Child customer group exists for this customer group. \
You can not trash/cancel/delete this customer group.", raise_exception=1)
diff --git a/setup/doctype/email_digest/email_digest.py b/setup/doctype/email_digest/email_digest.py
index 07efd16..9a00723 100644
--- a/setup/doctype/email_digest/email_digest.py
+++ b/setup/doctype/email_digest/email_digest.py
@@ -457,8 +457,8 @@
from webnotes.utils import getdate
now_date = now_datetime().date()
- import conf
- if hasattr(conf, "expires_on") and now_date > getdate(conf.expires_on):
+ from webnotes import conf
+ if "expires_on" in conf and now_date > getdate(conf.expires_on):
# do not send email digests to expired accounts
return
diff --git a/setup/doctype/email_settings/email_settings.py b/setup/doctype/email_settings/email_settings.py
index 219501e..aa511ee 100644
--- a/setup/doctype/email_settings/email_settings.py
+++ b/setup/doctype/email_settings/email_settings.py
@@ -3,7 +3,6 @@
from __future__ import unicode_literals
import webnotes
-sql = webnotes.conn.sql
from webnotes.utils import cint
diff --git a/setup/doctype/item_group/item_group.js b/setup/doctype/item_group/item_group.js
index c5a08d4..6dbb0d2 100644
--- a/setup/doctype/item_group/item_group.js
+++ b/setup/doctype/item_group/item_group.js
@@ -7,6 +7,12 @@
cur_frm.add_custom_button(wn._("Item Group Tree"), function() {
wn.set_route("Sales Browser", "Item Group");
})
+
+ if(!doc.__islocal && doc.show_in_website) {
+ cur_frm.add_custom_button("View In Website", function() {
+ window.open(doc.page_name);
+ }, "icon-globe");
+ }
}
cur_frm.cscript.set_root_readonly = function(doc) {
diff --git a/setup/doctype/naming_series/naming_series.py b/setup/doctype/naming_series/naming_series.py
index 9cc4de2..4e85b15 100644
--- a/setup/doctype/naming_series/naming_series.py
+++ b/setup/doctype/naming_series/naming_series.py
@@ -8,7 +8,6 @@
from webnotes import msgprint
import webnotes.model.doctype
-sql = webnotes.conn.sql
class DocType:
def __init__(self, d, dl):
@@ -23,7 +22,7 @@
where fieldname='naming_series'""")
)))),
"prefixes": "\n".join([''] + [i[0] for i in
- sql("""select name from tabSeries""")])
+ webnotes.conn.sql("""select name from tabSeries""")])
}
def scrub_options_list(self, ol):
@@ -126,12 +125,12 @@
def insert_series(self, series):
"""insert series if missing"""
if not webnotes.conn.exists('Series', series):
- sql("insert into tabSeries (name, current) values (%s,0)", (series))
+ webnotes.conn.sql("insert into tabSeries (name, current) values (%s,0)", (series))
def update_series_start(self):
if self.doc.prefix:
self.insert_series(self.doc.prefix)
- sql("update `tabSeries` set current = '%s' where name = '%s'" % (self.doc.current_value,self.doc.prefix))
+ webnotes.conn.sql("update `tabSeries` set current = '%s' where name = '%s'" % (self.doc.current_value,self.doc.prefix))
msgprint("Series Updated Successfully")
else:
msgprint("Please select prefix first")
diff --git a/setup/doctype/notification_control/notification_control.py b/setup/doctype/notification_control/notification_control.py
index 6133d9b..0a15115 100644
--- a/setup/doctype/notification_control/notification_control.py
+++ b/setup/doctype/notification_control/notification_control.py
@@ -6,7 +6,6 @@
from webnotes import msgprint
-sql = webnotes.conn.sql
class DocType:
def __init__(self,d,dl):
@@ -14,7 +13,7 @@
def get_message(self, arg):
fn = arg.lower().replace(' ', '_') + '_message'
- v = sql("select value from tabSingles where field=%s and doctype=%s", (fn, 'Notification Control'))
+ v = webnotes.conn.sql("select value from tabSingles where field=%s and doctype=%s", (fn, 'Notification Control'))
return v and v[0][0] or ''
def set_message(self, arg = ''):
diff --git a/setup/doctype/price_list/price_list.css b/setup/doctype/price_list/price_list.css
new file mode 100644
index 0000000..61b0694
--- /dev/null
+++ b/setup/doctype/price_list/price_list.css
@@ -0,0 +1,7 @@
+.table-grid tbody tr {
+ cursor: pointer;
+}
+
+.table-grid thead tr {
+ height: 50px;
+}
\ No newline at end of file
diff --git a/setup/doctype/price_list/price_list.js b/setup/doctype/price_list/price_list.js
index f3adc72..67090bc 100644
--- a/setup/doctype/price_list/price_list.js
+++ b/setup/doctype/price_list/price_list.js
@@ -5,4 +5,253 @@
onload: function() {
erpnext.add_for_territory();
},
+});
+
+cur_frm.cscript.refresh = function(doc, cdt, cdn) {
+ cur_frm.cscript.show_item_prices();
+}
+
+cur_frm.cscript.show_item_prices = function() {
+ var item_price = wn.model.get("Item Price", {parent: cur_frm.doc.name});
+
+ $(cur_frm.fields_dict.item_prices_html.wrapper).empty();
+
+ new wn.ui.form.TableGrid({
+ parent: cur_frm.fields_dict.item_prices_html.wrapper,
+ frm: cur_frm,
+ table_field: wn.meta.get_docfield("Price List", "item_prices", cur_frm.doc.name)
+ });
+}
+
+wn.ui.form.TableGrid = Class.extend({
+ init: function(opts) {
+ $.extend(this, opts);
+ this.fields = wn.meta.get_docfields("Item Price", cur_frm.doc.name);
+ this.make_table();
+ },
+ make_table: function() {
+ var me = this;
+ // Creating table & assigning attributes
+ var grid_table = document.createElement("table");
+ grid_table.className = "table table-hover table-bordered table-grid";
+
+ // Appending header & rows to table
+ grid_table.appendChild(this.make_table_headers());
+ grid_table.appendChild(this.make_table_rows());
+
+ // Creating button to add new row
+ var btn_div = document.createElement("div");
+ var new_row_btn = document.createElement("button");
+ new_row_btn.className = "btn btn-success table-new-row";
+ new_row_btn.title = "Add new row";
+
+ var btn_icon = document.createElement("i");
+ btn_icon.className = "icon-plus";
+ new_row_btn.appendChild(btn_icon);
+ new_row_btn.innerHTML += " Add new row";
+ btn_div.appendChild(new_row_btn);
+
+ // Appending table & button to parent
+ var $grid_table = $(grid_table).appendTo($(this.parent));
+ var $btn_div = $(btn_div).appendTo($(this.parent));
+
+ $btn_div.on("click", ".table-new-row", function() {
+ me.make_dialog();
+ return false;
+ });
+
+ $grid_table.on("click", ".table-row", function() {
+ me.make_dialog(this);
+ return false;
+ });
+ },
+ make_table_headers: function() {
+ var me = this;
+ var header = document.createElement("thead");
+
+ // Creating header row
+ var row = document.createElement("tr");
+ row.className = "active";
+
+ // Creating head first cell
+ var th = document.createElement("th");
+ th.width = "8%";
+ th.className = "text-center";
+ th.innerHTML = "#";
+ row.appendChild(th);
+
+ // Make other headers with label as heading
+ for(var i=0, l=this.fields.length; i<l; i++) {
+ var df = this.fields[i];
+
+ if(!!!df.hidden && df.in_list_view === 1) {
+ var th = document.createElement("th");
+
+ // If currency then move header to right
+ if(["Int", "Currency", "Float"].indexOf(df.fieldtype) !== -1) th.className = "text-right";
+
+ th.innerHTML = wn._(df.label);
+ row.appendChild(th);
+ }
+ }
+
+ header.appendChild(row);
+
+ return header;
+ },
+ make_table_rows: function() {
+ var me = this;
+
+ // Creating table body
+ var table_body = document.createElement("tbody");
+
+ var item_prices = wn.model.get_children(this.table_field.options, this.frm.doc.name,
+ this.table_field.fieldname, this.frm.doctype);
+
+ for(var i=0, l=item_prices.length; i<l; i++) {
+ var d = item_prices[i];
+
+ // Creating table row
+ var tr = this.add_new_row(d);
+
+ // append row to table body
+ table_body.appendChild(tr);
+ }
+
+ this.table_body = table_body;
+
+ return table_body;
+ },
+ make_dialog: function(row) {
+ var me = this;
+
+ this.dialog = new wn.ui.Dialog({
+ title: this.table_field.options,
+ fields: this.fields
+ });
+
+ if (row)
+ this.dialog.set_values(this.make_dialog_values(row));
+
+ $a(this.dialog.body, 'div', '', '', this.make_dialog_buttons(row));
+ this.dialog.show();
+
+ this.dialog.$wrapper.find('button.update').on('click', function() {
+ me.update_row(row);
+ });
+
+ this.dialog.$wrapper.find('button.delete').on('click', function() {
+ me.delete_row(row);
+ });
+ return row;
+ },
+ make_dialog_values: function(row) {
+ var me = this;
+ var dialog_values = {};
+
+ $.each(this.fields, function(i, item) {
+ dialog_values[item.fieldname] = $(row).find('td[data-fieldname="'+ item.fieldname +'"]').attr('data-fieldvalue');
+ });
+
+ return dialog_values;
+ },
+ make_dialog_buttons: function(row) {
+ var me = this;
+ var buttons = '<button class="btn btn-primary update">Update</button>';
+
+ // if user can delete then only add the delete button in dialog
+ if (wn.model.can_delete(me.frm.doc.doctype) && row)
+ buttons += ' <button class="btn btn-default delete">Delete</button>';
+
+ return buttons;
+ },
+ update_row: function(row) {
+ var me = this;
+
+ if (!row) {
+ var d = wn.model.add_child(this.frm.doc, this.table_field.options,
+ this.table_field.fieldname);
+ refresh_field(this.table_field.fieldname);
+ this.update_item_price(d.name);
+ var tr = this.add_new_row(d);
+ this.table_body.appendChild(tr);
+ }
+ else {
+ this.update_item_price(null, row);
+ }
+
+ this.dialog.hide();
+ },
+
+ update_item_price: function(docname, row) {
+ var me = this;
+ if(!docname && row) docname = $(row).attr("data-docname");
+ $.each(me.fields, function(i, df) {
+ var val = me.dialog.get_values()[df.fieldname];
+
+ if(["Currency", "Float"].indexOf(df.fieldtype)!==-1) {
+ val = flt(val);
+ } else if(["Int", "Check"].indexOf(df.fieldtype)!==-1) {
+ val = cint(val);
+ }
+
+ wn.model.set_value(me.table_field.options, docname,
+ df.fieldname, val);
+
+ if(row) {
+ var $td = $(row).find('td[data-fieldname="'+ df.fieldname +'"]');
+ $td.attr('data-fieldvalue', val);
+ // If field type is currency the update with format currency
+ $td.html(wn.format(val, df));
+ }
+ });
+ },
+
+ delete_row: function(row) {
+ var me = this;
+ var docname = $(row).find('td:last').attr('data-docname');
+ wn.model.clear_doc(me.table_field.options, docname);
+ $(row).remove();
+
+ // Re-assign idx
+ $.each($(this.parent).find("tbody tr"), function(idx, data) {
+ var $td = $(data).find('td:first');
+ $td.html(idx + 1);
+ });
+ this.dialog.hide();
+ },
+
+ add_new_row: function(d) {
+ var tr = document.createElement("tr");
+ tr.className = "table-row";
+ tr.setAttribute("data-docname", d.name);
+
+ // Creating table data & appending to row
+ var td = document.createElement("td");
+ td.className = "text-center";
+ td.innerHTML = d.idx;
+ tr.appendChild(td);
+
+ for(var f=0, lf=this.fields.length; f<lf; f++) {
+ var df = this.fields[f];
+ if(!!!df.hidden && df.in_list_view===1) {
+ var td = document.createElement("td");
+ td.setAttribute("data-fieldname", df.fieldname);
+ td.setAttribute("data-fieldvalue", d[df.fieldname]);
+ td.setAttribute("data-docname", d.name);
+
+ // If currency then move header to right
+ if(["Int", "Currency", "Float"].indexOf(df.fieldtype) !== -1) {
+ td.className = "text-right";
+ }
+
+ // format and set display
+ td.innerHTML = wn.format(d[df.fieldname], df);
+
+ // append column to tabel row
+ tr.appendChild(td);
+ }
+ }
+ return tr;
+ }
});
\ No newline at end of file
diff --git a/setup/doctype/price_list/price_list.txt b/setup/doctype/price_list/price_list.txt
index 46905a6..df91bd9 100644
--- a/setup/doctype/price_list/price_list.txt
+++ b/setup/doctype/price_list/price_list.txt
@@ -2,7 +2,7 @@
{
"creation": "2013-01-25 11:35:09",
"docstatus": 0,
- "modified": "2013-09-06 15:03:38",
+ "modified": "2013-10-02 11:36:09",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -85,6 +85,7 @@
"reqd": 1
},
{
+ "description": "To change row values, click on the respective row",
"doctype": "DocField",
"fieldname": "item_prices_section",
"fieldtype": "Section Break",
@@ -93,8 +94,14 @@
},
{
"doctype": "DocField",
+ "fieldname": "item_prices_html",
+ "fieldtype": "HTML"
+ },
+ {
+ "doctype": "DocField",
"fieldname": "item_prices",
"fieldtype": "Table",
+ "hidden": 1,
"label": "Item Prices",
"options": "Item Price"
},
diff --git a/setup/doctype/print_heading/print_heading.py b/setup/doctype/print_heading/print_heading.py
index ba7114a..134aaaf 100644
--- a/setup/doctype/print_heading/print_heading.py
+++ b/setup/doctype/print_heading/print_heading.py
@@ -7,7 +7,6 @@
from webnotes.model import db_exists
from webnotes.model.bean import copy_doclist
-sql = webnotes.conn.sql
diff --git a/setup/doctype/sales_partner/sales_partner.py b/setup/doctype/sales_partner/sales_partner.py
index 0d7e12d..9e3e2a8 100644
--- a/setup/doctype/sales_partner/sales_partner.py
+++ b/setup/doctype/sales_partner/sales_partner.py
@@ -5,13 +5,16 @@
import webnotes
from webnotes.utils import cint, cstr, filter_strip_join
-sql = webnotes.conn.sql
class DocType:
def __init__(self, doc, doclist=None):
self.doc = doc
self.doclist = doclist
+ def validate(self):
+ if self.doc.partner_website and not self.doc.partner_website.startswith("http"):
+ self.doc.partner_website = "http://" + self.doc.partner_website
+
def on_update(self):
if cint(self.doc.show_in_website):
from webnotes.webutils import update_page_name
@@ -24,7 +27,7 @@
def get_contacts(self,nm):
if nm:
- contact_details =webnotes.conn.convert_to_lists(sql("select name, CONCAT(IFNULL(first_name,''),' ',IFNULL(last_name,'')),contact_no,email_id from `tabContact` where sales_partner = '%s'"%nm))
+ contact_details =webnotes.conn.convert_to_lists(webnotes.conn.sql("select name, CONCAT(IFNULL(first_name,''),' ',IFNULL(last_name,'')),contact_no,email_id from `tabContact` where sales_partner = '%s'"%nm))
return contact_details
else:
return ''
diff --git a/setup/doctype/setup_control/README.md b/setup/doctype/setup_control/README.md
deleted file mode 100644
index 909fea4..0000000
--- a/setup/doctype/setup_control/README.md
+++ /dev/null
@@ -1 +0,0 @@
-Account setup utility on first login.
\ No newline at end of file
diff --git a/setup/doctype/setup_control/__init__.py b/setup/doctype/setup_control/__init__.py
deleted file mode 100644
index baffc48..0000000
--- a/setup/doctype/setup_control/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from __future__ import unicode_literals
diff --git a/setup/doctype/setup_control/setup_control.py b/setup/doctype/setup_control/setup_control.py
deleted file mode 100644
index b78bfcc..0000000
--- a/setup/doctype/setup_control/setup_control.py
+++ /dev/null
@@ -1,255 +0,0 @@
-# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd.
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import webnotes
-
-from webnotes.utils import cint, cstr, getdate, now, nowdate, get_defaults
-from webnotes.model.doc import Document, addchild
-from webnotes.model.code import get_obj
-from webnotes import session, form, msgprint
-
-class DocType:
- def __init__(self, d, dl):
- self.doc, self.doclist = d, dl
-
- def setup_account(self, args):
- import webnotes, json
- if isinstance(args, basestring):
- args = json.loads(args)
- webnotes.conn.begin()
-
- self.update_profile_name(args)
- add_all_roles_to(webnotes.session.user)
- self.create_fiscal_year_and_company(args)
- self.set_defaults(args)
- create_territories()
- self.create_price_lists(args)
- self.create_feed_and_todo()
- self.create_email_digest()
-
- webnotes.clear_cache()
- msgprint("Company setup is complete. This page will be refreshed in a moment.")
- webnotes.conn.commit()
-
- return {
- 'sys_defaults': get_defaults(),
- 'user_fullname': (args.get('first_name') or '') + (args.get('last_name')
- and (" " + args.get('last_name')) or '')
- }
-
- def update_profile_name(self, args):
- args['name'] = webnotes.session.get('user')
-
- # Update Profile
- if not args.get('last_name') or args.get('last_name')=='None': args['last_name'] = None
- webnotes.conn.sql("""\
- UPDATE `tabProfile` SET first_name=%(first_name)s,
- last_name=%(last_name)s
- WHERE name=%(name)s AND docstatus<2""", args)
-
- def create_fiscal_year_and_company(self, args):
- curr_fiscal_year, fy_start_date, fy_abbr = self.get_fy_details(args.get('fy_start'), True)
- webnotes.bean([{
- "doctype":"Fiscal Year",
- 'year': curr_fiscal_year,
- 'year_start_date': fy_start_date
- }]).insert()
-
- curr_fiscal_year, fy_start_date, fy_abbr = self.get_fy_details(args.get('fy_start'))
- webnotes.bean([{
- "doctype":"Fiscal Year",
- 'year': curr_fiscal_year,
- 'year_start_date': fy_start_date,
- }]).insert()
-
-
- # Company
- webnotes.bean([{
- "doctype":"Company",
- 'domain': args.get("industry"),
- 'company_name':args.get('company_name'),
- 'abbr':args.get('company_abbr'),
- 'default_currency':args.get('currency'),
- }]).insert()
-
- self.curr_fiscal_year = curr_fiscal_year
-
- def create_price_lists(self, args):
- for pl_type in ["Selling", "Buying"]:
- webnotes.bean([
- {
- "doctype": "Price List",
- "price_list_name": "Standard " + pl_type,
- "buying_or_selling": pl_type,
- "currency": args["currency"]
- },
- {
- "doctype": "For Territory",
- "parentfield": "valid_for_territories",
- "territory": "All Territories"
- }
- ]).insert()
-
- def set_defaults(self, args):
- # enable default currency
- webnotes.conn.set_value("Currency", args.get("currency"), "enabled", 1)
-
- global_defaults = webnotes.bean("Global Defaults", "Global Defaults")
- global_defaults.doc.fields.update({
- 'current_fiscal_year': self.curr_fiscal_year,
- 'default_currency': args.get('currency'),
- 'default_company':args.get('company_name'),
- 'date_format': webnotes.conn.get_value("Country", args.get("country"), "date_format"),
- "float_precision": 4
- })
- global_defaults.save()
-
- accounts_settings = webnotes.bean("Accounts Settings")
- accounts_settings.doc.auto_accounting_for_stock = 1
- accounts_settings.save()
-
- stock_settings = webnotes.bean("Stock Settings")
- stock_settings.doc.item_naming_by = "Item Code"
- stock_settings.doc.valuation_method = "FIFO"
- stock_settings.doc.stock_uom = "Nos"
- stock_settings.doc.auto_indent = 1
- stock_settings.save()
-
- selling_settings = webnotes.bean("Selling Settings")
- selling_settings.doc.cust_master_name = "Customer Name"
- selling_settings.doc.so_required = "No"
- selling_settings.doc.dn_required = "No"
- selling_settings.save()
-
- buying_settings = webnotes.bean("Buying Settings")
- buying_settings.doc.supp_master_name = "Supplier Name"
- buying_settings.doc.po_required = "No"
- buying_settings.doc.pr_required = "No"
- buying_settings.doc.maintain_same_rate = 1
- buying_settings.save()
-
- notification_control = webnotes.bean("Notification Control")
- notification_control.doc.quotation = 1
- notification_control.doc.sales_invoice = 1
- notification_control.doc.purchase_order = 1
- notification_control.save()
-
- hr_settings = webnotes.bean("HR Settings")
- hr_settings.doc.emp_created_by = "Naming Series"
- hr_settings.save()
-
- # control panel
- cp = webnotes.doc("Control Panel", "Control Panel")
- for k in ['country', 'timezone', 'company_name']:
- cp.fields[k] = args[k]
-
- cp.save()
-
- def create_feed_and_todo(self):
- """update activty feed and create todo for creation of item, customer, vendor"""
- import home
- home.make_feed('Comment', 'ToDo', '', webnotes.session['user'],
- '<i>"' + 'Setup Complete. Please check your <a href="#!todo">\
- To Do List</a>' + '"</i>', '#6B24B3')
-
- d = Document('ToDo')
- d.description = '<a href="#Setup">Complete ERPNext Setup</a>'
- d.priority = 'High'
- d.date = nowdate()
- d.save(1)
-
- def create_email_digest(self):
- """
- create a default weekly email digest
- * Weekly Digest
- * For all companies
- * Recipients: System Managers
- * Full content
- * Enabled by default
- """
- import webnotes
- companies_list = webnotes.conn.sql("SELECT company_name FROM `tabCompany`", as_list=1)
-
- from webnotes.profile import get_system_managers
- system_managers = get_system_managers()
- if not system_managers: return
-
- from webnotes.model.doc import Document
- for company in companies_list:
- if company and company[0]:
- edigest = webnotes.bean({
- "doctype": "Email Digest",
- "name": "Default Weekly Digest - " + company[0],
- "company": company[0],
- "frequency": "Weekly",
- "recipient_list": "\n".join(system_managers)
- })
-
- if webnotes.conn.sql("""select name from `tabEmail Digest` where name=%s""", edigest.doc.name):
- continue
-
- for fieldname in edigest.meta.get_fieldnames({"fieldtype": "Check"}):
- edigest.doc.fields[fieldname] = 1
-
- edigest.insert()
-
- # Get Fiscal year Details
- # ------------------------
- def get_fy_details(self, fy_start, last_year=False):
- st = {'1st Jan':'01-01','1st Apr':'04-01','1st Jul':'07-01', '1st Oct': '10-01'}
- if cint(getdate(nowdate()).month) < cint((st[fy_start].split('-'))[0]):
- curr_year = getdate(nowdate()).year - 1
- else:
- curr_year = getdate(nowdate()).year
-
- if last_year:
- curr_year = curr_year - 1
-
- stdt = cstr(curr_year)+'-'+cstr(st[fy_start])
-
- if(fy_start == '1st Jan'):
- fy = cstr(curr_year)
- abbr = cstr(fy)[-2:]
- else:
- fy = cstr(curr_year) + '-' + cstr(curr_year+1)
- abbr = cstr(curr_year)[-2:] + '-' + cstr(curr_year+1)[-2:]
- return fy, stdt, abbr
-
- def create_profile(self, user_email, user_fname, user_lname, pwd=None):
- pr = Document('Profile')
- pr.first_name = user_fname
- pr.last_name = user_lname
- pr.name = pr.email = user_email
- pr.enabled = 1
- pr.save(1)
- if pwd:
- webnotes.conn.sql("""insert into __Auth (user, `password`)
- values (%s, password(%s))
- on duplicate key update `password`=password(%s)""",
- (user_email, pwd, pwd))
-
- add_all_roles_to(pr.name)
-
-def add_all_roles_to(name):
- profile = webnotes.doc("Profile", name)
- for role in webnotes.conn.sql("""select name from tabRole"""):
- if role[0] not in ["Administrator", "Guest", "All", "Customer", "Supplier", "Partner"]:
- d = profile.addchild("user_roles", "UserRole")
- d.role = role[0]
- d.insert()
-
-def create_territories():
- """create two default territories, one for home country and one named Rest of the World"""
- from setup.utils import get_root_of
- country = webnotes.conn.get_value("Control Panel", None, "country")
- root_territory = get_root_of("Territory")
- for name in (country, "Rest Of The World"):
- if name and not webnotes.conn.exists("Territory", name):
- webnotes.bean({
- "doctype": "Territory",
- "territory_name": name.replace("'", ""),
- "parent_territory": root_territory,
- "is_group": "No"
- }).insert()
-
diff --git a/setup/doctype/setup_control/setup_control.txt b/setup/doctype/setup_control/setup_control.txt
deleted file mode 100644
index 7ebed3e..0000000
--- a/setup/doctype/setup_control/setup_control.txt
+++ /dev/null
@@ -1,24 +0,0 @@
-[
- {
- "creation": "2012-03-27 14:36:25",
- "docstatus": 0,
- "modified": "2012-03-27 14:36:25",
- "modified_by": "Administrator",
- "owner": "Administrator"
- },
- {
- "doctype": "DocType",
- "in_create": 1,
- "issingle": 1,
- "istable": 0,
- "module": "Setup",
- "name": "__common__",
- "read_only": 1,
- "section_style": "Simple",
- "version": 73
- },
- {
- "doctype": "DocType",
- "name": "Setup Control"
- }
-]
\ No newline at end of file
diff --git a/setup/doctype/uom/uom.txt b/setup/doctype/uom/uom.txt
index 6577f6c..51d9806 100644
--- a/setup/doctype/uom/uom.txt
+++ b/setup/doctype/uom/uom.txt
@@ -2,7 +2,7 @@
{
"creation": "2013-01-10 16:34:24",
"docstatus": 0,
- "modified": "2013-07-25 16:18:17",
+ "modified": "2013-10-10 15:06:53",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -40,22 +40,6 @@
},
{
"doctype": "DocField",
- "fieldname": "trash_reason",
- "fieldtype": "Small Text",
- "label": "Trash Reason",
- "oldfieldname": "trash_reason",
- "oldfieldtype": "Small Text",
- "read_only": 1
- },
- {
- "doctype": "DocField",
- "fieldname": "uom_details",
- "fieldtype": "Section Break",
- "label": "UOM Details",
- "oldfieldtype": "Section Break"
- },
- {
- "doctype": "DocField",
"fieldname": "uom_name",
"fieldtype": "Data",
"label": "UOM Name",
diff --git a/setup/page/setup_wizard/__init__.py b/setup/page/setup_wizard/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/setup/page/setup_wizard/__init__.py
diff --git a/setup/page/setup_wizard/setup_wizard.css b/setup/page/setup_wizard/setup_wizard.css
new file mode 100644
index 0000000..ad49ef0
--- /dev/null
+++ b/setup/page/setup_wizard/setup_wizard.css
@@ -0,0 +1,8 @@
+#page-setup-wizard .panel {
+ -webkit-box-shadow: 0px 1px 8px rgba(0,0,0,0.6);
+ box-shadow: 0px 1px 8px rgba(0,0,0,0.6);
+}
+
+#page-setup-wizard .col-md-6 .control-input .btn {
+ width: 100%;
+}
diff --git a/setup/page/setup_wizard/setup_wizard.js b/setup/page/setup_wizard/setup_wizard.js
new file mode 100644
index 0000000..648958e
--- /dev/null
+++ b/setup/page/setup_wizard/setup_wizard.js
@@ -0,0 +1,499 @@
+wn.pages['setup-wizard'].onload = function(wrapper) {
+ if(sys_defaults.company) {
+ wn.set_route("desktop");
+ return;
+ }
+ $(".navbar:first").toggle(false);
+ $("body").css({"padding-top":"30px"});
+
+ erpnext.wiz = new wn.wiz.Wizard({
+ page_name: "setup-wizard",
+ parent: wrapper,
+ on_complete: function(wiz) {
+ var values = wiz.get_values();
+ wiz.show_working();
+ wn.call({
+ method: "setup.page.setup_wizard.setup_wizard.setup_account",
+ args: values,
+ callback: function(r) {
+ if(r.exc) {
+ var d = msgprint(wn._("There were errors."));
+ d.custom_onhide = function() {
+ wn.set_route(erpnext.wiz.page_name, "0");
+ }
+ } else {
+ wiz.show_complete();
+ setTimeout(function() {
+ if(user==="Administrator") {
+ msgprint(wn._("Login with your new User ID") + ":" + values.email);
+ setTimeout(function() {
+ wn.app.logout();
+ }, 2000);
+ } else {
+ window.location = "app.html";
+ }
+ }, 2000);
+ }
+ }
+ })
+ },
+ title: wn._("ERPNext Setup Guide"),
+ welcome_html: '<h1 class="text-muted text-center"><i class="icon-magic"></i></h1>\
+ <h2 class="text-center">'+wn._('ERPNext Setup')+'</h2>\
+ <p class="text-center">' +
+ wn._('Welcome to ERPNext. Over the next few minutes we will help you setup your ERPNext account. Try and fill in as much information as you have even if it takes a bit longer. It will save you a lot of time later. Good Luck!') +
+ '</p>',
+ working_html: '<h3 class="text-muted text-center"><i class="icon-refresh icon-spin"></i></h3>\
+ <h2 class="text-center">'+wn._('Setting up...')+'</h2>\
+ <p class="text-center">' +
+ wn._('Sit tight while your system is being setup. This may take a few moments.') +
+ '</p>',
+ complete_html: '<h1 class="text-muted text-center"><i class="icon-thumbs-up"></i></h1>\
+ <h2 class="text-center">'+wn._('Setup Complete!')+'</h2>\
+ <p class="text-center">' +
+ wn._('Your setup is complete. Refreshing...') +
+ '</p>',
+ slides: [
+ // User
+ {
+ title: wn._("The First User: You"),
+ icon: "icon-user",
+ fields: [
+ {"fieldname": "first_name", "label": wn._("First Name"), "fieldtype": "Data", reqd:1},
+ {"fieldname": "last_name", "label": wn._("Last Name"), "fieldtype": "Data", reqd:1},
+ {"fieldname": "email", "label": wn._("Email Id"), "fieldtype": "Data", reqd:1, "description":"Your Login Id"},
+ {"fieldname": "password", "label": wn._("Password"), "fieldtype": "Password", reqd:1},
+ {fieldtype:"Attach Image", fieldname:"attach_profile", label:"Attach Your Profile..."},
+ ],
+ help: wn._('The first user will become the System Manager (you can change that later).'),
+ onload: function(slide) {
+ if(user!=="Administrator") {
+ slide.form.fields_dict.password.$wrapper.toggle(false);
+ slide.form.fields_dict.email_id.$wrapper.toggle(false);
+ delete slide.form.fields_dict.email;
+ delete slide.form.fields_dict.password;
+ }
+ }
+ },
+
+ // Organization
+ {
+ title: wn._("The Organization"),
+ icon: "icon-building",
+ fields: [
+ {fieldname:'company_name', label: wn._('Company Name'), fieldtype:'Data', reqd:1,
+ placeholder: 'e.g. "My Company LLC"'},
+ {fieldname:'company_abbr', label: wn._('Company Abbreviation'), fieldtype:'Data',
+ placeholder:'e.g. "MC"',reqd:1},
+ {fieldname:'fy_start', label:'Financial Year Start Date', fieldtype:'Select',
+ description:'Your financial year begins on', reqd:1,
+ options: ['', '1st Jan', '1st Apr', '1st Jul', '1st Oct'] },
+ {fieldname:'company_tagline', label: wn._('What does it do?'), fieldtype:'Data',
+ placeholder:'e.g. "Build tools for builders"', reqd:1},
+ ],
+ help: wn._('The name of your company for which you are setting up this system.'),
+ onload: function(slide) {
+ slide.get_input("company_name").on("change", function() {
+ var parts = slide.get_input("company_name").val().split(" ");
+ var abbr = $.map(parts, function(p) { return p ? p.substr(0,1) : null }).join("");
+ slide.get_input("company_abbr").val(abbr.toUpperCase());
+ });
+ }
+ },
+
+ // Country
+ {
+ title: wn._("Country, Timezone and Currency"),
+ icon: "icon-flag",
+ fields: [
+ {fieldname:'country', label: wn._('Country'), reqd:1,
+ options: "", fieldtype: 'Select'},
+ {fieldname:'currency', label: wn._('Default Currency'), reqd:1,
+ options: "", fieldtype: 'Select'},
+ {fieldname:'timezone', label: wn._('Time Zone'), reqd:1,
+ options: "", fieldtype: 'Select'},
+ ],
+ help: wn._('Select your home country and check the timezone and currency.'),
+ onload: function(slide, form) {
+ wn.call({
+ method:"webnotes.country_info.get_country_timezone_info",
+ callback: function(data) {
+ erpnext.country_info = data.message.country_info;
+ erpnext.all_timezones = data.message.all_timezones;
+ slide.get_input("country").empty()
+ .add_options([""].concat(keys(erpnext.country_info).sort()));
+ slide.get_input("currency").empty()
+ .add_options(wn.utils.unique([""].concat($.map(erpnext.country_info,
+ function(opts, country) { return opts.currency; }))).sort());
+ slide.get_input("timezone").empty()
+ .add_options([""].concat(erpnext.all_timezones));
+ }
+ })
+
+ slide.get_input("country").on("change", function() {
+ var country = slide.get_input("country").val();
+ var $timezone = slide.get_input("timezone");
+ $timezone.empty();
+ // add country specific timezones first
+ if(country){
+ var timezone_list = erpnext.country_info[country].timezones || [];
+ $timezone.add_options(timezone_list.sort());
+ slide.get_input("currency").val(erpnext.country_info[country].currency);
+ }
+ // add all timezones at the end, so that user has the option to change it to any timezone
+ $timezone.add_options([""].concat(erpnext.all_timezones));
+
+ });
+ }
+ },
+
+ // Logo
+ {
+ icon: "icon-bookmark",
+ title: wn._("Logo and Letter Heads"),
+ help: wn._('Upload your letter head and logo - you can edit them later.'),
+ fields: [
+ {fieldtype:"Attach Image", fieldname:"attach_letterhead", label:"Attach Letterhead..."},
+ {fieldtype:"Attach Image", fieldname:"attach_logo", label:"Attach Logo..."},
+ ],
+ },
+
+ // Taxes
+ {
+ icon: "icon-money",
+ "title": wn._("Add Taxes"),
+ "help": wn._("List your tax heads (e.g. VAT, Excise) (upto 3) and their standard rates. This will create a standard template, you can edit and add more later."),
+ "fields": [
+ {fieldtype:"Data", fieldname:"tax_1", label:"Tax 1", placeholder:"e.g. VAT"},
+ {fieldtype:"Column Break"},
+ {fieldtype:"Data", fieldname:"tax_rate_1", label:"Rate (%)", placeholder:"e.g. 5"},
+ {fieldtype:"Section Break"},
+ {fieldtype:"Data", fieldname:"tax_2", label:"Tax 2", placeholder:"e.g. Customs Duty"},
+ {fieldtype:"Column Break"},
+ {fieldtype:"Data", fieldname:"tax_rate_2", label:"Rate (%)", placeholder:"e.g. 5"},
+ {fieldtype:"Section Break"},
+ {fieldtype:"Data", fieldname:"tax_3", label:"Tax 3", placeholder:"e.g. Excise"},
+ {fieldtype:"Column Break"},
+ {fieldtype:"Data", fieldname:"tax_rate_3", label:"Rate (%)", placeholder:"e.g. 5"},
+ ],
+ },
+
+ // Items to Sell
+ {
+ icon: "icon-barcode",
+ "title": wn._("Your Products or Services"),
+ "help": wn._("List your products or services that you sell to your customers. Make sure to check the Item Group, Unit of Measure and other properties when you start."),
+ "fields": [
+ {fieldtype:"Data", fieldname:"item_1", label:"Item 1", placeholder:"A Product or Service"},
+ {fieldtype:"Column Break"},
+ {fieldtype:"Attach", fieldname:"item_img_1", label:"Attach Image..."},
+ {fieldtype:"Section Break"},
+ {fieldtype:"Column Break"},
+ {fieldtype:"Column Break"},
+ {fieldtype:"Select", fieldname:"item_group_1", options:["Products", "Services", "Raw Material", "Sub Assemblies"]},
+ {fieldtype:"Column Break"},
+ {fieldtype:"Select", fieldname:"item_uom_1", options:["Unit", "Nos", "Box", "Pair", "Kg", "Set", "Hour", "Minute"]},
+ {fieldtype:"Section Break"},
+ {fieldtype:"Data", fieldname:"item_2", label:"Item 2", placeholder:"A Product or Service"},
+ {fieldtype:"Column Break"},
+ {fieldtype:"Attach", fieldname:"item_img_2", label:"Attach Image..."},
+ {fieldtype:"Section Break"},
+ {fieldtype:"Column Break"},
+ {fieldtype:"Column Break"},
+ {fieldtype:"Select", fieldname:"item_group_2", options:["Products", "Services", "Raw Material", "Sub Assemblies"]},
+ {fieldtype:"Column Break"},
+ {fieldtype:"Select", fieldname:"item_uom_2", options:["Unit", "Nos", "Box", "Pair", "Kg", "Set", "Hour", "Minute"]},
+ {fieldtype:"Section Break"},
+ {fieldtype:"Data", fieldname:"item_3", label:"Item 3", placeholder:"A Product or Service"},
+ {fieldtype:"Column Break"},
+ {fieldtype:"Attach", fieldname:"item_img_3", label:"Attach Image..."},
+ {fieldtype:"Section Break"},
+ {fieldtype:"Column Break"},
+ {fieldtype:"Column Break"},
+ {fieldtype:"Select", fieldname:"item_group_3", options:["Products", "Services", "Raw Material", "Sub Assemblies"]},
+ {fieldtype:"Column Break"},
+ {fieldtype:"Select", fieldname:"item_uom_3", options:["Unit", "Nos", "Box", "Pair", "Kg", "Set", "Hour", "Minute"]},
+ {fieldtype:"Section Break"},
+ {fieldtype:"Data", fieldname:"item_4", label:"Item 4", placeholder:"A Product or Service"},
+ {fieldtype:"Column Break"},
+ {fieldtype:"Attach", fieldname:"item_img_4", label:"Attach Image..."},
+ {fieldtype:"Section Break"},
+ {fieldtype:"Column Break"},
+ {fieldtype:"Column Break"},
+ {fieldtype:"Select", fieldname:"item_group_4", options:["Products", "Services", "Raw Material", "Sub Assemblies"]},
+ {fieldtype:"Column Break"},
+ {fieldtype:"Select", fieldname:"item_uom_4", options:["Unit", "Nos", "Box", "Pair", "Kg", "Set", "Hour", "Minute"]},
+ {fieldtype:"Section Break"},
+ {fieldtype:"Data", fieldname:"item_5", label:"Item 5", placeholder:"A Product or Service"},
+ {fieldtype:"Column Break"},
+ {fieldtype:"Attach", fieldname:"item_img_5", label:"Attach Image..."},
+ {fieldtype:"Section Break"},
+ {fieldtype:"Column Break"},
+ {fieldtype:"Column Break"},
+ {fieldtype:"Select", fieldname:"item_group_5", options:["Products", "Services", "Raw Material", "Sub Assemblies"]},
+ {fieldtype:"Column Break"},
+ {fieldtype:"Select", fieldname:"item_uom_5", options:["Unit", "Nos", "Box", "Pair", "Kg", "Set", "Hour", "Minute"]},
+ ],
+ },
+
+ // Items to Buy
+ {
+ icon: "icon-barcode",
+ "title": wn._("Products or Services You Buy"),
+ "help": wn._("List a few products or services you buy from your suppliers or vendors. If these are same as your products, then do not add them."),
+ "fields": [
+ {fieldtype:"Data", fieldname:"item_buy_1", label:"Item 1", placeholder:"A Product or Service"},
+ {fieldtype:"Section Break"},
+ {fieldtype:"Column Break"},
+ {fieldtype:"Column Break"},
+ {fieldtype:"Select", fieldname:"item_buy_group_1", options:["Raw Material", "Consumable", "Sub Assemblies", "Services", "Products"]},
+ {fieldtype:"Column Break"},
+ {fieldtype:"Select", fieldname:"item_buy_uom_1", options:["Unit", "Nos", "Box", "Pair", "Kg", "Set", "Hour", "Minute"]},
+ {fieldtype:"Section Break"},
+ {fieldtype:"Data", fieldname:"item_buy_2", label:"Item 2", placeholder:"A Product or Service"},
+ {fieldtype:"Section Break"},
+ {fieldtype:"Column Break"},
+ {fieldtype:"Column Break"},
+ {fieldtype:"Select", fieldname:"item_buy_group_2", options:["Raw Material", "Consumable", "Sub Assemblies", "Services", "Products"]},
+ {fieldtype:"Column Break"},
+ {fieldtype:"Select", fieldname:"item_buy_uom_2", options:["Unit", "Nos", "Box", "Pair", "Kg", "Set", "Hour", "Minute"]},
+ {fieldtype:"Section Break"},
+ {fieldtype:"Data", fieldname:"item_buy_3", label:"Item 3", placeholder:"A Product or Service"},
+ {fieldtype:"Section Break"},
+ {fieldtype:"Column Break"},
+ {fieldtype:"Column Break"},
+ {fieldtype:"Select", fieldname:"item_buy_group_3", options:["Raw Material", "Consumable", "Sub Assemblies", "Services", "Products"]},
+ {fieldtype:"Column Break"},
+ {fieldtype:"Select", fieldname:"item_buy_uom_3", options:["Unit", "Nos", "Box", "Pair", "Kg", "Set", "Hour", "Minute"]},
+ {fieldtype:"Section Break"},
+ {fieldtype:"Data", fieldname:"item_buy_4", label:"Item 4", placeholder:"A Product or Service"},
+ {fieldtype:"Section Break"},
+ {fieldtype:"Column Break"},
+ {fieldtype:"Column Break"},
+ {fieldtype:"Select", fieldname:"item_buy_group_4", options:["Raw Material", "Consumable", "Sub Assemblies", "Services", "Products"]},
+ {fieldtype:"Column Break"},
+ {fieldtype:"Select", fieldname:"item_buy_uom_4", options:["Unit", "Nos", "Box", "Pair", "Kg", "Set", "Hour", "Minute"]},
+ {fieldtype:"Section Break"},
+ {fieldtype:"Data", fieldname:"item_buy_5", label:"Item 5", placeholder:"A Product or Service"},
+ {fieldtype:"Section Break"},
+ {fieldtype:"Column Break"},
+ {fieldtype:"Column Break"},
+ {fieldtype:"Select", fieldname:"item_buy_group_5", options:["Raw Material", "Consumable", "Sub Assemblies", "Services", "Products"]},
+ {fieldtype:"Column Break"},
+ {fieldtype:"Select", fieldname:"item_buy_uom_5", options:["Unit", "Nos", "Box", "Pair", "Kg", "Set", "Hour", "Minute"]},
+ ],
+ },
+
+ // Customers
+ {
+ icon: "icon-group",
+ "title": wn._("Your Customers"),
+ "help": wn._("List a few of your customers. They could be organizations or individuals."),
+ "fields": [
+ {fieldtype:"Data", fieldname:"customer_1", label:"Customer 1", placeholder:"Customer Name"},
+ {fieldtype:"Column Break"},
+ {fieldtype:"Data", fieldname:"customer_contact_1", label:"", placeholder:"Contact Name"},
+ {fieldtype:"Section Break"},
+ {fieldtype:"Data", fieldname:"customer_2", label:"Customer 2", placeholder:"Customer Name"},
+ {fieldtype:"Column Break"},
+ {fieldtype:"Data", fieldname:"customer_contact_2", label:"", placeholder:"Contact Name"},
+ {fieldtype:"Section Break"},
+ {fieldtype:"Data", fieldname:"customer_3", label:"Customer 3", placeholder:"Customer Name"},
+ {fieldtype:"Column Break"},
+ {fieldtype:"Data", fieldname:"customer_contact_3", label:"", placeholder:"Contact Name"},
+ {fieldtype:"Section Break"},
+ {fieldtype:"Data", fieldname:"customer_4", label:"Customer 4", placeholder:"Customer Name"},
+ {fieldtype:"Column Break"},
+ {fieldtype:"Data", fieldname:"customer_contact_4", label:"", placeholder:"Contact Name"},
+ {fieldtype:"Section Break"},
+ {fieldtype:"Data", fieldname:"customer_5", label:"Customer 5", placeholder:"Customer Name"},
+ {fieldtype:"Column Break"},
+ {fieldtype:"Data", fieldname:"customer_contact_5", label:"", placeholder:"Contact Name"},
+ ],
+ },
+
+ // Suppliers
+ {
+ icon: "icon-group",
+ "title": wn._("Your Suppliers"),
+ "help": wn._("List a few of your suppliers. They could be organizations or individuals."),
+ "fields": [
+ {fieldtype:"Data", fieldname:"supplier_1", label:"Supplier 1", placeholder:"Supplier Name"},
+ {fieldtype:"Column Break"},
+ {fieldtype:"Data", fieldname:"supplier_contact_1", label:"", placeholder:"Contact Name"},
+ {fieldtype:"Section Break"},
+ {fieldtype:"Data", fieldname:"supplier_2", label:"Supplier 2", placeholder:"Supplier Name"},
+ {fieldtype:"Column Break"},
+ {fieldtype:"Data", fieldname:"supplier_contact_2", label:"", placeholder:"Contact Name"},
+ {fieldtype:"Section Break"},
+ {fieldtype:"Data", fieldname:"supplier_3", label:"Supplier 3", placeholder:"Supplier Name"},
+ {fieldtype:"Column Break"},
+ {fieldtype:"Data", fieldname:"supplier_contact_3", label:"", placeholder:"Contact Name"},
+ {fieldtype:"Section Break"},
+ {fieldtype:"Data", fieldname:"supplier_4", label:"Supplier 4", placeholder:"Supplier Name"},
+ {fieldtype:"Column Break"},
+ {fieldtype:"Data", fieldname:"supplier_contact_4", label:"", placeholder:"Contact Name"},
+ {fieldtype:"Section Break"},
+ {fieldtype:"Data", fieldname:"supplier_5", label:"Supplier 5", placeholder:"Supplier Name"},
+ {fieldtype:"Column Break"},
+ {fieldtype:"Data", fieldname:"supplier_contact_5", label:"", placeholder:"Contact Name"},
+ ],
+ }
+
+ ]
+
+ })
+}
+
+wn.pages['setup-wizard'].onshow = function(wrapper) {
+ if(wn.get_route()[1])
+ erpnext.wiz.show(wn.get_route()[1]);
+}
+
+wn.provide("wn.wiz");
+
+wn.wiz.Wizard = Class.extend({
+ init: function(opts) {
+ $.extend(this, opts);
+ this.slides = this.slides;
+ this.slide_dict = {};
+ this.show_welcome();
+ },
+ get_message: function(html) {
+ return $(repl('<div class="panel panel-default" style="max-width: 400px; margin: auto;">\
+ <div class="panel-body" style="padding: 40px;">%(html)s</div>\
+ </div>', {html:html}))
+ },
+ show_welcome: function() {
+ if(this.$welcome)
+ return;
+ var me = this;
+ this.$welcome = this.get_message(this.welcome_html +
+ '<br><p class="text-center"><button class="btn btn-primary">'+wn._("Start")+'</button></p>')
+ .appendTo(this.parent);
+
+ this.$welcome.find(".btn").click(function() {
+ me.$welcome.toggle(false);
+ me.welcomed = true;
+ wn.set_route(me.page_name, "0");
+ })
+
+ this.current_slide = {"$wrapper": this.$welcome};
+ },
+ show_working: function() {
+ this.hide_current_slide();
+ wn.set_route(this.page_name);
+ this.current_slide = {"$wrapper": this.get_message(this.working_html).appendTo(this.parent)};
+ },
+ show_complete: function() {
+ this.hide_current_slide();
+ this.current_slide = {"$wrapper": this.get_message(this.complete_html).appendTo(this.parent)};
+ },
+ show: function(id) {
+ if(!this.welcomed) {
+ wn.set_route(this.page_name);
+ return;
+ }
+ id = cint(id);
+ if(this.current_slide && this.current_slide.id===id)
+ return;
+ if(!this.slide_dict[id]) {
+ this.slide_dict[id] = new wn.wiz.WizardSlide($.extend(this.slides[id], {wiz:this, id:id}));
+ this.slide_dict[id].make();
+ }
+
+ this.hide_current_slide();
+
+ this.current_slide = this.slide_dict[id];
+ this.current_slide.$wrapper.toggle(true);
+ },
+ hide_current_slide: function() {
+ if(this.current_slide) {
+ this.current_slide.$wrapper.toggle(false);
+ this.current_slide = null;
+ }
+ },
+ get_values: function() {
+ var values = {};
+ $.each(this.slide_dict, function(id, slide) {
+ $.extend(values, slide.values)
+ })
+ return values;
+ }
+});
+
+wn.wiz.WizardSlide = Class.extend({
+ init: function(opts) {
+ $.extend(this, opts);
+ },
+ make: function() {
+ var me = this;
+ this.$wrapper = $(repl('<div class="panel panel-default" style="margin: 0px 30px;">\
+ <div class="panel-heading"><div class="panel-title">%(main_title)s: Step %(step)s</div></div>\
+ <div class="panel-body">\
+ <div class="progress">\
+ <div class="progress-bar" style="width: %(width)s%"></div>\
+ </div>\
+ <div class="row">\
+ <div class="col-sm-6 form"></div>\
+ <div class="col-sm-6 help">\
+ <h3><i class="%(icon)s text-muted"></i> %(title)s</h3><br>\
+ <p class="text-muted">%(help)s</p>\
+ </div>\
+ </div>\
+ <hr>\
+ <div class="footer"></div>\
+ </div>\
+ </div>', {help:this.help, title:this.title, main_title:this.wiz.title, step: this.id + 1,
+ width: (flt(this.id + 1) / (this.wiz.slides.length+1)) * 100, icon:this.icon}))
+ .appendTo(this.wiz.parent);
+
+ this.body = this.$wrapper.find(".form")[0];
+
+ if(this.fields) {
+ this.form = new wn.ui.FieldGroup({
+ fields: this.fields,
+ body: this.body,
+ no_submit_on_enter: true
+ });
+ this.form.make();
+ } else {
+ $(this.body).html(this.html)
+ }
+
+ if(this.id > 0) {
+ this.$prev = $("<button class='btn btn-default'>Previous</button>")
+ .click(function() {
+ wn.set_route(me.wiz.page_name, me.id-1 + "");
+ })
+ .appendTo(this.$wrapper.find(".footer"))
+ .css({"margin-right": "5px"});
+ }
+ if(this.id+1 < this.wiz.slides.length) {
+ this.$next = $("<button class='btn btn-primary'>Next</button>")
+ .click(function() {
+ me.values = me.form.get_values();
+ if(me.values===null)
+ return;
+ wn.set_route(me.wiz.page_name, me.id+1 + "");
+ })
+ .appendTo(this.$wrapper.find(".footer"));
+ } else {
+ this.$complete = $("<button class='btn btn-primary'>Complete Setup</button>")
+ .click(function() {
+ me.values = me.form.get_values();
+ if(me.values===null)
+ return;
+ me.wiz.on_complete(me.wiz);
+ }).appendTo(this.$wrapper.find(".footer"));
+ }
+
+ if(this.onload) {
+ this.onload(this);
+ }
+
+ },
+ get_input: function(fn) {
+ return this.form.get_input(fn);
+ }
+})
\ No newline at end of file
diff --git a/setup/page/setup_wizard/setup_wizard.py b/setup/page/setup_wizard/setup_wizard.py
new file mode 100644
index 0000000..d506d41
--- /dev/null
+++ b/setup/page/setup_wizard/setup_wizard.py
@@ -0,0 +1,354 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd.
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import webnotes, json, base64
+
+from webnotes.utils import cint, cstr, getdate, now, nowdate, get_defaults
+from webnotes import _
+from webnotes.utils.file_manager import save_file
+
+@webnotes.whitelist()
+def setup_account(args=None):
+ # if webnotes.conn.sql("select name from tabCompany"):
+ # webnotes.throw(_("Setup Already Complete!!"))
+
+ if not args:
+ args = webnotes.local.form_dict
+ if isinstance(args, basestring):
+ args = json.loads(args)
+ args = webnotes._dict(args)
+
+ update_profile_name(args)
+ create_fiscal_year_and_company(args)
+ set_defaults(args)
+ create_territories()
+ create_price_lists(args)
+ create_feed_and_todo()
+ create_email_digest()
+ create_letter_head(args)
+ create_taxes(args)
+ create_items(args)
+ create_customers(args)
+ create_suppliers(args)
+ webnotes.conn.set_value('Control Panel', None, 'home_page', 'desktop')
+
+ webnotes.clear_cache()
+ webnotes.conn.commit()
+
+ # suppress msgprints
+ webnotes.local.message_log = []
+
+ return "okay"
+
+def update_profile_name(args):
+ if args.get("email"):
+ args['name'] = args.get("email")
+ webnotes.flags.mute_emails = True
+ webnotes.bean({
+ "doctype":"Profile",
+ "email": args.get("email"),
+ "first_name": args.get("first_name"),
+ "last_name": args.get("last_name")
+ }).insert()
+ webnotes.flags.mute_emails = False
+ from webnotes.auth import _update_password
+ _update_password(args.get("email"), args.get("password"))
+
+ else:
+ args['name'] = webnotes.session.user
+
+ # Update Profile
+ if not args.get('last_name') or args.get('last_name')=='None':
+ args['last_name'] = None
+ webnotes.conn.sql("""update `tabProfile` SET first_name=%(first_name)s,
+ last_name=%(last_name)s WHERE name=%(name)s""", args)
+
+ if args.get("attach_profile"):
+ filename, filetype, content = args.get("attach_profile").split(",")
+ fileurl = save_file(filename, content, "Profile", args.get("name"), decode=True).file_name
+ webnotes.conn.set_value("Profile", args.get("name"), "user_image", fileurl)
+
+ add_all_roles_to(args.get("name"))
+
+def create_fiscal_year_and_company(args):
+ curr_fiscal_year, fy_start_date, fy_abbr = get_fy_details(args.get('fy_start'), True)
+ webnotes.bean([{
+ "doctype":"Fiscal Year",
+ 'year': curr_fiscal_year,
+ 'year_start_date': fy_start_date
+ }]).insert()
+
+ curr_fiscal_year, fy_start_date, fy_abbr = get_fy_details(args.get('fy_start'))
+ webnotes.bean([{
+ "doctype":"Fiscal Year",
+ 'year': curr_fiscal_year,
+ 'year_start_date': fy_start_date,
+ }]).insert()
+
+
+ # Company
+ webnotes.bean([{
+ "doctype":"Company",
+ 'domain': args.get("industry"),
+ 'company_name':args.get('company_name'),
+ 'abbr':args.get('company_abbr'),
+ 'default_currency':args.get('currency'),
+ }]).insert()
+
+ args["curr_fiscal_year"] = curr_fiscal_year
+
+def create_price_lists(args):
+ for pl_type in ["Selling", "Buying"]:
+ webnotes.bean([
+ {
+ "doctype": "Price List",
+ "price_list_name": "Standard " + pl_type,
+ "buying_or_selling": pl_type,
+ "currency": args["currency"]
+ },
+ {
+ "doctype": "For Territory",
+ "parentfield": "valid_for_territories",
+ "territory": "All Territories"
+ }
+ ]).insert()
+
+def set_defaults(args):
+ # enable default currency
+ webnotes.conn.set_value("Currency", args.get("currency"), "enabled", 1)
+
+ global_defaults = webnotes.bean("Global Defaults", "Global Defaults")
+ global_defaults.doc.fields.update({
+ 'current_fiscal_year': args.curr_fiscal_year,
+ 'default_currency': args.get('currency'),
+ 'default_company':args.get('company_name'),
+ 'date_format': webnotes.conn.get_value("Country", args.get("country"), "date_format"),
+ "float_precision": 4
+ })
+ global_defaults.save()
+
+ accounts_settings = webnotes.bean("Accounts Settings")
+ accounts_settings.doc.auto_accounting_for_stock = 1
+ accounts_settings.save()
+
+ stock_settings = webnotes.bean("Stock Settings")
+ stock_settings.doc.item_naming_by = "Item Code"
+ stock_settings.doc.valuation_method = "FIFO"
+ stock_settings.doc.stock_uom = "Nos"
+ stock_settings.doc.auto_indent = 1
+ stock_settings.save()
+
+ selling_settings = webnotes.bean("Selling Settings")
+ selling_settings.doc.cust_master_name = "Customer Name"
+ selling_settings.doc.so_required = "No"
+ selling_settings.doc.dn_required = "No"
+ selling_settings.save()
+
+ buying_settings = webnotes.bean("Buying Settings")
+ buying_settings.doc.supp_master_name = "Supplier Name"
+ buying_settings.doc.po_required = "No"
+ buying_settings.doc.pr_required = "No"
+ buying_settings.doc.maintain_same_rate = 1
+ buying_settings.save()
+
+ notification_control = webnotes.bean("Notification Control")
+ notification_control.doc.quotation = 1
+ notification_control.doc.sales_invoice = 1
+ notification_control.doc.purchase_order = 1
+ notification_control.save()
+
+ hr_settings = webnotes.bean("HR Settings")
+ hr_settings.doc.emp_created_by = "Naming Series"
+ hr_settings.save()
+
+ # control panel
+ cp = webnotes.doc("Control Panel", "Control Panel")
+ for k in ['country', 'timezone', 'company_name']:
+ cp.fields[k] = args[k]
+
+ cp.save()
+
+def create_feed_and_todo():
+ """update activty feed and create todo for creation of item, customer, vendor"""
+ import home
+ home.make_feed('Comment', 'ToDo', '', webnotes.session['user'],
+ 'ERNext Setup Complete!', '#6B24B3')
+
+def create_email_digest():
+ from webnotes.profile import get_system_managers
+ system_managers = get_system_managers()
+ if not system_managers:
+ return
+
+ for company in webnotes.conn.sql_list("select name FROM `tabCompany`"):
+ if not webnotes.conn.exists("Email Digest", "Default Weekly Digest - " + company):
+ edigest = webnotes.bean({
+ "doctype": "Email Digest",
+ "name": "Default Weekly Digest - " + company,
+ "company": company,
+ "frequency": "Weekly",
+ "recipient_list": "\n".join(system_managers)
+ })
+
+ for fieldname in edigest.meta.get_fieldnames({"fieldtype": "Check"}):
+ edigest.doc.fields[fieldname] = 1
+
+ edigest.insert()
+
+def get_fy_details(fy_start, last_year=False):
+ st = {'1st Jan':'01-01','1st Apr':'04-01','1st Jul':'07-01', '1st Oct': '10-01'}
+ if cint(getdate(nowdate()).month) < cint((st[fy_start].split('-'))[0]):
+ curr_year = getdate(nowdate()).year - 1
+ else:
+ curr_year = getdate(nowdate()).year
+
+ if last_year:
+ curr_year = curr_year - 1
+
+ stdt = cstr(curr_year)+'-'+cstr(st[fy_start])
+
+ if(fy_start == '1st Jan'):
+ fy = cstr(curr_year)
+ abbr = cstr(fy)[-2:]
+ else:
+ fy = cstr(curr_year) + '-' + cstr(curr_year+1)
+ abbr = cstr(curr_year)[-2:] + '-' + cstr(curr_year+1)[-2:]
+ return fy, stdt, abbr
+
+def create_taxes(args):
+ for i in xrange(1,6):
+ if args.get("tax_" + str(i)):
+ webnotes.bean({
+ "doctype":"Account",
+ "company": args.get("company_name"),
+ "parent_account": "Duties and Taxes - " + args.get("company_abbr"),
+ "account_name": args.get("tax_" + str(i)),
+ "group_or_ledger": "Ledger",
+ "is_pl_account": "No",
+ "account_type": "Tax",
+ "tax_rate": args.get("tax_rate_" + str(i))
+ }).insert()
+
+def create_items(args):
+ for i in xrange(1,6):
+ item = args.get("item_" + str(i))
+ if item:
+ item_group = args.get("item_group_" + str(i))
+ webnotes.bean({
+ "doctype":"Item",
+ "item_code": item,
+ "item_name": item,
+ "description": item,
+ "is_sales_item": "Yes",
+ "is_stock_item": item_group!="Services" and "Yes" or "No",
+ "item_group": item_group,
+ "stock_uom": args.get("item_uom_" + str(i)),
+ "default_warehouse": item_group!="Service" and ("Finished Goods - " + args.get("company_abbr")) or ""
+ }).insert()
+
+ if args.get("item_img_" + str(i)):
+ filename, filetype, content = args.get("item_img_" + str(i)).split(",")
+ fileurl = save_file(filename, content, "Item", item, decode=True).file_name
+ webnotes.conn.set_value("Item", item, "image", fileurl)
+
+ for i in xrange(1,6):
+ item = args.get("item_buy_" + str(i))
+ if item:
+ item_group = args.get("item_buy_group_" + str(i))
+ webnotes.bean({
+ "doctype":"Item",
+ "item_code": item,
+ "item_name": item,
+ "description": item,
+ "is_sales_item": "No",
+ "is_stock_item": item_group!="Services" and "Yes" or "No",
+ "item_group": item_group,
+ "stock_uom": args.get("item_buy_uom_" + str(i)),
+ "default_warehouse": item_group!="Service" and ("Stores - " + args.get("company_abbr")) or ""
+ }).insert()
+
+ if args.get("item_img_" + str(i)):
+ filename, filetype, content = args.get("item_img_" + str(i)).split(",")
+ fileurl = save_file(filename, content, "Item", item, decode=True).file_name
+ webnotes.conn.set_value("Item", item, "image", fileurl)
+
+
+def create_customers(args):
+ for i in xrange(1,6):
+ customer = args.get("customer_" + str(i))
+ if customer:
+ webnotes.bean({
+ "doctype":"Customer",
+ "customer_name": customer,
+ "customer_type": "Company",
+ "customer_group": "Commercial",
+ "territory": args.get("country"),
+ "company": args.get("company_name")
+ }).insert()
+
+ if args.get("customer_contact_" + str(i)):
+ contact = args.get("customer_contact_" + str(i)).split(" ")
+ webnotes.bean({
+ "doctype":"Contact",
+ "customer": customer,
+ "first_name":contact[0],
+ "last_name": len(contact) > 1 and contact[1] or ""
+ }).insert()
+
+def create_suppliers(args):
+ for i in xrange(1,6):
+ supplier = args.get("supplier_" + str(i))
+ if supplier:
+ webnotes.bean({
+ "doctype":"Supplier",
+ "supplier_name": supplier,
+ "supplier_type": "Local",
+ "company": args.get("company_name")
+ }).insert()
+
+ if args.get("supplier_contact_" + str(i)):
+ contact = args.get("supplier_contact_" + str(i)).split(" ")
+ webnotes.bean({
+ "doctype":"Contact",
+ "supplier": supplier,
+ "first_name":contact[0],
+ "last_name": len(contact) > 1 and contact[1] or ""
+ }).insert()
+
+
+def create_letter_head(args):
+ if args.get("attach_letterhead"):
+ lh = webnotes.bean({
+ "doctype":"Letter Head",
+ "letter_head_name": "Standard",
+ "is_default": 1
+ }).insert()
+
+ filename, filetype, content = args.get("attach_letterhead").split(",")
+ fileurl = save_file(filename, content, "Letter Head", "Standard", decode=True).file_name
+ webnotes.conn.set_value("Letter Head", "Standard", "content", "<img src='%s' style='max-width: 100%%;'>" % fileurl)
+
+
+
+def add_all_roles_to(name):
+ profile = webnotes.doc("Profile", name)
+ for role in webnotes.conn.sql("""select name from tabRole"""):
+ if role[0] not in ["Administrator", "Guest", "All", "Customer", "Supplier", "Partner"]:
+ d = profile.addchild("user_roles", "UserRole")
+ d.role = role[0]
+ d.insert()
+
+def create_territories():
+ """create two default territories, one for home country and one named Rest of the World"""
+ from setup.utils import get_root_of
+ country = webnotes.conn.get_value("Control Panel", None, "country")
+ root_territory = get_root_of("Territory")
+ for name in (country, "Rest Of The World"):
+ if name and not webnotes.conn.exists("Territory", name):
+ webnotes.bean({
+ "doctype": "Territory",
+ "territory_name": name.replace("'", ""),
+ "parent_territory": root_territory,
+ "is_group": "No"
+ }).insert()
\ No newline at end of file
diff --git a/setup/page/setup_wizard/setup_wizard.txt b/setup/page/setup_wizard/setup_wizard.txt
new file mode 100644
index 0000000..996fd1a
--- /dev/null
+++ b/setup/page/setup_wizard/setup_wizard.txt
@@ -0,0 +1,32 @@
+[
+ {
+ "creation": "2013-10-04 13:49:33",
+ "docstatus": 0,
+ "modified": "2013-10-04 13:49:33",
+ "modified_by": "Administrator",
+ "owner": "Administrator"
+ },
+ {
+ "doctype": "Page",
+ "module": "Setup",
+ "name": "__common__",
+ "page_name": "setup-wizard",
+ "standard": "Yes",
+ "title": "Setup Wizard"
+ },
+ {
+ "doctype": "Page Role",
+ "name": "__common__",
+ "parent": "setup-wizard",
+ "parentfield": "roles",
+ "parenttype": "Page",
+ "role": "System Manager"
+ },
+ {
+ "doctype": "Page",
+ "name": "setup-wizard"
+ },
+ {
+ "doctype": "Page Role"
+ }
+]
\ No newline at end of file
diff --git a/setup/page/setup_wizard/test_setup_data.py b/setup/page/setup_wizard/test_setup_data.py
new file mode 100644
index 0000000..b5b6359
--- /dev/null
+++ b/setup/page/setup_wizard/test_setup_data.py
@@ -0,0 +1,53 @@
+
+args = {
+"attach_letterhead": "erpnext.jpg,data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEASABIAAD/4gxYSUNDX1BST0ZJTEUAAQEAAAxITGlubwIQAABtbnRyUkdCIFhZWiAHzgACAAkABgAxAABhY3NwTVNGVAAAAABJRUMgc1JHQgAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLUhQICAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABFjcHJ0AAABUAAAADNkZXNjAAABhAAAAGx3dHB0AAAB8AAAABRia3B0AAACBAAAABRyWFlaAAACGAAAABRnWFlaAAACLAAAABRiWFlaAAACQAAAABRkbW5kAAACVAAAAHBkbWRkAAACxAAAAIh2dWVkAAADTAAAAIZ2aWV3AAAD1AAAACRsdW1pAAAD+AAAABRtZWFzAAAEDAAAACR0ZWNoAAAEMAAAAAxyVFJDAAAEPAAACAxnVFJDAAAEPAAACAxiVFJDAAAEPAAACAx0ZXh0AAAAAENvcHlyaWdodCAoYykgMTk5OCBIZXdsZXR0LVBhY2thcmQgQ29tcGFueQAAZGVzYwAAAAAAAAASc1JHQiBJRUM2MTk2Ni0yLjEAAAAAAAAAAAAAABJzUkdCIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWFlaIAAAAAAAAPNRAAEAAAABFsxYWVogAAAAAAAAAAAAAAAAAAAAAFhZWiAAAAAAAABvogAAOPUAAAOQWFlaIAAAAAAAAGKZAAC3hQAAGNpYWVogAAAAAAAAJKAAAA+EAAC2z2Rlc2MAAAAAAAAAFklFQyBodHRwOi8vd3d3LmllYy5jaAAAAAAAAAAAAAAAFklFQyBodHRwOi8vd3d3LmllYy5jaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABkZXNjAAAAAAAAAC5JRUMgNjE5NjYtMi4xIERlZmF1bHQgUkdCIGNvbG91ciBzcGFjZSAtIHNSR0IAAAAAAAAAAAAAAC5JRUMgNjE5NjYtMi4xIERlZmF1bHQgUkdCIGNvbG91ciBzcGFjZSAtIHNSR0IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZGVzYwAAAAAAAAAsUmVmZXJlbmNlIFZpZXdpbmcgQ29uZGl0aW9uIGluIElFQzYxOTY2LTIuMQAAAAAAAAAAAAAALFJlZmVyZW5jZSBWaWV3aW5nIENvbmRpdGlvbiBpbiBJRUM2MTk2Ni0yLjEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHZpZXcAAAAAABOk/gAUXy4AEM8UAAPtzAAEEwsAA1yeAAAAAVhZWiAAAAAAAEwJVgBQAAAAVx/nbWVhcwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAo8AAAACc2lnIAAAAABDUlQgY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAoAC0AMgA3ADsAQABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8AMEAxgDLANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFuAXUBfAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJnAnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YDogOuA7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUcBSsFOgVJBVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG9QcHBxkHKwc9B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQCSUJOglPCWQJeQmPCaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4ALmAuwC8gL4Qv5DBIMKgxDDFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5kDn8Omw62DtIO7g8JDyUPQQ9eD3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwRqhHJEegSBxImEkUSZBKEEqMSwxLjEwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbWFvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZRRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2ZHcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAVIEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUigiKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVoJZclxyX3JicmVyaHJrcm6CcYJ0kneierJ9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2K2krnSvRLAUsOSxuLKIs1y0MLUEtdi2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGCMbox8jIqMmMymzLUMw0zRjN/M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQOIw4yDkFOUI5fzm8Ofk6Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+iP+JAI0BkQKZA50EpQWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7R8BIBUhLSJFI10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/dUCdQcVC7UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjLWRpZaVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJYpxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr/2xXbK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXhdj52m3b4d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeAqIEKgWuBzYIwgpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuWi/yMY4zKjTGNmI3/jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqXdZfgmEyYuJkkmZCZ/JpomtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2o+akVqTHpTilqaYapoum/adup+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACwdbDqsWCx1rJLssKzOLOutCW0nLUTtYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2Pvgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NYw9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbLtsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6LzpRunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio+Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t////4QDKRXhpZgAATU0AKgAAAAgABwESAAMAAAABAAEAAAEaAAUAAAABAAAAYgEbAAUAAAABAAAAagEoAAMAAAABAAIAAAExAAIAAAARAAAAcgEyAAIAAAAUAAAAhIdpAAQAAAABAAAAmAAAAAAAAABIAAAAAQAAAEgAAAABUGl4ZWxtYXRvciAyLjIuMQAAMjAxMzowOToyNyAxODowOTo0OAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAEOqADAAQAAAABAAABrQAAAAD/4QJlaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJYTVAgQ29yZSA1LjEuMiI+CiAgIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICAgICAgICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyI+CiAgICAgICAgIDx4bXA6TW9kaWZ5RGF0ZT4yMDEzLTA5LTI3VDE4OjA5OjQ4PC94bXA6TW9kaWZ5RGF0ZT4KICAgICAgICAgPHhtcDpDcmVhdG9yVG9vbD5QaXhlbG1hdG9yIDIuMi4xPC94bXA6Q3JlYXRvclRvb2w+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iPgogICAgICAgICA8ZGM6c3ViamVjdD4KICAgICAgICAgICAgPHJkZjpCYWcvPgogICAgICAgICA8L2RjOnN1YmplY3Q+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgr/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wAARCAGtBDoDAREAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD+/igAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgBGZVBZmCgAsxYgAKvLMSeAAOSTwO9AHxV4x/4KU/8E6Ph5rl74Y+IH7ff7FPgbxLps8ttqPh7xj+1R8C/DOuWFzA2ya3vdJ1rx3ZX9rPC/ySxT28ckbHa6g8UAcv/wAPYf8Agln/ANJK/wBgH/xMj9nX8f8Amo1ACf8AD2H/AIJZf9JLP2AP/EyP2de//dRu+f1oAX/h7D/wSz/6SV/sA/8AiZH7Ov8A88agA/4ew/8ABLP/AKSV/sA/+Jkfs7f/ADxqAD/h7D/wSz7/APBSv9gH/wATI/Z1/wDnjUAH/D2H/gln/wBJK/2AeOv/ABmR+zrx/wCZGoAP+HsP/BLP/pJX+wD/AOJkfs6//PGoA+hvg1+1N+zH+0al7J+z3+0b8B/jummxLcai/wAGvi98PviglhA7KqTXreCPEOuC1idnVVknKIzMoDEsMgHvHXmgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAQkKCx6AEn6Dk0Af5wP/B1n/wAFovjN4n/aI8a/8E0v2evHOs+APgv8I7LRtO/aI1jwfqt7pGs/GL4ka/olh4ivPAmra1YPbXx+HHgDSdW03StS8L2s9taeI/Gs2vt4ni1Wz0LQIbUA/iMbnOV/75VRnkf3QM/U89SeaAGjrnB6senXI/yPegAHB6HkL+HH+c/jQApJOcqeAR9ckd/8+tAC55xg9euPf/Jz/wDroARuexPH/sw4/HH5c80AGfvHDc+31/yT7/mAL3A56k5xx/F/n/8AXQB6F8Kfi18TfgZ4/wDDHxU+Dvjvxb8MviN4N1ODV/C/jbwNruoeGfE+h39vIHjnsNY0qa3uowwXy7i3leW0u4Ge2vLe4tpZYmAP9ef/AIN/v+CpWs/8FUP2FdM+JXxLTS7b9oX4P+Kp/g98dl0i1i03T/EviPTdG0zW/DvxM03R7eNLXSbP4heG9St77UdPsUt9MsvGGm+LLPRrKz0a3sLaIA/cmgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgBCMgg9Dwfcd/wA6AP8AFM/4LVzzXH/BWv8A4KLvPI8rr+2J8d4VZzkiK28a6hbW8YP92GCGKJB/CiKO1AH5gc4P178Dkr1B555+vP8AeoAQZz/30Oo3Hjue+P0oAXn+R65PQc8fr/eHSgA556dPoOvcdc46n8KADnrjv3Pqf5eg659aAA5//Wf9rv6jB/DJHegBOfm6Z4789O/9emRmgBecj6nvx1bt6/5NABzx069zk/e556env2oA/wBDj/gx5mmf4Yf8FErdpGMEPjv9mqeKInKRzT+GPi/FPIo7PLHbQI57rEg/hoA/u7oAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKAP8AFF/4LS/8paf+CjPr/wANkfH3/wBTvVPf/wDX60AfmMc4Of8A638PU8H1/wA4oAQZyPXLc8dcc9+oP4GgAGc47kLnt0A98nvnj/64ApyScjsR147d+OvvjjmgAyc49we3r9c/Q9c8HrQAN7+/6sMDr7df60AHPzevGenp356EfzPXFABzkdcZPp1+b3z/AJ9aADnAHvknj+96e/8AkmgD/Q0/4Md/+Sb/APBRf/sdf2ZP/Ub+M1AH939ABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQB/ijf8ABaX/AJS0/wDBRn/s8j4+8/8Ac+ap3/zzQB+Y3+Prx1Ht/TqT60AIM+p/i53c+nPH5dcGgBec9+MdT+PoefXnoe+RQAvPX29ePr06/gaADBz1PXPX8x06fj+XWgBDn3/P1P8AnHXjIoAOeRz25z/9bvxnPqT9QA7g5PUjrx3P+fT8KADkfn1J56/5789OtAH+hn/wY7/8k3/4KL/9jr+zJ/6jfxmoA/u/oAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKAP8UX/AILS/wDKWn/gozxn/jMj4+9s/wDM+ap+fNAH5jEdeP8Ax0H+76/j+vpQAgHPT1/g/L3/AK9jQAd+hxx/APxoAO7cHv8AwD1/X/JoACOenf8AuZH59f8APFACkdePX+EH+L/D9OaAE7Hj0/gH4/8A1/060ALjnoOp/g7c9+n4/ie9ABj2/wDHPf8AT8e3PWgD/Q0/4Md/+Sb/APBRf/sdf2ZP/Ub+M3+fegD+7+gAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoA/xRf+C0v/KWn/goz/2eR8fffr481X/P55oA/Mc85P8AMH1HXuenP/6qAGjqOB1bnB75xn2Pbnp34NAC4Oc46Y7ZPAHTk/j+mTQAYPXA5z29wfm75/Dr3oAMHrhfXpz/ACz75657UABHt+nqwPPf/HnPNAAAeRgdeuODj/PPpk0AHcH/AGj2OepOeme/596ADHTgdR7H72eOvGPf1zQB/oaf8GO//JN/+Ci//Y6/syf+o38ZqAP7v6ACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKAMzWda0jw7pOp69r+qadomiaLp97q+saxq99a6ZpWk6VptvJeajqep6jfTQWWn6fY2sUtzeX15PDa2sEbzTyxxqWAB+H/AMUv+DlH/giz8JfE194T139trwp4k1bTbmW0vp/hl4A+LfxT8PRzRMysLXxf4D8C654U1WLKnFxo+s6hbE8CY9wDzP8A4io/+CIf/R2mu/8AiPH7RX/zsqAD/iKj/wCCIf8A0dprv/iPH7RX/wA7KgA/4io/+CIf/R2mu/8AiPH7RX/zsqAD/iKj/wCCIf8A0dprv/iPH7RX/wA7KgA/4io/+CIf/R2mu/8AiPH7RX/zsqAP8xH/AIKa/Gj4d/tF/wDBQn9s747/AAi1yTxN8MPi7+0n8XPiF4B8QTaVquhzaz4U8U+LL/VNE1J9H12007WdNa7sp45WstTsbS+t9xjubeGUFFAPho55+uc8e3v25/TnPUATJz27+mef+Bfn/XrQADIPYZx6c8fX/H+VAC5OfpnnjPb/AGvz6dqADJz/APqzj/vr8KAA5/rzj1Hv6fTmgBMnnp79P1+b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ABQAUAFABQAUAFABQAUAFABQAUAFABQAUAf4ov8AwWk/5Sz/APBRrP8A0eT8f/8A1PdU/pn/ABoA/MY9fTnv/wAB75Pt/XvkATg9x/F+H6jr3yfpgcgAUfUdvqcYOP5YwfqTzQAHHJyffkZHPTOfU/8A1+xADj15znqM56Z69fXtj+HNAB+P5/73+POcYzjoOKADjnnuOhHp9evHHOOmckGgA79ecn69W/n3yMcn2AAAY459OMjHX6k/r168cUAf6Gn/AAY7/wDJN/8Agov/ANjr+zJ/6jfxmoA/u/oAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKAP8AFF/4LSf8pZ/+CjX/AGeT8f8A/wBT3VKAPzFJ5J9+/wBU70AGQe3Zsc+uc8Y/LP60AGf129+eAPbn8+DycA0AGeSe+PUdMj2wP1Pr2oAM8/8AAj375+np+GOPvUABP9T1/wBv/PJ/xoAM8Hp1Hf29+e3f8ehoAUfeHrk/zb/PX0oAQHgDscdSP73p1NAH+hr/AMGO/wDyTf8A4KL/APY6/syf+o38Zs0Af3f0AFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFAH+KL/wWj/5S0f8FGucf8Zk/H/r/wBj7qn/AOv9fqAfmOfbHXp+K+3uT+INADefUfxenb8PXk9c9aAF5yeR2z05yB7c5/Cg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BQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFAH/2Q==",
+"attach_logo": "logo-2013-color-small.png,data:image/png;base64,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",
+"attach_profile": "rushabh.jpeg,data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD//gA8Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcgSlBFRyB2NjIpLCBxdWFsaXR5ID0gMTAwCv/bAEMAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAf/bAEMBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAf/AABEIALIAsgMBIgACEQEDEQH/xAAfAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgv/xAC1EAACAQMDAgQDBQUEBAAAAX0BAgMABBEFEiExQQYTUWEHInEUMoGRoQgjQrHBFVLR8CQzYnKCCQoWFxgZGiUmJygpKjQ1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4eLj5OXm5+jp6vHy8/T19vf4+fr/xAAfAQADAQEBAQEBAQEBAAAAAAAAAQIDBAUGBwgJCgv/xAC1EQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4+Tl5ufo6ery8/T19vf4+fr/2gAMAwEAAhEDEQA/AP7qv+FLfB3n/i1Hw268f8UL4X+n/QK9fXnj2JJ/wpb4Oc/8Wp+GvUD/AJEXwvxn/uFH8zx26816Znr8x646dPbp/nj8Vz7nj2Pr06d+grs/tHMP+g7Gf+FNf/5M8f8A1eyD/oR5P/4bMF/8oPMv+FLfBzn/AItR8Nhzj/kRfC/fHrpWe/8A9eg/Bb4OAEn4U/DUAdSfAvhfjp/1Cuw/+ufTo/G3jnwV8NfCmveO/iN4x8L+APA/hewl1bxN4y8a69pPhXwp4d0q32i41PXfEWu3VjpGk6fDuUS3uoXlvbR7hvkGRX84f/BXH/go9/wRq+J/7OXiTQPid+3x4j+LenJbSaXY/s5/8E/f2n7Wb4ifGvXdYElvp3hPWLT4Ravdf2vol7JEIr1fHWtW/gSwiVZ721n1KfT7a+P7RzD/AKDsZ/4U1/8A5MP9Xsg/6EeT/wDhswX/AMoMH/grF/wWZ/Ym/ZO8M+J/gF+xp4W/Z/8A2mf28vEQvPC/hrwd4N8L+D/F3w++Bt/LFLbah8QfjZ4p0nS7rw1YJ4MfM6/Dsas/ifVdZSy07WLDRtJnuNQT+Qz4X6B4p8D6NqcviDxv4h8YePvGOv6r42+I/jTUL+6W88V+NvENw97rWqFVkSO2s/tEjQ6fZQxQwW9uisIhNLcO/wAwfAb9nCz8KeOfFfxStNI8UfCPwn4i1u71PwB8A4PiNrniqHwX4ek3Jo9t8QfE6ppEfjzxRb2OwXksuk2Wki8e5uBpdsZLbStI+uvEfiPQfCOjah4i8TatY6HomlwNc3+p6jOltaW0QO0bpHA3SSOyxwwoHnuJpEhgjllkRHyq4nE10lXxFesou8VVq1Kii3o2lOUkm1pdHVhcsy3ASnPA5fgcHOpFRnPC4Shh5TindRnKjTg5RT1SbaT1tc5Jvh6uueMh4+8eaxqvi/X7MtD4a0/UNQvZPC/g2w3h44dE0OSdrO41SVo4rjUfEWpw3GpXV7HHLYrpNlFaada+jav44vdEt/PvNY8SS53eXbaXHr2s6hLtALCHTtHivb6VVJUPKluYYy6ebIoYZ/JT4zf8FFNTnuLvRPgpo8FlZI0kI8aeJLQXN9c4JX7Ro+gS4tbOIkboZtaF7NNE48/SrOUFa+GNc/aF+OXiKaSbVfiz49cyks8Fl4l1PSbLLEk7NP0m4sbGMcnCx26qBwABxWB3H7reJv2ifjPa+Yvgn4EfFzxQEyFutb8S6F4OtZz2khhn1TV9VEZGCBc6ZaTdmiHBPzH4y/bc/ay8G/aLzXP2f/EGhaVCCzXl9e+L9SsbdFGWafW9Pgi0voCd37pSoJAIBI/J3/haPxMHT4i+Ov8AwrvEH/ywroLD49/G/TCv2P4u/EiNVxiJ/GfiC4g4xjdb3N/NAw4Aw0ZGOOlAH6A6X/wVR8cRvjW/h8LyM4GdK8eavprqe7BbvStVVx3CFkyeC+Dx6VpH/BUnwvOyjXfCnxJ0xSQHfS9d07WtnPJC3V7oRbH1B9s1+Qfijxp4i8Z3K3viS7tNRvwSZNQXR9FsdRumIwXv9Q07T7S81F/R7+e5cHkEHmuWoA/oe8Gft5fBTxnPBZp8TtX8M39wyLHaeLxquiRhnIXbJqzSXGgxHccHzNWQHOV3AE17V450Cb4k6RHaT+PviVocMsO+21XwJ8RfE/hqZ4Z0DK//ABKdSXS9SjkVleM6hZX0RQgx/Ix3fy717j8OP2kPjT8KoIrDwd451O20eEjy9B1NLbXNFjTOWitbDVobuPT0c8v/AGabN2OWL7uaAPtv49/s7/HT4P6TqnxQ+H3x5+I/iHRvDxXU9Rg1LxZ4isPFmk2yzIrX8V/aaiLPWEtS4mvZFh0qaOBXkS1nVHxl+Kv2orj9ob9l7xH4b8XeJ73Tvi/8PW0XxHZs2qXFkni62029h0+/1fSwk0SjUhoeoak+s6VB0kT+0bOEWLSw6f5X4g/b3+K3i7wV4o8EeJvDXgS8svFPhzV/Dt1qNjY6zp2owRavYT2El3GDrd3ZNPAs3nRILOOMyIMjbgD4boA+v/gB8f8A+yvK+GPxX8UeMo/hrqvifTPFGneIdE8S6zpniP4d+NdP2Q2HivR9Usp2voLYxqkGpxQB5bZFTUrFBPDcQX39Ff7D/wDwVi/aQ/Y/8QxXHhHxx4K/4KRfs86VJDceOfgJ8d4PC/iX49aB4faRftGs/C74zX2i3XjZ9Rgg2ppui+L7fXvCs9tCdP0zRH1O7iv7X+Y74J/DPwB8VLnUNC8V/FbQ/hTq9r/pGk3fiDTHubHxAkyKjWf9pXWvaNpWmy2EkW9Ypj9rvhe/6N5/2Z44/wBjfgp8Kb248M+ENR8ea3q+v+IvAGsFfCHimLWvDur/AGzSbGL7Ms3h/wAV+H9M03VdT8DeJ9PuDDeeHfFf2u9geJ7WaWRbKxvpNqWJxFDm9hXrUea3N7KrOnzW25uSUb2u7X2uceLy7L8e4PHYDB4x07qm8XhaGIdNStzKHtoT5b2V+W17K+x/pxfsMfH/APYW/wCCh/7Pvhj9o79m3wn8Ode8H63LPpGv6Bq3w/8ACOm+N/hx4106K3fXvh/8Q/DyWVzJ4f8AFuhPdQG4tfPubDUbC5sNd0HUNW0DVNM1S7+wv+FLfB3IH/CqPhtzn/mRfC/b/uE1/nT/APBLX9sS/wD+Ca//AAUV+G3jebVpNJ/Zb/bM8TeH/gP+03oDSmHw54e8d65dTWnwe+OJgJW00690LxLenR/FmsuscCeE9Z8RTXCXGo3ttLF/pSA5A5PPt/8AW/Xpx1652/tHMP8AoOxn/hTX/wDkzj/1eyD/AKEeT/8AhswX/wAoPM/+FLfBzOP+FU/Dbv8A8yL4W/8AlV/n8DQfgt8HM/8AJKfhr0z/AMiL4X7f9wr2OR/KvTcj39AMEf06e/Sk9OT6dMfoRwP0FH9o5h/0HYz/AMKa/wD8mH+r2Qf9CPJ//DZgv/lB5n/wpT4Of9En+Gv/AIQnhb/5VUV6Z+Lf98//AGNFH9o5h/0HYz/wpr//ACYf6vZB/wBCPJ//AA2YL/5R5L7j8Qv+HiHx6I/5B3w65/6l7V8nGO3/AAkmTngfz4o/4eI/Hrn/AIl3w74/6l7V/wD5pM/55r4QyfUdSeccE49c+3PXOc9CaT8Rx2xn/EZ//VxgV/U/+pnC3/Qiy/pb9wtdvP09V66/5Yf8Rk8U/wDouuItdn9da7d4776W16bH3Tcf8FCfjndwT2l3o/w2uba5ikt7m2uPDWqSwTwSo0c0E8UniNo5YpY2aOSJ1ZJEYoykEg/xC/8ABXD4KeGvh7+3n8DvHf7PPwS+F/7LH/C7dH8T3/iXxl4Esb0fCjxz4x0y4Nx4g8M2nwemiuNF8C+Kk0lrK/E/hXX/AAxpHiePWpJLaystdtNRvb7+pHPGOP19/fHv6846cV/OL/wUl8b/APC0/wDgoD8M/hrbym58P/sw/BnVPGuqxqxMMPxH+M13Dp1raXEQyDNB4H0nTNUspJBuiF7K0QUuzH4zj7h/h3KuGsXicLluEweLlWwtHDVaFGCnKpKvCU6fNJS5U8PCtJuDjP3Lc3K5KX7P4BeIHiLxT4lZXluacS5rnWUwwWa4vMsNjsXWlQp0KOCqQo4hQpypxnOOPq4SlCNZVKS9vzOHtI05x+cfGnxC8G/DfQ59c8ceJdI0G0tbSWdmvbuG2nvnghZ3g0rT5J2u7+7lZStvY2a3VzI7JEgkYg1+Cn7R/wC0p4q+PviNjK9xo/gTSrmU+GfCqS/u0Ubo11bWfLby73WriInLkvBp0MjWdj8rXNzefrP8ffhZ+yxpWj6r8QvjD4Z023Ylg2owaprtjrusX7I7w6fpkGmaraS6lqExBMcGx4o4w9xcNBawzTx/hZ4zv/C2qeJtTuvBHh+78NeGJLjZo2j32pz6zqENsuFR7y+m5kurg5leKIGK33rbpJceWbmb+fz/AEAOVr6L+Bf7I/7S37S139m+B3wX8dfEG3Wf7Nca5pmktZ+E7G4zgwal4y1h9O8KabMOT5V/rFvIQGKoQpx/Ub/wSw/4II+B9P8ACnhP9oP9uPw+3inxVr1lZeIvCP7PupCW38N+FNNuo47vTb34p26mK41/xJcQtFcS+CZ3h0LRI2ax8TW2t6hLc6do39DFvpmh6PDHpPhrRdJ8PeHtNVbHRND0PTrPSNI0rS7VRDZWWnaZYQ29lZWkECIkVtawRQxgYRFFfm+eeIWGwVWphcqoQx1anJwniasnHCRnHRqnGDVTEJO6clOlB2vCdSLufrnDnhVjMwoUcbneJnltCtGNSng6MIyx0qckmpVpVE6WFcotNQlCtUV7VKdKS5T+Nb4S/wDBu5+1P4sitb34s/E74WfCO0nCNLpuntq3xH8T2YOPMS4stLj0Lwy0idB9j8Y3cbH/AJaAc17l8S/+DbnxRZeHoLn4QftNaJ4j8Uw27fatJ+IfgO98JaJqFwMsr2mt+Hdb8YXmlqw2xrbXOh6mC5EjX0akov8AV/0/z/n/AAFFfE1OPeJZ1VUji6NKKd/YU8Jh/ZNdm6kKlVr/ALi3W6aep+iUvDLhCnQlSlgcRWnKNvrNXHYr26fSUVSqUqClfX+ByvZpx0P84f8Aao/YQ/ae/Y0utL/4Xt8O5NC0HxBfXWneHPGei6rpniXwfrl5aRC4ktbfV9JuZ20++ktt91baZr1rpGq3NtDc3EFi8VpcvF738J/+CPH/AAUA+MPhbw9428P/AAXt9D8K+KtHsdf0HVfGfjjwV4bmvtJ1OCO70+7bQbnXJfE9kt5aSpdQpqOiWkjQMshRVki3/wB3PxT+Evw2+Nvg6/8Ah98V/Buh+O/BmpXOnXt54f8AEFmt5Yy3mk30Go6bdqCVlhurK8t4poZ4JI5AA8TM0Mssb+goiRIscapHGiqiIqhVVVAVVVVAUAAAKBgAYAGBXr1PErMnhKMKeDwkcapVFiK041ZUJU0o+ydGkq0ZwqNufteec4Lli4L33Gn4NPwhyhY7ETq4/HSy506TwtCnOjHFQq3l7dV60sNKnOkkoOj7OnTqPnmptezUqv8ADO//AAQV/wCCg6qWHhr4XOQOEX4m6UGY+gL2iJnr95lHB5rybxj/AMEZv+Cjfg2Oa4l/Z8ufEdpChdrjwf45+HfiKRwByIdLtPFQ1yZwQRsj0tnJHCnIz/fpSYB6gGuWHiPn0ZXnQy2pHrF0K8dPJwxSs/NprXY7avhJwzKNoYnN6Uuko4nDT+9Twck16NO19eq/zNfGH7NP7RXw/wBVOieN/gR8YPCmqhyiWWvfDjxfpss53bQ1r9p0iNLyJjxHNatNFJ1R2HNe4aH/AME9P2q7z4DfGT9pDxR8IfiD8Pvhd8HvDWh6/PqvjLwP4m0W88ZTa94t0PwzFZ+EdO1Cws7vUrDR7PVb3xR4n8SpE2geHtB0W7kv71bq6sLa4/0W1LIyvGdjoyujLwVZSGVgQOCCMj8DS/tJeLvivov7OHxG8UfAz4SaH8dPiang++Hh34WeI9WtNK0TxRcXcZs9TstQN6nkavb2tpLeXMvhk3GnyeJ47ZtBt9TsLjUIrlPSh4k46vLD0YZbhaVSdehGpUninGm4OrBVEnVhGFBShzRdWpUnGknztPl18p+EWW0IYuvVzfHVqdLDYmdKlSwKnVjNUZ+zk40ak6mJdOpyz9hRp0512lTi1za/5Y1fp5/wTe+I2tp4p8XfC26up7nw/c+H5vF2lW0rvJFpep2GpaZp2oLaKTiGPVbfVYpbpB+7M2nxSqFlklMv5yeLLy+1DxR4jvdS0Wx8N391r2s3N74c0vS/7E0zw/d3Op3VxdaHpuinnSLDSrmWWys9LJP9nwQpa/8ALKv0U/4JU2/wx1v9o+bwZ488WT+Atb8ZeD9b0bwR4ruFt7jwx/a0L2Wt3OieJ7WZreW2h1C10Uy6brNtf20en3NvNDf217FewSWH68ndJ7XSdk7rXz6+vU/BpK0pJNtJtJtWbs7Xaeqfk9tj9N/i/wCAG+KXwz8ZeA4LeS71LxDotzbaFFCpef8A4SSDbeeG5bdVJkM8WuW9hJGIyJGZQoIyK/sI/wCCff8AwVw/aE+O37Fn7N3xM1IeBtV13VvhdoGjeKtT1DQtVl1DUfGPgpJPBPjDUb6RPEESm71DxL4d1W9nxHGN85wiggV+ZnwY+AXwOg0nSrPS77SvGvjD4eeLdK1rxF4q0uSOYSeKLew+22mmR3flzI/h61+1wzJYWc/lPfWEbX7tf293APNv+CQEn9l/sweO/h1jYfg7+1L+0h8NGtzjNqdO+Id74g+zlf4Ao8S7tmf493cV+h+G2Cy7Ms8xGCzPB4fGUamXVatKOIpqfJXo18PaUb6q9KdVSS3sux/PP0k874i4b4Hy/OuGs6zDJcXh+IsHhsVVwFZ0XWwWLwWPUoVbJ83LiaOGcG17rcrfFc/pB/4eI/Hv/oHfDvj/AKl7V/8A5pPY/kfSk/4eJfHr/oHfDv8A8J7V/wD5pK+Ec+uO2cc9OnQ4Pfvj17CjJ9uOh/H29OcenbtX7j/qbwt/0I8u/wDBHp/X399f4d/4jL4paf8AGd8Qvv8A7bLyvf3dNLuz8+yPuz/h4l8fO2nfDn/wntY/+aSivg/P+0v+f+BUUf6m8Lf9CLLv/BC/z8vz7sz/AOIzeKn/AEXfEP8A4Wv/AORP6Uz8Avgdz/xaL4ajnH/Ik+HuP/Kf+Z9cdO6/8KC+Bpzj4Q/DUcj/AJknw9x0GDnTvXNet5xnJPPOcehA9OowOeOvSlyB3PJHVeeMHHQc+n6dK/lv+1Mz/wChjj//AArxH/yzyX3H+p3+q/DP/RO5F/4aMv8A/mc8j/4UF8Def+LRfDXtz/whPh44H1GnY59f0r/MK8c/EDQvjv8Atkft6/tJeGrLTLHwp8U/2pvHnh74cx6Pa21lpj/Cb4Q3TfDr4c3Flb2qw20Ud1omk+fKluvltcNJIdzsWP8Apo/tY+IfHHhL9lj9pbxV8MbWW/8AiV4Z+AHxj8QfD2wiDLLe+ONG+HXiLUfCdpGURpPNuNettPiTYpfc42KTiv8AK7/ZUg0q3/Z4+FI0ebz7efwxHe3U24lm1m9vbu78QCVi+Xkj1ybUInZjuLRnPpWNbG4zERUMRi8TXgpKShWr1asVJJxUlGc5JSUW0mldJtXs2deDybJ8uqyrZflWW4GtKDpSrYPA4XC1ZU5SjJ05VKFKE3ByhCTg24uUItq8U15V+2RL8APDvh2x8V/F3wlJ428Tyw3OleCPDyeI/EWlT3c0eJrl4xpmrWttp2m2zSW0msamtpLId1rb7Lq5ktYD+dn7CHwqtv2gv25f2avhqmj20GieL/jh4Pu9a0O0N5dWtv4M0LWo/FXiyxt31C5vb6WGDwpo2rRxy3t1dTbEElzLKQ7N61/wUe8I+L0+IfhjxrLbXt14Kn8LWmh2V9FHLJp+laza6lqd1e2N0yborW4v4ru1u7aSYxtfossUXmDTnEf1f/wbs+D9O8Tf8FINA1m+WNrj4ffB74p+MNILgFl1G7s9J8Bu0WQcSf2X421IZGD5ZkGea8LO8TLB5PmeKjpOjgcTOm10qeykqb+U3FvyPrOG8HHMM/ybBTt7PEZlg6dW/Wj7eDrKz3bpqdl1dkf3reJL0WGi30y/LJJF9mhxwTJc4iBXpyiM0g6H5DXhijAHH5+pHP0//X616P4/vCTYacrf37yYZOeP3MHr/wBNzz7HryfOfwP54/r75/8Ar4r+Yz+yKjblpsru17tvT09Nfz1S+v8An+X1P40f59KP8/5+tH8/8/5NBCX36dNel7676flvYKTjn1+vPf3+uO2eetLR+H+f8/yoD7vLTpp/X3dtSiiigLenlpstP8vlppoJ/n9R/hz/AFzXp3gK+D293prtlreQXUAJ58qUBZQo9ElCk+82cg15iPpj8vb0/L8PpUSeOvC3gO+s9V8U+JtB8M6ZI7QXF74g1jT9GsxDMVjZ2udRuLaLZDI0crHzPl2gZGQKaTk0optvRJJtt9klqxqah70pRjFfE5WUUtN23ZfN/k7/AMC3/BZv4C2/7P3/AAUV/aA0LSrIWPhn4g6zYfGXwzGkflQm1+J1kniDxAlrEAI4rSy8cyeLNMtIoh5UVvYxxosYXyk/OLwLqXivRPF3h/W/BEOoT+KdF1O01TRo9Ms59Qumu7OZJY1+xWySSXUMpHk3NvsaO4gkkgkVkkZT/R7/AMHM/hmw1H9oH4BfFjw/JZaxpN78Mta+Euva5o1za6lY6b4s8DeJ7rxj/wAItrF1Yyzx6b4gg0T4l2WrrpV+YL99Mv4LyOF7Y71/m18Ma1rWg6zZX2heJr7whfedHENfsL/VtOk0+N5F33Etxocc2qeRFgSypZ29zOypiK3lfah/pPhrFSxmQ5VXm+ap9Up0qjbvJzw96EpSvrzSdPmlfq2fyJxjgqeX8UZ3haUVGksdVrUor4Y08VbFQhG32YKsoR8oo/sR/wCCbH7UENnYeILz4seGfEPwmXxB4ftrnUNO8U6ZeWsSeINA86aI6SkkRvZrPVLK81AWYubW3vWuYrWxlgeV4JJv3a/4Nm3+Fnxcf/gqd4W8Q/D3wrrU+jfty3Hxf0qPxLoGkaxqmmaF8e/Aum6jY2nn3VrO0Nu9z4Hv7oW0T/Zkuri7MW7czt/Gn+zd8avB0uk6f4c8TftL6d8VPFuqS21tptpf6E3haW2nkG0adYvquk6Xr2vXMsjbRd6nKbifC7LOE8H+nL/g2U+LFl8OP+Ckn7a3wC1Nkt3/AGl/2dvhF8b/AAtK7bI59U+APiDWPh9ruj24Pyz6jcaX8SRrTxYMiWOkzyjCq+foaNeth5+0oVqtCpZr2lGpOnOz3XNBxlZ9Vez6nyOLwWDx9F4fHYTDY2g5Rm6GLoUsTRc4/DJ0q0Jwco3fLLluujR/aUPgF8Dv+iQ/DUnr/wAiT4e9j0/s/uD69enpR/woL4G8f8Wi+GvT/oSfDo79f+Qdx6c/z6+uZx3PTsPp0+U9frjnijOMDJ/L9RgH147dPx6v7UzP/oY4/wD8K8R/8s8l9x5f+q/DP/RO5F/4aMv/APmc8k/4UF8De/wh+Gn/AIRfh0fp/Z/H0or1v8W/75/+xoo/tXNP+hlj/wDwsxH/AMsD/Vfhn/onci/8NGX/APzOfiL/AMPEvjz/ANAn4c88/wDIva51/wDCn+mP06cH/DxL48/9An4cc/8AUva3z0/6mf8AU46e1fB+P9noTwT9M9hn0BycE9ehpcH09e47jHpxntj8eK/qT/UvhX/oR5f/AOCltpv7/wCHr5s/y4/4jL4p/wDRdcQf+Ffp/c8vz7n3bJ/wUP8AjvKjxy6N8NpI5VZJI38O606OjAqyOh8TkMrKSGVgQwJGCK/gqj8FXHwM/aB/a2/Zss7Ox0iz+HXxY1bxn8MdNSO4i0bT/hp8X4pPHPgrS7GIzSXEmm6B9vl026aKaSSOXfA0hnRs/wBdG3/Z9uvTknOcfl1Pr7/zwf8ABU3wYvw9/bY/Zs+MNnD5OnfHT4Y+Ofgr4peMbYf7d+HV3B458KXt0RhZL/ULTW73RraU5k+y6d5IOxBXwviFwjlGD4enmGVZfh8HWwWKoTrSoQ5XUw1aX1aUZLmbaVWrRqXa91Rl0bZ+5/R78WeLc38QKXD/ABTxDmGc4POssx1HBU8fWVRYfMsHTjmFOrTlyx5XPCYXG0ZR+3OpT6xSf85n7YPxz1Hxzc3Xww+IPwy/4Rrxt4G1hm0/WfD/AMQJtV0RzdRwmVp9FfQIrfU7XUbExTWcz3Nlq2mysqO0O7UdOuP2j/YY/tr9kz9lnSrfwZ8EL74B/tOfGX4S+INF8bfFXxnoV5L8XNVvvjP+0f8ACP4Ofs1eIfhvFr87yeCPBg8O658UvG+o6PPo1ra+IdX+EcPiSW1u7GDTr9v5w9H+Huv/ABY/aA0z4VeF5rCPxR8SfjBZ/D/w7cazejTtMTXvF/jOPw7pM2qag6y/YrFdQ1C3e8uykht4BJLscptP+jL8RvgX4I8Lfsx/CrSv2lviv8PdQ1P4NeGvh9D4y+PfxXj0bwsviLxH4D8P6npGm6/qHiK+1CyOmOL7XfEN/p0d3e3939r1a6uA8uqXd5c3P8l8b5nSwkMtwVSHtlisRKpUw8XUc6kKKUYRnSivZ1qMqlTWnUbvVhSlCEnCTh/qD4a5LVx9XN8whV+q/U8LCjSxk40lTozrycqsoV5v22HrwpUlarRS/czrU6lSCqRU/wCNb4tf8Frf26bf9oX4ueJ/hv8AG+Zfhte/ErxdJ4K8F694U8EeKdCsfBUGuXlv4Z0yC41fw7cazDAujQ2TSy2GrWc01w884lVpnz9zfs5/8HGet29xZ6N+1T8F7HULEmOKXx18F5JLHUIEBCebfeBPFerXNpqDsD5lxcad4v0lYwrfZtJlLrGvf+I/An/BMPU7690X4Ufss63+0hawsYG8Qfs/fBX4m6p4YlkbC7bLx5rEHgfw3OVJwLqw8QTWhOWhu5FXfXzX8Q/+CcHgX4n201z8J/8Agnj+2z8N55AXtLzRvGnwpe0d3/1T3nhvx98TfFN20LD5ntLLVNJkjO1PNTOD57hwtj6UMPi8gq4HlpwhGvKGEwNdKEVFTm44mhXnK2r56dVSfxJ7HsOlxtltapjMv4sw2ac9WpVlhaU8yzPDNzm5ypUlLA4nCwgruNoVqDglaMovb+iT4Jf8FJ/2Iv2gILMeAf2hvAFvrF4I0Twp431QfD3xYLqTaDZQ6H41XRLnVLiNjsd9EOqWrkFoLmaPDn6O+NPxJHwu+CPxY+Lmm2tprrfDr4XeOfiJY2Ml35Vhq7+EvCup+I7azkvoBKY7W/awSB7qFZSkUpljDkKD/B94+/4JT/tveE9WngsP2ePiW2lNF9psj4oPw40PxBJbs8ihpNE0j4j+Jg6Bo3jSeC7cXEiSBYYmUxjwPxX4S/bK/Z00C98PeKtN/aD+EXg/xJbXnh+/0+a48deGPBniK01W3lsr/RLhrSe38Oa1b6laTzWt3pzSXUd5bzSQzQyRSMrec+BMnxGIpvLs+pVIOcJSwld0K1WULqUoc9CrSnFuN1Z0Lq+tmrnf/wARH4iweFqrN+FsTRl7KcYY+hTxWHoQqcrjCqoYmhXpTiptSusTyvZXTP8AQ1/Z8+JV/wDGX4EfBj4vappNtoOo/FL4WeAPiJeaJZ3E11aaRP408K6V4jk062uriKGe5gs21IwQzyxRvMiLIygtT/j/APEO++EXwI+NXxX0uCwutT+GXwm+IvxB0211VJ5dLur/AMG+ENY8RWdtqMVpc2V1LYz3OnRw3cdteWlxJA8iQXMEpSVeJ/Y6tJNM/ZG/Zc065tbzT59N/Z2+ClhcWWoWdzpt9ZT2Hw18M2txaXthdx291ZXVtJE8NxbXEMUsEqNG6Kylazf20fCnjH4lfsiftH+A/hnpD+J/Gvjn4NeP/CfhjRrW+0+zfVdS8Q+H73SI7OG91K8s9NheVbqRVa6vLeHd8hkBbB/O1So/2oqMuSGH/tBUpc8rQhR+s8j5pyekI0/ilKWiTbfU/VHWxP8AYrrx9pUxf9lutH2cOarUxH1RTThCKu6kqnwwjHWTSUXon4D8Fv2/PhNafBP4b+Pf2kPi58JPht4h8b/Dnwl491W0vfFmnaPbWOs+KPDmneIdX0DR9G1XWdQ8Qy21jdahNDpFmz6nqIto0tpZp5ViL/I3x8/4OAf2PvhvBe2Hwe0nxv8AH/xHEJEtJtK0648BeBzOmVKXniTxdYxa+I9+Nk2leC9XtpkDNHcBSjv/ACwv+xD8dNP8ay/DnXNFtk8f2XlSav4C8GPcfF3xpotvIAwk1vSPhJb+NbLw3MylDDb+LdX8OGYSRyK627Gdf0I+C3/BKXxVO1pq3jP9lf8Aba+LUWUmGmaPo/wa+DWiTfdLQXcniD4p+IvEtxbH5l8yGTw1dvw22A5Wv018J8LYarLE4rHVcZGcnUp4XD1KWHw84Sd4qE+e8oWaSksXTTV3ft+Sf668bY+EMLgMro5bKlGNDEYzF0q+LxVOtTioTlVpKk3Co5JynTlgKsot2a2v49+0t/wWf/be/aHubvTtE8dn4EeC52eO38LfBqW+8O6rLAx2xf2r48a4l8Z3t0YiYrldM1TQ9Iutxb+xYshV/NzVND+LHjW8l8Q61o/xD8W6hfMZJ9c1TT/EmvXl4zHcZJdTu4bqe4YlixZ53JJyTzX9cnw1+F2t/s0aVHd+Cv8Agkt8VvAFrbmC3n8USa58AtX8UzMw2wxy+Jte+Kl54kvndlYrBJq7IrNJIiL8wPrP/DanhrQGjb4q/Av9qD4P6ZGyLqPiHxF8G9T8W+GdMi3ASz3Gt/CvUvHtmlvEuW86c28bAZ3KnzjvpZ7Ryxewyjh2hCmkrKljMI8TUXedLCrE1Zydrc0qlWT01Zx1OD6mcf7VxBxliZVpNuUq2WZjHB0pJK8aeIx7wOHpxSe0aNGMb3slc+NP2ZvC7/tf+B/gfc/Fr4JeKfi14Wk134Aa78aPBl14V8U3kN5rWlafqn7Bnxr8S38ejwQ3+m+Jovh7qP7L/wAfbfXtJu9P8SWVv4B8d+IrV1sbLWJov51v2vPhEvwD/af+O/waiHhlLb4c/E3xT4ZsYvB+papq3h6DTbHUZf7Mt7K81zVdc1sS22nyW0Go2Ws6vqOraZqcd5pmpXUt7aTtX+jV+zjqv7Pfxm+GPiLxB+zn8dNO8by+IdAv/Dt/8RPh5r2i3fiTwTfavZSJBKvh7U7TVoPCviTSpxHqen6Z408O3dzHeWkR1DT7y0EltJ/np/t9fDL4HfBz9qT4l/Db4CfFrxn8bvC/hXVJ9P8AEvxG8bjSp9R1n4hi9vZPGMFlrOk+XB4nstN1B1sLjxNJaWLaxrcGsXFrDcaaLHUr6OD80qYzNcyoSpV8JClSXJgZQruFFKqrc7lGnToTpqTpxUoKtiFJt8qo2eXiFklDLsmyjE0a+HzCVau/aZmp4aFXEydBKfs4wnVq4inWlH2snCpKhhXCMU3LENrE/Yp12TQv2g/CTLrmgaDBqsd7o15ca/b+ct9b3gidNG0iUhVs9c1a7gtrOwunuLZQJJoA1y066fe/1Qf8EhNQ8WP/AMFJvGH7TvhS3sZdE/ZD+GGp/C/RJ9Qiv30vWvif8ZrGeDxDb3A0/UNObVrTw14Miv7W70y6nlt9O1y70nURELgW7r/Mf+yN4X8J6d4a+Nfxp8e6Xa6v4c+H/hEabp2mXwP2XUtf1WT7ZbwxurI8d6k9hplhaSI6tFPrUUqMskaOn9sv/BL79nrUP2ef2Pvh5pviq2eL4mfFB7742fFe4uI/KvpfHHxKNvrMllfoy7kvNA0D+wvDVzGWZBdaRcMp/ekn+g/D3IKOe55/tlFVsvwFCeIxVOavTqznelhqMtVfmqSdbl2lChNPRn8VfSD4/wAXwLwPbKMZPBZ/n2NpZflmIoySxGFo0XHFZjjaXMmv3dCEMJzWvTq46jNaq6/fj/h4n8ev+gT8Of8Awntb/wDmoo/4eJ/Hr/oE/Dn/AMJ7W/8A5qK+EMf7I5PqOMdO3uT/ADJ6UuAe3XjPpj9fYg9MYxiv3f8A1L4V/wChHgP/AAUt1a/2vJ27+d2fwj/xGXxT/wCi64g/8K15f3PL8X3Pu7/h4r8eB/zB/hv/AOCHXB+n/CTcUV8E7R/kL/hRS/1N4W/6EWX/APgn/gmX/EafFX/ous//APCqP/ys/pKP7O/wF/6I/wDDjrz/AMUjo3qcj/j0POB/nPB/wzt8Bhn/AIs/8OTyP+ZR0bAzgcn7Hjrnv7V7Keh6ZyP4W59M9/59vWgkeo7Z4P1Hcenrnt1AB/l7+182/wChnmP/AIW4n/5b5L7j/Un/AFT4V/6Jnh//AMM2Xf8AzMf503/BTf8AaPuP24/27v2qPgp4P/aK0P8A4J9f8E6v+CfviTTvhT8Svip8MrCDwv4w+Lvx1vtR1Dw1qGjz6zoE+l61rofxfoni3RNB8OWmoahoVlofg5fEc+gahrGvwzad+S/7Zv7NX7Rfww+APgz9ob9nj9tef/goF+yN8K/iRpnj+e+125h8WeLvhN4ntbWfQftV5qcuqeI9bbwRNbay2meKtO0fxB4et9Lv72yvNf8AB1u9jBr1p6h/wTwmT4g/EL4M+OvGsIvtN+K3/BUj9sLxt4pl1BRJbXvxK8J/szWniv4VjUBJvjm1HTtX8Z/EHWdFaQtJFqZeW3xMN9fa3jXxX49+AX7S/iL9pqT9k6/+EP7FPxB1mf4Fftg2Pi7xV4Om034teHvFHiKL4e6H8d7/AOCWiW96nhmDSdU1Z4ta8T32q3l7418Aam0+p6NZySRahN+bZtxbxBHPKtCOaYqvRVJWwOJxdH6tjKdOvOg8CvbSWKnjcRWw9aphp0qlWCnLD0p4VU/aVz9g4f8ADrgxcPYfErhzKcHifrHN/auAyycMbl2Ir4WliI5mnhabwVLAYTDYuhRxkK1KhVlRji6tLG+19lh3/GfN4u1aHxvL490C6u/DmuxeKpPF2i3mmXcsN9oOrJq51nTrrT7+IQzRXel3ghltLuMRSpNBHMgRwMf2H/8ABHmTSv2nP2q/FHhL/gob8d9O/a+8bfCrQfh1P+zL4c8T6J408WeGdPPxEtNb13xf4vHhjxb8PPDl9p3izTBo/gvSLjxL458PWV94bhub9NP1b7Pe2l8PxZ8A/wDBIX4oftK/tAftu/CD4CeMvh5oXiL9lv4tahomleBviDqWu6VL4t+Hus+JPGdr4U1nw74hstL19JZYtM8P6X56axb2ltImt6bcTawjTbW/pR+GPw8+LX7BfxY/ZX/aQ+POmfGfVPhrpf7JHwU+CP7SkH7N2p+JfF+m+H/i7+zuNb0nwR8QPi74W8F2ieMPH/wsvfA/i7X4rmbRdP1bS9E8RaXHJ4p0S+0vVLWe3jiHNsrxKo4WnWpzxdSmnGMOSGNpqtDC4ynGlKVKVTkxWF9pSlChUjHEKp9XvKdSPL28J5HneDeIxtShWhgaVZqc5+1nl1aVCeMwFWdaMK8KPtMFjfY1ozxNKUsK6P1u0KdGfP8AvR4+8GWeg6tr1rpGnaR4Ynms510XVLHRdPCWFtJbPHp12kBijiul09sNJBM/lzTQSpM2HZj+fUvwZ/ab1S6+Bg12Xw18Nh+0Tb+LNQ+GWo/Hj9on4teFtR1vSfCGkwazd65rmn+D9Z8O6B4Ni160vtMn0DR30y3u7uXV7C1srIQCSaP9A/AX7Yv7Fv7U/hpNT+F3x9+GXjxUhMyQ+HfFWkXPiLSHlUA2uq6IJ21PSJ2O1brS9ZsrWePA8+GF0DrwPx70j4zfHSx0K1179oL4XeItG+H1/wCItS8E6j4g8BR3/jbTNC8QadYx6r4P1PWdA8Z6Jpuv6K0+kaTd291e+Hz4labSNNF3rl2Rfm/4ckzDKcJKtSzCcMPKEYOnFqcHHkupxlTpR9pFtOLUZxUVaWie/scT4DPcwp4OrlUJ4lOVRVWpUpqpGfs3SqQqV5OjOKkpJypycm5x1cb2/jA+LP7af7UHjj/gof4h/Zf+CT+JfGnxFuPi9Z/s6fDG38AfHODV/AvjfxzpWoxeFWstPk/aDg+IHgrU9K1vxZNeR6VqNvrPgZr60u9OnbWrGW4Wev3z+DPw18Z/EvwRaeGf2mvg/wCIvBfiay02bw18ZfhX8WvBKaFeN4k0yWbSdYsL/wAL30uq6bc+HvENzaSa/ot3YX+s6JqnhjUNM1DS9UvbS+tLuT5h+B//AAT7/aD/AGdfjv8AD79orwr8UP2QtS8Q/C/4k638Y9C0DxZ+zR8QV8Hf8LD1S31JYfFOrx6F+0JoOsXd7ol9fxa/pksGuaZGus6Ro81/Fe2Onx2LfYH7Zf8AwUYvvGel694P8NfFHwj+0L+3J428HzeCfAXgb4NabY3KaV4tvbabT7DxJrmg+Hr/AMR2vwy+HXgy/wBRm8RX974+8SyT2+i2EkF54h1vVHe8uPK4jxeQ5rRoR4fm62YyxMPaV6VDE04UcO4y9pWxNevTpqnTptwmp83LTSlN8sU5L2uD6XFWR1cUuKIxoZOsFOnQw9fE4OrOtiuen7KhgsNhatR1ataPtKcqfI5VZShCLlNqL+Mv2Gf2PPg18Z/gDaeKvitP8Svil4Oi+Jfxu8NfCfwF4y+LnxIvvhj4P+FXgn4yeOfBngTR/D/gK08SWPh26sIvD2g2iRXfiKz169e3EMdveRadHZ2sH0x4/wD+CeP7L+jeDPGGs/CD4WT/AAr+IeneEtfuPB2u/Bvxx8QvhXq1r4ktdKu59CmU+AfFWgW9+66mlsWg1K2vbe5GYriGWNmU/UP7Pfwl0f8AZ7+Bfwp+DWm3cdxZfDXwN4e8LTao+Y/7X1PT9PhXWdcm8zG2fXdZa/1e4BwfPvpOB0r2cMkihkYMrAEMvII6ggjgjvnOPzFfDYvOMbLHYmtQxuLWHliqs6NNV6sKbpOq5QjKlGSh70OX2i5febbldt3/AEDA5Bl8MuwdHFZdgpYuOCoU69Z4ejUqxrqhGNSUa04yneFTmVJqXuJRUbJI8K/4JW/Bf4Y6f+wl+zz8TfAXhnTP+Eo+Ivw08HeN/iLr0kR1LxL4l+IGv+G9Lv8Axr4h1rWbw3Go6l4i1DxJcapcaxc3E8k8s0nkoy21rb28fK/tS/FP9ob4R6nol3418E/EPwL4Z+Ii65qfww0vU9Y0nwLe6r4e0O6trKXVtU0LRLO4+JVily1zb3Fq2s+IPC73cFwZLbSgkTSJ57+xH+1P8N/2AvFHj/8AY5/aR8VQ/B7wpYfFXxd44/ZT+InjMPo3w58X/C/x7rU/jWw8I6d46vgnh+Lxb8Otd1rVfCF74Z1G9sdUGm6ZoF5pVlfWF6JIP02/aD1nxT+2VqHgnxP/AMLx/Z61TRfB1v4ht/B3iKDwHc6h4lvdB8Ww6a2q6TrupWXxMsPDWsWbXOmWVxZ3WkeG9CniSFoy7RXupx6h+lZLjcppVK9bM6ijCvL2uCq1YTlGtSqTlNTU6cZSlU5HTTt/DkqlKclOEoL8k4hwuf1qGEw2UU254WKoY+jQnThPD1aVKnT9jKFSUYRoxnGry8y/ewdGvCLpzjN/gz+1B8O/2q/jRa/sQeAPhN8QP2eNc8W/thfEHxvZ+AvAN5+2D+094S+IcWr+A/A/ibxAtn4ng8J/EXz/AAtHcNpuo+H4b7WTeeH18Xap4U02/SEakl3adL+yN4E+POl6J4l+GPxs0D4i+GvjH4X+K3i7wJ4g+FvxJ1w+M/Evw51HQLqCyfRoviJcWtvqXj3wnq9ssPj7wt4u1WTUml8LeKNOSz1/XdEtdO1e7+gL79kz9oj4UftUeBfj18F/j7+z7oupfBnS9Vi+Hel61+zT4h8WeHdH8V+IvDOvaHdeMzZ6d8ffDNpq+s6Hb+LtWm8Pxaml7olpqyWeoajpOrXGmaY1l9en4w+FPhdP/wALI/au/aG8LXnjvUdDiXxv8aPixrfgz4ev4s1qzsLOG7votPWTQ/Dvh3TnitRBpHhjw7awabpNhFp+k2cUot4pJODivOsixeGhh8olLF494lQlGnSxXLGnCM+ZJVoKLcqjgougnNqMlzKEmp+3wBk/E+WY2ri89jDBZUsHKVN1q2BTqV6tSi4Sk8NUc1CFJVW44mXsoylCSpurGM6f5Ef8FlPhN4E+EXgn4AeJfgn8SW/ZS/ae+IXxH8MfCPVvjt4NuvHPwzsvFHwv1m3+xeNk+L3jv4b6WI7vStD1qTwv4rtE8QXN34jFlpmsf8IzY3tpBrK2v8kf7ZXwP0P4DfFfT/DmjfG6T9oSbxT4PsvHviD4lHwd4v8ACFnqniXXfEPiaz1ZNKk8byNrvjDTHk0iPUrbx4yQ2XihtSkvLJJIUFxP/bh421TSv+CkX7TX7LPiX9nzVfjRa/AT9mn4gal8XPH3x9tovEPhL4PePPEHhuwmtvAPgb4X+G/HVhJ4Y+KfiCbxLfXM/iL4iWvgq8s/Cvg221TTfDvjFNQ8SWwT8hP+C4//AATr/aN+I3x4/aR/bevL/wCHPhP9nn4b/DD4ctpuseJfFc48ReJ5dJ8O6NoMvh3w/wCHNG0nVrmLV73xxqD6PZf8JBLoOn3EmoWk8N9LG8nl78JZtHA1cDlmOxHsKs8NiKleOIlSVVV6uLhRweBcJUHiFNwl7WlQWIjKEZSc6PJKHLw8d5FPNKGZ53luFWJoU8ZhqWGqYSFZ0HhqWAliMxzNThiVhHSVSH1evinhXGpOnCMMQ5xnzfjt+w/8Bfjx+2mLX9lL4F6fp/h7SYfE7/Fj4wfFfxC91H4b8H6Hp66dYaRNrE1rDMfstvPp0M+kaLDHPqniLxA0SW6WOm6Xqeop+x2tfDnxT+zlD4w+Nn7Fv/BXjX/2rP2mv2bNB1D4u/FL4H+O9Xm8deBfiF4D8ESQTfEmM+GtZ8VeL/DPiW28O2Est7q3hy9l8RXsNjHMLfUNC1uHT7xeP/Zo+G3xg8G/sP8AwT/Y7+Angi18RfGL9sDTLn9r39pyTQfG9t8N/ED/ALJbatpHhjwF4An+I+o6TeN4Z1D4oWJktdMEFlqMen6ZeeLjbW0y6xeXi/pL8TPDHh7WvD//AATj0XR/2f7v9m/xFP8AtGeK/g3cfBq9tvDgvvD/AMMNf+CHxp8L/GjSLa88K3F5pPiDwfrfhzSLfW012OXy/ENnHo+t30UNzcBR6mN4tzbCZnF4HMa2CwsMRXhTw+Dr4aM60cFTrTeJx0ZOWKdHEzoYijQ9hGhGlQviPrEpzhTl85g+AOHMzydxzjJMJm2MqYXDuriczweJrU8NLM6uFgsFlkuWOCVfCU6+ExGL+sSxMsRirYT6rGFKdSH9kH/BPDxz+zH+33+xV+zr+114a+B3wz0WH40/D2y1zXfD1v4Z0i7g8MeOdIvb3wt8Q/C9tdSWnm3Vp4c8daF4i0azu51invLKyt7yWKJpyi/Z/wDwzt8Bf+iP/DnH/Yo6NyeOn+h8/gfXrjj+df8A4NAvFGoa7/wR60bRr2d5bbwJ+0n8c/CukB2zHFp11N4W8aSR2+SdsTat4v1ScgbQZZpW2ksWP9RuenIPXjnPHOAOxA9e+Bxmv1H+181/6GmYf+FuJ8v+nvkvuR+I/wCqfCv/AETPD/8A4Zsu/wDmY8Y/4Z2+Af8A0R74cf8AhIaL/wDIdFe0joP6dPwoo/tbNP8AoZZh/wCFmJ/+WeS+4P8AVLhX/omeH/8Awy5b/wDM3kvuPxI/4eL/AB2/6AXww5z/AMy/4j7ck/8AI4c4/HocUv8Aw8X+O5/5gfww64/5F/xIeR9PF5/yMjjmvgnOMcgY3Hp39ME4zg9OxBGetL0H44+73Gc4AP4n8Rz0H9Rf6l8K6f8ACFgOv/Lt/wCfnru/Q/y8/wCIz+Kf/Rc5/wD+FUf/AJX5fn3Z+Cn/AASd8GeC4PiL/wAFC/2YfiF4X0PUfEv7OX7a0/7Q/wAMrW/tSZdFm8UReJPBeneMvDCTSPcW5tPDGkaeizeZL5Nj4otUmMjXKNXrP/BQ34xax8Qr/wAe/sfeGPEnhv4d/D6y+C938Q/2tfjN4l0W08Rx+Bfhd4kGr6fo3gnwlo99usD438YW2ia7qk2t6hHIfC3h/T01LRYbnxDd6cbX5z/4KDWOu/sMfty+Bv27NA1DxP4a+CX7S/gnUP2Zf2ofEXgiONvEXgu51vRI9F0X4jaQk9pfWf8Ab2jaVpXh3xX4ZSbT7uK4134X3GnXY3eI44pviLx7qHxy8a/FX9sX4CePdK/4WV8XvE37PPwD+I2neNfD1hJL4M/aN8Ifs7+M7fWPC3jHwxNp6XWnjRP2gPhbrWhq0cEn9mx+OpvEfh5fIla208fwxx9wXWyLj/N5VuWOEjUp43Lo1PejTo1MRhYfWoxqr2NSOFoVXipxcnGGIlSjUhOKqJf7LeCPifhOOvB7hPE0Oatj6+Enlue+xfLUq5jhMFjKlTL6s6EliaU8djaH1KjJRhUqYKNWpSlSc6Epe9/8EqvGnjnwH/wU4+Elx8TNK1nw9r/7Yf7BHh4XNp4gsrnS73xDrvwusbPQ9I8aS2V4kVyl54x8KfAHUfGii6iiu5bfxhJd3MEU10QP6+HdUVpJGVERS7s5wqqoyzMSAFCjcSTjjr7fyn/tkfGzwBe/tGf8EiP+Ch3wu1e3u/h7qXxVT4YeK9XjMcM+jeHfGN9o1nqPh7WIUP8AxLNb8NaTqvxR07W9HudktpqNpdWkgTy5Gb+qm9txd2d1bNytxbTQn/trGyA9BggnPt/L8s4mcsQ8szCVH6vLE4OphquHad6FfLsZiMLKg+ZJ/uqKw8VdKVrXSvY/f+CowwtPOcrjiFi44TMaWLoYpWaxOGzbL8JjYYhcratWxEsXO8W05OSTdmfAvxz/AGU/2VPj9rtxrPxH/Z5+EfirUy7keKb3wNodp4yuZCSzXb+L9OsrLxOsjOPMiK6qrQ4WRdsxZj89j/gmd+x/Cpj0/wAH/EzRrds/6Dof7Sf7Suj6cuQeItPsPi5DZ26bflEcEMcYHAT1+98FcqeCpKkYOQVyCMHnP9R36Uc9iO3b/wCv0P8Ak14lPMswpQVOlj8ZTpx2hTxVeEF6RjNRWy6HvVspyrEVJVa+WZfWqzbcqlXBYapUk7rVznTcnv1bbt9/wVZ/8Exv2H4pUm1L4Jjxg6MHKfEX4ifFj4lW0jg5JmsfiB468SWMu4jLRvbGM9CmMCvR/iBqn7Ov7CnwM8d/ETQ/Anw6+FnhTwzos91beHPA/hfw94Obxh4lEEqeGvCel2GhafZNrHiXxNqzQaTpNsIrm7nu7wyN+7WeVPq78R7/AOc1+X3hPw5oH7QH7ev7Q/jD4xz2us+H/wBi65+E3g/4HeBtdmT/AIRfwn4n8efD3TviL4n+M15pFyRp934vv59Ws/DnhLXrtHOh2GgXb6cItSEV3bdeHr4nHOrLMMZjMRhMHSjia9KWIq1Z1Y+3oUIUaSqTlGEqtavTi6jT9lBzqqFSUI058GJw2Ey1UIZVl2X4XHY+s8Hh60MLQowoy+r4jE1K9Z0qanUjRoYarONFNe3qqnRc6UJyrU+p/ZT+DXx6sf2fvhxpvxm1rVr34h3unal4z8aN4t1m9v7ux8VfEHXdT8ca1oUSNJqUttbeGrvxA/h60tR5dtaWmmQWtqBDCij618GaJ458Lagmn3EFve6DOx86SO8jaKzYgkT2qTGK6XcwCywrblJc7hscF69hR1dVaN1dCAVZfmDKRlSCDggjBBHB7deFZggJdgFAJJIwABySSSQAB1JrjxGNqYmpWnOFGKrVJ1HCEOWEJTk5PkSfuqLb5UnZbWtoexhqEMLhqGGg5yjh6FKhCU5c1ScaNOMIynJ6ym0ryk95Nt7nlj638IfjBffET4W3sngv4g3fgLUNI0b4leBdYsdO1+PQr3XtEtPEeh23iHQdWtri3aPVNEv4L7T55LeW1uE+0RRStPa3cMHzfrX/AATW/Yb1m9n1GH9njwh4TvLhzJNP8NNR8VfCYlz1ZU+F/iHwhHGTxzGiYAHYDHBfHCC1+Ef7cP7Kfxv8J3MMFt+0HfeI/wBlX4x6dYyRvB4nitvB/ib4mfCLxFcWsTCKTWvBuveGNc0d9WlWS8Tw54kl04Sx2sMULfpDz3I/Ij9c1vOpicBDDVMFjMVRp4vDxrWp1qlGUasJzoV4T9lKCdq1Gc6b1fsZ0uZ8/Ml50KWEzOpjKOY4DBYitgMXPDp1sPSrwlRqU6WKw1SHtozcW6FenCqr2+sU63KlDlPgiP8A4Jn/ALIioIX8L/Fe4tMH/QLj9p39p6axdSQ2yS0f4wGGSPccmORWRv41YbgfQfAX7B/7HPwz1aHxD4T/AGcvhUniO2miubbxL4k8NW3jjxNBcRMjxzxeIvG58Q63FOrDJmjv1kOfmYhmz9a8/wB4H8Pp6Ee35+hxTufb8v8A6/1/yOcp5nmU4uE8wx04S0lGWLryjL1i6jT87rU2hk+UUpRnTyrLqc4tOM4YLDRnF6JOMlTUk+1me+aPeQX+mWdzbJHFEYEQQxgJHA8QEbwIigBUiZNiKFACBSowQK/Df/g4f8ZXuk/sD6b8ONJ3S6z8cvj38LPhzZ2EbkS3iWcms+PFG1cs8Q1Twdo8DgBsTXMHynIr9pPAcMqaTPM7MI7i8cwp1VVjRI2dRjgvIGVucfuwcDJr8Ef+Cu923xb/AG+f+CVX7MNs32i2i+KOufHTxhpYy63WjeFdU0C/06SaHkCEaP4H+IVvJIynMU8+xk8t8+hw1Ff21g60knDBe3zCbeiUcBh6uLTb6LmoxXq1qtzh4xnL/VvH0IO1XMFhcqpJWvKWZ4vD4FqK2vyV5y6aJ+aPjTwL8Vfip+yr8ff2p/jdayW2oeF/gn4p+GvwR+Mf7Pev+Fl07xno/wCyN8JfBuh+DvhH8Yfhjr8gS7i/4lkXifx8uhwyXXhPxdo8+pi4msvEFna3EP7m/tGeIfhr4K+BPxJ/an1vTdD1bVfgR8EPit47+Fviy5jSW50fXfFvw/vtAsD4euWIWC78XR6laaAkigvcRagIVKiVgfwm/wCCi/xq8P8AiPxl+2l8ZfAdrLrfw/8AAv7Mej/sU654v0mymvtE8UfFzx/8Rb2618W1/ZpJbXth8INH8Rrp+p6jJIkUXiXWbjw/bzTXE1vFJ538ZfE/7Sf7RPh/4Lf8E538Ta/p/j/9qzW/hf8AEL4kfC1re0+w/sn/ALJ/w00e2f4YeFvFvk24vW+JHjDRrOD41fFO21i9hmg1Wy8DeGrHS9PfWFtrr7LCcPYziTH5CsPRVPGYqvhMFVpQTjXq0fq2A5qDjCK5pQo1qi9rV5fcr0cHiKkqvs1L86zXi7K+Ccm4mxOYYn/hLy3AY/Nliqz5sJhqlLGZpKGN560pckKlfD0pujh3Je0wuIx+EowourKH9An/AAb1fHb4t/spf8EvPg14P0HQfBawePvEvxG+Lk//AAkGj61c6m6+LfFV3YaRcyTWfiHTYGgvPDGgaDe2hW1X/RbiIl3JzX7bf8PF/jvj/kBfDDGB/wAy/wCJOh/7nD6/XHHUZ/ODwL4M8P8Aw48E+EPh94Rsk0vwr4I8M6H4R8OadGAVsdD8OaZa6RpVqCMb/IsbOFGcgF2UswyxFdX83rk4OOwJ4z9e5HGO444H+hOC4F4boYPCUMRlGCxFelhqFKviJU5OVetTpQjWrSbau6k1Kb0VrvTZP/DXOvHLxIx+c5rjsv4tzvL8BjMyxuJwOApYiMaeCwdfE1KuFwkI8jtHD0JU6KV3pDVu7b+9P+HjPx1/6AXwy/8ABB4j/wDmvor4EyP72PxX/Ciuj/UvhX/oRYD/AMFP/wCSPG/4jV4q/wDRdZ9/4Uw/+VH9Iv8Awzh8A+f+LQfD3Ax/zLGm5wcj/nh64/XjNB/Zw+AYz/xaD4e/j4Y03vjr/o/B9uvQ17VuXB5B5z6dT+uOvH8+aCRjt2ByPfv9OTj/ACf5f/tjNv8AoaZj/wCFuJ/+Wn+o/wDqjwp/0THD3/hly3/5mPjT4/8A/BP39kD9pb4OfET4FfFX4H+B9S8CfErw5e+G9bTTtFsdM1nT1ukDWWueHtXt7Y3GjeJNA1CO11rw9rFtmfTNYsbO8jDmHY3+e7+05+x94r/4JMfF3wb+y1/wUTsfid4w/Y1tfEWtWv7C3/BST4NX2v8Ahb4lfBXTdflmvL74c+I9b8Mtd3VvpQt3uJ/FPwr1mDWYtOlTUvEvgXSvEnhyeOPSf9O3cBnp1HUEcdfTr1I9+a80+MHwa+E37QPw68T/AAj+N/w68HfFX4Y+MbI6f4m8D+O9A0/xH4d1e3LLLEbjTdSgnhW6tJ0ju9O1CBYr/TL6GC/0+5try3gnTyc1pSzmm4ZhiMTWmo2pYiVepLE4dpSSlRrTcpQspzThrTnCdSnUhOnUnCX0nD7w/C9XnyTAZfgaMp89fB4fBUKGCxbbptrE4ehClCo26NFqouWtTnSo1KVSnVpUpw/zDP8Agof+wP8AsVfDn9gLx18d/gX8SrL4heN4/GHhf4h+Gfi/rfx7XxnqPxBvvEPivTbPxLZadaWOuad4SvNbu9K1rUNbki0rwlB4gkvNMZp5DKs7j+pH9lP4uw/H39mj4CfGiOdLib4mfCTwH4v1MoVIg13V/DmnT+IrF9p2ibTdeOo6fcKuVW4tZFUkAE9/4t/4NKv+CPWpXvjnXvC3wr+Jnh/VPEHh/wAUWnhTw5L8afH1/wCA/BfiPWdHvbPQ9esNPn1B/E9+nhvVbm21iy0vWfFeq6ZPLaR2l/Z3unl7R/x//wCCBfxA1u9/Yw8Qfs/eNo5dO+Iv7Jfxu+JvwY8UaFduTqGmRHX7jxZbLcg4cQ22t694m8OWwkAaMeGpoAiQwxg/lHGGQ4jA5FQrVsfXzOphszqT9viI8tSjhsbSp03SXv1E4RrYeja3JHmqvlhFWR+7eH3E+FzLibFUMPlmHyenjcmpQeGw1Tnp4jGZdiKlVV3elSftJ4fF4jm5vaT5aMXKpN3Z+ouu232PWdTgwAq3ckqAjok489AOnASRQMdgaysj1Hr+H5/Xmuz8dWxh1eG5Awl3aR5bBIM0LNGwzxyI/J68464rjMD2568dfw9e/OelflZ+0SVm12fn5P5/8Ou4cd8dfy6n8Djr/wDqr82P2yf+CfVv+0brmreO/h/440jwB4v8XaB4b8JfFXwx4v8ACtx42+Evxr8M+DNaXxB4LtfiD4X07XvC+rw6/wCDtVDv4f8AF2h67a6pDpktz4fvob7SJxBB9pfFT42fDD4J6Pb698UvFun+DtGu76DTLTUdVju0tLvUrqOea3sLeaK3kilvJo7W5dLZHaUrE7bcDnx6T9tj4GuEOly/ELxF5wbyG8N/Cb4la/HOF27vJfSfC155wG9c+UH+8v8AeXPr5XQzqFSOLyrCY2rJNxVShhKmIpy5XGTjJeyqU58s4wmk03CpGFSPLOMJLw84q5FUpSwWc4zAUYSSm4YnHUsJUjdSipwk61KpDnhOpTk4ySqUpVKU+elOcH8afDzwx/wUK/Ys0C2+FPgf4e+Dv2w/g34fjNh8Ob+5+IQ+HPxR8I6Cjs1l4Z1xfEttquh+I9M0WF00zRtRj1fTL/8As61jW9tJJVRpMr4pfBz/AIKC/t3eGtR8BfFNfBf7GfwavoC2reF9E8QH4s/ELx7e2xE+n6X4pvvD114W0nS/AT3yWk+u6DpOuHWfEFpbzaO+taVbXstwn3DH+1ha6i23w78Bf2qvE7MoaL+xv2X/AI+XKyIchXEx+GwgVGIIV2mCk9DWrB8dPjDqBxo/7EX7ZF4pG5JLr4GeMNCSQcYKnxJpmigZyOGZTjkgKN1fRU8v4olVWKpcLzjj+f2n155diFV9rpL2yoVan1GNW/ve0WFTU/fjy1PePmquZ8HwovBVuMcPLLuT2Ty1ZzgHRVFLlVB4mjTWZyope77OWPcZQ/dS5qXuHzd+yb/wTi039nvV/Anif4i/Gnxd8dNZ+E9r4jtvg9oWo6VF4S+HHwrfxcLuLxBqnhjwkNY8TarfeIbrT9QvdDstb8UeLNem0Xw/dNo+iQadaR2ywfpj36g8f4d89OnFeH+G9Z/bJ8b3U1n4L/4J5ftQavcwwrPKL+1+FXhiGOJiFUyXPiv4o6FEjFjtCtgkghQdpx4uPj7+0l/w0Uv7O2pfsw23hPxB4Y/szUfjHqet/GL4deKIvhLo97IXTSvEi/CfVfiJotv8QtTs1afRvAF54msfEYhlttU1ew0zQ5v7THBmeRcUVPa47NcHVoxpQcqlbEywuEp04OTqNRhzUoJ1KlSUlCnDmq1pyajOrU97uyniHg+j7HLcmzHD4iVapGFKhg5YrMK1WahClFyqRjiKko0qVOFN1KtTkoUKUYuUKNJKP2x/3zyOvXPT6cduvpSEntg9sDrnIGO/v+lA6D8O3Tp/j+H4GtjQbP7drOn25GY/PE0oxkGO3HnMDx0YJs78sB7V8qfZLVpd/wDgHtGkWf2DTLG0AAaG3jDgd5mUvMfbMruc+/pX8rvxK8J/Br9u3/guZ8cfBnxiu9I1f4f/ALOvwE0r4d+FNCu/Hl94I1HW/GOn/wBh6hq1vol1o3iHw5rssvh3XPiB8QXv10y+Y282mW815F5Uq7f6kPGPizQ/AfhDxV458TXi6d4b8GeG9c8V+INQkICWOh+HdLutY1a8csVAS1sLO4mYsygCM5IHNfih/wAERf8Aghl+yX/wVk/Y/wDjD+3N+3Z4J8e3fxH/AGk/2tPjF41+GvinwR8QvEXg/UtP+HthqUNhrVtFDC934c1a21H4nv480+abUvD97d2kfhyyi0y9s0ku4H++4FymrmM84qU8RUwco4FYOliqcW50auLqKTqU7TpvnjSoTi+WpGUVVTUk7M/NPE3PKGU0uH6NXC0sfCeZvH18FVko08RQwFJxjSquVOtFQnWxVOa5qU1J0LcskpJfnN8W/B37Jf7NXxD+Hv7N37FWk/FP9vn9sm+8VPF+zv8As0TfEe/+MfwJ+Afju+u7q+h8b674W0WOy8M614o8Nz3F/rNpoviW+15tBMF34n8Y6r4csrVNUn/sC/4I0/8ABEDwt+xL8N/GHxk/bA/4R/8AaF/bz/aTu08WfHn4jeIoYPFFj4NF/ONUT4YeCdQu4ik1hYX8v2rxZr9lFaW3ifW7ayis4E8OeHPDMMH35+wX/wAEm/2Cf+Ca2lajb/sn/AjRPB3irXrIWHib4peIrzUfHHxX8SWQdJZNPv8Ax34lnv8AVrDRJZ4obmXw14cOh+GHuoYbz+xhcokw/Rvcvt0x0J/DoOOlfr+TZc8mSq08XisRj955hVqSjiW7ttUnBr2FNybk4U3ecm51Z1J+8fgHEmaU+JVPC4rLsBRylpRhk8aEKuC5YqMYvEQrRksXUjGEIRnWi404QjToU6NNKC8V/wCGcPgH1/4VB8Psf9ixpuf/AER9O3GfcGj/AIZw+Af/AESD4e/+Evpv4ceR9OPcHPIz7Xx3Azg9vrkeueuR165xmgFfUHHPA+pPr26+/XrivpP7Yzb/AKGmY/8Ahbif/lp8R/qjwp/0THD3/hly3/5mPFP+GbvgGef+FPfDw57/APCM6Zz7/wDHsaK9qynfH5f/AFqKP7Yzb/oaZj/4W4n/AOWh/qjwn/0THD3/AIZct/8AmbyX3H4tH/go98Zz/wAyl8Mzn/qG+J//AJqOnAz6c54Bpf8Ah498Zuf+KT+GfvnTfE+OPY+KMZ6ZA5/Kvz67cEYwc9O/0GR06flnFJj0K9T24zyT1HQdO/Y8V/T3+pHCn/QjwX3Vf/lnl/V2f5hf8Rt8V/8AouM66fawvlf/AJh/61t0t+gx/wCCj3xm7+E/hnyMn/iW+JyOP+5owenXrwKD/wAFHvjPjJ8JfDToD/yDfE59eCf+Eox1/XNfnz+IOBknGeMDH8PGP880YGOo6YJA+pzjaegznkE4GcUf6kcKf9CPB/dU8v8Ap55f1di/4jb4r/8ARcZzuvtYXbS9/wDZ99/6sfoN/wAPHvjNz/xSfwz9/wDiW+Jz/wC7R7cevHqK/km/aj1P9oz/AIJt/tv/AB+/4KJ/Bv4bWnxP/ZS/at8UL44/ay+Dng6K6tNU+H3i+5vL3VNb+IWhLcSajc21rLr+teJfFNprcz3WjW0/ifxF4b8UQ6RYv4d8SW/7p9uSM8HOPqeTt7+h5P8AOOaKG4ilgnihmt545IZoZo1limhkVkkiljdWjkjkQsrRupV1JVgQSD4+f+GPCOe5VisrnllHCLEwSVehz89OcbShJxdS04qSjJq8JJpSp1KdWMakfq+CvpKeK3B3EuW5/LiPF51TwVVurl2YSoqjiKM0oVYwqU8OpUKzg5KnVtUgm3CtRxGHnVoTwf2a/wBqT9n39tr4U2fxL+CPjOw8YeHLnyYNZ0qQpY+LfBOtvFvk0LxboEkj3/h/WrciTyxKJbHU7dRf6PfappNxb3k/Xa74eu9Ek3EtPYucQ3Srjbn7sU6gHy5AOh+5JjK4O5F/C39of/gl/wDEL4RfEm7/AGsv+CXfjg/AL4327vfeJ/g3bXUNh8JPilaif7Zd6NBpV2p0DRm1OUMG8Ma1ay+Brm6a1uLA+C7u1OrSfTX7Fv8AwV/+Hvxr8Tf8M2fte+EW/ZS/a30q4h0DVvAXjuK50TwR461V1SKKTwTrOuMP7PvdXcpNpnhbXbuSfUI7uyXwnr3jBJWuIv4U8Q/CHiLgbE1KvsKmPyiUpOhjaEZVFGCu7VOWKu4xTcvdhUglKVSlGny1J/7F+CX0luA/GbLqMMLjaOVcSUqVP69k2MqU6FeFR8sXKEJTadOpUaVKpCpVw9SUo06Vd1/aYel94eMfBXhP4h+GtV8H+N/D+l+KPDGt25tdU0XWbOK7sbuLcHjZo5FJjnt5UjuLS7gaK6s7mKG6tJoLiKORdv4f/tIftsfs3+GJvhp4P8X+Hf2rfg5Fb2y+Hvht+0D4r1Tw/wDGTwBp1ncRNbab4L+P1povi278V+H7ZFe0s9L+KfhPXNbs7OOGytviBDbQpbV6B4i8Jy6bvvdODz2GS0kQy81oO5PVpYB/f5dB/rMgGQ+WeIfDtp4htVimeW2uoGMtjqFsxju7KbAG+GRSrFHAAliLBZFA5V1SRPz3Jc/zTIq6q5fipUYucZ1KUoqth5yja0p0Z+65KyXPBwqW0U+W8X+1Z/wtkvElD6vm+DjWahKFPEQk6WKpRle6p14WlyO7bpz5qd3dw5kmu0uf29v2xrxQuk/sYfCTSZHQgTeJ/wBrPUJoonzhWeHw/wDs737zIpw5RJoiy5USIxBrm7r9rz/goNeIzJ4J/Yv8Eo3Ky3/iT40/EFoFI4EsMWk/DGKVoz1KXcauCcFMAn4h+KngT9r25k1CL4f694NfSBeWlnpZm1XV9R8TX9pOkf2jU7+bX5NJ8N+EI7OXzlljt9K+JdxLAkUtvp9xNPJb2uv4D+DPx6LRyfErxD8O2hTS7W1Wy0BNf1PWH1KERJcarqPimey8OaXqJu1WR5LHTvAPhyCO4lMlu8NvGlpX3NXxD4olRVV5zlVG/wDy7oYSnKunaLS5Zxna6eju0mnGTUk4nwGH8J+B4Yh0Hk+cVlHX22IxVaOHkk2m1ONSi2rx1jyqbTjKEZQlzH03L+0J+3z4tttW8H3v7ZngL4caR4stZdM13Tv2a/gmfDfi3+zJ1CXw0Lx947+I3xSvfCerG0823g8U6Xo1lrWlGT7Xpl3Y3kcEsT/h58N/Bnws8N2/hbwRosWjaVFPc390xluL3U9Z1e/kNxqmv+INZvpbnVfEHiLV7pnu9W13WLy91XUbp3nu7qV2Bqx4R8Gaf4UtWETG61CdR9qvpECu4BDCGFPmMMAb5igdmdsNIzFYwnf6fp15qlytrZRGSQ4LuciKFMjMkr4IVfzZj8qKzECviM44hzfO5R/tHMK2LhB80IyhSoU+a1ub2VCnTg2lpGU1KaTaTSbR99kfCuQcOqaybLaODlVSjOopVq1aUb35Pa4ipWqRi5ayhCUYSkk3FtJlSKOWeRIYY3mmlYLHEilndjjhVAJ9z6YOTgV614X8MPpO69vHDX00Jj8pCpjt42KsyFgCHlOxQ7LlFGVXcDuPnPxN+KnwV/Za+HurfFH42/EDw38PvCekxFdQ8T+JrwWwuLgo8kWk6Fp8azalrOq3gjYWGh6JaahrOpSKUtrS4ddq/wA//jz9ub9t7/gqnresfCf/AIJ9+Gtf/Zw/ZaW+uND8d/tbeNre40jxXr9hGzQ6laeCDaSedo808TMIdG8J3V74wJfT5de8T+AbO9u7UdfDHB+fcXY6ngclwNbESnNRlWUJexpq65m52s3BNSkk/cj79R06d5ryuOvEXg/w1yfEZ3xbnGFy7D4en7RUqtaEa1Rtfu4wpt35qjTjSVnKtO9PDwrVnGnL2z/gr7+3novi7wl4o/4JvfspLe/GT9qj49GD4c+K9M8Cypf6Z8L/AApfXtvJ4xsvFerwb9Pg1rV9Bgv9E1bSZLiKHwp4dvtY8ReLr3RYrLT7TV/3w/Yg+Pnjv9in9kb9nr9lTwX4V+Gk+ifBD4X+G/BUmonTfEZk17X7a1N74w8TXHl+IbWM3Xinxde654iu/LtbdDdapLsgiXCL+Xv7FP8AwT9+Av7DvhS50/4b6ZP4h+IPiK3iHjz4veKkhvfHHi+4Mi3E8BugjR6F4e+2gXFt4d0kpa744bjVZ9X1VJNUl+5MZ544Hpx+Hy8gHr6Z5I5z/enhx4NZPwlk8sPnFOlmuYYucK+IdS7pUKiik403CS5pNKKlZypwUVCm6jdStW/xr8evpb8VeI3FFLEcFYvGcMZDlkKuGwlSioRxmZU5TjKNWvCrTqOjRg/aOjGSjiKjqzrYiNFexweE/QX/AIePfGbv4S+GnIyf+Jb4mPrkf8jR25z+PvS/8PHfjOOvhL4Zjjp/ZviboAf+po6Dmvz5xjuM9OnB7novI4/DHJ5xR+RHuPx4G0kDHJ+vWv0T/UjhT/oR4L7qn/yzy/Puz8K/4jb4rf8ARcZ1/wCBYby/6htu783t0/QX/h498ZxjPhL4aDj/AKBvifj8P+Ep7Y544z70D/go98Z8D/ik/hoOP+gb4n/ED/iqO2OeK/PrHrgkkdsc9Sfu/wD1uec90x2yO2OB+J+7yPQ9Pej/AFI4U/6EeC+6p/8ALPL8+7D/AIjb4r/9FxnT2+1hvLX/AHfbe/r6H6C/8PIPjN/0KXw1/wDBb4m/+amivz3oo/1I4U/6EeC+6p/8s8vz7sy/4jf4sf8ARc5z/wCBYb/5n8v6uz+j/wD4Zs+APP8AxaLwD25/4R6w6dz9w9Pf9c0f8M1/ALoPhF4A7Y/4p6x9v9j68fTrzXt/GCcKefXj69P89RnPJkeg5I78nPrgfpyDzziv5i/tjN/+hrmX/hdiv/lvkvuP9QP9TuEf+iV4c/8ADHln/wAyniH/AAzX8Au3wi8Adv8AmXrH/wCI/H8hzR/wzZ8Af+iReAO3/MvWP4/wdR/nrx7fnOeF6jOT1HY9Ppj/ABqteXlnp9nd6hf3NtZWNjbzXl9e3dxFbWlnaW0bTXN1dXMzJDb28EUbyzzyukccSNJI6qpIP7Yzf/oa5l/4XYr/AOW+S+4P9TuEf+iV4c/8MeWf/Mp4x/wzZ8Av+iReAP8AwnrH/wCI/H9AT1pf+Ga/gF/0SHwD25Ph6x9Mn+Dr6fhyeo/lC/a6/wCDib9qr9q39onxJ+w1/wAED/2f4v2jPH+gzXeneNP2qtc0i11z4d6Ettctp994h8CWes3ukeAtN8H6dfqLWz+LHxZ1k+EfEd75ln4b8Ha5Z3eh67q3yT+zT/wS1/4L3f8ABRT4Yw/tOfET/guJ8W/g3rfifXdb0o+B/APjv41aPoenDRrvyJ2XQ/hR4o+EfgHR3e4eRVs/D3hl7NI0XZeyIFVT+2M3/wChrmXT/mOxPy/5e+X4B/qfwj/0S3Dn/hkyz/5lP7c/+Ga/gFz/AMWi8AdeD/wj1jz3/ufh696/kK/4L2fsz/An9vL/AIKKf8E6/wDgkp8CvhZ4B8LfFTxTrF/8eP2m/jl4N8IaAvxD+EP7OWl2esQf8I7a+Jnsrk6emu6VZeNPEy+HdZkXTbrxXb/CiSS0nTX4BP8AL37LX/BG7/gsl+038HG+Mlv/AMF9v2nfAmkx+JfEHhyfT9Y+L37UuotbtoF1b2sl/NfwfHKzgjt7h7hWG+JRAqs0sm0Fh8m/8EpfF37YP7HH/BUb4pfCjxf4g1X9pz9qv/goJ+zsfAnwR/ap+JOu+IfE3jW1vfA/i9IPEd1P4r8b6p4j1q50/QfB3w41+bVLLUdXuhp0fgbwBqMsE2i2EWl3WdbMMyxFN0sRjsbWoyaUqdbFV6lKTi01zRnOUHyvla0dvdfY6MHw7w5l+IhisvyHJcFi6cZezxGDyvA4bEwjUi4S5KtGhCrBTi5QlaSUk3F3V0eF2/8AwUC/a3/4I3ftn/HH9gD483uvftm/Ab4AeKL/AEzRfFgKv8VNH+E0UNnqHh3xfoGvLeata3VnYeGtTsF8SeBPFeqahZ+FddstQ8J2Xi3wzb6NdGv6JP2bf2k/2Zf21/CP/Cwv2cvilpHiK1jigfxJ4X8oWXjHwfeXIyLPxV4PvpbXWNBlMqyww3TQTaLqjwyTaHqOoWgFzJxv/BZr/gkV8Nf+FT/ssWv7MPiDWLj/AIKveFvGU2r/AA78VaT5FxrXx0g8RXLXPj3TPihLqVw0WkeCYdRS5HgnUtce702xaXxJpWuw3mieIPH3iHTv5VdR+FX7NerfGPUvh7+1FofxR/4JW/t1eErltN8a6Xpd1L8KPh/4j1d3KnxNod7qttN4f0DTdd8s6lbTW2teGNC1aOWC90bxL4wjuRqUn5XxVwnl+Ik8bRw9bCSnrVxOAofWIc9/elisvjKE3GSu/rOFlzRk5OvRkkpv954H43zTD045dicZhscqclChgs2xX1Ko6TjFRhgc3qRqUo1IStH6njYqE4ciwteMuamv7Z2+H0o+7q8bY5G6yK/Q8XL4479ue1RN8P70fd1K2OOPmgkXr9C3Pt9K/mJ0f9gH9sV9LtNQ+Gv/AAVa/bEbwvexCbTrvSvGHxD8T6Jd27BAkun6jofxitdJuEIUBJ7VWQoQFO3Ob83/AATw/bQurS5vPGX/AAVO/bTutPtY3nurq48YfEfQdJs4UC75bi41f4vXlhaxqozJJI0aA/O55JP528kytOz4iwqd7cv1DMOe/ZxdJRv5c716n65/bWeNXXCOKa5ebn/trJ/ZWsndT+s8zi07p8i0Vz+l+XwTPZxyXeo6vp1np1rHJcX15K5hjtbSBGlnnkknWOCKOKJWeSWaWOKJA0jttU5/EX9tb/gvH+zz+zq958Iv2SdNsP2mPjbPdDRLbUtHuprj4SaFr9zKLO2W/wDEumOt58Q9TF3LAsOi+B5v7Kuy0lnL4w0m8gNmfww+P/wk/Z6s9c074Vf8NZ/tc/t9/HrxTqlvoHhL4HfCP4gj4it4j8R3MnlWGjanrdpo/wARrDzJ7wGCbSPDtxr3iyOXMSaLCS1zF++3/BEj/giN8Nf2f/2pYdf/AOCrPwTt/AHxh+Jfw/huf2UfgpqN9bX/AMMfC7+I7G4sNUvNd8SwavrE+ofHLSrO6itPCFld6/dzeD9de9v7i5bx6/gsaP8AZ8PcC5fXksXjK2LxmHjaVOnLCTwFCq007v2tR4mtTs7pwjRpyt/EmrxX5zxX4k5pg4SwOXUMvwGLneNWrSx9LN8RQTW0XQorBUKqaakp1cTVi3/CptRmfKPxf/YJ+O37O/7bX/BMT9p//gvR4n8K/tHfszftXfEHWfBfjfwh4d8W69a/CD9mjxPr2kWt18MvDXiS+8OXGheFB4djvtW0bxb4ttfDKzeGvEHh7wX47i1LXfGltp82rap/ogeHf2Sv2YfCWhaT4a8JfAf4U+HPDWiWNvp+h6DoHg3RNI0TStNt4wtrZaXpunW1vY2VlEmFgt7WCKGNPuKBjP8AFd/wV1/Zn/aJ/aB/a6/YK/4N/bT4v/a/g38Q/iTrf7UPh74iyafH4k8bfDr4N+GfCnxB0axTxHpCano8V9D4P0zS/iyND028u9L0/UtYTTU06+srK5s9K0Xm/wBmP/g2j+LX7QOrftB6Vcf8Fc/2n/CqfA745eMfg5ayW3hnxLqv/CQ23hS6a2j12aOT49aeuly3irubTo2vY7fIUXko5r9bwVSrlkPZ5bVqYGnGEaajgqksLFQTvGHLRcFyp6qOyeu+p+AZtgsFxBNVM+weFzqr7SdbnzbDUcxmqskozqKWLhWanJJRc01JxSV7Kx/cj/wzX8Af+iReAcdv+KesT2zz8g9+n19aT/hmv4Bdf+FReAf/AAnrHGcD0T1OD+HHc/w4fs2f8G0fxb+Pnin9o3w7c/8ABXT9qDwwnwI+NniX4SWt3B4Z8Taq/iWDw/dXVuuuTwyfHuwGly3Itg7WEcl+kRfaLtwu4+ceBPh//wAF+P8Agk54h/aG+I37HX7Rfi7/AIKF/ssfsqfFfxD4H+LfwQ+KkniPxhrNx4V8P3WoS3vifSPhdrviLxN4l8O6SLLT5LzUdR+CHj5fEWnXQfUtb8Naj4btdTlPd/bGcf8AQ0zLRJ/79idFpb/l76fgeMuEOEHb/jFuHNb2/wCEPLOm/wDzCn963/DNfwB6/wDCovAOPfw9Y/lnZ357HoaP+Ga/gF/0SHwD07+HrHrn/c6dfpg9elfDf/BJn/grX+zx/wAFbPgBL8WPhEk/gv4i+C5tO0T42/A7X9Rtb/xX8LvEuoQTS2LreQQ2ieJfBPiJbS/uPB3jO1sLG31mGyv7G+07RvEGk61omnfqn14wvQd/6YPGfc0f2xm//Q1zL/wuxX/y3yX3D/1O4R/6Jbhz/wAMeWf/ADKeID9mv4A9T8IvAP8A4T1j/wDEEf8A6xSj9mv4A/8ARIfAOD/1L1jnrx/AQe9e3Z6fd59+vbAHQnp39s96AfoMZ4B6euQOB3+h4zzR/bGb/wDQ1zL/AMLsV/8ALfJfcH+p3CP/AES3Dn/hjyz/AOZTxD/hmn4BHn/hUXgLn/qXrP8AouPyor3Dn0A/H/61FH9sZv8A9DXMv/C7Ff8Ay3yX3B/qdwj/ANErw5/4Y8s/+ZfJfcfjB/w8h+L54/4Qv4anB/58/E/tx/yM4yORx7euKP8Ah5D8X/8AoS/hryeR9j8T8H0IPifgnH59+lfnnwDyF/Mdc85HOMZ6Z/PAIMKDjI9xnpnH4noR7Z5GM1/Tv+o3Cf8A0I8J0/5+vXT/AKe6/hq9d2f5jf8AEb/Ff/ot84/8tfL/AKhvLX/h7/oYf+CkPxfwc+DPhrgnP/Hn4o5xjp/xU3PTnrX883/Bff8A4KrftWfGD4dfCH/gmZ8AtM0DRvix+3P4t0vwVrw8CnW9P8S6h8PtT17TvDFl4J/tC71u9/svR/ib4p1CLStevo40Sfwp4d8U6NqDjStVvkf9DuPYexJHrj/Aj65zkY/GH9n/AETT/id/wdR/s+ab4si/tDTvhT+z94y13wxZTfvbeLUtI/Z9+LHiXSZ2R+I307xF4pudbtmjBZdQs7WUHI3L8Tx/w/w9kvDtbE4HKMLQxVbE4bC0q8VUcqPPKVWpOPNUlHmlToTpptO3tHJWkkz9r8AeP/EHjTxEwuXZ5xdmuNyvBZXmOaYnA1Xh1SxfsYUsLQpVHChCfJDEYyjiGoyXM6PLK8JST/pP/wCCCH7P3wd/Yp/4JfeF4fCnhvR7XXdU8ReMNT+JfjOy060tvE/xP8Y2WuXWjWd/reoeWLy8S3iRNL8PWNzPLbeH9GVba3VFW7mnj/4Jp/F3xn4S/ZU8O6FodzY21jB4u8aXA8ywiuZnkudXaWTzJJy4wGOFEaRkADJLZNdF+w/fyQ/8E9vhBpiMVjvviF8ULqUA/fGn+I9TjRW9V3X+8g8b1Q4yox4f/wAE+f8Ak23RP+xo8W9P+wmf8+3tX5PleDoydKdWEairRxTUZpSjy0alCEPdd1dOVS710a7H9lZxjq8I16dGpKk6E8HHmhJxm3Xp16k7yTTs1Gnpto31Z9Bf8EsPi3omg/srzeCfFOl3D6Pqfjn4gCbU7FlnkSPVp7eK4juLFwjGJVZiJYJZJQDhbaRgCf5e/wBvW28Z/Af9nL9jn/gpl8KbWXUfHf8AwT5/bevfE2oWwkmgt9W+FfxB8R+GrDxJo2qSwgyLpWueItE8L+FryLAEeneNNcKsjTSCT9//APgn1/yb1B/2O/i//wBK7f8AnXgPwz+Auh/tR/8ABPT4+/s9eIBAlh8XYPjD4KhvLhPMj0nWdUtIl8Oa+q4b9/4e8RR6VrtqdrFbnTomCnbitY4ClOjSjBNSxODqykruUfbQVBwmk2+VuTXNZpNW23Mp5lWhWryqSThhMfhoJxilN0Kn1hVIScbKajFPkum027vofp/+zPB4S+Enwj17/gpx+1f4y0rxJ8Sfjb4L0TxzoWqWUlvqOn+Efhx4w0q01fwF8O/hnaC5lhn1XxFo93pkQFlcsi28kOnm8+yW+va1qv42/wDBVHxp+y7Y/s5+I/29v+CrPwF8N/Hbxn8W9Ouvhd/wT4/YY1G+1TRvFeoDWHjm0/Uode8NGz8deHiIb6DxB4s8V6PJa3Gn2d9btFbzeJPEvgLw1ZfD3/BMT9vb4ZeE/wDgmp4d+N3/AAUy+JthL8L/APglp4m1X9lj4b/sz6fcW9346+M/xp8Nx3Ot/DvR5PC93dhPEdzpHhC90rwbpElw0Xh+30zwVql/rk2k+GtC8XTa167/AME+/hN8XP8AgqB+01rn/BYX/goFZxv4h0jXLjwn+xp+zHcC6ufBv7OngvSVtdV0bxDJpuoxRLdeKY4tWhvtIvLuyt72bxFPqfj3U7W21ibwtZeFfEw2Hniq0aMGlKV3OT+GnTVuZpaXaWlurajpds9/GYungsPPEVFeMbKEVvUnL4dVdJPdt7K73SR+Uf7CPi7Sf2M/22rj4GR/DTxZ8CP2eP22vBnhPxp8Jfhz428R3fi6H4SftEeGPCWiN8Tfg3Z+NdUnlvdfjsNc1PWPDtjqetGLxTqFh/wqu01u2k1fUJbif6A/bt0+8/br/bi/Ze/4Jq+EbDW/GHgrQLy6/aN/aw8P+Ftbh8OXV14G8J6W2o+HvAt14rk3QeE77xNpz3ekW2qX9vd2ula18Rfh5r32S5ltIImxP+Cvfif4B+HP+CfE+oeNdd8QeGv2ldA/bU8T+NP2StZ8K6HeXurWvjDw4vgJ/Gy32vL9lsNA8Njw7dW9/dm41OO+l8SaZ4T1DTdL1h9Hlhi9T/4IvfEL4Vfs06T8eP2jv2ufFut+Mv29f2svGMGu3/w3+GXw/wDGnxo+Kth8JYNP07VfCVjpfgb4Q+GPF1/4c0vxHr2oao0u9NO8MxWHhvwvoxubdvC8iQfOrgvB1eMcLnVStSVBYRVq9JtKX1yMVSpVHpypujstJqpGjWSk5St9ZLxAx9DgPG8O06FWWIeO9hh66TlFYCc1XrUopPnaWI3fwOlOvQlypQb/AEj/AOCN3hX/AIJZftt/s9/EX4F/s4fsq+Dv+Ccn/BQr9nLxPfTeNvB0Guar4v8Ajv4N8ReFdZl07QPHWnfGDxb5fxM+Jnw4m1FbbS/FemXV9EPBfieRoRYac914K8S65+y/h690n9trwX4r/Yw/a/01Ph5+118IV/tHRfE2npDa3mq3OnwqNE+MXw0u0NpHe2t/CLafxRoli9vZ39nOL2zSwhkjj8Pfy1/8MVfFf9pTR/E//BRn9hb/AIWF+yx/wUM+AP7RPxAvPhzJ42tdM8HXvxk8HabpXhy9Pw7+IWgjWNU02xbVrfU9U0XTF8YLb2+p2l5qngn4haWnhq/0/UvDv2n4t/4LM/AD9tX/AIJ5/tBftGePrmX9jn/gq3/wTk+H+t614h+HQZ9H8UD4hafqdr4L0lPBVtq88Wp+J/hd40+JGt6PoOpaFfy3+ufC/wARa0um6+uqaNqNrqnjz6Gth6mH9nKSfs6qc6VRK90nZp7WktFKL6NNXVr/ACtDE0sS6kYte1ouMa1PZpuKkmlfWLT9yV904t3Tt5f/AMEpfE/xi/aK/wCCtv8AwUX/AG1/if4x0/4hax+zT4f0r/gnt8KviLawteaZqEfgrVf7M8beJ/Cd3cRRBr6+h8BLrM+tS26XN9B8V9TvRDFPqLyJ+237A/xh1nwPJ+19Y2FhbahqWs/tS/EDU5tU1O4nnCySyrHI0lvGY5bieSRHmeaS7AZny8bnJP47/wDBuD8Nrv4dfsDSz6yJ38UfEb4iah8VPFF1dl3vL3UfG2g6HfadcXskuZnuz4bi0KO6aYmQ3KTM+GYiv0o/Y8/5Dv7Uvb/jJHx5/wClbf5/zmvdwmDw86OAU6cZe29vUqXVnOUU3FSta8YbKO2+l2z5vG43FU8Rmbp1pQ9jHD0qVmmoRlKmpuKldKU7tylZO9rSVlb1P9iP4w+MPDPxD/bKv7B9NlOv/tPeNda1S2urIPbz3l1qF+8xQxyxTwRkuQqRzgKMdSM19G/8EztbGv8AxL/b81WWOGC51T9pvVdWntI5N4txfy69IAu4BzEZPNjjZgN3lsMkg18Pfsj/API6ftZc4/4yH8Xfj/pl6P5kV9Lf8Etblk+OX7a9nuwlx8V9auWXsWtNauIlJ7fKt5IP+Bcda58bhaKwVKpTpxhUbq80oqzlGEpOzs0nZRSTabVkkdWAxlZ4+vSqVJzpqOGUIyd1CVSFOLavqryldrZu7eup/IX+018QvGn/AARX/wCC5PiT9uD4BaNpnhf9m342ftE/E/4JfG/wPa2sln8PVj8Qa7DqHinT73Q9Hn02OytrdL3TPiv4PtbWa12eJ/B2uQWrxaOLmwf+wdf+CkXxeZVZfBvwzZXUMCtp4nKspAIIx4oIIKnIIyMdDzz/ADvf8F7vgzYfEz/gn1/wVY8czW0cupfs+/8ABQT4KfFPS7gKPOhXXfE/i34L6jEkn3hBNZfFd5p492x3tLd2DPFHj6D/AGNvH1z8Uv2S/wBm34hX8zT6l4s+CHwx1jV5nZmeTWp/CGkJrTM5OSTqsd3liTzgknqft/DTK8mzepmuDzTLsPjKlGnhcVhqlTn5owm5U68PdnG8b+xlFa2bn0kfz39JfibjLg/DcKZvwvxDj8ow+Mq5hl2Y0MM6LpVa0IYfFYGrarSqNVPZrGwqNP3oxpq3uu/7Qf8ADyD4wD/mSvhr05/0LxPwPf8A4qf6/rSH/gpD8X/+hL+GnT/nz8UdPp/wk/OOT3xz68/nnx7cDjBJ9ufYcYznIzgc4pTjrxwT69+uecAYPPXk9M8H9Y/1H4T/AOhJhP8Aytv/AODfJa+um9/5M/4jf4r/APRb5v8A+Wnl/wBQ3l/V2foT/wAPI/jB28F/DX/wB8Uf/NRRX54ZHqPzFFH+pHCn/Qjwf/lX/wCWGP8AxHHxZ/6LjOPuwn/zL5fn3Z/Rt/wzB+z5j/kkngvqP+Ycv8y/+ecUH9mD9nz/AKJJ4L7f8w0fX++T9fbn1x7wT146kfxdT7ehHH+RQccjC9j1A/MY7fyPviv5k/tvOf8Aob5n/wCF+K/+Wn+nf+pPBn/RI8Mf+GDKv/mQ8H/4Zg/Z7Gc/CTwWOR/zDR7Z/j6fX9BX8eFx4P8ADHgX/g8i0Hw94Q0PT/D2iQfsla9cQ6ZpsIgtI57r9lDxZLcSrHk4eWQl3Ofmav7hzjJwB1GefoM+oI59OeRkmv4nPGf/ACud6Nx/zaJq/HHH/GJniv8ADj+dY4jMcwxdNUsVj8biaSkpqniMVXrU1NJpSUKlSUVJJtKVrpNq+rO7L+HOHsprvE5XkOTZbiXTlSeIy/K8Dg67pTcZTpurh6FOo6cpQhKUHLlk4RbTcVb9e/2JkP8AwwV8E37L42+MS+2X8UFh+OE/n6CvHv8Agnz/AMm26Jxj/ip/FnA5/wCYma9s/Yijz/wT7+DkuPufEH4qR/8Af3xFqTenfyc9fWvE/wDgnz/ybbon/Y0eLP8A05mvfyt3p4NdqePXX/oIoP8AX+tTxM4Xv5h51stf/lrXXl2/4Iz/AIJ9f8m9Qf8AY7+L/wD0rt/51L+wJ/yQq7/7KN42/wDSqz/z+FRf8E+v+TeoP+x38X/+ldvUv7Av/JCrv/so3jXv/wBPVn9evOfy4rpw/wDzL93/ALLW/wDdfb/g/kcmL2zX/sNwvbtivP8Aq3fQ/Ef9hL/gjB8JfHn7Y3xh/bl+M0+meN/Adp8WdQ8QfCD4IzwzXOgW/wAUY47C/wDFXxA8eWdzEum6qtlr0rXnhXQo0ubSe9mbUdcZ002wsZv06/Z8/Zn+Dn7R/wAGtVs/izoHiDVG8JfG74l3fhnUvC3xF+JHwy17RLnVrPwrFfzWHiL4Y+LvB+uxtcJYWe5X1B41MCFEBL7vfv2Cv+SO+Jf+yteO/wD0LSvfr/LnpXqf/BNb4OeK/G/wc8f3+mz6NaWdp8dfiHpss19fyENdWtr4dacQiwtr5ZkUSoVmVvKkDZikZPmrlpRwuGhg51VCFOrTrzquaTU5zdBq973tpyrWyWi3OuvLGYuePp0XVqVKNTCQpRp3Tp04qsny8rSje/vS3d3d20P5bP2vvDfwF/ZG/wCCfHx68dfELQf2j/2k/D/jj40ePPgt4K+CPxE+Knjr4hfAPw7431nw/plx4Y+K/jfV/E8useKfBWv+DDBcXPhvxP4f8aaL4r8Qautn4atr6O31DUdU079C/wDghH+yh+034e/4Jx+LPht4V/aA1DSPin4o+IX9u+ENas/CXhX4s6B8HdCj0X4fahe6R4V0vWJbOLxcuq6TdXFlcSX2uXvhnSby8hvvC2leRaXlz4g9X+Nnw38E+JP+CSP/AAUn074jXGk3GgxR/tY3wt7pd7ad4j+G3w+8O+MfBviGNZQFa4s/GthoV7okYCz3GqadHDCrSEIPev8Ag1Ti13xJ+wB8N/EeuSzmbQLzxHp4eZnLy2MOneHvD3hqJ2bkwf8ACO2EEsTHIC28AGVORx+0jQlRrxcXFYGs4pc0HzqfJKLlGUZ2lJpQacVF3cFe7ffKm8RHEYaUZKTzLDRm7xn+7lTjUjJQmpU1yRUpTTi3JJKV1aK8U/Yb/ZC+MWoN8QvGXjz9tf8Aat1KHwR+014m/tPwf4Rtfgz8Mvh14r1rw8fC2pX0PjDTvDnwhfxJdWmtEx2Ot6DbeM7K0k0zFrHHCZZJpfiz47f8EZfgn/wUmsfir47/ALTHwt+O/g/9prxDpknxD0+1kntPGXw6g1PRLjXPBnjDTYGT7ZcQ6dLqE3hHxFEDqWiao0NvctfaLJJYxf0z/wDBNHx74O8I+AP2qY/EerWtvLP+2X8ZZ4rAI93eXMD6R4JjR0tII5ZGhmkjkiWWRUt2ZHVpAEfHyr+yFqOmX8P7S0ljpwhe5/as+K2ow3hkeNjpl4mimx082KZtofsrJNKZEZmY3HlcJCu7TCNYp0KFbCTdK1X95KUpRqNxTvFzaacHpenKTV1e2pnj08EsVicPjYe2f1e9KEYRnSXOlaag2pKom9KkIppPV6I4z9hXRtL8OWX7QPh/Q7GDTNF0L48eK9G0jTbVBFa2Gl6ZBaWWn2VvGOI4LS0gighReFjjVR0rZ/Y8/wCQ7+1L/wBnI+Pf/Stv8/5zUP7F/wDx9/tLcf8ANxfjn/0OHn9am/Y8/wCQ7+1L/wBnI+PP/Stq9Ghp/Z9lb/ee3Z/h+PY8rFavMm76xwV79daPmtdXu1vvuH7I3/I6ftZf9nD+Lf8A0sva+gf+CX8mz9of9sIdBJ8U/FSHgHIGqmX0PeMdO469q+fv2Rv+R0/ay/7OH8W/+ll7Xu3/AATHfb+0b+1iOnmfF/xanUc/v75/f+5254znFcuKV8DTX93GP7oVn+h24N2zGv5yy5ffPDr9fubPyw/4KzxpJ/wTC/4ODVkRXC/HP4MyANyBJF+0r4DkjYZ5yjqrqeoZQa/U/wD4Ibfs8/BXxH/wSN/YC13xD8OPDGta1q37PPhW+1DVNQsBLeXc89xqLl5pA67zGpWJDgYiRASSCT+Wf/BWP/lGH/wcHdP+S4fBzv8A9XJeBenc/wCAzX7Y/wDBB7/lDx/wTx6f8m2+Du+D/rL/APLjrjnHPYA/P0cXisJNzwmJxGFnKnCMp4etUoylHli+WUqcotxuk7NtXSdro+gx+U5Vm9GlRzbLMvzSjSmqtKlmOCw2NpU6vK4e0hTxNOrCFTklKPPFKXLJxvZtH3p/wy/+z5x/xaPwZ0yf+JYP/jnP+eRxk/4Zf/Z8GD/wqPwXj200fp8/PHP0B617vxxwp7DkflkjPcfr7ZXjjgfUH8OvX0yev44z0/21nP8A0Nsz/wDC/Ff/AC08n/Ungz/okeGP/DBlX/zIeD/8Mu/s9nr8IvBee/8AxKx/8cor3kZwMAY7cn/Cij+2s5/6G+Z/+HDF/wDy7y/q7D/Ungz/AKJHhj/wwZV/8yCdv+Bfybj8qCBzx/Ev/stFFeYfTh6/7y/+y1/E1414/wCDzrR8cf8AGImr9OP+bTPFdFFAH7J/sQAf8O7fhIcc/wDCzPiMM98f254g4rwf/gnx/wAm26J/2NHi3/05miivqsp+HDf4cf8A+ncKfG518WN/6+Zb/wCmMSN/4J9/8m9w/wDY8eMP/Su3qT9gT/khV5/2Ubxr/wClVpRRXZhv+Zf/ANgtb/3XOPGf8zf/ALDsN/7th+wV/wAkc8S/9la8d/8AoWl19Vf8Ebry7Pwy+Kdgbq5NjH8YfG9zHZmeU2iXElv4eSSdLct5KzSIiK8qoHdURWYhQAUV5WY/7lg/+vVX/wBKonr5X/yMsw/6+0f/AEmsfz4f8FEru6s/+CNf/BS+W0ubi1lb4yX9q0lvNJBI1rffGD4L2d7bM8TKxgvLO4ntbqEkx3FtPNBMrxSOjfYv/BC9msf+CeHwnt7JjZ28+j+CzNBak28Mxk+EHwwmkMsUWxJC8skkrl1JaSR3bLMxJRWuG/3jA/8AYvf/AKkMxxn+6Zj/ANjOX/qPRPpr9hn/AJFj44f9nGfET/03+F6d+xL/AMeH7Qf/AGcZ4/8A/RWlUUV6FD/mB/w4n9DzcXvmnrg/ziN/Yu/4+/2l/wDs4zx1/wCjIam/Y7/5Dv7U3/ZyXj3/ANLHoop0t8D64n8pBit8z/wYL86R8MeOfFvivwhp37QN94S8TeIfC97dfteeLbS5vPDutalol1cWv2DxJL9mnuNNubaWW382KOXyZHaPzI0fbuRSPnzwz8Xviz4K1LVdY8G/FD4ieEtX128m1HXNV8M+NfEug6lrOoXG/wC0X2q32lanaXWo3k/mP511eSzTy733u245KK8LFfwv/B//ALcfSYT436YT/wBwnLeLPE3iPx74Y+IXgjx14g1vxp4L+Ld9Z6n8VvCPizVb/wAR+GPibqWnalBrOn6h8QtA1i4vNK8aX1jq9ra6rZ3fiS01K4tdStoL6CRLqGOVe28B/HD41fCzwd4d+Hfwx+L/AMUfhx8P/CGmw6N4T8DeA/iB4s8IeDvC+j2xY2+k+HfDPh/VtO0XRdNgLsYbHTbG2tYizbIl3HJRXjPf5R/JHtnW/wDDV/7Uv/Rynx+/8PH8RP8A5o6P+Gr/ANqX/o5T4/f+Hj+In/zR0UUgD/hq/wDal/6OU+P3/h4/iJ/80dFFFAH/2Q==",
+"company_abbr": "WN",
+"company_name": "Web Notes",
+"company_tagline": "Open Source ERP",
+"country": "India",
+"currency": "INR",
+"customer_1": "RIGPL",
+"customer_2": "Mahesh Engg",
+"customer_contact_1": "Aditya Duggal",
+"customer_contact_2": "Mahesh Malani",
+"first_name": "Rushabh",
+"fy_start": "1st Apr",
+"item_1": "Enterprise Plan",
+"item_2": "Small Business",
+"item_3": "Solo",
+"item_4": "Manual",
+"item_buy_1": "Server Hosting",
+"item_buy_2": "Adwords",
+"item_buy_group_1": "Services",
+"item_buy_group_2": "Services",
+"item_buy_group_3": "Raw Material",
+"item_buy_group_4": "Raw Material",
+"item_buy_group_5": "Raw Material",
+"item_buy_uom_1": "Unit",
+"item_buy_uom_2": "Unit",
+"item_buy_uom_3": "Unit",
+"item_buy_uom_4": "Unit",
+"item_buy_uom_5": "Unit",
+"item_group_1": "Services",
+"item_group_2": "Services",
+"item_group_3": "Services",
+"item_group_4": "Products",
+"item_group_5": "Products",
+"item_img_1": "logo-2013-color-small.png,data:image/png;base64,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",
+"item_uom_1": "Unit",
+"item_uom_2": "Unit",
+"item_uom_3": "Unit",
+"item_uom_4": "Unit",
+"item_uom_5": "Unit",
+"last_name": "Mehta",
+"supplier_1": "Google",
+"supplier_2": "Hetzner",
+"supplier_3": "Digital Ocean",
+"tax_1": "Service Tax",
+"tax_rate_1": "12.5",
+"timezone": "Asia/Calcutta",
+"password": "password",
+"email": "test@erpnext.com",
+}
\ No newline at end of file
diff --git a/setup/page/setup_wizard/test_setup_wizard.py b/setup/page/setup_wizard/test_setup_wizard.py
new file mode 100644
index 0000000..7f09a9b
--- /dev/null
+++ b/setup/page/setup_wizard/test_setup_wizard.py
@@ -0,0 +1,14 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd.
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import webnotes
+
+from setup.page.setup_wizard.test_setup_data import args
+from setup.page.setup_wizard.setup_wizard import setup_account
+
+if __name__=="__main__":
+ webnotes.connect()
+ webnotes.local.form_dict = webnotes._dict(args)
+ setup_account()
+
\ No newline at end of file
diff --git a/startup/__init__.py b/startup/__init__.py
index 227846c..802daac 100644
--- a/startup/__init__.py
+++ b/startup/__init__.py
@@ -27,8 +27,6 @@
"Territory": "territory"
}
-application_home_page = "desktop"
-
# add startup propertes
mail_footer = """<div style="padding: 7px; text-align: right; color: #888"><small>Sent via
<a style="color: #888" href="https://erpnext.com">ERPNext</a></div>"""
diff --git a/startup/boot.py b/startup/boot.py
index 886b805..d27c0b7 100644
--- a/startup/boot.py
+++ b/startup/boot.py
@@ -32,9 +32,9 @@
# load subscription info
- import conf
+ from webnotes import conf
for key in ['max_users', 'expires_on', 'max_space', 'status', 'commercial_support']:
- if hasattr(conf, key): bootinfo[key] = getattr(conf, key)
+ if key in conf: bootinfo[key] = conf.get(key)
bootinfo['docs'] += webnotes.conn.sql("""select name, default_currency, cost_center
from `tabCompany`""", as_dict=1, update={"doctype":":Company"})
@@ -53,4 +53,4 @@
ret = webnotes.conn.sql("""select name, content from `tabLetter Head`
where ifnull(disabled,0)=0""")
return dict(ret)
-
\ No newline at end of file
+
diff --git a/startup/event_handlers.py b/startup/event_handlers.py
index 57345f3..a012611 100644
--- a/startup/event_handlers.py
+++ b/startup/event_handlers.py
@@ -35,13 +35,13 @@
set_cart_count()
def on_logout(login_manager):
- webnotes.add_cookies["cart_count"] = ""
+ webnotes._response.set_cookie("cart_count", "")
def check_if_expired():
"""check if account is expired. If expired, do not allow login"""
- import conf
+ from webnotes import conf
# check if expires_on is specified
- if not hasattr(conf, 'expires_on'): return
+ if not 'expires_on' in conf: return
# check if expired
from datetime import datetime, date
@@ -65,9 +65,6 @@
raise webnotes.AuthenticationError
def on_build():
- from website.doctype.website_settings.make_web_include_files import make
- make()
-
from home.page.latest_updates import latest_updates
latest_updates.make()
@@ -75,4 +72,4 @@
"""add comment to feed"""
home.make_feed('Comment', doc.comment_doctype, doc.comment_docname, doc.comment_by,
'<i>"' + doc.comment + '"</i>', '#6B24B3')
-
\ No newline at end of file
+
diff --git a/startup/install.py b/startup/install.py
index d795594..fd72213 100644
--- a/startup/install.py
+++ b/startup/install.py
@@ -13,13 +13,13 @@
import_country_and_currency()
# home page
- webnotes.conn.set_value('Control Panel', None, 'home_page', 'desktop')
+ webnotes.conn.set_value('Control Panel', None, 'home_page', 'setup-wizard')
# features
feature_setup()
# all roles to Administrator
- from setup.doctype.setup_control.setup_control import add_all_roles_to
+ from setup.page.setup_wizard.setup_wizard import add_all_roles_to
add_all_roles_to("Administrator")
webnotes.conn.commit()
@@ -71,6 +71,7 @@
{'doctype': 'Item Group', 'item_group_name': 'Raw Material', 'is_group': 'No', 'parent_item_group': 'All Item Groups'},
{'doctype': 'Item Group', 'item_group_name': 'Services', 'is_group': 'No', 'parent_item_group': 'All Item Groups'},
{'doctype': 'Item Group', 'item_group_name': 'Sub Assemblies', 'is_group': 'No', 'parent_item_group': 'All Item Groups'},
+ {'doctype': 'Item Group', 'item_group_name': 'Consumable', 'is_group': 'No', 'parent_item_group': 'All Item Groups'},
# deduction type
{'doctype': 'Deduction Type', 'name': 'Income Tax', 'description': 'Income Tax', 'deduction_name': 'Income Tax'},
diff --git a/stock/doctype/bin/bin.py b/stock/doctype/bin/bin.py
index 788642f..014c47a 100644
--- a/stock/doctype/bin/bin.py
+++ b/stock/doctype/bin/bin.py
@@ -5,7 +5,6 @@
import webnotes
from webnotes.utils import add_days, cint,flt, nowdate, get_url_to_form, formatdate
from webnotes import msgprint, _
-sql = webnotes.conn.sql
import webnotes.defaults
@@ -16,7 +15,7 @@
self.doclist = doclist
def validate(self):
- if not self.doc.stock_uom:
+ if self.doc.fields.get("__islocal") or not self.doc.stock_uom:
self.doc.stock_uom = webnotes.conn.get_value('Item', self.doc.item_code, 'stock_uom')
self.validate_mandatory()
@@ -61,7 +60,7 @@
self.doc.save()
def get_first_sle(self):
- sle = sql("""
+ sle = webnotes.conn.sql("""
select * from `tabStock Ledger Entry`
where item_code = %s
and warehouse = %s
diff --git a/stock/doctype/delivery_note/delivery_note.js b/stock/doctype/delivery_note/delivery_note.js
index 1542c7a..c3fc0ac 100644
--- a/stock/doctype/delivery_note/delivery_note.js
+++ b/stock/doctype/delivery_note/delivery_note.js
@@ -10,6 +10,7 @@
wn.require('app/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.js');
wn.require('app/utilities/doctype/sms_control/sms_control.js');
wn.require('app/selling/doctype/sales_common/sales_common.js');
+wn.require('app/accounts/doctype/sales_invoice/pos.js');
wn.provide("erpnext.stock");
erpnext.stock.DeliveryNoteController = erpnext.selling.SellingController.extend({
@@ -21,7 +22,7 @@
var from_sales_invoice = false;
from_sales_invoice = cur_frm.get_doclist({parentfield: "delivery_note_details"})
.some(function(item) {
- return item.prevdoc_doctype==="Sales Invoice" ? true : false;
+ return item.against_sales_invoice ? true : false;
});
if(!from_sales_invoice)
@@ -180,12 +181,12 @@
if(cl.length){
prevdoc_list = new Array();
for(var i=0;i<cl.length;i++){
- if(cl[i].prevdoc_doctype == 'Sales Order' && cl[i].prevdoc_docname && prevdoc_list.indexOf(cl[i].prevdoc_docname) == -1) {
- prevdoc_list.push(cl[i].prevdoc_docname);
+ if(cl[i].against_sales_order && prevdoc_list.indexOf(cl[i].against_sales_order) == -1) {
+ prevdoc_list.push(cl[i].against_sales_order);
if(prevdoc_list.length ==1)
- out += make_row(cl[i].prevdoc_doctype, cl[i].prevdoc_docname, cl[i].prevdoc_date,0);
+ out += make_row("Sales Order", cl[i].against_sales_order, null, 0);
else
- out += make_row('', cl[i].prevdoc_docname, cl[i].prevdoc_date,0);
+ out += make_row('', cl[i].against_sales_order, null,0);
}
}
}
diff --git a/stock/doctype/delivery_note/delivery_note.py b/stock/doctype/delivery_note/delivery_note.py
index e5e412e..deaf024 100644
--- a/stock/doctype/delivery_note/delivery_note.py
+++ b/stock/doctype/delivery_note/delivery_note.py
@@ -28,7 +28,7 @@
'target_parent_field': 'per_delivered',
'target_ref_field': 'qty',
'source_field': 'qty',
- 'percent_join_field': 'prevdoc_docname',
+ 'percent_join_field': 'against_sales_order',
'status_field': 'delivery_status',
'keyword': 'Delivered'
}]
@@ -66,14 +66,11 @@
"""Re-calculates Basic Rate & amount based on Price List Selected"""
get_obj('Sales Common').get_adj_percent(self)
- def get_rate(self,arg):
- return get_obj('Sales Common').get_rate(arg)
-
def so_required(self):
"""check in manage account if sales order required or not"""
if webnotes.conn.get_value("Selling Settings", None, 'so_required') == 'Yes':
for d in getlist(self.doclist,'delivery_note_details'):
- if not d.prevdoc_docname:
+ if not d.against_sales_order:
msgprint("Sales Order No. required against item %s"%d.item_code)
raise Exception
@@ -89,7 +86,6 @@
sales_com_obj = get_obj(dt = 'Sales Common')
sales_com_obj.check_stop_sales_order(self)
sales_com_obj.check_active_sales_items(self)
- sales_com_obj.get_prevdoc_date(self)
self.validate_for_items()
self.validate_warehouse()
self.validate_uom_is_integer("stock_uom", "qty")
@@ -106,26 +102,27 @@
if not self.doc.installation_status: self.doc.installation_status = 'Not Installed'
def validate_with_previous_doc(self):
- prev_doctype = [d.prevdoc_doctype for d in self.doclist.get({
- "parentfield": "delivery_note_details"}) if cstr(d.prevdoc_doctype) != ""]
-
- if prev_doctype:
- super(DocType, self).validate_with_previous_doc(self.tname, {
- prev_doctype[0]: {
- "ref_dn_field": "prevdoc_docname",
- "compare_fields": [["customer", "="], ["company", "="], ["project_name", "="],
- ["currency", "="]],
- },
- })
- if cint(webnotes.defaults.get_global_default('maintain_same_sales_rate')):
+ items = self.doclist.get({"parentfield": "delivery_note_details"})
+
+ for fn in (("Sales Order", "against_sales_order"), ("Sales Invoice", "against_sales_invoice")):
+ if items.get_distinct_values(fn[1]):
super(DocType, self).validate_with_previous_doc(self.tname, {
- prev_doctype[0] + " Item": {
- "ref_dn_field": "prevdoc_detail_docname",
- "compare_fields": [["export_rate", "="]],
- "is_child_table": True
- }
+ fn[0]: {
+ "ref_dn_field": fn[1],
+ "compare_fields": [["customer", "="], ["company", "="], ["project_name", "="],
+ ["currency", "="]],
+ },
})
-
+
+ if cint(webnotes.defaults.get_global_default('maintain_same_sales_rate')):
+ super(DocType, self).validate_with_previous_doc(self.tname, {
+ fn[0] + " Item": {
+ "ref_dn_field": "prevdoc_detail_docname",
+ "compare_fields": [["export_rate", "="]],
+ "is_child_table": True
+ }
+ })
+
def validate_proj_cust(self):
"""check for does customer belong to same project as entered.."""
if self.doc.project_name and self.doc.customer:
@@ -137,8 +134,8 @@
def validate_for_items(self):
check_list, chk_dupl_itm = [], []
for d in getlist(self.doclist,'delivery_note_details'):
- e = [d.item_code, d.description, d.warehouse, d.prevdoc_docname or '', d.batch_no or '']
- f = [d.item_code, d.description, d.prevdoc_docname or '']
+ e = [d.item_code, d.description, d.warehouse, d.against_sales_order or d.against_sales_invoice, d.batch_no or '']
+ f = [d.item_code, d.description, d.against_sales_order or d.against_sales_invoice]
if webnotes.conn.get_value("Item", d.item_code, "is_stock_item") == 'Yes':
if e in check_list:
@@ -185,7 +182,6 @@
# create stock ledger entry
self.update_stock_ledger()
- self.update_serial_nos()
self.credit_limit()
@@ -203,42 +199,12 @@
self.update_prevdoc_status()
self.update_stock_ledger()
- self.update_serial_nos(cancel=True)
webnotes.conn.set(self.doc, 'status', 'Cancelled')
self.cancel_packing_slips()
self.make_cancel_gl_entries()
- def update_serial_nos(self, cancel=False):
- from stock.doctype.stock_ledger_entry.stock_ledger_entry import update_serial_nos_after_submit, get_serial_nos
- update_serial_nos_after_submit(self, "Delivery Note", "delivery_note_details")
- update_serial_nos_after_submit(self, "Delivery Note", "packing_details")
-
- for table_fieldname in ("delivery_note_details", "packing_details"):
- for d in self.doclist.get({"parentfield": table_fieldname}):
- for serial_no in get_serial_nos(d.serial_no):
- sr = webnotes.bean("Serial No", serial_no)
- if cancel:
- sr.doc.status = "Available"
- for fieldname in ("warranty_expiry_date", "delivery_document_type",
- "delivery_document_no", "delivery_date", "delivery_time", "customer",
- "customer_name"):
- sr.doc.fields[fieldname] = None
- else:
- sr.doc.delivery_document_type = "Delivery Note"
- sr.doc.delivery_document_no = self.doc.name
- sr.doc.delivery_date = self.doc.posting_date
- sr.doc.delivery_time = self.doc.posting_time
- sr.doc.customer = self.doc.customer
- sr.doc.customer_name = self.doc.customer_name
- if sr.doc.warranty_period:
- sr.doc.warranty_expiry_date = add_days(cstr(self.doc.posting_date),
- cint(sr.doc.warranty_period))
- sr.doc.status = 'Delivered'
-
- sr.save()
-
def validate_packed_qty(self):
"""
Validate that if packed qty exists, it should be equal to qty
@@ -323,7 +289,7 @@
"""check credit limit of items in DN Detail which are not fetched from sales order"""
amount, total = 0, 0
for d in getlist(self.doclist, 'delivery_note_details'):
- if not d.prevdoc_docname:
+ if not (d.against_sales_order or d.against_sales_invoice):
amount += d.amount
if amount != 0:
total = (amount/self.doc.net_total)*self.doc.grand_total
@@ -375,7 +341,7 @@
"name": "dn_detail",
"parent": "delivery_note",
"prevdoc_detail_docname": "so_detail",
- "prevdoc_docname": "sales_order",
+ "against_sales_order": "sales_order",
"serial_no": "serial_no"
},
"postprocess": update_item
@@ -399,7 +365,6 @@
def make_installation_note(source_name, target_doclist=None):
def update_item(obj, target, source_parent):
target.qty = flt(obj.qty) - flt(obj.installed_qty)
- target.prevdoc_date = source_parent.posting_date
target.serial_no = obj.serial_no
doclist = get_mapped_doclist("Delivery Note", source_name, {
diff --git a/stock/doctype/delivery_note/delivery_note.txt b/stock/doctype/delivery_note/delivery_note.txt
index f1493bb..6834365 100644
--- a/stock/doctype/delivery_note/delivery_note.txt
+++ b/stock/doctype/delivery_note/delivery_note.txt
@@ -2,7 +2,7 @@
{
"creation": "2013-05-24 19:29:09",
"docstatus": 0,
- "modified": "2013-08-09 14:46:32",
+ "modified": "2013-10-11 13:19:40",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -230,7 +230,7 @@
"depends_on": "eval:doc.po_no",
"doctype": "DocField",
"fieldname": "po_date",
- "fieldtype": "Data",
+ "fieldtype": "Date",
"hidden": 1,
"label": "Customer's Purchase Order Date",
"no_copy": 0,
@@ -263,7 +263,6 @@
"reqd": 1
},
{
- "default": "1.00",
"description": "Rate at which customer's currency is converted to company's base currency",
"doctype": "DocField",
"fieldname": "conversion_rate",
diff --git a/stock/doctype/delivery_note/test_delivery_note.py b/stock/doctype/delivery_note/test_delivery_note.py
index 7c52550..2c4308b 100644
--- a/stock/doctype/delivery_note/test_delivery_note.py
+++ b/stock/doctype/delivery_note/test_delivery_note.py
@@ -9,20 +9,22 @@
from webnotes.utils import cint
from stock.doctype.purchase_receipt.test_purchase_receipt import get_gl_entries, set_perpetual_inventory, test_records as pr_test_records
+def _insert_purchase_receipt(item_code=None):
+ if not item_code:
+ item_code = pr_test_records[0][1]["item_code"]
+
+ pr = webnotes.bean(copy=pr_test_records[0])
+ pr.doclist[1].item_code = item_code
+ pr.insert()
+ pr.submit()
+
class TestDeliveryNote(unittest.TestCase):
- def _insert_purchase_receipt(self, item_code=None):
- pr = webnotes.bean(copy=pr_test_records[0])
- if item_code:
- pr.doclist[1].item_code = item_code
- pr.insert()
- pr.submit()
-
def test_over_billing_against_dn(self):
self.clear_stock_account_balance()
- self._insert_purchase_receipt()
+ _insert_purchase_receipt()
from stock.doctype.delivery_note.delivery_note import make_sales_invoice
- self._insert_purchase_receipt()
+ _insert_purchase_receipt()
dn = webnotes.bean(copy=test_records[0]).insert()
self.assertRaises(webnotes.ValidationError, make_sales_invoice,
@@ -44,7 +46,7 @@
set_perpetual_inventory(0)
self.assertEqual(cint(webnotes.defaults.get_global_default("auto_accounting_for_stock")), 0)
- self._insert_purchase_receipt()
+ _insert_purchase_receipt()
dn = webnotes.bean(copy=test_records[0])
dn.insert()
@@ -69,7 +71,7 @@
self.assertEqual(cint(webnotes.defaults.get_global_default("auto_accounting_for_stock")), 1)
webnotes.conn.set_value("Item", "_Test Item", "valuation_method", "FIFO")
- self._insert_purchase_receipt()
+ _insert_purchase_receipt()
dn = webnotes.bean(copy=test_records[0])
dn.doclist[1].expense_account = "Cost of Goods Sold - _TC"
@@ -123,8 +125,8 @@
self.clear_stock_account_balance()
set_perpetual_inventory()
- self._insert_purchase_receipt()
- self._insert_purchase_receipt("_Test Item Home Desktop 100")
+ _insert_purchase_receipt()
+ _insert_purchase_receipt("_Test Item Home Desktop 100")
dn = webnotes.bean(copy=test_records[0])
dn.doclist[1].item_code = "_Test Sales BOM Item"
@@ -160,7 +162,7 @@
def test_serialized(self):
from stock.doctype.stock_entry.test_stock_entry import make_serialized_item
- from stock.doctype.stock_ledger_entry.stock_ledger_entry import get_serial_nos
+ from stock.doctype.serial_no.serial_no import get_serial_nos
se = make_serialized_item()
serial_nos = get_serial_nos(se.doclist[1].serial_no)
@@ -180,7 +182,7 @@
return dn
def test_serialized_cancel(self):
- from stock.doctype.stock_ledger_entry.stock_ledger_entry import get_serial_nos
+ from stock.doctype.serial_no.serial_no import get_serial_nos
dn = self.test_serialized()
dn.cancel()
@@ -192,7 +194,7 @@
"delivery_document_no"))
def test_serialize_status(self):
- from stock.doctype.stock_ledger_entry.stock_ledger_entry import SerialNoStatusError, get_serial_nos
+ from stock.doctype.serial_no.serial_no import SerialNoStatusError, get_serial_nos
from stock.doctype.stock_entry.test_stock_entry import make_serialized_item
se = make_serialized_item()
diff --git a/stock/doctype/delivery_note_item/delivery_note_item.txt b/stock/doctype/delivery_note_item/delivery_note_item.txt
index b74c33a..8d1792d 100644
--- a/stock/doctype/delivery_note_item/delivery_note_item.txt
+++ b/stock/doctype/delivery_note_item/delivery_note_item.txt
@@ -2,7 +2,7 @@
{
"creation": "2013-04-22 13:15:44",
"docstatus": 0,
- "modified": "2013-08-29 16:58:16",
+ "modified": "2013-10-10 17:03:11",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -350,48 +350,17 @@
},
{
"doctype": "DocField",
- "fieldname": "prevdoc_doctype",
- "fieldtype": "Data",
- "hidden": 1,
- "in_filter": 1,
- "label": "Document Type",
- "no_copy": 1,
- "oldfieldname": "prevdoc_doctype",
- "oldfieldtype": "Data",
- "print_hide": 1,
- "print_width": "150px",
- "read_only": 1,
- "search_index": 1,
- "width": "150px"
+ "fieldname": "against_sales_order",
+ "fieldtype": "Link",
+ "label": "Against Sales Order",
+ "options": "Sales Order"
},
{
"doctype": "DocField",
- "fieldname": "prevdoc_docname",
- "fieldtype": "Data",
- "hidden": 0,
- "in_filter": 1,
- "label": "Against Document No",
- "no_copy": 1,
- "oldfieldname": "prevdoc_docname",
- "oldfieldtype": "Data",
- "print_hide": 1,
- "print_width": "150px",
- "read_only": 1,
- "search_index": 1,
- "width": "150px"
- },
- {
- "doctype": "DocField",
- "fieldname": "prevdoc_date",
- "fieldtype": "Date",
- "hidden": 1,
- "in_filter": 1,
- "label": "Against Document Date",
- "no_copy": 1,
- "oldfieldname": "prevdoc_date",
- "oldfieldtype": "Date",
- "print_hide": 1,
- "read_only": 1
+ "fieldname": "against_sales_invoice",
+ "fieldtype": "Link",
+ "label": "Against Sales Invoice",
+ "options": "Sales Invoice"
},
{
"doctype": "DocField",
@@ -421,6 +390,17 @@
"read_only": 1
},
{
+ "doctype": "DocField",
+ "fieldname": "buying_amount",
+ "fieldtype": "Currency",
+ "hidden": 1,
+ "label": "Buying Amount",
+ "no_copy": 1,
+ "options": "Company:company:default_currency",
+ "print_hide": 1,
+ "read_only": 1
+ },
+ {
"allow_on_submit": 1,
"doctype": "DocField",
"fieldname": "page_break",
diff --git a/stock/doctype/item/item.js b/stock/doctype/item/item.js
index 9d47095..9e5430b 100644
--- a/stock/doctype/item/item.js
+++ b/stock/doctype/item/item.js
@@ -11,14 +11,16 @@
&& doc.__islocal)
cur_frm.toggle_display("item_code", sys_defaults.item_naming_by!="Naming Series"
&& doc.__islocal)
+
+ if(!doc.__islocal && doc.show_in_website) {
+ cur_frm.add_custom_button("View In Website", function() {
+ window.open(doc.page_name);
+ }, "icon-globe");
+ }
-
- if ((!doc.__islocal) && (doc.is_stock_item == 'Yes')) {
- var callback = function(r, rt) {
- var enabled = (r.message == 'exists') ? false : true;
- cur_frm.toggle_enable(['has_serial_no', 'is_stock_item', 'valuation_method'], enabled);
- }
- return $c_obj(make_doclist(doc.doctype, doc.name),'check_if_sle_exists','',callback);
+ if (!doc.__islocal && doc.is_stock_item == 'Yes') {
+ cur_frm.toggle_enable(['has_serial_no', 'is_stock_item', 'valuation_method'],
+ doc.__sle_exists=="exists" ? false : true);
}
}
diff --git a/stock/doctype/item/item.py b/stock/doctype/item/item.py
index aedb71c..595895f 100644
--- a/stock/doctype/item/item.py
+++ b/stock/doctype/item/item.py
@@ -14,6 +14,9 @@
class WarehouseNotSet(Exception): pass
class DocType(DocListController):
+ def onload(self):
+ self.doc.fields["__sle_exists"] = self.check_if_sle_exists()
+
def autoname(self):
if webnotes.conn.get_default("item_naming_by")=="Naming Series":
from webnotes.model.doc import make_autoname
diff --git a/stock/doctype/item/test_item.py b/stock/doctype/item/test_item.py
index 12bb4e0..c8930ab 100644
--- a/stock/doctype/item/test_item.py
+++ b/stock/doctype/item/test_item.py
@@ -31,7 +31,7 @@
"is_sales_item": "Yes",
"is_service_item": "No",
"inspection_required": "No",
- "is_pro_applicable": "No",
+ "is_pro_applicable": "Yes",
"is_sub_contracted_item": "No",
"stock_uom": "_Test UOM",
"default_income_account": "Sales - _TC",
@@ -47,6 +47,26 @@
],
[{
"doctype": "Item",
+ "item_code": "_Test Item 2",
+ "item_name": "_Test Item 2",
+ "description": "_Test Item 2",
+ "item_group": "_Test Item Group",
+ "is_stock_item": "Yes",
+ "is_asset_item": "No",
+ "has_batch_no": "No",
+ "has_serial_no": "No",
+ "is_purchase_item": "Yes",
+ "is_sales_item": "Yes",
+ "is_service_item": "No",
+ "inspection_required": "No",
+ "is_pro_applicable": "Yes",
+ "is_sub_contracted_item": "No",
+ "stock_uom": "_Test UOM",
+ "default_income_account": "Sales - _TC",
+ "default_warehouse": "_Test Warehouse - _TC",
+ }],
+ [{
+ "doctype": "Item",
"item_code": "_Test Item Home Desktop 100",
"item_name": "_Test Item Home Desktop 100",
"description": "_Test Item Home Desktop 100",
@@ -63,6 +83,7 @@
"inspection_required": "No",
"is_pro_applicable": "No",
"is_sub_contracted_item": "No",
+ "is_manufactured_item": "No",
"stock_uom": "_Test UOM"
},
{
@@ -88,6 +109,7 @@
"inspection_required": "No",
"is_pro_applicable": "No",
"is_sub_contracted_item": "No",
+ "is_manufactured_item": "No",
"stock_uom": "_Test UOM"
}],
[{
@@ -182,8 +204,49 @@
"is_sales_item": "Yes",
"is_service_item": "No",
"inspection_required": "No",
- "is_pro_applicable": "No",
+ "is_pro_applicable": "Yes",
"is_sub_contracted_item": "No",
"stock_uom": "_Test UOM"
}],
+ [{
+ "doctype": "Item",
+ "item_code": "_Test Item Home Desktop Manufactured",
+ "item_name": "_Test Item Home Desktop Manufactured",
+ "description": "_Test Item Home Desktop Manufactured",
+ "item_group": "_Test Item Group Desktops",
+ "default_warehouse": "_Test Warehouse - _TC",
+ "default_income_account": "Sales - _TC",
+ "is_stock_item": "Yes",
+ "is_asset_item": "No",
+ "has_batch_no": "No",
+ "has_serial_no": "No",
+ "is_purchase_item": "Yes",
+ "is_sales_item": "Yes",
+ "is_service_item": "No",
+ "inspection_required": "No",
+ "is_pro_applicable": "Yes",
+ "is_sub_contracted_item": "No",
+ "is_manufactured_item": "Yes",
+ "stock_uom": "_Test UOM"
+ }],
+ [{
+ "doctype": "Item",
+ "item_code": "_Test FG Item 2",
+ "item_name": "_Test FG Item 2",
+ "description": "_Test FG Item 2",
+ "item_group": "_Test Item Group Desktops",
+ "is_stock_item": "Yes",
+ "default_warehouse": "_Test Warehouse - _TC",
+ "default_income_account": "Sales - _TC",
+ "is_asset_item": "No",
+ "has_batch_no": "No",
+ "has_serial_no": "No",
+ "is_purchase_item": "Yes",
+ "is_sales_item": "Yes",
+ "is_service_item": "No",
+ "inspection_required": "No",
+ "is_pro_applicable": "Yes",
+ "is_sub_contracted_item": "Yes",
+ "stock_uom": "_Test UOM"
+ }],
]
\ No newline at end of file
diff --git a/stock/doctype/landed_cost_wizard/landed_cost_wizard.py b/stock/doctype/landed_cost_wizard/landed_cost_wizard.py
index 0924957..89a3b81 100644
--- a/stock/doctype/landed_cost_wizard/landed_cost_wizard.py
+++ b/stock/doctype/landed_cost_wizard/landed_cost_wizard.py
@@ -85,7 +85,6 @@
pr_bean = webnotes.bean("Purchase Receipt", pr)
pr_bean.run_method("update_ordered_qty", is_cancelled="Yes")
- pr_bean.run_method("update_serial_nos", cancel=True)
webnotes.conn.sql("""delete from `tabStock Ledger Entry`
where voucher_type='Purchase Receipt' and voucher_no=%s""", pr)
@@ -97,5 +96,4 @@
pr_bean = webnotes.bean("Purchase Receipt", pr)
pr_bean.run_method("update_ordered_qty")
pr_bean.run_method("update_stock")
- pr_bean.run_method("update_serial_nos")
pr_bean.run_method("make_gl_entries")
\ No newline at end of file
diff --git a/stock/doctype/material_request/material_request.js b/stock/doctype/material_request/material_request.js
index 61be636..9fc852f 100644
--- a/stock/doctype/material_request/material_request.js
+++ b/stock/doctype/material_request/material_request.js
@@ -21,7 +21,11 @@
+ wn._("Fulfilled"), cint(doc.per_ordered));
}
- if(doc.docstatus == 1 && doc.status != 'Stopped'){
+ if(doc.docstatus==0) {
+ cur_frm.add_custom_button(wn._("Get Items from BOM"), cur_frm.cscript.get_items_from_bom, "icon-sitemap");
+ }
+
+ if(doc.docstatus == 1 && doc.status != 'Stopped') {
if(doc.material_request_type === "Purchase")
cur_frm.add_custom_button(wn._("Make Supplier Quotation"),
this.make_supplier_quotation);
@@ -63,6 +67,53 @@
},
+ schedule_date: function(doc, cdt, cdn) {
+ var val = locals[cdt][cdn].schedule_date;
+ if(val) {
+ $.each(wn.model.get("Material Request Item", { parent: cur_frm.doc.name }), function(i, d) {
+ if(!d.schedule_date) {
+ d.schedule_date = val;
+ }
+ });
+ refresh_field("indent_details");
+ }
+ },
+
+ get_items_from_bom: function() {
+ var d = new wn.ui.Dialog({
+ title: wn._("Get Items from BOM"),
+ fields: [
+ {"fieldname":"bom", "fieldtype":"Link", "label":wn._("BOM"),
+ options:"BOM"},
+ {"fieldname":"fetch_exploded", "fieldtype":"Check",
+ "label":wn._("Fetch exploded BOM (including sub-assemblies)"), "default":1},
+ {fieldname:"fetch", "label":wn._("Get Items from BOM"), "fieldtype":"Button"}
+ ]
+ });
+ d.get_input("fetch").on("click", function() {
+ var values = d.get_values();
+ if(!values) return;
+
+ wn.call({
+ method:"manufacturing.doctype.bom.bom.get_bom_items",
+ args: values,
+ callback: function(r) {
+ $.each(r.message, function(i, item) {
+ var d = wn.model.add_child(cur_frm.doc, "Material Request Item", "indent_details");
+ d.item_code = item.item_code;
+ d.description = item.description;
+ d.warehouse = item.default_warehouse;
+ d.uom = item.stock_uom;
+ d.qty = item.qty;
+ });
+ d.hide();
+ refresh_field("indent_details");
+ }
+ });
+ });
+ d.show();
+ },
+
tc_name: function() {
this.get_terms();
},
diff --git a/stock/doctype/material_request/material_request.py b/stock/doctype/material_request/material_request.py
index 249062f..7aea336 100644
--- a/stock/doctype/material_request/material_request.py
+++ b/stock/doctype/material_request/material_request.py
@@ -20,10 +20,6 @@
self.tname = 'Material Request Item'
self.fname = 'indent_details'
- # get available qty at warehouse
- def get_bin_details(self, arg = ''):
- return get_obj(dt='Purchase Common').get_bin_details(arg)
-
def check_if_already_pulled(self):
pass#if self.[d.sales_order_no for d in getlist(self.doclist, 'indent_details')]
@@ -68,22 +64,14 @@
self.doc.status = "Draft"
import utilities
- utilities.validate_status(self.doc.status, ["Draft", "Submitted", "Stopped",
- "Cancelled"])
+ utilities.validate_status(self.doc.status, ["Draft", "Submitted", "Stopped", "Cancelled"])
- # restrict material request type
self.validate_value("material_request_type", "in", ["Purchase", "Transfer"])
- # Get Purchase Common Obj
pc_obj = get_obj(dt='Purchase Common')
-
-
- # Validate for items
pc_obj.validate_for_items(self)
-
- # Validate qty against SO
- self.validate_qty_against_so()
+ self.validate_qty_against_so()
def update_bin(self, is_submit, is_stopped):
""" Update Quantity Requested for Purchase in Bin for Material Request of type 'Purchase'"""
diff --git a/stock/doctype/material_request/material_request.txt b/stock/doctype/material_request/material_request.txt
index a5f092d..5fd576a 100644
--- a/stock/doctype/material_request/material_request.txt
+++ b/stock/doctype/material_request/material_request.txt
@@ -2,12 +2,13 @@
{
"creation": "2013-03-07 14:48:38",
"docstatus": 0,
- "modified": "2013-08-08 14:22:27",
+ "modified": "2013-10-02 14:24:42",
"modified_by": "Administrator",
"owner": "Administrator"
},
{
"allow_attach": 1,
+ "allow_import": 1,
"allow_print": 0,
"autoname": "naming_series:",
"doctype": "DocType",
diff --git a/stock/doctype/material_request/test_material_request.py b/stock/doctype/material_request/test_material_request.py
index 8ebad15..13fdd02 100644
--- a/stock/doctype/material_request/test_material_request.py
+++ b/stock/doctype/material_request/test_material_request.py
@@ -315,10 +315,10 @@
self.assertRaises(webnotes.MappingMismatchError, se.insert)
def test_warehouse_company_validation(self):
- from controllers.buying_controller import WrongWarehouseCompany
+ from stock.utils import InvalidWarehouseCompany
mr = webnotes.bean(copy=test_records[0])
mr.doc.company = "_Test Company 1"
- self.assertRaises(WrongWarehouseCompany, mr.insert)
+ self.assertRaises(InvalidWarehouseCompany, mr.insert)
test_dependencies = ["Currency Exchange"]
test_records = [
diff --git a/stock/doctype/material_request_item/material_request_item.txt b/stock/doctype/material_request_item/material_request_item.txt
index dae97e0..2ef4acd 100644
--- a/stock/doctype/material_request_item/material_request_item.txt
+++ b/stock/doctype/material_request_item/material_request_item.txt
@@ -2,7 +2,7 @@
{
"creation": "2013-02-22 01:28:02",
"docstatus": 0,
- "modified": "2013-08-07 14:45:11",
+ "modified": "2013-10-11 14:21:32",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -138,7 +138,7 @@
"oldfieldname": "item_name",
"oldfieldtype": "Data",
"print_width": "100px",
- "reqd": 1,
+ "reqd": 0,
"search_index": 1,
"width": "100px"
},
diff --git a/stock/doctype/purchase_receipt/purchase_receipt.js b/stock/doctype/purchase_receipt/purchase_receipt.js
index 2438926..8006d56 100644
--- a/stock/doctype/purchase_receipt/purchase_receipt.js
+++ b/stock/doctype/purchase_receipt/purchase_receipt.js
@@ -8,6 +8,7 @@
wn.require('app/accounts/doctype/purchase_taxes_and_charges_master/purchase_taxes_and_charges_master.js');
wn.require('app/utilities/doctype/sms_control/sms_control.js');
wn.require('app/buying/doctype/purchase_common/purchase_common.js');
+wn.require('app/accounts/doctype/sales_invoice/pos.js');
wn.provide("erpnext.stock");
erpnext.stock.PurchaseReceiptController = erpnext.buying.BuyingController.extend({
diff --git a/stock/doctype/purchase_receipt/purchase_receipt.py b/stock/doctype/purchase_receipt/purchase_receipt.py
index 7d663b8..1379406 100644
--- a/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -7,7 +7,7 @@
from webnotes.utils import cstr, flt, cint
from webnotes.model.bean import getlist
from webnotes.model.code import get_obj
-from webnotes import msgprint
+from webnotes import msgprint, _
import webnotes.defaults
from stock.utils import update_bin
@@ -38,10 +38,6 @@
total_qty = sum((item.qty for item in self.doclist.get({"parentfield": "purchase_receipt_details"})))
self.doc.fields["__billing_complete"] = billed_qty[0][0] == total_qty
- # get available qty at warehouse
- def get_bin_details(self, arg = ''):
- return get_obj(dt='Purchase Common').get_bin_details(arg)
-
def validate(self):
super(DocType, self).validate()
@@ -63,7 +59,6 @@
pc_obj = get_obj(dt='Purchase Common')
pc_obj.validate_for_items(self)
- pc_obj.get_prevdoc_date(self)
self.check_for_stopped_status(pc_obj)
# sub-contracting
@@ -246,26 +241,12 @@
self.update_stock()
- self.update_serial_nos()
+ from stock.doctype.serial_no.serial_no import update_serial_nos_after_submit
+ update_serial_nos_after_submit(self, "purchase_receipt_details")
purchase_controller.update_last_purchase_rate(self, 1)
self.make_gl_entries()
-
- def update_serial_nos(self, cancel=False):
- from stock.doctype.stock_ledger_entry.stock_ledger_entry import update_serial_nos_after_submit, get_serial_nos
- update_serial_nos_after_submit(self, "Purchase Receipt", "purchase_receipt_details")
-
- for d in self.doclist.get({"parentfield": "purchase_receipt_details"}):
- for serial_no in get_serial_nos(d.serial_no):
- sr = webnotes.bean("Serial No", serial_no)
- if cancel:
- sr.doc.supplier = None
- sr.doc.supplier_name = None
- else:
- sr.doc.supplier = self.doc.supplier
- sr.doc.supplier_name = self.doc.supplier_name
- sr.save()
def check_next_docstatus(self):
submit_rv = webnotes.conn.sql("select t1.name from `tabPurchase Invoice` t1,`tabPurchase Invoice Item` t2 where t1.name = t2.parent and t2.purchase_receipt = '%s' and t1.docstatus = 1" % (self.doc.name))
@@ -292,7 +273,6 @@
self.update_ordered_qty()
self.update_stock()
- self.update_serial_nos(cancel=True)
self.update_prevdoc_status()
pc_obj.update_last_purchase_rate(self, 0)
@@ -305,10 +285,6 @@
bin = webnotes.conn.sql("select actual_qty from `tabBin` where item_code = %s and warehouse = %s", (d.rm_item_code, self.doc.supplier_warehouse), as_dict = 1)
d.current_stock = bin and flt(bin[0]['actual_qty']) or 0
-
- def get_rate(self,arg):
- return get_obj('Purchase Common').get_rate(arg,self)
-
def get_gl_entries_for_stock(self, warehouse_account=None):
against_stock_account = self.get_company_default("stock_received_but_not_billed")
diff --git a/stock/doctype/purchase_receipt/purchase_receipt.txt b/stock/doctype/purchase_receipt/purchase_receipt.txt
index 1184643..f228a14 100755
--- a/stock/doctype/purchase_receipt/purchase_receipt.txt
+++ b/stock/doctype/purchase_receipt/purchase_receipt.txt
@@ -2,7 +2,7 @@
{
"creation": "2013-05-21 16:16:39",
"docstatus": 0,
- "modified": "2013-08-09 14:47:05",
+ "modified": "2013-10-11 13:20:13",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -215,7 +215,6 @@
"reqd": 1
},
{
- "default": "1.00",
"description": "Rate at which supplier's currency is converted to company's base currency",
"doctype": "DocField",
"fieldname": "conversion_rate",
diff --git a/stock/doctype/purchase_receipt/test_purchase_receipt.py b/stock/doctype/purchase_receipt/test_purchase_receipt.py
index 010c29b..613dfbc 100644
--- a/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -104,6 +104,7 @@
pr.doclist[1].received_qty = 1
pr.insert()
pr.submit()
+
self.assertEquals(webnotes.conn.get_value("Serial No", pr.doclist[1].serial_no,
"supplier"), pr.doc.supplier)
@@ -112,7 +113,7 @@
def test_serial_no_cancel(self):
pr = self.test_serial_no_supplier()
pr.cancel()
-
+
self.assertFalse(webnotes.conn.get_value("Serial No", pr.doclist[1].serial_no,
"warehouse"))
diff --git a/stock/doctype/purchase_receipt_item/purchase_receipt_item.txt b/stock/doctype/purchase_receipt_item/purchase_receipt_item.txt
index ce49168..9c0d0fb 100755
--- a/stock/doctype/purchase_receipt_item/purchase_receipt_item.txt
+++ b/stock/doctype/purchase_receipt_item/purchase_receipt_item.txt
@@ -2,7 +2,7 @@
{
"creation": "2013-05-24 19:29:10",
"docstatus": 0,
- "modified": "2013-09-20 11:36:55",
+ "modified": "2013-10-10 17:02:51",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -426,19 +426,6 @@
},
{
"doctype": "DocField",
- "fieldname": "prevdoc_date",
- "fieldtype": "Date",
- "hidden": 1,
- "in_filter": 1,
- "label": "Purchase Order Date",
- "no_copy": 1,
- "oldfieldname": "prevdoc_date",
- "oldfieldtype": "Date",
- "print_hide": 1,
- "read_only": 1
- },
- {
- "doctype": "DocField",
"fieldname": "rm_supp_cost",
"fieldtype": "Currency",
"hidden": 1,
diff --git a/stock/doctype/serial_no/serial_no.py b/stock/doctype/serial_no/serial_no.py
index 1feab02..22a16b5 100644
--- a/stock/doctype/serial_no/serial_no.py
+++ b/stock/doctype/serial_no/serial_no.py
@@ -4,30 +4,39 @@
from __future__ import unicode_literals
import webnotes
-from webnotes.utils import cint, getdate, nowdate
+from webnotes.utils import cint, getdate, cstr, flt, add_days
import datetime
-from webnotes import msgprint, _
+from webnotes import msgprint, _, ValidationError
from controllers.stock_controller import StockController
-class SerialNoCannotCreateDirectError(webnotes.ValidationError): pass
-class SerialNoCannotCannotChangeError(webnotes.ValidationError): pass
+class SerialNoCannotCreateDirectError(ValidationError): pass
+class SerialNoCannotCannotChangeError(ValidationError): pass
+class SerialNoNotRequiredError(ValidationError): pass
+class SerialNoRequiredError(ValidationError): pass
+class SerialNoQtyError(ValidationError): pass
+class SerialNoItemError(ValidationError): pass
+class SerialNoWarehouseError(ValidationError): pass
+class SerialNoStatusError(ValidationError): pass
+class SerialNoNotExistsError(ValidationError): pass
+class SerialNoDuplicateError(ValidationError): pass
class DocType(StockController):
- def __init__(self, doc, doclist=[]):
+ def __init__(self, doc, doclist=None):
self.doc = doc
- self.doclist = doclist
+ self.doclist = doclist or []
self.via_stock_ledger = False
def validate(self):
if self.doc.fields.get("__islocal") and self.doc.warehouse:
- webnotes.throw(_("New Serial No cannot have Warehouse. Warehouse must be set by Stock Entry or Purchase Receipt"),
- SerialNoCannotCreateDirectError)
+ webnotes.throw(_("New Serial No cannot have Warehouse. Warehouse must be \
+ set by Stock Entry or Purchase Receipt"), SerialNoCannotCreateDirectError)
self.validate_warranty_status()
self.validate_amc_status()
self.validate_warehouse()
self.validate_item()
+ self.on_stock_ledger_entry()
def validate_amc_status(self):
"""
@@ -41,7 +50,8 @@
validate warranty status
"""
if (self.doc.maintenance_status == 'Out of Warranty' and self.doc.warranty_expiry_date and getdate(self.doc.warranty_expiry_date) >= datetime.date.today()) or (self.doc.maintenance_status == 'Under Warranty' and (not self.doc.warranty_expiry_date or getdate(self.doc.warranty_expiry_date) < datetime.date.today())):
- msgprint("Warranty expiry date and maintenance status mismatch. Please verify", raise_exception=1)
+ msgprint("Warranty expiry date and maintenance status mismatch. Please verify",
+ raise_exception=1)
def validate_warehouse(self):
@@ -49,10 +59,11 @@
item_code, warehouse = webnotes.conn.get_value("Serial No",
self.doc.name, ["item_code", "warehouse"])
if item_code != self.doc.item_code:
- webnotes.throw(_("Item Code cannot be changed for Serial No."), SerialNoCannotCannotChangeError)
+ webnotes.throw(_("Item Code cannot be changed for Serial No."),
+ SerialNoCannotCannotChangeError)
if not self.via_stock_ledger and warehouse != self.doc.warehouse:
- webnotes.throw(_("Warehouse cannot be changed for Serial No."), SerialNoCannotCannotChangeError)
-
+ webnotes.throw(_("Warehouse cannot be changed for Serial No."),
+ SerialNoCannotCannotChangeError)
def validate_item(self):
"""
@@ -67,16 +78,89 @@
self.doc.item_name = item.item_name
self.doc.brand = item.brand
self.doc.warranty_period = item.warranty_period
-
+
+ def set_status(self):
+ last_sle = webnotes.conn.sql("""select * from `tabStock Ledger Entry`
+ where (serial_no like %s or serial_no like %s or serial_no=%s)
+ and item_code=%s and ifnull(is_cancelled, 'No')='No'
+ order by name desc limit 1""",
+ ("%%%s%%" % (self.doc.name+"\n"), "%%%s%%" % ("\n"+self.doc.name), self.doc.name,
+ self.doc.item_code), as_dict=1)
+
+ if last_sle:
+ if last_sle[0].voucher_type == "Stock Entry":
+ document_type = webnotes.conn.get_value("Stock Entry", last_sle[0].voucher_no,
+ "purpose")
+ else:
+ document_type = last_sle[0].voucher_type
+
+ if last_sle[0].actual_qty > 0:
+ if document_type == "Sales Return":
+ self.doc.status = "Sales Returned"
+ else:
+ self.doc.status = "Available"
+ else:
+ if document_type == "Purchase Return":
+ self.doc.status = "Purchase Returned"
+ elif last_sle[0].voucher_type in ("Delivery Note", "Sales Invoice"):
+ self.doc.status = "Delivered"
+ else:
+ self.doc.status = "Not Available"
+
+ def set_purchase_details(self):
+ purchase_sle = webnotes.conn.sql("""select * from `tabStock Ledger Entry`
+ where (serial_no like %s or serial_no like %s or serial_no=%s)
+ and item_code=%s and actual_qty > 0
+ and ifnull(is_cancelled, 'No')='No' order by name asc limit 1""",
+ ("%%%s%%" % (self.doc.name+"\n"), "%%%s%%" % ("\n"+self.doc.name), self.doc.name,
+ self.doc.item_code), as_dict=1)
+
+ if purchase_sle:
+ self.doc.purchase_document_type = purchase_sle[0].voucher_type
+ self.doc.purchase_document_no = purchase_sle[0].voucher_no
+ self.doc.purchase_date = purchase_sle[0].posting_date
+ self.doc.purchase_time = purchase_sle[0].posting_time
+ self.doc.purchase_rate = purchase_sle[0].incoming_rate
+ if purchase_sle[0].voucher_type == "Purchase Receipt":
+ self.doc.supplier, self.doc.supplier_name = \
+ webnotes.conn.get_value("Purchase Receipt", purchase_sle[0].voucher_no,
+ ["supplier", "supplier_name"])
+ else:
+ for fieldname in ("purchase_document_type", "purchase_document_no",
+ "purchase_date", "purchase_time", "purchase_rate", "supplier", "supplier_name"):
+ self.doc.fields[fieldname] = None
+
+ def set_sales_details(self):
+ delivery_sle = webnotes.conn.sql("""select * from `tabStock Ledger Entry`
+ where (serial_no like %s or serial_no like %s or serial_no=%s)
+ and item_code=%s and actual_qty<0
+ and voucher_type in ('Delivery Note', 'Sales Invoice')
+ and ifnull(is_cancelled, 'No')='No' order by name desc limit 1""",
+ ("%%%s%%" % (self.doc.name+"\n"), "%%%s%%" % ("\n"+self.doc.name), self.doc.name,
+ self.doc.item_code), as_dict=1)
+ if delivery_sle:
+ self.doc.delivery_document_type = delivery_sle[0].voucher_type
+ self.doc.delivery_document_no = delivery_sle[0].voucher_no
+ self.doc.delivery_date = delivery_sle[0].posting_date
+ self.doc.delivery_time = delivery_sle[0].posting_time
+ self.doc.customer, self.doc.customer_name = \
+ webnotes.conn.get_value(delivery_sle[0].voucher_type, delivery_sle[0].voucher_no,
+ ["customer", "customer_name"])
+ if self.doc.warranty_period:
+ self.doc.warranty_expiry_date = add_days(cstr(delivery_sle[0].posting_date),
+ cint(self.doc.warranty_period))
+ else:
+ for fieldname in ("delivery_document_type", "delivery_document_no",
+ "delivery_date", "delivery_time", "customer", "customer_name",
+ "warranty_expiry_date"):
+ self.doc.fields[fieldname] = None
+
def on_trash(self):
if self.doc.status == 'Delivered':
- msgprint("Cannot trash Serial No : %s as it is already Delivered" % (self.doc.name), raise_exception = 1)
+ webnotes.throw(_("Delivered Serial No ") + self.doc.name + _(" can not be deleted"))
if self.doc.warehouse:
- webnotes.throw(_("Cannot delete Serial No in warehouse. First remove from warehouse, then delete.") + \
- ": " + self.doc.name)
-
- def on_cancel(self):
- self.on_trash()
+ webnotes.throw(_("Cannot delete Serial No in warehouse. \
+ First remove from warehouse, then delete.") + ": " + self.doc.name)
def on_rename(self, new, old, merge=False):
"""rename serial_no text fields"""
@@ -93,3 +177,122 @@
webnotes.conn.sql("""update `tab%s` set serial_no = %s
where name=%s""" % (dt[0], '%s', '%s'),
('\n'.join(serial_nos), item[0]))
+
+ def on_stock_ledger_entry(self):
+ if self.via_stock_ledger and not self.doc.fields.get("__islocal"):
+ self.set_status()
+ self.set_purchase_details()
+ self.set_sales_details()
+
+ def on_stock_ledger_entry(self):
+ if self.via_stock_ledger and not self.doc.fields.get("__islocal"):
+ self.set_status()
+ self.set_purchase_details()
+ self.set_sales_details()
+
+def process_serial_no(sle):
+ item_det = get_item_details(sle.item_code)
+ validate_serial_no(sle, item_det)
+ update_serial_nos(sle, item_det)
+
+def validate_serial_no(sle, item_det):
+ if item_det.has_serial_no=="No":
+ if sle.serial_no:
+ webnotes.throw(_("Serial Number should be blank for Non Serialized Item" + ": "
+ + sle.item_code), SerialNoNotRequiredError)
+ else:
+ if sle.serial_no:
+ serial_nos = get_serial_nos(sle.serial_no)
+ if cint(sle.actual_qty) != flt(sle.actual_qty):
+ webnotes.throw(_("Serial No qty cannot be a fraction") + \
+ (": %s (%s)" % (sle.item_code, sle.actual_qty)))
+ if len(serial_nos) and len(serial_nos) != abs(cint(sle.actual_qty)):
+ webnotes.throw(_("Serial Nos do not match with qty") + \
+ (": %s (%s)" % (sle.item_code, sle.actual_qty)), SerialNoQtyError)
+
+ for serial_no in serial_nos:
+ if webnotes.conn.exists("Serial No", serial_no):
+ sr = webnotes.bean("Serial No", serial_no)
+
+ if sr.doc.item_code!=sle.item_code:
+ webnotes.throw(_("Serial No does not belong to Item") +
+ (": %s (%s)" % (sle.item_code, serial_no)), SerialNoItemError)
+
+ if sr.doc.warehouse and sle.actual_qty > 0:
+ webnotes.throw(_("Same Serial No") + ": " + sr.doc.name +
+ _(" can not be received twice"), SerialNoDuplicateError)
+
+ if sle.actual_qty < 0:
+ if sr.doc.warehouse!=sle.warehouse:
+ webnotes.throw(_("Serial No") + ": " + serial_no +
+ _(" does not belong to Warehouse") + ": " + sle.warehouse,
+ SerialNoWarehouseError)
+
+ if sle.voucher_type in ("Delivery Note", "Sales Invoice") \
+ and sr.doc.status != "Available":
+ webnotes.throw(_("Serial No status must be 'Available' to Deliver")
+ + ": " + serial_no, SerialNoStatusError)
+ elif sle.actual_qty < 0:
+ # transfer out
+ webnotes.throw(_("Serial No must exist to transfer out.") + \
+ ": " + serial_no, SerialNoNotExistsError)
+ elif not item_det.serial_no_series:
+ webnotes.throw(_("Serial Number Required for Serialized Item" + ": "
+ + sle.item_code), SerialNoRequiredError)
+
+def update_serial_nos(sle, item_det):
+ if not sle.serial_no and sle.actual_qty > 0 and item_det.serial_no_series:
+ from webnotes.model.doc import make_autoname
+ serial_nos = []
+ for i in xrange(cint(sle.actual_qty)):
+ serial_nos.append(make_autoname(item_det.serial_no_series))
+ webnotes.conn.set(sle, "serial_no", "\n".join(serial_nos))
+
+ if sle.serial_no:
+ serial_nos = get_serial_nos(sle.serial_no)
+ for serial_no in serial_nos:
+ if webnotes.conn.exists("Serial No", serial_no):
+ sr = webnotes.bean("Serial No", serial_no)
+ sr.make_controller().via_stock_ledger = True
+ sr.doc.warehouse = sle.warehouse if sle.actual_qty > 0 else None
+ sr.save()
+ elif sle.actual_qty > 0:
+ make_serial_no(serial_no, sle)
+
+def get_item_details(item_code):
+ return webnotes.conn.sql("""select name, has_batch_no, docstatus,
+ is_stock_item, has_serial_no, serial_no_series
+ from tabItem where name=%s""", item_code, as_dict=True)[0]
+
+def get_serial_nos(serial_no):
+ return [s.strip() for s in cstr(serial_no).strip().replace(',', '\n').split('\n') if s.strip()]
+
+def make_serial_no(serial_no, sle):
+ sr = webnotes.new_bean("Serial No")
+ sr.doc.serial_no = serial_no
+ sr.doc.item_code = sle.item_code
+ sr.make_controller().via_stock_ledger = True
+ sr.insert()
+ sr.doc.warehouse = sle.warehouse
+ sr.doc.status = "Available"
+ sr.save()
+ webnotes.msgprint(_("Serial No created") + ": " + sr.doc.name)
+ return sr.doc.name
+
+def update_serial_nos_after_submit(controller, parentfield):
+ stock_ledger_entries = webnotes.conn.sql("""select voucher_detail_no, serial_no
+ from `tabStock Ledger Entry` where voucher_type=%s and voucher_no=%s""",
+ (controller.doc.doctype, controller.doc.name), as_dict=True)
+
+ if not stock_ledger_entries: return
+
+ for d in controller.doclist.get({"parentfield": parentfield}):
+ serial_no = None
+ for sle in stock_ledger_entries:
+ if sle.voucher_detail_no==d.name:
+ serial_no = sle.serial_no
+ break
+
+ if d.serial_no != serial_no:
+ d.serial_no = serial_no
+ webnotes.conn.set_value(d.doctype, d.name, "serial_no", serial_no)
diff --git a/stock/doctype/stock_entry/stock_entry.py b/stock/doctype/stock_entry/stock_entry.py
index a54f9bf..217a0aa 100644
--- a/stock/doctype/stock_entry/stock_entry.py
+++ b/stock/doctype/stock_entry/stock_entry.py
@@ -6,7 +6,7 @@
import webnotes.defaults
from webnotes.utils import cstr, cint, flt, comma_or, nowdate
-from webnotes.model.doc import Document, addchild
+from webnotes.model.doc import addchild
from webnotes.model.bean import getlist
from webnotes.model.code import get_obj
from webnotes import msgprint, _
@@ -15,12 +15,12 @@
from controllers.queries import get_match_cond
import json
-sql = webnotes.conn.sql
class NotUpdateStockError(webnotes.ValidationError): pass
class StockOverReturnError(webnotes.ValidationError): pass
class IncorrectValuationRateError(webnotes.ValidationError): pass
class DuplicateEntryForProductionOrderError(webnotes.ValidationError): pass
+class StockOverProductionError(webnotes.ValidationError): pass
from controllers.stock_controller import StockController
@@ -52,14 +52,15 @@
def on_submit(self):
self.update_stock_ledger()
- self.update_serial_no(1)
- self.update_production_order(1)
+
+ from stock.doctype.serial_no.serial_no import update_serial_nos_after_submit
+ update_serial_nos_after_submit(self, "mtn_details")
+ self.update_production_order()
self.make_gl_entries()
def on_cancel(self):
self.update_stock_ledger()
- self.update_serial_no(0)
- self.update_production_order(0)
+ self.update_production_order()
self.make_cancel_gl_entries()
def validate_fiscal_year(self):
@@ -294,24 +295,6 @@
from `tabStock Entry Detail` where parent in (
select name from `tabStock Entry` where `%s`=%s and docstatus=1)
group by item_code""" % (ref_fieldname, "%s"), (self.doc.fields.get(ref_fieldname),)))
-
- def update_serial_no(self, is_submit):
- """Create / Update Serial No"""
-
- from stock.doctype.stock_ledger_entry.stock_ledger_entry import update_serial_nos_after_submit, get_serial_nos
- update_serial_nos_after_submit(self, "Stock Entry", "mtn_details")
-
- for d in getlist(self.doclist, 'mtn_details'):
- for serial_no in get_serial_nos(d.serial_no):
- if self.doc.purpose == 'Purchase Return':
- sr = webnotes.bean("Serial No", serial_no)
- sr.doc.status = "Purchase Returned" if is_submit else "Available"
- sr.save()
-
- if self.doc.purpose == "Sales Return":
- sr = webnotes.bean("Serial No", serial_no)
- sr.doc.status = "Sales Returned" if is_submit else "Delivered"
- sr.save()
def update_stock_ledger(self):
sl_entries = []
@@ -342,42 +325,49 @@
self.make_sl_entries(sl_entries, self.doc.amended_from and 'Yes' or 'No')
- def update_production_order(self, is_submit):
+ def update_production_order(self):
+ def _validate_production_order(pro_bean):
+ if flt(pro_bean.doc.docstatus) != 1:
+ webnotes.throw(_("Production Order must be submitted") + ": " +
+ self.doc.production_order)
+
+ if pro_bean.doc.status == 'Stopped':
+ msgprint(_("Transaction not allowed against stopped Production Order") + ": " +
+ self.doc.production_order)
+
if self.doc.production_order:
- # first perform some validations
- # (they are here coz this fn is also called during on_cancel)
- pro_obj = get_obj("Production Order", self.doc.production_order)
- if flt(pro_obj.doc.docstatus) != 1:
- msgprint("""You cannot do any transaction against
- Production Order : %s, as it's not submitted"""
- % (pro_obj.doc.name), raise_exception=1)
-
- if pro_obj.doc.status == 'Stopped':
- msgprint("""You cannot do any transaction against Production Order : %s,
- as it's status is 'Stopped'"""% (pro_obj.doc.name), raise_exception=1)
-
- # update bin
- if self.doc.purpose == "Manufacture/Repack":
- from stock.utils import update_bin
- pro_obj.doc.produced_qty = flt(pro_obj.doc.produced_qty) + \
- (is_submit and 1 or -1 ) * flt(self.doc.fg_completed_qty)
- args = {
- "item_code": pro_obj.doc.production_item,
- "warehouse": pro_obj.doc.fg_warehouse,
- "posting_date": self.doc.posting_date,
- "planned_qty": (is_submit and -1 or 1 ) * flt(self.doc.fg_completed_qty)
- }
- update_bin(args)
+ pro_bean = webnotes.bean("Production Order", self.doc.production_order)
+ _validate_production_order(pro_bean)
+ self.update_produced_qty(pro_bean)
+ self.update_planned_qty(pro_bean)
- # update production order status
- pro_obj.doc.status = (flt(pro_obj.doc.qty)==flt(pro_obj.doc.produced_qty)) \
- and 'Completed' or 'In Process'
- pro_obj.doc.save()
+ def update_produced_qty(self, pro_bean):
+ if self.doc.purpose == "Manufacture/Repack":
+ produced_qty = flt(pro_bean.doc.produced_qty) + \
+ (self.doc.docstatus==1 and 1 or -1 ) * flt(self.doc.fg_completed_qty)
+
+ if produced_qty > flt(pro_bean.doc.qty):
+ webnotes.throw(_("Production Order") + ": " + self.doc.production_order + "\n" +
+ _("Total Manufactured Qty can not be greater than Planned qty to manufacture")
+ + "(%s/%s)" % (produced_qty, flt(pro_bean.doc.qty)), StockOverProductionError)
+
+ status = 'Completed' if flt(produced_qty) >= flt(pro_bean.doc.qty) else 'In Process'
+ webnotes.conn.sql("""update `tabProduction Order` set status=%s, produced_qty=%s
+ where name=%s""", (status, produced_qty, self.doc.production_order))
+
+ def update_planned_qty(self, pro_bean):
+ from stock.utils import update_bin
+ update_bin({
+ "item_code": pro_bean.doc.production_item,
+ "warehouse": pro_bean.doc.fg_warehouse,
+ "posting_date": self.doc.posting_date,
+ "planned_qty": (self.doc.docstatus==1 and -1 or 1 ) * flt(self.doc.fg_completed_qty)
+ })
def get_item_details(self, arg):
arg = json.loads(arg)
- item = sql("""select stock_uom, description, item_name from `tabItem`
+ item = webnotes.conn.sql("""select stock_uom, description, item_name from `tabItem`
where name = %s and (ifnull(end_of_life,'')='' or end_of_life ='0000-00-00'
or end_of_life > now())""", (arg.get('item_code')), as_dict = 1)
if not item:
@@ -401,7 +391,7 @@
def get_uom_details(self, arg = ''):
arg, ret = eval(arg), {}
- uom = sql("""select conversion_factor from `tabUOM Conversion Detail`
+ uom = webnotes.conn.sql("""select conversion_factor from `tabUOM Conversion Detail`
where parent = %s and uom = %s""", (arg['item_code'], arg['uom']), as_dict = 1)
if not uom or not flt(uom[0].conversion_factor):
msgprint("There is no Conversion Factor for UOM '%s' in Item '%s'" % (arg['uom'],
@@ -431,7 +421,8 @@
return ret
def get_items(self):
- self.doclist = self.doc.clear_table(self.doclist, 'mtn_details', 1)
+ self.doclist = filter(lambda d: d.parentfield!="mtn_details", self.doclist)
+ # self.doclist = self.doc.clear_table(self.doclist, 'mtn_details')
pro_obj = None
if self.doc.production_order:
@@ -470,7 +461,7 @@
"stock_uom": pro_obj.doc.stock_uom
}
}, bom_no=pro_obj.doc.bom_no)
-
+
elif self.doc.purpose in ["Material Receipt", "Manufacture/Repack"]:
if self.doc.purpose=="Material Receipt":
self.doc.from_warehouse = ""
@@ -487,70 +478,19 @@
}, bom_no=self.doc.bom_no)
self.get_stock_and_rate()
+
def get_bom_raw_materials(self, qty):
- """
- get all items from flat bom except
- child items of sub-contracted and sub assembly items
- and sub assembly items itself.
- """
+ from manufacturing.doctype.bom.bom import get_bom_items_as_dict
+
# item dict = { item_code: {qty, description, stock_uom} }
- item_dict = {}
+ item_dict = get_bom_items_as_dict(self.doc.bom_no, qty=qty, fetch_exploded = self.doc.use_multi_level_bom)
- def _make_items_dict(items_list):
- """makes dict of unique items with it's qty"""
- for item in items_list:
- if item_dict.has_key(item.item_code):
- item_dict[item.item_code]["qty"] += flt(item.qty)
- else:
- item_dict[item.item_code] = {
- "qty": flt(item.qty),
- "description": item.description,
- "stock_uom": item.stock_uom,
- "from_warehouse": item.default_warehouse
- }
-
- if self.doc.use_multi_level_bom:
- # get all raw materials with sub assembly childs
- fl_bom_sa_child_item = sql("""select
- fb.item_code,
- ifnull(sum(fb.qty_consumed_per_unit),0)*%s as qty,
- fb.description,
- fb.stock_uom,
- it.default_warehouse
- from
- `tabBOM Explosion Item` fb,`tabItem` it
- where
- it.name = fb.item_code
- and ifnull(it.is_pro_applicable, 'No') = 'No'
- and ifnull(it.is_sub_contracted_item, 'No') = 'No'
- and fb.docstatus < 2
- and fb.parent=%s group by item_code, stock_uom""",
- (qty, self.doc.bom_no), as_dict=1)
-
- if fl_bom_sa_child_item:
- _make_items_dict(fl_bom_sa_child_item)
- else:
- # get only BOM items
- fl_bom_sa_items = sql("""select
- `tabItem`.item_code,
- ifnull(sum(`tabBOM Item`.qty_consumed_per_unit), 0) *%s as qty,
- `tabItem`.description,
- `tabItem`.stock_uom,
- `tabItem`.default_warehouse
- from
- `tabBOM Item`, `tabItem`
- where
- `tabBOM Item`.parent = %s and
- `tabBOM Item`.item_code = tabItem.name and
- `tabBOM Item`.docstatus < 2
- group by item_code""", (qty, self.doc.bom_no), as_dict=1)
-
- if fl_bom_sa_items:
- _make_items_dict(fl_bom_sa_items)
+ for item in item_dict.values():
+ item.from_warehouse = item.default_warehouse
return item_dict
-
+
def get_pending_raw_materials(self, pro_obj):
"""
issue (item quantity) that is pending to issue or desire to transfer,
@@ -590,7 +530,7 @@
def get_issued_qty(self):
issued_item_qty = {}
- result = sql("""select t1.item_code, sum(t1.qty)
+ result = webnotes.conn.sql("""select t1.item_code, sum(t1.qty)
from `tabStock Entry Detail` t1, `tabStock Entry` t2
where t1.parent = t2.name and t2.production_order = %s and t2.docstatus = 1
and t2.purpose = 'Material Transfer'
@@ -636,7 +576,7 @@
def get_cust_addr(self):
from utilities.transaction_base import get_default_address, get_address_display
- res = sql("select customer_name from `tabCustomer` where name = '%s'"%self.doc.customer)
+ res = webnotes.conn.sql("select customer_name from `tabCustomer` where name = '%s'"%self.doc.customer)
address_display = None
customer_address = get_default_address("customer", self.doc.customer)
if customer_address:
@@ -657,7 +597,7 @@
def get_supp_addr(self):
from utilities.transaction_base import get_default_address, get_address_display
- res = sql("""select supplier_name from `tabSupplier`
+ res = webnotes.conn.sql("""select supplier_name from `tabSupplier`
where name=%s""", self.doc.supplier)
address_display = None
supplier_address = get_default_address("customer", self.doc.customer)
@@ -896,8 +836,7 @@
ref.doclist[0].name)
if not invoices_against_delivery:
- sales_orders_against_delivery = [d.prevdoc_docname for d in
- ref.doclist.get({"prevdoc_doctype": "Sales Order"}) if d.prevdoc_docname]
+ sales_orders_against_delivery = [d.against_sales_order for d in ref.doclist if d.against_sales_order]
if sales_orders_against_delivery:
invoices_against_delivery = get_invoice_list("Sales Invoice Item", "sales_order",
diff --git a/stock/doctype/stock_entry/test_stock_entry.py b/stock/doctype/stock_entry/test_stock_entry.py
index e2358eb..b8e168a 100644
--- a/stock/doctype/stock_entry/test_stock_entry.py
+++ b/stock/doctype/stock_entry/test_stock_entry.py
@@ -1,13 +1,10 @@
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd.
# License: GNU General Public License v3. See license.txt
-# ERPNext - web based ERP (http://erpnext.com)
-# For license information, please see license.txt
-
from __future__ import unicode_literals
import webnotes, unittest
from webnotes.utils import flt
-from stock.doctype.stock_ledger_entry.stock_ledger_entry import *
+from stock.doctype.serial_no.serial_no import *
from stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory
@@ -43,12 +40,13 @@
webnotes.conn.set_default("company", self.old_default_company)
def test_warehouse_company_validation(self):
+ set_perpetual_inventory(0)
self._clear_stock_account_balance()
- webnotes.session.user = "test2@example.com"
webnotes.bean("Profile", "test2@example.com").get_controller()\
.add_roles("Sales User", "Sales Manager", "Material User", "Material Manager")
+ webnotes.session.user = "test2@example.com"
- from stock.doctype.stock_ledger_entry.stock_ledger_entry import InvalidWarehouseCompany
+ from stock.utils import InvalidWarehouseCompany
st1 = webnotes.bean(copy=test_records[0])
st1.doclist[1].t_warehouse="_Test Warehouse 2 - _TC1"
st1.insert()
@@ -57,15 +55,15 @@
webnotes.session.user = "Administrator"
def test_warehouse_user(self):
+ set_perpetual_inventory(0)
from stock.utils import UserNotAllowedForWarehouse
- webnotes.session.user = "test@example.com"
webnotes.bean("Profile", "test@example.com").get_controller()\
.add_roles("Sales User", "Sales Manager", "Material User", "Material Manager")
webnotes.bean("Profile", "test2@example.com").get_controller()\
.add_roles("Sales User", "Sales Manager", "Material User", "Material Manager")
-
+ webnotes.session.user = "test@example.com"
st1 = webnotes.bean(copy=test_records[0])
st1.doc.company = "_Test Company 1"
st1.doclist[1].t_warehouse="_Test Warehouse 2 - _TC1"
@@ -721,6 +719,18 @@
se.insert()
self.assertRaises(SerialNoNotExistsError, se.submit)
+ def test_serial_duplicate(self):
+ self._clear_stock_account_balance()
+ self.test_serial_by_series()
+
+ se = webnotes.bean(copy=test_records[0])
+ se.doclist[1].item_code = "_Test Serialized Item With Series"
+ se.doclist[1].qty = 1
+ se.doclist[1].serial_no = "ABCD00001"
+ se.doclist[1].transfer_qty = 1
+ se.insert()
+ self.assertRaises(SerialNoDuplicateError, se.submit)
+
def test_serial_by_series(self):
self._clear_stock_account_balance()
se = make_serialized_item()
diff --git a/stock/doctype/stock_ledger/stock_ledger.py b/stock/doctype/stock_ledger/stock_ledger.py
new file mode 100644
index 0000000..1624b00
--- /dev/null
+++ b/stock/doctype/stock_ledger/stock_ledger.py
@@ -0,0 +1,57 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd.
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import webnotes
+
+from webnotes.utils import add_days, cstr, flt, nowdate, cint, now
+from webnotes.model.doc import Document
+from webnotes.model.bean import getlist
+from webnotes.model.code import get_obj
+from webnotes import session, msgprint
+from stock.utils import get_valid_serial_nos
+
+
+class DocType:
+ def __init__(self, doc, doclist=[]):
+ self.doc = doc
+ self.doclist = doclist
+
+ def update_stock(self, values, is_amended = 'No'):
+ for v in values:
+ sle_id = ''
+
+ # reverse quantities for cancel
+ if v.get('is_cancelled') == 'Yes':
+ v['actual_qty'] = -flt(v['actual_qty'])
+ # cancel matching entry
+ webnotes.conn.sql("""update `tabStock Ledger Entry` set is_cancelled='Yes',
+ modified=%s, modified_by=%s
+ where voucher_no=%s and voucher_type=%s""",
+ (now(), webnotes.session.user, v['voucher_no'], v['voucher_type']))
+
+ if v.get("actual_qty"):
+ sle_id = self.make_entry(v)
+
+ args = v.copy()
+ args.update({
+ "sle_id": sle_id,
+ "is_amended": is_amended
+ })
+
+ get_obj('Warehouse', v["warehouse"]).update_bin(args)
+
+
+ def make_entry(self, args):
+ args.update({"doctype": "Stock Ledger Entry"})
+ sle = webnotes.bean([args])
+ sle.ignore_permissions = 1
+ sle.insert()
+ return sle.doc.name
+
+ def repost(self):
+ """
+ Repost everything!
+ """
+ for wh in webnotes.conn.sql("select name from tabWarehouse"):
+ get_obj('Warehouse', wh[0]).repost_stock()
diff --git a/stock/doctype/stock_ledger_entry/stock_ledger_entry.py b/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
index 1c3d3e1..d2f8704 100644
--- a/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
+++ b/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
@@ -3,37 +3,21 @@
from __future__ import unicode_literals
import webnotes
-from webnotes import _, msgprint, ValidationError
-from webnotes.utils import cint, flt, getdate, cstr
+from webnotes import msgprint
+from webnotes.utils import flt, getdate
from webnotes.model.controller import DocListController
-class InvalidWarehouseCompany(ValidationError): pass
-class SerialNoNotRequiredError(ValidationError): pass
-class SerialNoRequiredError(ValidationError): pass
-class SerialNoQtyError(ValidationError): pass
-class SerialNoItemError(ValidationError): pass
-class SerialNoWarehouseError(ValidationError): pass
-class SerialNoStatusError(ValidationError): pass
-class SerialNoNotExistsError(ValidationError): pass
-
-def get_serial_nos(serial_no):
- return [s.strip() for s in cstr(serial_no).strip().replace(',', '\n').split('\n') if s.strip()]
-
class DocType(DocListController):
def __init__(self, doc, doclist=[]):
self.doc = doc
self.doclist = doclist
def validate(self):
- from stock.utils import validate_warehouse_user
- if not hasattr(webnotes, "new_stock_ledger_entries"):
- webnotes.new_stock_ledger_entries = []
-
- webnotes.new_stock_ledger_entries.append(self.doc)
+ from stock.utils import validate_warehouse_user, validate_warehouse_company
self.validate_mandatory()
self.validate_item()
validate_warehouse_user(self.doc.warehouse)
- self.validate_warehouse_company()
+ validate_warehouse_company(self.doc.warehouse, self.doc.company)
self.scrub_posting_time()
from accounts.utils import validate_fiscal_year
@@ -42,7 +26,9 @@
def on_submit(self):
self.check_stock_frozen_date()
self.actual_amt_check()
- self.validate_serial_no()
+
+ from stock.doctype.serial_no.serial_no import process_serial_no
+ process_serial_no(self.doc)
#check for item quantity available in stock
def actual_amt_check(self):
@@ -62,14 +48,7 @@
<b>%(item_code)s</b> at Warehouse <b>%(warehouse)s</b> \
as on %(posting_date)s %(posting_time)s""" % self.doc.fields)
- sself.doc.fields.pop('batch_bal')
-
- def validate_warehouse_company(self):
- warehouse_company = webnotes.conn.get_value("Warehouse", self.doc.warehouse, "company")
- if warehouse_company and warehouse_company != self.doc.company:
- webnotes.msgprint(_("Warehouse does not belong to company.") + " (" + \
- self.doc.warehouse + ", " + self.doc.company +")",
- raise_exception=InvalidWarehouseCompany)
+ self.doc.fields.pop('batch_bal')
def validate_mandatory(self):
mandatory = ['warehouse','posting_date','voucher_type','voucher_no','actual_qty','company']
@@ -81,7 +60,10 @@
msgprint("Warehouse: '%s' does not exist in the system. Please check." % self.doc.fields.get(k), raise_exception = 1)
def validate_item(self):
- item_det = self.get_item_details()
+ item_det = webnotes.conn.sql("""select name, has_batch_no, docstatus,
+ is_stock_item, has_serial_no, serial_no_series
+ from tabItem where name=%s""",
+ self.doc.item_code, as_dict=True)[0]
if item_det.is_stock_item != 'Yes':
webnotes.throw("""Item: "%s" is not a Stock Item.""" % self.doc.item_code)
@@ -99,95 +81,6 @@
if not self.doc.stock_uom:
self.doc.stock_uom = item_det.stock_uom
- def get_item_details(self):
- return webnotes.conn.sql("""select name, has_batch_no, docstatus,
- is_stock_item, has_serial_no, serial_no_series
- from tabItem where name=%s""",
- self.doc.item_code, as_dict=True)[0]
-
- def validate_serial_no(self):
- item_det = self.get_item_details()
-
- if item_det.has_serial_no=="No":
- if self.doc.serial_no:
- webnotes.throw(_("Serial Number should be blank for Non Serialized Item" + ": " + self.doc.item),
- SerialNoNotRequiredError)
- else:
- if self.doc.serial_no:
- serial_nos = get_serial_nos(self.doc.serial_no)
- if cint(self.doc.actual_qty) != flt(self.doc.actual_qty):
- webnotes.throw(_("Serial No qty cannot be a fraction") + \
- (": %s (%s)" % (self.doc.item_code, self.doc.actual_qty)))
- if len(serial_nos) and len(serial_nos) != abs(cint(self.doc.actual_qty)):
- webnotes.throw(_("Serial Nos do not match with qty") + \
- (": %s (%s)" % (self.doc.item_code, self.doc.actual_qty)), SerialNoQtyError)
-
- # check serial no exists, if yes then source
- for serial_no in serial_nos:
- if webnotes.conn.exists("Serial No", serial_no):
- sr = webnotes.bean("Serial No", serial_no)
-
- if sr.doc.item_code!=self.doc.item_code:
- webnotes.throw(_("Serial No does not belong to Item") + \
- (": %s (%s)" % (self.doc.item_code, serial_no)), SerialNoItemError)
-
- sr.make_controller().via_stock_ledger = True
-
- if self.doc.actual_qty < 0:
- if sr.doc.warehouse!=self.doc.warehouse:
- webnotes.throw(_("Serial No") + ": " + serial_no +
- _(" does not belong to Warehouse") + ": " + self.doc.warehouse,
- SerialNoWarehouseError)
-
- if self.doc.voucher_type in ("Delivery Note", "Sales Invoice") \
- and sr.doc.status != "Available":
- webnotes.throw(_("Serial No status must be 'Available' to Deliver")
- + ": " + serial_no, SerialNoStatusError)
-
-
- sr.doc.warehouse = None
- sr.save()
- else:
- sr.doc.warehouse = self.doc.warehouse
- sr.save()
- else:
- if self.doc.actual_qty < 0:
- # transfer out
- webnotes.throw(_("Serial No must exist to transfer out.") + \
- ": " + serial_no, SerialNoNotExistsError)
- else:
- # transfer in
- self.make_serial_no(serial_no)
- else:
- if item_det.serial_no_series:
- from webnotes.model.doc import make_autoname
- serial_nos = []
- for i in xrange(cint(self.doc.actual_qty)):
- serial_nos.append(self.make_serial_no(make_autoname(item_det.serial_no_series)))
- self.doc.serial_no = "\n".join(serial_nos)
- else:
- webnotes.throw(_("Serial Number Required for Serialized Item" + ": " + self.doc.item),
- SerialNoRequiredError)
-
- def make_serial_no(self, serial_no):
- sr = webnotes.new_bean("Serial No")
- sr.doc.serial_no = serial_no
- sr.doc.item_code = self.doc.item_code
- sr.doc.purchase_rate = self.doc.incoming_rate
- sr.doc.purchase_document_type = self.doc.voucher_type
- sr.doc.purchase_document_no = self.doc.voucher_no
- sr.doc.purchase_date = self.doc.posting_date
- sr.doc.purchase_time = self.doc.posting_time
- sr.make_controller().via_stock_ledger = True
- sr.insert()
-
- # set warehouse
- sr.doc.warehouse = self.doc.warehouse
- sr.doc.status = "Available"
- sr.save()
- webnotes.msgprint(_("Serial No created") + ": " + sr.doc.name)
- return sr.doc.name
-
def check_stock_frozen_date(self):
stock_frozen_upto = webnotes.conn.get_value('Stock Settings', None, 'stock_frozen_upto') or ''
if stock_frozen_upto:
@@ -199,21 +92,6 @@
if not self.doc.posting_time or self.doc.posting_time == '00:0':
self.doc.posting_time = '00:00'
-def update_serial_nos_after_submit(controller, parenttype, parentfield):
- if not hasattr(webnotes, "new_stock_ledger_entries"):
- return
-
- for d in controller.doclist.get({"parentfield": parentfield}):
- serial_no = None
- for sle in webnotes.new_stock_ledger_entries:
- if sle.voucher_detail_no==d.name:
- serial_no = sle.serial_no
- break
-
- if d.serial_no != serial_no:
- d.serial_no = serial_no
- webnotes.conn.set_value(d.doctype, d.name, "serial_no", serial_no)
-
def on_doctype_update():
if not webnotes.conn.sql("""show index from `tabStock Ledger Entry`
where Key_name="posting_sort_index" """):
diff --git a/stock/doctype/stock_reconciliation/stock_reconciliation.py b/stock/doctype/stock_reconciliation/stock_reconciliation.py
index 465edc4..9feb57e 100644
--- a/stock/doctype/stock_reconciliation/stock_reconciliation.py
+++ b/stock/doctype/stock_reconciliation/stock_reconciliation.py
@@ -246,6 +246,7 @@
"stock_uom": webnotes.conn.get_value("Item", row.item_code, "stock_uom"),
"voucher_detail_no": row.voucher_detail_no,
"fiscal_year": self.doc.fiscal_year,
+ "is_cancelled": "No"
})
args.update(opts)
self.make_sl_entries([args])
diff --git a/stock/doctype/stock_uom_replace_utility/stock_uom_replace_utility.py b/stock/doctype/stock_uom_replace_utility/stock_uom_replace_utility.py
index 321e3c9..1f1bafa 100644
--- a/stock/doctype/stock_uom_replace_utility/stock_uom_replace_utility.py
+++ b/stock/doctype/stock_uom_replace_utility/stock_uom_replace_utility.py
@@ -10,7 +10,6 @@
from webnotes.model.code import get_obj
from webnotes import msgprint, _
-sql = webnotes.conn.sql
class DocType:
def __init__(self, d, dl=[]):
@@ -34,7 +33,7 @@
msgprint("Please Enter Conversion Factor.")
raise Exception
- stock_uom = sql("select stock_uom from `tabItem` where name = '%s'" % self.doc.item_code)
+ stock_uom = webnotes.conn.sql("select stock_uom from `tabItem` where name = '%s'" % self.doc.item_code)
stock_uom = stock_uom and stock_uom[0][0]
if cstr(self.doc.new_stock_uom) == cstr(stock_uom):
msgprint("Item Master is already updated with New Stock UOM " + cstr(self.doc.new_stock_uom))
@@ -50,9 +49,9 @@
def update_bin(self):
# update bin
if flt(self.doc.conversion_factor) != flt(1):
- sql("update `tabBin` set stock_uom = '%s' , indented_qty = ifnull(indented_qty,0) * %s, ordered_qty = ifnull(ordered_qty,0) * %s, reserved_qty = ifnull(reserved_qty,0) * %s, planned_qty = ifnull(planned_qty,0) * %s, projected_qty = actual_qty + ordered_qty + indented_qty + planned_qty - reserved_qty where item_code = '%s'" % (self.doc.new_stock_uom, self.doc.conversion_factor, self.doc.conversion_factor, self.doc.conversion_factor, self.doc.conversion_factor, self.doc.item_code) )
+ webnotes.conn.sql("update `tabBin` set stock_uom = '%s' , indented_qty = ifnull(indented_qty,0) * %s, ordered_qty = ifnull(ordered_qty,0) * %s, reserved_qty = ifnull(reserved_qty,0) * %s, planned_qty = ifnull(planned_qty,0) * %s, projected_qty = actual_qty + ordered_qty + indented_qty + planned_qty - reserved_qty where item_code = '%s'" % (self.doc.new_stock_uom, self.doc.conversion_factor, self.doc.conversion_factor, self.doc.conversion_factor, self.doc.conversion_factor, self.doc.item_code) )
else:
- sql("update `tabBin` set stock_uom = '%s' where item_code = '%s'" % (self.doc.new_stock_uom, self.doc.item_code) )
+ webnotes.conn.sql("update `tabBin` set stock_uom = '%s' where item_code = '%s'" % (self.doc.new_stock_uom, self.doc.item_code) )
# acknowledge user
msgprint(" All Bins Updated Successfully.")
@@ -62,16 +61,16 @@
from stock.stock_ledger import update_entries_after
if flt(self.doc.conversion_factor) != flt(1):
- sql("update `tabStock Ledger Entry` set stock_uom = '%s', actual_qty = ifnull(actual_qty,0) * '%s' where item_code = '%s' " % (self.doc.new_stock_uom, self.doc.conversion_factor, self.doc.item_code))
+ webnotes.conn.sql("update `tabStock Ledger Entry` set stock_uom = '%s', actual_qty = ifnull(actual_qty,0) * '%s' where item_code = '%s' " % (self.doc.new_stock_uom, self.doc.conversion_factor, self.doc.item_code))
else:
- sql("update `tabStock Ledger Entry` set stock_uom = '%s' where item_code = '%s' " % (self.doc.new_stock_uom, self.doc.item_code))
+ webnotes.conn.sql("update `tabStock Ledger Entry` set stock_uom = '%s' where item_code = '%s' " % (self.doc.new_stock_uom, self.doc.item_code))
# acknowledge user
msgprint("Stock Ledger Entries Updated Successfully.")
# update item valuation
if flt(self.doc.conversion_factor) != flt(1):
- wh = sql("select name from `tabWarehouse`")
+ wh = webnotes.conn.sql("select name from `tabWarehouse`")
for w in wh:
update_entries_after({"item_code": self.doc.item_code, "warehouse": w[0]})
diff --git a/stock/doctype/warehouse/warehouse.py b/stock/doctype/warehouse/warehouse.py
index 58cf11b..97a8826 100644
--- a/stock/doctype/warehouse/warehouse.py
+++ b/stock/doctype/warehouse/warehouse.py
@@ -5,10 +5,8 @@
import webnotes
from webnotes.utils import cint, flt, validate_email_add
-from webnotes.model.code import get_obj
from webnotes import msgprint, _
-sql = webnotes.conn.sql
class DocType:
def __init__(self, doc, doclist=[]):
@@ -32,7 +30,7 @@
if not webnotes.conn.get_value("Account", {"account_type": "Warehouse",
"master_name": self.doc.name}) and not webnotes.conn.get_value("Account",
{"account_name": self.doc.warehouse_name}):
- if self.doc.__islocal or not webnotes.conn.get_value("Stock Ledger Entry",
+ if self.doc.fields.get("__islocal") or not webnotes.conn.get_value("Stock Ledger Entry",
{"warehouse": self.doc.name}):
self.validate_parent_account()
ac_bean = webnotes.bean({
@@ -59,7 +57,16 @@
else:
webnotes.throw(_("Please enter account group under which account \
for warehouse ") + self.doc.name +_(" will be created"))
-
+
+ def on_rename(self, new, old, merge=False):
+ webnotes.conn.set_value("Account", {"account_type": "Warehouse", "master_name": old},
+ "master_name", new)
+
+ if merge:
+ from stock.stock_ledger import update_entries_after
+ for item_code in webnotes.conn.sql("""select item_code from `tabBin`
+ where warehouse=%s""", new):
+ update_entries_after({"item_code": item_code, "warehouse": new})
def merge_warehouses(self):
webnotes.conn.auto_commit_on_many_writes = 1
@@ -83,113 +90,25 @@
webnotes.conn.delete_doc("Account", old_warehouse_account)
+ from utilities.repost_stock import repost
for item_code in items:
- self.repost(item_code[0], self.doc.merge_with)
+ repost(item_code[0], self.doc.merge_with)
webnotes.conn.auto_commit_on_many_writes = 0
msgprint("Warehouse %s merged into %s. Now you can delete warehouse: %s"
% (self.doc.name, self.doc.merge_with, self.doc.name))
-
- def repost(self, item_code, warehouse=None):
- from stock.utils import get_bin
- self.repost_actual_qty(item_code, warehouse)
-
- bin = get_bin(item_code, warehouse)
- self.repost_reserved_qty(bin)
- self.repost_indented_qty(bin)
- self.repost_ordered_qty(bin)
- self.repost_planned_qty(bin)
- bin.doc.projected_qty = flt(bin.doc.actual_qty) + flt(bin.doc.planned_qty) \
- + flt(bin.doc.indented_qty) + flt(bin.doc.ordered_qty) - flt(bin.doc.reserved_qty)
- bin.doc.save()
-
-
- def repost_actual_qty(self, item_code, warehouse=None):
- from stock.stock_ledger import update_entries_after
- if not warehouse:
- warehouse = self.doc.name
-
- update_entries_after({ "item_code": item_code, "warehouse": warehouse })
-
- def repost_reserved_qty(self, bin):
- reserved_qty = webnotes.conn.sql("""
- select
- sum((dnpi_qty / so_item_qty) * (so_item_qty - so_item_delivered_qty))
- from
- (
- select
- qty as dnpi_qty,
- (
- select qty from `tabSales Order Item`
- where name = dnpi.parent_detail_docname
- ) as so_item_qty,
- (
- select ifnull(delivered_qty, 0) from `tabSales Order Item`
- where name = dnpi.parent_detail_docname
- ) as so_item_delivered_qty
- from
- (
- select qty, parent_detail_docname
- from `tabDelivery Note Packing Item` dnpi_in
- where item_code = %s and warehouse = %s
- and parenttype="Sales Order"
- and exists (select * from `tabSales Order` so
- where name = dnpi_in.parent and docstatus = 1 and status != 'Stopped')
- ) dnpi
- ) tab
- where
- so_item_qty >= so_item_delivered_qty
- """, (bin.doc.item_code, bin.doc.warehouse))
-
- if flt(bin.doc.reserved_qty) != flt(reserved_qty[0][0]):
- webnotes.conn.set_value("Bin", bin.doc.name, "reserved_qty", flt(reserved_qty[0][0]))
-
-
- def repost_indented_qty(self, bin):
- indented_qty = webnotes.conn.sql("""select sum(pr_item.qty - pr_item.ordered_qty)
- from `tabMaterial Request Item` pr_item, `tabMaterial Request` pr
- where pr_item.item_code=%s and pr_item.warehouse=%s
- and pr_item.qty > pr_item.ordered_qty and pr_item.parent=pr.name
- and pr.status!='Stopped' and pr.docstatus=1"""
- , (bin.doc.item_code, bin.doc.warehouse))
-
- if flt(bin.doc.indented_qty) != flt(indented_qty[0][0]):
- webnotes.conn.set_value("Bin", bin.doc.name, "indented_qty", flt(indented_qty[0][0]))
-
-
- def repost_ordered_qty(self, bin):
- ordered_qty = webnotes.conn.sql("""
- select sum((po_item.qty - po_item.received_qty)*po_item.conversion_factor)
- from `tabPurchase Order Item` po_item, `tabPurchase Order` po
- where po_item.item_code=%s and po_item.warehouse=%s
- and po_item.qty > po_item.received_qty and po_item.parent=po.name
- and po.status!='Stopped' and po.docstatus=1"""
- , (bin.doc.item_code, bin.doc.warehouse))
-
- if flt(bin.doc.ordered_qty) != flt(ordered_qty[0][0]):
- webnotes.conn.set_value("Bin", bin.doc.name, "ordered_qty", flt(ordered_qty[0][0]))
-
- def repost_planned_qty(self, bin):
- planned_qty = webnotes.conn.sql("""
- select sum(qty - produced_qty) from `tabProduction Order`
- where production_item = %s and fg_warehouse = %s and status != "Stopped"
- and docstatus=1""", (bin.doc.item_code, bin.doc.warehouse))
-
- if flt(bin.doc.planned_qty) != flt(planned_qty[0][0]):
- webnotes.conn.set_value("Bin", bin.doc.name, "planned_qty", flt(planned_qty[0][0]))
-
def on_trash(self):
# delete bin
- bins = sql("select * from `tabBin` where warehouse = %s", self.doc.name, as_dict=1)
+ bins = webnotes.conn.sql("select * from `tabBin` where warehouse = %s", self.doc.name, as_dict=1)
for d in bins:
if d['actual_qty'] or d['reserved_qty'] or d['ordered_qty'] or \
d['indented_qty'] or d['projected_qty'] or d['planned_qty']:
msgprint("""Warehouse: %s can not be deleted as qty exists for item: %s"""
% (self.doc.name, d['item_code']), raise_exception=1)
else:
- sql("delete from `tabBin` where name = %s", d['name'])
+ webnotes.conn.sql("delete from `tabBin` where name = %s", d['name'])
warehouse_account = webnotes.conn.get_value("Account",
{"account_type": "Warehosue", "master_name": self.doc.name})
@@ -197,18 +116,8 @@
webnotes.delete_doc("Account", warehouse_account)
# delete cancelled sle
- if sql("""select name from `tabStock Ledger Entry` where warehouse = %s""", self.doc.name):
+ if webnotes.conn.sql("""select name from `tabStock Ledger Entry` where warehouse = %s""", self.doc.name):
msgprint("""Warehosue can not be deleted as stock ledger entry
exists for this warehouse.""", raise_exception=1)
else:
- sql("delete from `tabStock Ledger Entry` where warehouse = %s", self.doc.name)
-
- def on_rename(self, newdn, olddn, merge=False):
- webnotes.conn.set_value("Account", {"account_type": "Warehouse", "master_name": olddn},
- "master_name", newdn)
-
- if merge:
- from stock.stock_ledger import update_entries_after
- for item_code in webnotes.conn.sql("""select item_code from `tabBin`
- where warehouse=%s""", newdn):
- update_entries_after({"item_code": item_code, "warehouse": newdn})
+ webnotes.conn.sql("delete from `tabStock Ledger Entry` where warehouse = %s", self.doc.name)
diff --git a/stock/page/stock_balance/stock_balance.js b/stock/page/stock_balance/stock_balance.js
index 9ccc028..c6b9fda 100644
--- a/stock/page/stock_balance/stock_balance.js
+++ b/stock/page/stock_balance/stock_balance.js
@@ -126,10 +126,11 @@
} else {
item.inflow_value += value_diff;
}
- }
- item.closing_qty += qty_diff;
- item.closing_value += value_diff;
+ item.closing_qty += qty_diff;
+ item.closing_value += value_diff;
+ }
+
} else {
break;
}
diff --git a/stock/stock_ledger.py b/stock/stock_ledger.py
index 197aa0d..1555dab 100644
--- a/stock/stock_ledger.py
+++ b/stock/stock_ledger.py
@@ -10,6 +10,9 @@
# future reposting
class NegativeStockError(webnotes.ValidationError): pass
+_exceptions = webnotes.local('stockledger_exceptions')
+# _exceptions = []
+
def make_sl_entries(sl_entries, is_amended=None):
if sl_entries:
from stock.utils import update_bin
@@ -55,7 +58,6 @@
webnotes.conn.sql("""delete from `tabStock Ledger Entry`
where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no))
-_exceptions = []
def update_entries_after(args, verbose=1):
"""
update valution rate and qty after transaction
@@ -68,8 +70,8 @@
"posting_time": "12:00"
}
"""
- global _exceptions
- _exceptions = []
+ if not _exceptions:
+ webnotes.local.stockledger_exceptions = []
previous_sle = get_sle_before_datetime(args)
@@ -190,10 +192,12 @@
will not consider cancelled entries
"""
diff = qty_after_transaction + flt(sle.actual_qty)
+
+ if not _exceptions:
+ webnotes.local.stockledger_exceptions = []
if diff < 0 and abs(diff) > 0.0001:
# negative stock!
- global _exceptions
exc = sle.copy().update({"diff": diff})
_exceptions.append(exc)
return False
@@ -327,4 +331,4 @@
sle = get_stock_ledger_entries(args, ["name != %(sle)s",
"timestamp(posting_date, posting_time) <= timestamp(%(posting_date)s, %(posting_time)s)"],
"desc", "limit 1", for_update=for_update)
- return sle and sle[0] or {}
\ No newline at end of file
+ return sle and sle[0] or {}
diff --git a/stock/utils.py b/stock/utils.py
index 8836c6c..b3980e4 100644
--- a/stock/utils.py
+++ b/stock/utils.py
@@ -9,6 +9,7 @@
from webnotes.utils.email_lib import sendmail
class UserNotAllowedForWarehouse(webnotes.ValidationError): pass
+class InvalidWarehouseCompany(webnotes.ValidationError): pass
def get_stock_balance_on(warehouse, posting_date=None):
if not posting_date: posting_date = nowdate()
@@ -208,7 +209,7 @@
return wlist
def validate_warehouse_user(warehouse):
- if webnotes.session.user=="Administrator":
+ if webnotes.session.user=="Administrator" or not warehouse:
return
warehouse_users = [p[0] for p in webnotes.conn.sql("""select user from `tabWarehouse User`
where parent=%s""", warehouse)]
@@ -216,6 +217,12 @@
if warehouse_users and not (webnotes.session.user in warehouse_users):
webnotes.throw(_("Not allowed entry in Warehouse") \
+ ": " + warehouse, UserNotAllowedForWarehouse)
+
+def validate_warehouse_company(warehouse, company):
+ warehouse_company = webnotes.conn.get_value("Warehouse", warehouse, "company")
+ if warehouse_company and warehouse_company != company:
+ webnotes.msgprint(_("Warehouse does not belong to company.") + " (" + \
+ warehouse + ", " + company +")", raise_exception=InvalidWarehouseCompany)
def get_sales_bom_buying_amount(item_code, warehouse, voucher_type, voucher_no, voucher_detail_no,
stock_ledger_entries, item_sales_bom):
@@ -245,10 +252,10 @@
def reorder_item():
""" Reorder item if stock reaches reorder level"""
- if not hasattr(webnotes, "auto_indent"):
- webnotes.auto_indent = cint(webnotes.conn.get_value('Stock Settings', None, 'auto_indent'))
+ if getattr(webnotes.local, "auto_indent", None) is None:
+ webnotes.local.auto_indent = cint(webnotes.conn.get_value('Stock Settings', None, 'auto_indent'))
- if webnotes.auto_indent:
+ if webnotes.local.auto_indent:
material_requests = {}
bin_list = webnotes.conn.sql("""select item_code, warehouse, projected_qty
from tabBin where ifnull(item_code, '') != '' and ifnull(warehouse, '') != ''
@@ -333,18 +340,18 @@
mr_list.append(mr_bean)
except:
- if webnotes.message_log:
- exceptions_list.append([] + webnotes.message_log)
- webnotes.message_log = []
+ if webnotes.local.message_log:
+ exceptions_list.append([] + webnotes.local.message_log)
+ webnotes.local.message_log = []
else:
exceptions_list.append(webnotes.getTraceback())
if mr_list:
- if not hasattr(webnotes, "reorder_email_notify"):
- webnotes.reorder_email_notify = cint(webnotes.conn.get_value('Stock Settings', None,
+ if getattr(webnotes.local, "reorder_email_notify", None) is None:
+ webnotes.local.reorder_email_notify = cint(webnotes.conn.get_value('Stock Settings', None,
'reorder_email_notify'))
- if(webnotes.reorder_email_notify):
+ if(webnotes.local.reorder_email_notify):
send_email_notification(mr_list)
if exceptions_list:
@@ -387,12 +394,3 @@
from webnotes.profile import get_system_managers
sendmail(get_system_managers(), subject=subject, msg=msg)
-
-
-def repost():
- """
- Repost everything!
- """
- from webnotes.model.code import get_obj
- for wh in webnotes.conn.sql("select name from tabWarehouse"):
- get_obj('Warehouse', wh[0]).repost_stock()
diff --git a/support/doctype/customer_issue/customer_issue.py b/support/doctype/customer_issue/customer_issue.py
index 910e9b8..c099bf6 100644
--- a/support/doctype/customer_issue/customer_issue.py
+++ b/support/doctype/customer_issue/customer_issue.py
@@ -7,7 +7,6 @@
from webnotes import session, msgprint
from webnotes.utils import today
-sql = webnotes.conn.sql
from utilities.transaction_base import TransactionBase
@@ -28,7 +27,7 @@
self.doc.resolved_by = webnotes.session.user
def on_cancel(self):
- lst = sql("select t1.name from `tabMaintenance Visit` t1, `tabMaintenance Visit Purpose` t2 where t2.parent = t1.name and t2.prevdoc_docname = '%s' and t1.docstatus!=2"%(self.doc.name))
+ lst = webnotes.conn.sql("select t1.name from `tabMaintenance Visit` t1, `tabMaintenance Visit Purpose` t2 where t2.parent = t1.name and t2.prevdoc_docname = '%s' and t1.docstatus!=2"%(self.doc.name))
if lst:
lst1 = ','.join([x[0] for x in lst])
msgprint("Maintenance Visit No. "+lst1+" already created against this customer issue. So can not be Cancelled")
diff --git a/support/doctype/maintenance_schedule/maintenance_schedule.py b/support/doctype/maintenance_schedule/maintenance_schedule.py
index 59d3f8e..1682da5 100644
--- a/support/doctype/maintenance_schedule/maintenance_schedule.py
+++ b/support/doctype/maintenance_schedule/maintenance_schedule.py
@@ -9,7 +9,6 @@
from webnotes.model.bean import getlist
from webnotes import msgprint
-sql = webnotes.conn.sql
from utilities.transaction_base import TransactionBase, delete_events
@@ -20,7 +19,7 @@
self.doclist = doclist
def get_item_details(self, item_code):
- item = sql("select item_name, description from `tabItem` where name = '%s'" %(item_code), as_dict=1)
+ item = webnotes.conn.sql("select item_name, description from `tabItem` where name = '%s'" %(item_code), as_dict=1)
ret = {
'item_name': item and item[0]['item_name'] or '',
'description' : item and item[0]['description'] or ''
@@ -30,7 +29,7 @@
def generate_schedule(self):
self.doclist = self.doc.clear_table(self.doclist, 'maintenance_schedule_detail')
count = 0
- sql("delete from `tabMaintenance Schedule Detail` where parent='%s'" %(self.doc.name))
+ webnotes.conn.sql("delete from `tabMaintenance Schedule Detail` where parent='%s'" %(self.doc.name))
for d in getlist(self.doclist, 'item_maintenance_detail'):
self.validate_maintenance_detail()
s_list =[]
@@ -67,7 +66,7 @@
email_map[d.incharge_name] = webnotes.bean("Sales Person",
d.incharge_name).run_method("get_email_id")
- scheduled_date =sql("select scheduled_date from `tabMaintenance Schedule Detail` \
+ scheduled_date =webnotes.conn.sql("select scheduled_date from `tabMaintenance Schedule Detail` \
where incharge_name='%s' and item_code='%s' and parent='%s' " %(d.incharge_name, \
d.item_code, self.doc.name), as_dict=1)
@@ -172,7 +171,7 @@
def validate_sales_order(self):
for d in getlist(self.doclist, 'item_maintenance_detail'):
if d.prevdoc_docname:
- chk = sql("select t1.name from `tabMaintenance Schedule` t1, `tabMaintenance Schedule Item` t2 where t2.parent=t1.name and t2.prevdoc_docname=%s and t1.docstatus=1", d.prevdoc_docname)
+ chk = webnotes.conn.sql("select t1.name from `tabMaintenance Schedule` t1, `tabMaintenance Schedule Item` t2 where t2.parent=t1.name and t2.prevdoc_docname=%s and t1.docstatus=1", d.prevdoc_docname)
if chk:
msgprint("Maintenance Schedule against "+d.prevdoc_docname+" already exist")
raise Exception
@@ -186,7 +185,7 @@
cur_s_no = cur_serial_no.split(',')
for x in cur_s_no:
- chk = sql("select name, status from `tabSerial No` where docstatus!=2 and name=%s", (x))
+ chk = webnotes.conn.sql("select name, status from `tabSerial No` where docstatus!=2 and name=%s", (x))
chk1 = chk and chk[0][0] or ''
status = chk and chk[0][1] or ''
@@ -209,7 +208,7 @@
cur_s_no = cur_serial_no.split(',')
for x in cur_s_no:
- dt = sql("select delivery_date from `tabSerial No` where name = %s", x)
+ dt = webnotes.conn.sql("select delivery_date from `tabSerial No` where name = %s", x)
dt = dt and dt[0][0] or ''
if dt:
@@ -225,7 +224,7 @@
cur_s_no = cur_serial_no.split(',')
for x in cur_s_no:
- sql("update `tabSerial No` set amc_expiry_date = '%s', maintenance_status = 'Under AMC' where name = '%s'"% (amc_end_date,x))
+ webnotes.conn.sql("update `tabSerial No` set amc_expiry_date = '%s', maintenance_status = 'Under AMC' where name = '%s'"% (amc_end_date,x))
def on_update(self):
webnotes.conn.set(self.doc, 'status', 'Draft')
@@ -233,7 +232,7 @@
def validate_serial_no_warranty(self):
for d in getlist(self.doclist, 'item_maintenance_detail'):
if cstr(d.serial_no).strip():
- dt = sql("""select warranty_expiry_date, amc_expiry_date
+ dt = webnotes.conn.sql("""select warranty_expiry_date, amc_expiry_date
from `tabSerial No` where name = %s""", d.serial_no, as_dict=1)
if dt[0]['warranty_expiry_date'] and dt[0]['warranty_expiry_date'] >= d.start_date:
webnotes.msgprint("""Serial No: %s is already under warranty upto %s.
diff --git a/support/doctype/maintenance_visit/maintenance_visit.py b/support/doctype/maintenance_visit/maintenance_visit.py
index bf8767b..60cc371 100644
--- a/support/doctype/maintenance_visit/maintenance_visit.py
+++ b/support/doctype/maintenance_visit/maintenance_visit.py
@@ -8,7 +8,6 @@
from webnotes.model.bean import getlist
from webnotes import msgprint
-sql = webnotes.conn.sql
from utilities.transaction_base import TransactionBase
@@ -19,7 +18,7 @@
self.doclist = doclist
def get_item_details(self, item_code):
- item = sql("select item_name,description from `tabItem` where name = '%s'" %(item_code), as_dict=1)
+ item = webnotes.conn.sql("select item_name,description from `tabItem` where name = '%s'" %(item_code), as_dict=1)
ret = {
'item_name' : item and item[0]['item_name'] or '',
'description' : item and item[0]['description'] or ''
@@ -28,7 +27,7 @@
def validate_serial_no(self):
for d in getlist(self.doclist, 'maintenance_visit_details'):
- if d.serial_no and not sql("select name from `tabSerial No` where name = '%s' and docstatus != 2" % d.serial_no):
+ if d.serial_no and not webnotes.conn.sql("select name from `tabSerial No` where name = '%s' and docstatus != 2" % d.serial_no):
msgprint("Serial No: "+ d.serial_no + " not exists in the system")
raise Exception
@@ -52,7 +51,7 @@
elif self.doc.completion_status == 'Partially Completed':
status = 'Work In Progress'
else:
- nm = sql("select t1.name, t1.mntc_date, t2.service_person, t2.work_done from `tabMaintenance Visit` t1, `tabMaintenance Visit Purpose` t2 where t2.parent = t1.name and t1.completion_status = 'Partially Completed' and t2.prevdoc_docname = %s and t1.name!=%s and t1.docstatus = 1 order by t1.name desc limit 1", (d.prevdoc_docname, self.doc.name))
+ nm = webnotes.conn.sql("select t1.name, t1.mntc_date, t2.service_person, t2.work_done from `tabMaintenance Visit` t1, `tabMaintenance Visit Purpose` t2 where t2.parent = t1.name and t1.completion_status = 'Partially Completed' and t2.prevdoc_docname = %s and t1.name!=%s and t1.docstatus = 1 order by t1.name desc limit 1", (d.prevdoc_docname, self.doc.name))
if nm:
status = 'Work In Progress'
@@ -65,7 +64,7 @@
service_person = ''
work_done = ''
- sql("update `tabCustomer Issue` set resolution_date=%s, resolved_by=%s, resolution_details=%s, status=%s where name =%s",(mntc_date,service_person,work_done,status,d.prevdoc_docname))
+ webnotes.conn.sql("update `tabCustomer Issue` set resolution_date=%s, resolved_by=%s, resolution_details=%s, status=%s where name =%s",(mntc_date,service_person,work_done,status,d.prevdoc_docname))
def check_if_last_visit(self):
@@ -77,7 +76,7 @@
check_for_doctype = d.prevdoc_doctype
if check_for_docname:
- check = sql("select t1.name from `tabMaintenance Visit` t1, `tabMaintenance Visit Purpose` t2 where t2.parent = t1.name and t1.name!=%s and t2.prevdoc_docname=%s and t1.docstatus = 1 and (t1.mntc_date > %s or (t1.mntc_date = %s and t1.mntc_time > %s))", (self.doc.name, check_for_docname, self.doc.mntc_date, self.doc.mntc_date, self.doc.mntc_time))
+ check = webnotes.conn.sql("select t1.name from `tabMaintenance Visit` t1, `tabMaintenance Visit Purpose` t2 where t2.parent = t1.name and t1.name!=%s and t2.prevdoc_docname=%s and t1.docstatus = 1 and (t1.mntc_date > %s or (t1.mntc_date = %s and t1.mntc_time > %s))", (self.doc.name, check_for_docname, self.doc.mntc_date, self.doc.mntc_date, self.doc.mntc_time))
if check:
check_lst = [x[0] for x in check]
diff --git a/support/doctype/newsletter/newsletter.py b/support/doctype/newsletter/newsletter.py
index b201cc5..8e45768 100644
--- a/support/doctype/newsletter/newsletter.py
+++ b/support/doctype/newsletter/newsletter.py
@@ -76,22 +76,25 @@
sender = self.doc.send_from or webnotes.utils.get_formatted_email(self.doc.owner)
from webnotes.utils.email_lib.bulk import send
- webnotes.conn.auto_commit_on_many_writes = True
+
+ if not webnotes.flags.in_test:
+ webnotes.conn.auto_commit_on_many_writes = True
send(recipients = self.recipients, sender = sender,
subject = self.doc.subject, message = self.doc.message,
doctype = self.send_to_doctype, email_field = "email_id",
ref_doctype = self.doc.doctype, ref_docname = self.doc.name)
- webnotes.conn.auto_commit_on_many_writes = False
+ if not webnotes.flags.in_test:
+ webnotes.conn.auto_commit_on_many_writes = False
def validate_send(self):
if self.doc.fields.get("__islocal"):
webnotes.msgprint(_("""Please save the Newsletter before sending."""),
raise_exception=1)
- import conf
- if getattr(conf, "status", None) == "Trial":
+ from webnotes import conf
+ if (conf.get("status") or None) == "Trial":
webnotes.msgprint(_("""Sending newsletters is not allowed for Trial users, \
to prevent abuse of this feature."""), raise_exception=1)
@@ -105,7 +108,6 @@
}
-lead_naming_series = None
def create_lead(email_id):
"""create a lead if it does not exist"""
from email.utils import parseaddr
@@ -119,7 +121,7 @@
"email_id": email_id,
"lead_name": real_name or email_id,
"status": "Contacted",
- "naming_series": lead_naming_series or get_lead_naming_series(),
+ "naming_series": get_lead_naming_series(),
"company": webnotes.conn.get_default("company"),
"source": "Email"
})
@@ -127,7 +129,7 @@
def get_lead_naming_series():
"""gets lead's default naming series"""
- global lead_naming_series
+ lead_naming_series = None
naming_series_field = webnotes.get_doctype("Lead").get_field("naming_series")
if naming_series_field.default:
lead_naming_series = naming_series_field.default
diff --git a/support/doctype/newsletter/test_newsletter.py b/support/doctype/newsletter/test_newsletter.py
index fae3e40..3f4e021 100644
--- a/support/doctype/newsletter/test_newsletter.py
+++ b/support/doctype/newsletter/test_newsletter.py
@@ -45,20 +45,23 @@
"subject": "_Test Newsletter to Lead",
"send_to_type": "Lead",
"lead_source": "All",
- "message": "This is a test newsletter"
+ "message": "This is a test newsletter",
+ "send_from": "admin@example.com"
}],
[{
"doctype": "Newsletter",
"subject": "_Test Newsletter to Contact",
"send_to_type": "Contact",
"contact_type": "Customer",
- "message": "This is a test newsletter"
+ "message": "This is a test newsletter",
+ "send_from": "admin@example.com"
}],
[{
"doctype": "Newsletter",
"subject": "_Test Newsletter to Custom",
"send_to_type": "Custom",
"email_list": "test_custom@example.com, test_custom1@example.com, test_custom2@example.com",
- "message": "This is a test newsletter"
+ "message": "This is a test newsletter",
+ "send_from": "admin@example.com"
}],
]
diff --git a/support/doctype/support_ticket/get_support_mails.py b/support/doctype/support_ticket/get_support_mails.py
index 4dcb59e..395052a 100644
--- a/support/doctype/support_ticket/get_support_mails.py
+++ b/support/doctype/support_ticket/get_support_mails.py
@@ -54,7 +54,7 @@
def auto_close_tickets(self):
webnotes.conn.sql("""update `tabSupport Ticket` set status = 'Closed'
- where status = 'Waiting for Customer'
+ where status = 'Replied'
and date_sub(curdate(),interval 15 Day) > modified""")
def get_support_mails():
@@ -81,7 +81,7 @@
make(content=content, sender=sender, subject = subject,
doctype="Support Ticket", name=ticket.doc.name,
- date=mail.date if mail else today())
+ date=mail.date if mail else today(), sent_or_received="Received")
if mail:
mail.save_attachments_in_doc(ticket.doc)
diff --git a/support/doctype/support_ticket/support_ticket.js b/support/doctype/support_ticket/support_ticket.js
index 8e34a89..bcd3658 100644
--- a/support/doctype/support_ticket/support_ticket.js
+++ b/support/doctype/support_ticket/support_ticket.js
@@ -6,7 +6,7 @@
wn.provide("erpnext.support");
// TODO commonify this code
-erpnext.support.CustomerIssue = wn.ui.form.Controller.extend({
+erpnext.support.SupportTicket = wn.ui.form.Controller.extend({
customer: function() {
var me = this;
if(this.frm.doc.customer) {
@@ -18,7 +18,7 @@
}
});
-$.extend(cur_frm.cscript, new erpnext.support.CustomerIssue({frm: cur_frm}));
+$.extend(cur_frm.cscript, new erpnext.support.SupportTicket({frm: cur_frm}));
$.extend(cur_frm.cscript, {
onload: function(doc, dt, dn) {
diff --git a/support/doctype/support_ticket/support_ticket.py b/support/doctype/support_ticket/support_ticket.py
index bf2a9fb..eb68ff3 100644
--- a/support/doctype/support_ticket/support_ticket.py
+++ b/support/doctype/support_ticket/support_ticket.py
@@ -14,7 +14,7 @@
def get_sender(self, comm):
return webnotes.conn.get_value('Email Settings',None,'support_email')
-
+
def get_subject(self, comm):
return '[' + self.doc.name + '] ' + (comm.subject or 'No Subject Specified')
@@ -35,16 +35,7 @@
if self.doc.status == "Closed":
from webnotes.widgets.form.assign_to import clear
clear(self.doc.doctype, self.doc.name)
-
- def on_communication(self, comm):
- if comm.sender == self.get_sender(comm) or \
- webnotes.conn.get_value("Profile", extract_email_id(comm.sender), "user_type")=="System User":
- self.doc.status = "Waiting for Customer"
- else:
- self.doc.status = "Open"
- self.update_status()
- self.doc.save()
-
+
def set_lead_contact(self, email_id):
import email.utils
email_id = email.utils.parseaddr(email_id)
diff --git a/support/doctype/support_ticket/support_ticket.txt b/support/doctype/support_ticket/support_ticket.txt
index 9f385b2..76d9dcf 100644
--- a/support/doctype/support_ticket/support_ticket.txt
+++ b/support/doctype/support_ticket/support_ticket.txt
@@ -2,7 +2,7 @@
{
"creation": "2013-02-01 10:36:25",
"docstatus": 0,
- "modified": "2013-09-10 10:54:02",
+ "modified": "2013-10-03 16:45:41",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -71,7 +71,7 @@
"no_copy": 1,
"oldfieldname": "status",
"oldfieldtype": "Select",
- "options": "\nOpen\nTo Reply\nWaiting for Customer\nHold\nClosed",
+ "options": "Open\nReplied\nHold\nClosed",
"read_only": 0,
"reqd": 0,
"search_index": 1
diff --git a/support/report/maintenance_schedules/maintenance_schedules.txt b/support/report/maintenance_schedules/maintenance_schedules.txt
index 525f483..766eb20 100644
--- a/support/report/maintenance_schedules/maintenance_schedules.txt
+++ b/support/report/maintenance_schedules/maintenance_schedules.txt
@@ -2,7 +2,7 @@
{
"creation": "2013-05-06 14:25:21",
"docstatus": 0,
- "modified": "2013-05-06 14:32:47",
+ "modified": "2013-10-09 12:23:27",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -10,7 +10,7 @@
"doctype": "Report",
"is_standard": "Yes",
"name": "__common__",
- "query": "SELECT\n ms_item.scheduled_date as \"Schedule Date:Date:120\",\n\tms_item.item_code as \"Item Code:Link/Item:120\",\n\tms_item.item_name as \"Item Name::120\",\n\tms_item.serial_no as \"Serial No::120\",\n\tms_item.incharge_name as \"Incharge::120\",\n\tms.customer_name as \"Customer:Link/Customer:120\",\n\tms.address_display as \"Customer Address::120\",\n\tms.sales_order_no as \"Sales Order:Link/Sales Order:120\",\n\tms.company as \"Company:Link/Company:120\"\n\t\nFROM\n\t`tabMaintenance Schedule` ms, `tabMaintenance Schedule Detail` ms_item\nWHERE\n\tms.name = ms_item.parent and ms.docstatus = 1\nORDER BY\n\tms_item.scheduled_date asc, ms_item.item_code asc",
+ "query": "SELECT\n ms_sch.scheduled_date as \"Schedule Date:Date:120\",\n\tms_sch.item_code as \"Item Code:Link/Item:120\",\n\tms_sch.item_name as \"Item Name::120\",\n\tms_sch.serial_no as \"Serial No::120\",\n\tms_sch.incharge_name as \"Incharge::120\",\n\tms.customer_name as \"Customer:Link/Customer:120\",\n\tms.address_display as \"Customer Address::120\",\n\tms_item.prevdoc_docname as \"Sales Order:Link/Sales Order:120\",\n\tms.company as \"Company:Link/Company:120\"\n\t\nFROM\n\t`tabMaintenance Schedule` ms, \n `tabMaintenance Schedule Detail` ms_sch, \n `tabMaintenance Schedule Item` ms_item\nWHERE\n\tms.name = ms_sch.parent and ms.name = ms_item.parent and ms.docstatus = 1\nORDER BY\n\tms_sch.scheduled_date asc, ms_sch.item_code asc",
"ref_doctype": "Maintenance Schedule",
"report_name": "Maintenance Schedules",
"report_type": "Query Report"
diff --git a/utilities/demo/demo_control_panel.py b/utilities/demo/demo_control_panel.py
index 1f381d5..694f7d1 100644
--- a/utilities/demo/demo_control_panel.py
+++ b/utilities/demo/demo_control_panel.py
@@ -1,13 +1,16 @@
+from __future__ import unicode_literals
+import webnotes
- def on_login(self):
- from webnotes.utils import validate_email_add
- import conf
- if hasattr(conf, "demo_notify_url"):
- if webnotes.form_dict.lead_email and validate_email_add(webnotes.form_dict.lead_email):
- import requests
- response = requests.post(conf.demo_notify_url, data={
- "cmd":"portal.utils.send_message",
- "subject":"Logged into Demo",
- "sender": webnotes.form_dict.lead_email,
- "message": "via demo.erpnext.com"
- })
\ No newline at end of file
+class CustomDocType(DocType):
+ def on_login(self):
+ from webnotes.utils import validate_email_add
+ from webnotes import conf
+ if "demo_notify_url" in conf:
+ if webnotes.form_dict.lead_email and validate_email_add(webnotes.form_dict.lead_email):
+ import requests
+ response = requests.post(conf.demo_notify_url, data={
+ "cmd":"portal.utils.send_message",
+ "subject":"Logged into Demo",
+ "sender": webnotes.form_dict.lead_email,
+ "message": "via demo.erpnext.com"
+ })
diff --git a/utilities/demo/demo_docs/Lead.csv b/utilities/demo/demo_docs/Lead.csv
index c00ab44..a7004e4 100644
--- a/utilities/demo/demo_docs/Lead.csv
+++ b/utilities/demo/demo_docs/Lead.csv
@@ -1,68 +1,68 @@
-Data Import Template,,,,,,,,,,,,,,,,,,,,,,,,,,,
-Table:,Lead,,,,,,,,,,,,,,,,,,,,,,,,,,
-,,,,,,,,,,,,,,,,,,,,,,,,,,,
-,,,,,,,,,,,,,,,,,,,,,,,,,,,
-Notes:,,,,,,,,,,,,,,,,,,,,,,,,,,,
-Please do not change the template headings.,,,,,,,,,,,,,,,,,,,,,,,,,,,
-First data column must be blank.,,,,,,,,,,,,,,,,,,,,,,,,,,,
-Only mandatory fields are necessary for new records. You can delete non-mandatory columns if you wish.,,,,,,,,,,,,,,,,,,,,,,,,,,,
-"For updating, you can update only selective columns.",,,,,,,,,,,,,,,,,,,,,,,,,,,
-"If you are uploading new records, leave the ""name"" (ID) column blank.",,,,,,,,,,,,,,,,,,,,,,,,,,,
-"If you are uploading new records, ""Naming Series"" becomes mandatory, if present.",,,,,,,,,,,,,,,,,,,,,,,,,,,
-You can only upload upto 5000 records in one go. (may be less in some cases),,,,,,,,,,,,,,,,,,,,,,,,,,,
-,,,,,,,,,,,,,,,,,,,,,,,,,,,
-Column Labels,ID,Contact Name,Status,Naming Series,Company Name,Email Id,Source,From Customer,Campaign Name,Remark,Phone,Mobile No.,Fax,Website,Territory,Lead Type,Lead Owner,Market Segment,Industry,Request Type,Lost Reason,Next Contact By,Next Contact Date,Last Contact Date,Company,Unsubscribed,Blog Subscriber
-Column Name:,name,lead_name,status,naming_series,company_name,email_id,source,customer,campaign_name,remark,phone,mobile_no,fax,website,territory,type,lead_owner,market_segment,industry,request_type,order_lost_reason,contact_by,contact_date,last_contact_date,company,unsubscribed,blog_subscriber
-Mandatory:,Yes,Yes,Yes,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No
-Type:,Data (text),Data,Select,Select,Data,Data,Select,Link,Link,Small Text,Data,Data,Data,Data,Link,Select,Link,Select,Link,Select,Link,Link,Date,Date,Link,Check,Check
-Info:,,,"One of: Open, Replied, Attempted to Contact, Contact in Future, Contacted, Interested, Not interested, Lead Lost, Converted","One of: LEAD, LEAD/10-11/, LEAD/MUMBAI/",,,"One of: Advertisement, Blog Post, Campaign, Call, Customer, Exhibition, Supplier, Website, Email",Valid Customer,Valid Campaign,,,,,,Valid Territory,"One of: Client, Channel Partner, Consultant",Valid Profile,"One of: Lower Income, Middle Income, Upper Income",Valid Industry Type,"One of: Product Enquiry, Request for Information, Suggestions, Other",Valid Quotation Lost Reason,Valid Profile,,,Valid Company,0 or 1,0 or 1
-Start entering data below this line,,,,,,,,,,,,,,,,,,,,,,,,,,,
-,,Mart Lakeman,Passive,,Zany Brainy,MartLakeman@einrot.com,,,,,,,,,,,,,,,,,,,,,
-,,Saga Lundqvist,Passive,,Patterson-Fletcher,SagaLundqvist@dayrep.com,,,,,,,,,,,,,,,,,,,,,
-,,Adna Sjöberg,Passive,,Griff's Hamburgers,AdnaSjoberg@gustr.com,,,,,,,,,,,,,,,,,,,,,
-,,Ida Svendsen,Passive,,Rhodes Furniture,IdaDSvendsen@superrito.com,,,,,,,,,,,,,,,,,,,,,
-,,Emppu Hämeenniemi,Passive,,Burger Chef,EmppuHameenniemi@teleworm.us,,,,,,,,,,,,,,,,,,,,,
-,,Eugenio Pisano,Passive,,Stratabiz,EugenioPisano@cuvox.de,,,,,,,,,,,,,,,,,,,,,
-,,Semhar Hagos,Passive,,Home Quarters Warehouse,SemharHagos@einrot.com,,,,,,,,,,,,,,,,,,,,,
-,,Branimira Ivanković,Passive,,Enviro Architectural Designs,BranimiraIvankovic@einrot.com,,,,,,,,,,,,,,,,,,,,,
-,,Shelly Fields,Passive,,Ideal Garden Management,ShellyLFields@superrito.com,,,,,,,,,,,,,,,,,,,,,
-,,Leo Mikulić,Passive,,Listen Up,LeoMikulic@gustr.com,,,,,,,,,,,,,,,,,,,,,
-,,Denisa Jarošová,Passive,,I. Magnin,DenisaJarosova@teleworm.us,,,,,,,,,,,,,,,,,,,,,
-,,Janek Rutkowski,Passive,,First Rate Choice,JanekRutkowski@dayrep.com,,,,,,,,,,,,,,,,,,,,,
-,,美月 宇藤,Passive,,Multi Tech Development,mm@gustr.com,,,,,,,,,,,,,,,,,,,,,
-,,Даниил Афанасьев,Passive,,National Auto Parts,dd@einrot.com,,,,,,,,,,,,,,,,,,,,,
-,,Zorislav Petković,Passive,,Integra Investment Plan,ZorislavPetkovic@cuvox.de,,,,,,,,,,,,,,,,,,,,,
-,,Nanao Niwa,Passive,,The Lawn Guru,NanaoNiwa@superrito.com,,,,,,,,,,,,,,,,,,,,,
-,,Hreiðar Jörundsson,Passive,,Buena Vista Realty Service,HreiarJorundsson@armyspy.com,,,,,,,,,,,,,,,,,,,,,
-,,Lai Chu,Passive,,Bountiful Harvest Health Food Store,ChuThiBichLai@einrot.com,,,,,,,,,,,,,,,,,,,,,
-,,Victor Aksakov,Passive,,P. Samuels Men's Clothiers,VictorAksakov@dayrep.com,,,,,,,,,,,,,,,,,,,,,
-,,Saidalim Bisliev,Passive,,Vinyl Fever,SaidalimBisliev@cuvox.de,,,,,,,,,,,,,,,,,,,,,
-,,Totte Jakobsson,Passive,,Garden Master,TotteJakobsson@armyspy.com,,,,,,,,,,,,,,,,,,,,,
-,,Naná Armas,Passive,,Big Apple,NanaArmasRobles@cuvox.de,,,,,,,,,,,,,,,,,,,,,
-,,Walerian Duda,Passive,,Monk House Sales,WalerianDuda@dayrep.com,,,,,,,,,,,,,,,,,,,,,
-,,Moarimikashi ,Passive,,ManCharm,Moarimikashi@teleworm.us,,,,,,,,,,,,,,,,,,,,,
-,,Dobromił Dąbrowski ,Passive,,Custom Lawn Care,DobromilDabrowski@dayrep.com,,,,,,,,,,,,,,,,,,,,,
-,,Teigan Sinclair,Passive,,The Serendipity Dip,TeiganSinclair@gustr.com,,,,,,,,,,,,,,,,,,,,,
-,,Fahad Guirguis,Passive,,Cavages,FahadSaidGuirguis@gustr.com,,,,,,,,,,,,,,,,,,,,,
-,,Morten Olsen,Passive,,Gallenkamp,MortenJOlsen@armyspy.com,,,,,,,,,,,,,,,,,,,,,
-,,Christian Baecker,Passive,,Webcom Business Services,ChristianBaecker@armyspy.com,,,,,,,,,,,,,,,,,,,,,
-,,Sebastianus Dohmen,Passive,,Accord Investments,SebastianusDohmen@cuvox.de,,,,,,,,,,,,,,,,,,,,,
-,,Eero Koskinen,Passive,,American Appliance,EeroKoskinen@superrito.com,,,,,,,,,,,,,,,,,,,,,
-,,富奎 盧,Passive,,Bettendorf's,LuFuKui@teleworm.us,,,,,,,,,,,,,,,,,,,,,
-,,Milica Jelić,Passive,,House Of Denmark,MilicaJelic@dayrep.com,,,,,,,,,,,,,,,,,,,,,
-,,Barbora Holubová,Passive,,10000 Auto Parts,BarboraHolubova@cuvox.de,,,,,,,,,,,,,,,,,,,,,
-,,Marta Kos,Passive,,Mages,MartaKos@einrot.com,,,,,,,,,,,,,,,,,,,,,
-,,Simret Zula,Passive,,CSK Auto,SimretZula@cuvox.de,,,,,,,,,,,,,,,,,,,,,
-,,Kamil Chlubna,Passive,,Eagle Hardware & Garden,KamilChlubna@einrot.com,,,,,,,,,,,,,,,,,,,,,
-,,Aceline Bolduc,Passive,,Rustler Steak House,AcelineBolduc@armyspy.com,,,,,,,,,,,,,,,,,,,,,
-,,Lucie Stupková,Passive,,ABCO Foods,LucieStupkova@gustr.com,,,,,,,,,,,,,,,,,,,,,
-,,Roland Solvik,Passive,,Trak Auto,RolandSolvik@cuvox.de,,,,,,,,,,,,,,,,,,,,,
-,,Mekirinzukushitakufu ,Passive,,Choices,Mekirinzukushitakufu@teleworm.us,,,,,,,,,,,,,,,,,,,,,
-,,Mukharbek Sultanovich,Passive,,Megatronic,MukharbekSultanovich@cuvox.de,,,,,,,,,,,,,,,,,,,,,
-,,Osman Amanuel,Passive,,Handy Dan,OsmanAmanuel@dayrep.com,,,,,,,,,,,,,,,,,,,,,
-,,幸子 阪部,Passive,,Channel Home Centers,dd@armyspy.com,,,,,,,,,,,,,,,,,,,,,
-,,Masakazu Kamitani,Passive,,Honest Air Group,MasakazuKamitani@superrito.com,,,,,,,,,,,,,,,,,,,,,
-,,Omran Sabbagh,Passive,,Pleasures and Pasttimes,OmranNuhaidSabbagh@einrot.com,,,,,,,,,,,,,,,,,,,,,
-,,Rikako Matsumura,Passive,,Lazysize,RikakoMatsumura@einrot.com,,,,,,,,,,,,,,,,,,,,,
-,,Anayolisa Chukwukadibia,Passive,,Prestiga-Biz,AnayolisaChukwukadibia@einrot.com,,,,,,,,,,,,,,,,,,,,,
-,,Gudmunda Hinna,Passive,,Childs Restaurants,GudmundaHinna@armyspy.com,,,,,,,,,,,,,,,,,,,,,
\ No newline at end of file
+Data Import Template,,,,,,,,,,,,,,,,,,,,,,,,,,
+Table:,Lead,,,,,,,,,,,,,,,,,,,,,,,,,
+,,,,,,,,,,,,,,,,,,,,,,,,,,
+,,,,,,,,,,,,,,,,,,,,,,,,,,
+Notes:,,,,,,,,,,,,,,,,,,,,,,,,,,
+Please do not change the template headings.,,,,,,,,,,,,,,,,,,,,,,,,,,
+First data column must be blank.,,,,,,,,,,,,,,,,,,,,,,,,,,
+Only mandatory fields are necessary for new records. You can delete non-mandatory columns if you wish.,,,,,,,,,,,,,,,,,,,,,,,,,,
+"For updating, you can update only selective columns.",,,,,,,,,,,,,,,,,,,,,,,,,,
+"If you are uploading new records, leave the ""name"" (ID) column blank.",,,,,,,,,,,,,,,,,,,,,,,,,,
+"If you are uploading new records, ""Naming Series"" becomes mandatory, if present.",,,,,,,,,,,,,,,,,,,,,,,,,,
+You can only upload upto 5000 records in one go. (may be less in some cases),,,,,,,,,,,,,,,,,,,,,,,,,,
+,,,,,,,,,,,,,,,,,,,,,,,,,,
+Column Labels,ID,Contact Name,Status,Naming Series,Company Name,Email Id,Source,From Customer,Campaign Name,Remark,Phone,Mobile No.,Fax,Website,Territory,Lead Type,Lead Owner,Market Segment,Industry,Request Type,Next Contact By,Next Contact Date,Company,Unsubscribed,Blog Subscriber,
+Column Name:,name,lead_name,status,naming_series,company_name,email_id,source,customer,campaign_name,remark,phone,mobile_no,fax,website,territory,type,lead_owner,market_segment,industry,request_type,contact_by,contact_date,company,unsubscribed,blog_subscriber,
+Mandatory:,Yes,Yes,Yes,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,
+Type:,Data (text),Data,Select,Select,Data,Data,Select,Link,Link,Small Text,Data,Data,Data,Data,Link,Select,Link,Select,Link,Select,Link,Date,Link,Check,Check,
+Info:,,,"Lead, Open, Replied, Opportunity, Interested, Converted, Do Not Contact","One of: LEAD, LEAD/10-11/, LEAD/MUMBAI/",,,"One of: Advertisement, Blog Post, Campaign, Call, Customer, Exhibition, Supplier, Website, Email",Valid Customer,Valid Campaign,,,,,,Valid Territory,"One of: Client, Channel Partner, Consultant",Valid Profile,"One of: Lower Income, Middle Income, Upper Income",Valid Industry Type,"One of: Product Enquiry, Request for Information, Suggestions, Other",Valid Profile,,Valid Company,0 or 1,0 or 1,
+Start entering data below this line,,,,,,,,,,,,,,,,,,,,,,,,,,
+,,Mart Lakeman,Lead,,Zany Brainy,MartLakeman@einrot.com,,,,,,,,,,,,,,,,,,,,
+,,Saga Lundqvist,Lead,,Patterson-Fletcher,SagaLundqvist@dayrep.com,,,,,,,,,,,,,,,,,,,,
+,,Adna Sjöberg,Lead,,Griff's Hamburgers,AdnaSjoberg@gustr.com,,,,,,,,,,,,,,,,,,,,
+,,Ida Svendsen,Lead,,Rhodes Furniture,IdaDSvendsen@superrito.com,,,,,,,,,,,,,,,,,,,,
+,,Emppu Hämeenniemi,Lead,,Burger Chef,EmppuHameenniemi@teleworm.us,,,,,,,,,,,,,,,,,,,,
+,,Eugenio Pisano,Lead,,Stratabiz,EugenioPisano@cuvox.de,,,,,,,,,,,,,,,,,,,,
+,,Semhar Hagos,Lead,,Home Quarters Warehouse,SemharHagos@einrot.com,,,,,,,,,,,,,,,,,,,,
+,,Branimira Ivanković,Lead,,Enviro Architectural Designs,BranimiraIvankovic@einrot.com,,,,,,,,,,,,,,,,,,,,
+,,Shelly Fields,Lead,,Ideal Garden Management,ShellyLFields@superrito.com,,,,,,,,,,,,,,,,,,,,
+,,Leo Mikulić,Lead,,Listen Up,LeoMikulic@gustr.com,,,,,,,,,,,,,,,,,,,,
+,,Denisa Jarošová,Lead,,I. Magnin,DenisaJarosova@teleworm.us,,,,,,,,,,,,,,,,,,,,
+,,Janek Rutkowski,Lead,,First Rate Choice,JanekRutkowski@dayrep.com,,,,,,,,,,,,,,,,,,,,
+,,美月 宇藤,Lead,,Multi Tech Development,mm@gustr.com,,,,,,,,,,,,,,,,,,,,
+,,Даниил Афанасьев,Lead,,National Auto Parts,dd@einrot.com,,,,,,,,,,,,,,,,,,,,
+,,Zorislav Petković,Lead,,Integra Investment Plan,ZorislavPetkovic@cuvox.de,,,,,,,,,,,,,,,,,,,,
+,,Nanao Niwa,Lead,,The Lawn Guru,NanaoNiwa@superrito.com,,,,,,,,,,,,,,,,,,,,
+,,Hreiðar Jörundsson,Lead,,Buena Vista Realty Service,HreiarJorundsson@armyspy.com,,,,,,,,,,,,,,,,,,,,
+,,Lai Chu,Lead,,Bountiful Harvest Health Food Store,ChuThiBichLai@einrot.com,,,,,,,,,,,,,,,,,,,,
+,,Victor Aksakov,Lead,,P. Samuels Men's Clothiers,VictorAksakov@dayrep.com,,,,,,,,,,,,,,,,,,,,
+,,Saidalim Bisliev,Lead,,Vinyl Fever,SaidalimBisliev@cuvox.de,,,,,,,,,,,,,,,,,,,,
+,,Totte Jakobsson,Lead,,Garden Master,TotteJakobsson@armyspy.com,,,,,,,,,,,,,,,,,,,,
+,,Naná Armas,Lead,,Big Apple,NanaArmasRobles@cuvox.de,,,,,,,,,,,,,,,,,,,,
+,,Walerian Duda,Lead,,Monk House Sales,WalerianDuda@dayrep.com,,,,,,,,,,,,,,,,,,,,
+,,Moarimikashi ,Lead,,ManCharm,Moarimikashi@teleworm.us,,,,,,,,,,,,,,,,,,,,
+,,Dobromił Dąbrowski ,Lead,,Custom Lawn Care,DobromilDabrowski@dayrep.com,,,,,,,,,,,,,,,,,,,,
+,,Teigan Sinclair,Lead,,The Serendipity Dip,TeiganSinclair@gustr.com,,,,,,,,,,,,,,,,,,,,
+,,Fahad Guirguis,Lead,,Cavages,FahadSaidGuirguis@gustr.com,,,,,,,,,,,,,,,,,,,,
+,,Morten Olsen,Lead,,Gallenkamp,MortenJOlsen@armyspy.com,,,,,,,,,,,,,,,,,,,,
+,,Christian Baecker,Lead,,Webcom Business Services,ChristianBaecker@armyspy.com,,,,,,,,,,,,,,,,,,,,
+,,Sebastianus Dohmen,Lead,,Accord Investments,SebastianusDohmen@cuvox.de,,,,,,,,,,,,,,,,,,,,
+,,Eero Koskinen,Lead,,American Appliance,EeroKoskinen@superrito.com,,,,,,,,,,,,,,,,,,,,
+,,富奎 盧,Lead,,Bettendorf's,LuFuKui@teleworm.us,,,,,,,,,,,,,,,,,,,,
+,,Milica Jelić,Lead,,House Of Denmark,MilicaJelic@dayrep.com,,,,,,,,,,,,,,,,,,,,
+,,Barbora Holubová,Lead,,10000 Auto Parts,BarboraHolubova@cuvox.de,,,,,,,,,,,,,,,,,,,,
+,,Marta Kos,Lead,,Mages,MartaKos@einrot.com,,,,,,,,,,,,,,,,,,,,
+,,Simret Zula,Lead,,CSK Auto,SimretZula@cuvox.de,,,,,,,,,,,,,,,,,,,,
+,,Kamil Chlubna,Lead,,Eagle Hardware & Garden,KamilChlubna@einrot.com,,,,,,,,,,,,,,,,,,,,
+,,Aceline Bolduc,Lead,,Rustler Steak House,AcelineBolduc@armyspy.com,,,,,,,,,,,,,,,,,,,,
+,,Lucie Stupková,Lead,,ABCO Foods,LucieStupkova@gustr.com,,,,,,,,,,,,,,,,,,,,
+,,Roland Solvik,Lead,,Trak Auto,RolandSolvik@cuvox.de,,,,,,,,,,,,,,,,,,,,
+,,Mekirinzukushitakufu ,Lead,,Choices,Mekirinzukushitakufu@teleworm.us,,,,,,,,,,,,,,,,,,,,
+,,Mukharbek Sultanovich,Lead,,Megatronic,MukharbekSultanovich@cuvox.de,,,,,,,,,,,,,,,,,,,,
+,,Osman Amanuel,Lead,,Handy Dan,OsmanAmanuel@dayrep.com,,,,,,,,,,,,,,,,,,,,
+,,幸子 阪部,Lead,,Channel Home Centers,dd@armyspy.com,,,,,,,,,,,,,,,,,,,,
+,,Masakazu Kamitani,Lead,,Honest Air Group,MasakazuKamitani@superrito.com,,,,,,,,,,,,,,,,,,,,
+,,Omran Sabbagh,Lead,,Pleasures and Pasttimes,OmranNuhaidSabbagh@einrot.com,,,,,,,,,,,,,,,,,,,,
+,,Rikako Matsumura,Lead,,Lazysize,RikakoMatsumura@einrot.com,,,,,,,,,,,,,,,,,,,,
+,,Anayolisa Chukwukadibia,Lead,,Prestiga-Biz,AnayolisaChukwukadibia@einrot.com,,,,,,,,,,,,,,,,,,,,
+,,Gudmunda Hinna,Lead,,Childs Restaurants,GudmundaHinna@armyspy.com,,,,,,,,,,,,,,,,,,,,
\ No newline at end of file
diff --git a/utilities/demo/demo_docs/Profile.csv b/utilities/demo/demo_docs/Profile.csv
index 2e7a2ce..eb456c1 100644
--- a/utilities/demo/demo_docs/Profile.csv
+++ b/utilities/demo/demo_docs/Profile.csv
@@ -1,40 +1,40 @@
-Data Import Template,,,,,,,,,,,,,,,,,,,,,
-Table:,Profile,,,,,,,,,,,,,,,,,,,,
-,,,,,,,,,,,,,,,,,,,,,
-,,,,,,,,,,,,,,,,,,,,,
-Notes:,,,,,,,,,,,,,,,,,,,,,
-Please do not change the template headings.,,,,,,,,,,,,,,,,,,,,,
-First data column must be blank.,,,,,,,,,,,,,,,,,,,,,
-"If you are uploading new records, leave the ""name"" (ID) column blank.",,,,,,,,,,,,,,,,,,,,,
-"If you are uploading new records, ""Naming Series"" becomes mandatory, if present.",,,,,,,,,,,,,,,,,,,,,
-Only mandatory fields are necessary for new records. You can delete non-mandatory columns if you wish.,,,,,,,,,,,,,,,,,,,,,
-"For updating, you can update only selective columns.",,,,,,,,,,,,,,,,,,,,,
-You can only upload upto 5000 records in one go. (may be less in some cases),,,,,,,,,,,,,,,,,,,,,
-,,,,,,,,,,,,,,,,,,,,,
-DocType:,Profile,,,,,,,,,,,,,,,,,,,,
-Column Labels:,ID,Email,First Name,User Type,Enabled,Middle Name (Optional),Last Name,Send Invite Email,Language,Birth Date,Gender,New Password,User Image,Background Image,Bio,Email Signature,Login After,Login Before,Restrict IP,Last Login,Last IP
-Column Name:,name,email,first_name,user_type,enabled,middle_name,last_name,send_invite_email,language,birth_date,gender,new_password,user_image,background_image,bio,email_signature,login_after,login_before,restrict_ip,last_login,last_ip
-Mandatory:,Yes,Yes,Yes,Yes,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No
-Type:,Data (text),Data,Data,Select,Check,Data,Data,Check,Select,Date,Select,Password,Select,Select,Small Text,Small Text,Int,Int,Data,Read Only,Read Only
-Info:,,,,"One of: System User, Website User",0 or 1,,,0 or 1,"One of: العربية, Deutsch, english, español, français, हिंदी, Hrvatski, nederlands, português, português brasileiro, српски, தமிழ், ไทย",,"One of: Male, Female, Other",,One of: attach_files:,One of: attach_files:,,,Integer,Integer,,,
-Start entering data below this line,,,,,,,,,,,,,,,,,,,,,
-,,DikmanShervashidze@armyspy.com,Dikman,System User,1,V,Shervashidze,0,,,,testpass,,,,,,,,,
-,,Zukutakitoteka@teleworm.us,Zukutakitoteka,System User,1,,,0,,,,testpass,,,,,,,,,
-,,HatsueKashiwagi@cuvox.de,Hatsue,System User,1,H,Kashiwagi,0,,,,testpass,,,,,,,,,
-,,NuranVerkleij@einrot.com,Nuran,System User,1,T,Verkleij,0,,,,testpass,,,,,,,,,
-,,aromn@armyspy.com,Дмитрий,System User,1,З,Пирогов,0,,,,testpass,,,,,,,,,
-,,TildeLindqvist@cuvox.de,Tilde,System User,1,T,Lindqvist,0,,,,testpass,,,,,,,,,
-,,MichalSobczak@teleworm.us,Michał,System User,1,S,Sobczak,0,,,,testpass,,,,,,,,,
-,,GabrielleLoftus@superrito.com,Gabrielle,System User,1,J,Loftus,0,,,,testpass,,,,,,,,,
-,,VakhitaRyzaev@teleworm.us,Vakhita,System User,1,A,Ryzaev,0,,,,testpass,,,,,,,,,
-,,CharmaineGaudreau@cuvox.de,Charmaine,System User,1,D,Gaudreau,0,,,,testpass,,,,,,,,,
-,,RafaelaMaartens@cuvox.de,Rafaëla,System User,1,Z,Maartens,0,,,,testpass,,,,,,,,,
-,,NuguseYohannes@dayrep.com,Nuguse,System User,0,S,Yohannes,0,,,,testpass,,,,,,,,,
-,,panca@armyspy.com,Раиса,System User,0,В,Белякова,0,,,,testpass,,,,,,,,,
-,,CaYinLong@gustr.com,胤隆,System User,1,婷,蔡,0,,,,testpass,,,,,,,,,
-,,FreddieScott@armyspy.com,Freddie,System User,1,A,Scott,0,,,,testpass,,,,,,,,,
-,,BergoraVigfusdottir@superrito.com,Bergþóra,System User,1,Ö,Vigfúsdóttir,0,,,,testpass,,,,,,,,,
-,,WardNajmalDinKalb@cuvox.de,Ward,System User,1,N,Kalb,0,,,,testpass,,,,,,,,,
-,,WanMai@teleworm.us,Wan,System User,1,A,Mai,0,,,,testpass,,,,,,,,,
-,,LeonAbdulov@superrito.com,Leon,System User,1,A,Abdulov,0,,,,testpass,,,,,,,,,
-,,SabinaNovotna@superrito.com,Sabina,System User,1,J,Novotná,0,,,,testpass,,,,,,,,,
\ No newline at end of file
+Data Import Template,,,,,,,,,,,,,,,,,,,,
+Table:,Profile,,,,,,,,,,,,,,,,,,,
+,,,,,,,,,,,,,,,,,,,,
+,,,,,,,,,,,,,,,,,,,,
+Notes:,,,,,,,,,,,,,,,,,,,,
+Please do not change the template headings.,,,,,,,,,,,,,,,,,,,,
+First data column must be blank.,,,,,,,,,,,,,,,,,,,,
+"If you are uploading new records, leave the ""name"" (ID) column blank.",,,,,,,,,,,,,,,,,,,,
+"If you are uploading new records, ""Naming Series"" becomes mandatory, if present.",,,,,,,,,,,,,,,,,,,,
+Only mandatory fields are necessary for new records. You can delete non-mandatory columns if you wish.,,,,,,,,,,,,,,,,,,,,
+"For updating, you can update only selective columns.",,,,,,,,,,,,,,,,,,,,
+You can only upload upto 5000 records in one go. (may be less in some cases),,,,,,,,,,,,,,,,,,,,
+,,,,,,,,,,,,,,,,,,,,
+DocType:,Profile,,,,,,,,,,,,,,,,,,,
+Column Labels:,ID,Email,First Name,User Type,Enabled,Middle Name (Optional),Last Name,Language,Birth Date,Gender,New Password,User Image,Background Image,Bio,Email Signature,Login After,Login Before,Restrict IP,Last Login,Last IP
+Column Name:,name,email,first_name,user_type,enabled,middle_name,last_name,language,birth_date,gender,new_password,user_image,background_image,bio,email_signature,login_after,login_before,restrict_ip,last_login,last_ip
+Mandatory:,Yes,Yes,Yes,Yes,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No,No
+Type:,Data (text),Data,Data,Select,Check,Data,Data,Select,Date,Select,Password,Select,Select,Small Text,Small Text,Int,Int,Data,Read Only,Read Only
+Info:,,,,"One of: System User, Website User",0 or 1,,,"One of: العربية, Deutsch, english, español, français, हिंदी, Hrvatski, nederlands, português, português brasileiro, српски, தமிழ், ไทย",,"One of: Male, Female, Other",,One of: attach_files:,One of: attach_files:,,,Integer,Integer,,,
+Start entering data below this line,,,,,,,,,,,,,,,,,,,,
+,,DikmanShervashidze@armyspy.com,Dikman,System User,1,V,Shervashidze,,,,testpass,,,,,,,,,
+,,Zukutakitoteka@teleworm.us,Zukutakitoteka,System User,1,,,,,,testpass,,,,,,,,,
+,,HatsueKashiwagi@cuvox.de,Hatsue,System User,1,H,Kashiwagi,,,,testpass,,,,,,,,,
+,,NuranVerkleij@einrot.com,Nuran,System User,1,T,Verkleij,,,,testpass,,,,,,,,,
+,,aromn@armyspy.com,Дмитрий,System User,1,З,Пирогов,,,,testpass,,,,,,,,,
+,,TildeLindqvist@cuvox.de,Tilde,System User,1,T,Lindqvist,,,,testpass,,,,,,,,,
+,,MichalSobczak@teleworm.us,Michał,System User,1,S,Sobczak,,,,testpass,,,,,,,,,
+,,GabrielleLoftus@superrito.com,Gabrielle,System User,1,J,Loftus,,,,testpass,,,,,,,,,
+,,VakhitaRyzaev@teleworm.us,Vakhita,System User,1,A,Ryzaev,,,,testpass,,,,,,,,,
+,,CharmaineGaudreau@cuvox.de,Charmaine,System User,1,D,Gaudreau,,,,testpass,,,,,,,,,
+,,RafaelaMaartens@cuvox.de,Rafaëla,System User,1,Z,Maartens,,,,testpass,,,,,,,,,
+,,NuguseYohannes@dayrep.com,Nuguse,System User,0,S,Yohannes,,,,testpass,,,,,,,,,
+,,panca@armyspy.com,Раиса,System User,0,В,Белякова,,,,testpass,,,,,,,,,
+,,CaYinLong@gustr.com,胤隆,System User,1,婷,蔡,,,,testpass,,,,,,,,,
+,,FreddieScott@armyspy.com,Freddie,System User,1,A,Scott,,,,testpass,,,,,,,,,
+,,BergoraVigfusdottir@superrito.com,Bergþóra,System User,1,Ö,Vigfúsdóttir,,,,testpass,,,,,,,,,
+,,WardNajmalDinKalb@cuvox.de,Ward,System User,1,N,Kalb,,,,testpass,,,,,,,,,
+,,WanMai@teleworm.us,Wan,System User,1,A,Mai,,,,testpass,,,,,,,,,
+,,LeonAbdulov@superrito.com,Leon,System User,1,A,Abdulov,,,,testpass,,,,,,,,,
+,,SabinaNovotna@superrito.com,Sabina,System User,1,J,Novotná,,,,testpass,,,,,,,,,
diff --git a/utilities/demo/make_demo.py b/utilities/demo/make_demo.py
index 046e81a..21da30b 100644
--- a/utilities/demo/make_demo.py
+++ b/utilities/demo/make_demo.py
@@ -30,17 +30,27 @@
}
def make(reset=False, simulate=True):
- #webnotes.print_messages = True
- webnotes.mute_emails = True
- webnotes.rollback_on_exception = True
+ #webnotes.flags.print_messages = True
+ webnotes.flags.mute_emails = True
+ webnotes.flags.rollback_on_exception = True
+
+ if not webnotes.conf.demo_db_name:
+ raise Exception("conf.py does not have demo_db_name")
if reset:
setup()
+ else:
+ if webnotes.conn:
+ webnotes.conn.close()
+
+ webnotes.connect(db_name=webnotes.conf.demo_db_name)
+
if simulate:
_simulate()
-
+
def setup():
install()
+ webnotes.connect(db_name=webnotes.conf.demo_db_name)
complete_setup()
make_customers_suppliers_contacts()
make_items()
@@ -65,7 +75,7 @@
for i in xrange(runs_for):
print current_date.strftime("%Y-%m-%d")
- webnotes.utils.current_date = current_date
+ webnotes.local.current_date = current_date
if current_date.weekday() in (5, 6):
current_date = webnotes.utils.add_days(current_date, 1)
@@ -138,20 +148,23 @@
# make purchase requests
if can_make("Purchase Receipt"):
from buying.doctype.purchase_order.purchase_order import make_purchase_receipt
+ from stock.stock_ledger import NegativeStockError
report = "Purchase Order Items To Be Received"
for po in list(set([r[0] for r in query_report.run(report)["result"] if r[0]!="Total"]))[:how_many("Purchase Receipt")]:
pr = webnotes.bean(make_purchase_receipt(po))
pr.doc.posting_date = current_date
pr.doc.fiscal_year = "2013"
pr.insert()
- pr.submit()
- webnotes.conn.commit()
+ try:
+ pr.submit()
+ webnotes.conn.commit()
+ except NegativeStockError: pass
# make delivery notes (if possible)
if can_make("Delivery Note"):
from selling.doctype.sales_order.sales_order import make_delivery_note
from stock.stock_ledger import NegativeStockError
- from stock.doctype.stock_ledger_entry.stock_ledger_entry import SerialNoRequiredError, SerialNoQtyError
+ from stock.doctype.serial_no.serial_no import SerialNoRequiredError, SerialNoQtyError
report = "Ordered Items To Be Delivered"
for so in list(set([r[0] for r in query_report.run(report)["result"] if r[0]!="Total"]))[:how_many("Delivery Note")]:
dn = webnotes.bean(make_delivery_note(so))
@@ -357,13 +370,14 @@
def install():
print "Creating Fresh Database..."
from webnotes.install_lib.install import Installer
- import conf
+ from webnotes import conf
inst = Installer('root')
- inst.import_from_db(conf.demo_db_name, verbose = 1)
+ inst.install(conf.demo_db_name, verbose=1, force=1)
def complete_setup():
print "Complete Setup..."
- webnotes.get_obj("Setup Control").setup_account({
+ from setup.page.setup_wizard.setup_wizard import setup_account
+ setup_account({
"first_name": "Test",
"last_name": "User",
"fy_start": "1st Jan",
@@ -412,7 +426,7 @@
for doctype in dt:
print "Importing", doctype.replace("_", " "), "..."
- webnotes.form_dict = webnotes._dict()
+ webnotes.local.form_dict = webnotes._dict()
if submit:
webnotes.form_dict["params"] = json.dumps({"_submit": 1})
webnotes.uploaded_file = os.path.join(os.path.dirname(__file__), "demo_docs", doctype+".csv")
diff --git a/utilities/demo/make_erpnext_demo.py b/utilities/demo/make_erpnext_demo.py
index 766da26..cda38d6 100644
--- a/utilities/demo/make_erpnext_demo.py
+++ b/utilities/demo/make_erpnext_demo.py
@@ -5,17 +5,21 @@
import webnotes, os
import utilities.demo.make_demo
-def make_demo_app():
- webnotes.mute_emails = 1
- webnotes.connect()
+def make_demo_app(site=None):
+ webnotes.init(site=site)
+ webnotes.flags.mute_emails = 1
+
utilities.demo.make_demo.make(reset=True, simulate=False)
# setup demo user etc so that the site it up faster, while the data loads
make_demo_user()
make_demo_login_page()
make_demo_on_login_script()
utilities.demo.make_demo.make(reset=False, simulate=True)
+ webnotes.destroy()
def make_demo_user():
+ from webnotes.auth import _update_password
+
roles = ["Accounts Manager", "Analytics", "Expense Approver", "Accounts User",
"Leave Approver", "Blogger", "Customer", "Sales Manager", "Employee", "Support Manager",
"HR Manager", "HR User", "Maintenance Manager", "Maintenance User", "Material Manager",
@@ -42,11 +46,10 @@
p.doc.last_name = "User"
p.doc.enabled = 1
p.doc.user_type = "ERPNext Demo"
- p.doc.send_invite_email = 0
- p.doc.new_password = "demo"
p.insert()
add_roles(p)
p.save()
+ _update_password("demo@erpnext.com", "demo")
# make system manager user
if webnotes.conn.exists("Profile", "admin@erpnext.com"):
@@ -58,12 +61,11 @@
p.doc.last_name = "User"
p.doc.enabled = 1
p.doc.user_type = "System User"
- p.doc.send_invite_email = 0
- p.doc.new_password = "admin010123"
p.insert()
roles.append("System Manager")
add_roles(p)
p.save()
+ _update_password("admin@erpnext.com", "admin010123")
# only read for newsletter
webnotes.conn.sql("""update `tabDocPerm` set `write`=0, `create`=0, `cancel`=0
@@ -103,13 +105,12 @@
webnotes.conn.commit()
def make_demo_on_login_script():
- webnotes.conn.sql("""delete from `tabCustom Script` where dt='Control Panel'""")
- s = webnotes.new_bean("Custom Script")
- s.doc.dt = "Control Panel"
- s.doc.script_type = "Server"
- with open(os.path.join(os.path.dirname(__file__), "demo_control_panel.py"), "r") as dfile:
- s.doc.script = dfile.read()
- s.insert()
+ import shutil
+ from core.doctype.custom_script.custom_script import get_custom_server_script_path
+ custom_script_path = get_custom_server_script_path("Control Panel")
+ webnotes.create_folder(os.path.dirname(custom_script_path))
+
+ shutil.copyfile(os.path.join(os.path.dirname(__file__), "demo_control_panel.py"), custom_script_path)
cp = webnotes.bean("Control Panel")
cp.doc.custom_startup_code = """wn.ui.toolbar.show_banner('You are using ERPNext Demo. To start your own ERPNext Trial, <a href="https://erpnext.com/pricing-and-signup" target="_blank">click here</a>')"""
@@ -118,4 +119,6 @@
webnotes.conn.commit()
if __name__=="__main__":
- make_demo_app()
\ No newline at end of file
+ import sys
+ site = sys.argv[1:]
+ make_demo_app(site=site and site[0] or None)
diff --git a/utilities/doctype/contact/contact.py b/utilities/doctype/contact/contact.py
index 84c8a59..db233fb 100644
--- a/utilities/doctype/contact/contact.py
+++ b/utilities/doctype/contact/contact.py
@@ -12,14 +12,6 @@
self.doc = doc
self.doclist = doclist
- def on_communication(self, comm):
- if webnotes.conn.get_value("Profile", extract_email_id(comm.sender), "user_type")=="System User":
- status = "Replied"
- else:
- status = "Open"
-
- webnotes.conn.set(self.doc, 'status', status)
-
def autoname(self):
# concat first and last name
self.doc.name = " ".join(filter(None,
@@ -32,28 +24,28 @@
break
def validate(self):
+ self.set_status()
self.validate_primary_contact()
def validate_primary_contact(self):
- sql = webnotes.conn.sql
if self.doc.is_primary_contact == 1:
if self.doc.customer:
- sql("update tabContact set is_primary_contact=0 where customer = '%s'" % (self.doc.customer))
+ webnotes.conn.sql("update tabContact set is_primary_contact=0 where customer = '%s'" % (self.doc.customer))
elif self.doc.supplier:
- sql("update tabContact set is_primary_contact=0 where supplier = '%s'" % (self.doc.supplier))
+ webnotes.conn.sql("update tabContact set is_primary_contact=0 where supplier = '%s'" % (self.doc.supplier))
elif self.doc.sales_partner:
- sql("update tabContact set is_primary_contact=0 where sales_partner = '%s'" % (self.doc.sales_partner))
+ webnotes.conn.sql("update tabContact set is_primary_contact=0 where sales_partner = '%s'" % (self.doc.sales_partner))
else:
if self.doc.customer:
- if not sql("select name from tabContact where is_primary_contact=1 and customer = '%s'" % (self.doc.customer)):
+ if not webnotes.conn.sql("select name from tabContact where is_primary_contact=1 and customer = '%s'" % (self.doc.customer)):
self.doc.is_primary_contact = 1
elif self.doc.supplier:
- if not sql("select name from tabContact where is_primary_contact=1 and supplier = '%s'" % (self.doc.supplier)):
+ if not webnotes.conn.sql("select name from tabContact where is_primary_contact=1 and supplier = '%s'" % (self.doc.supplier)):
self.doc.is_primary_contact = 1
elif self.doc.sales_partner:
- if not sql("select name from tabContact where is_primary_contact=1 and sales_partner = '%s'" % (self.doc.sales_partner)):
+ if not webnotes.conn.sql("select name from tabContact where is_primary_contact=1 and sales_partner = '%s'" % (self.doc.sales_partner)):
self.doc.is_primary_contact = 1
def on_trash(self):
webnotes.conn.sql("""update `tabSupport Ticket` set contact='' where contact=%s""",
- self.doc.name)
\ No newline at end of file
+ self.doc.name)
diff --git a/utilities/doctype/contact/contact.txt b/utilities/doctype/contact/contact.txt
index 92dcf2e..199aaea 100644
--- a/utilities/doctype/contact/contact.txt
+++ b/utilities/doctype/contact/contact.txt
@@ -2,7 +2,7 @@
{
"creation": "2013-01-10 16:34:32",
"docstatus": 0,
- "modified": "2013-09-10 10:50:27",
+ "modified": "2013-10-08 16:48:50",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -70,11 +70,12 @@
"fieldtype": "Column Break"
},
{
+ "default": "Passive",
"doctype": "DocField",
"fieldname": "status",
"fieldtype": "Select",
"label": "Status",
- "options": "\nOpen\nReplied"
+ "options": "Passive\nOpen\nReplied"
},
{
"doctype": "DocField",
@@ -84,7 +85,7 @@
"label": "Email Id",
"oldfieldname": "email_id",
"oldfieldtype": "Data",
- "reqd": 1,
+ "reqd": 0,
"search_index": 1
},
{
@@ -94,7 +95,7 @@
"label": "Phone",
"oldfieldname": "contact_no",
"oldfieldtype": "Data",
- "reqd": 1
+ "reqd": 0
},
{
"doctype": "DocField",
diff --git a/utilities/doctype/sms_control/sms_control.py b/utilities/doctype/sms_control/sms_control.py
index 9c2319f..f183920 100644
--- a/utilities/doctype/sms_control/sms_control.py
+++ b/utilities/doctype/sms_control/sms_control.py
@@ -10,8 +10,6 @@
from webnotes import msgprint
from webnotes.model.bean import getlist, copy_doclist
-sql = webnotes.conn.sql
-
class DocType:
def __init__(self, doc, doclist=[]):
self.doc = doc
@@ -50,7 +48,7 @@
def get_contact_number(self, arg):
"returns mobile number of the contact"
args = load_json(arg)
- number = sql("""select mobile_no, phone from tabContact where name=%s and %s=%s""" %
+ number = webnotes.conn.sql("""select mobile_no, phone from tabContact where name=%s and %s=%s""" %
('%s', args['key'], '%s'), (args['contact_name'], args['value']))
return number and (number[0][0] or number[0][1]) or ''
@@ -119,4 +117,4 @@
sl.message = arg['message']
sl.no_of_requested_sms = len(arg['receiver_list'])
sl.no_of_sent_sms = sent_sms
- sl.save(new=1)
\ No newline at end of file
+ sl.save(new=1)
diff --git a/utilities/repost_stock.py b/utilities/repost_stock.py
new file mode 100644
index 0000000..7359304
--- /dev/null
+++ b/utilities/repost_stock.py
@@ -0,0 +1,119 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd.
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import webnotes
+
+from webnotes.utils import flt
+
+
+def repost():
+ """
+ Repost everything!
+ """
+ webnotes.conn.auto_commit_on_many_writes = 1
+
+ for d in webnotes.conn.sql("select item_code, warehouse from tabBin"):
+ repost_stock(d[0], d[1])
+
+ webnotes.conn.auto_commit_on_many_writes = 0
+
+def repost_stock(item_code, warehouse):
+ repost_actual_qty(item_code, warehouse)
+
+ if item_code and warehouse:
+ update_bin(item_code, warehouse, {
+ "reserved_qty": get_reserved_qty(item_code, warehouse),
+ "indented_qty": get_indented_qty(item_code, warehouse),
+ "ordered_qty": get_ordered_qty(item_code, warehouse),
+ "planned_qty": get_planned_qty(item_code, warehouse)
+ })
+
+def repost_actual_qty(item_code, warehouse):
+ from stock.stock_ledger import update_entries_after
+ update_entries_after({ "item_code": item_code, "warehouse": warehouse })
+
+def get_reserved_qty(item_code, warehouse):
+ reserved_qty = webnotes.conn.sql("""
+ select
+ sum((dnpi_qty / so_item_qty) * (so_item_qty - so_item_delivered_qty))
+ from
+ (
+ (select
+ qty as dnpi_qty,
+ (
+ select qty from `tabSales Order Item`
+ where name = dnpi.parent_detail_docname
+ ) as so_item_qty,
+ (
+ select ifnull(delivered_qty, 0) from `tabSales Order Item`
+ where name = dnpi.parent_detail_docname
+ ) as so_item_delivered_qty,
+ parent, name
+ from
+ (
+ select qty, parent_detail_docname, parent, name
+ from `tabDelivery Note Packing Item` dnpi_in
+ where item_code = %s and warehouse = %s
+ and parenttype="Sales Order"
+ and item_code != parent_item
+ and exists (select * from `tabSales Order` so
+ where name = dnpi_in.parent and docstatus = 1 and status != 'Stopped')
+ ) dnpi)
+ union
+ (select qty as dnpi_qty, qty as so_item_qty,
+ ifnull(delivered_qty, 0) as so_item_delivered_qty, parent, name
+ from `tabSales Order Item` so_item
+ where item_code = %s and reserved_warehouse = %s
+ and exists(select * from `tabSales Order` so
+ where so.name = so_item.parent and so.docstatus = 1
+ and so.status != 'Stopped'))
+ ) tab
+ where
+ so_item_qty >= so_item_delivered_qty
+ """, (item_code, warehouse, item_code, warehouse))
+
+ return flt(reserved_qty[0][0]) if reserved_qty else 0
+
+def get_indented_qty(item_code, warehouse):
+ indented_qty = webnotes.conn.sql("""select sum(pr_item.qty - ifnull(pr_item.ordered_qty, 0))
+ from `tabMaterial Request Item` pr_item, `tabMaterial Request` pr
+ where pr_item.item_code=%s and pr_item.warehouse=%s
+ and pr_item.qty > ifnull(pr_item.ordered_qty, 0) and pr_item.parent=pr.name
+ and pr.status!='Stopped' and pr.docstatus=1""", (item_code, warehouse))
+
+ return flt(indented_qty[0][0]) if indented_qty else 0
+
+def get_ordered_qty(item_code, warehouse):
+ ordered_qty = webnotes.conn.sql("""
+ select sum((po_item.qty - ifnull(po_item.received_qty, 0))*po_item.conversion_factor)
+ from `tabPurchase Order Item` po_item, `tabPurchase Order` po
+ where po_item.item_code=%s and po_item.warehouse=%s
+ and po_item.qty > ifnull(po_item.received_qty, 0) and po_item.parent=po.name
+ and po.status!='Stopped' and po.docstatus=1""", (item_code, warehouse))
+
+ return flt(ordered_qty[0][0]) if ordered_qty else 0
+
+def get_planned_qty(item_code, warehouse):
+ planned_qty = webnotes.conn.sql("""
+ select sum(ifnull(qty, 0) - ifnull(produced_qty, 0)) from `tabProduction Order`
+ where production_item = %s and fg_warehouse = %s and status != "Stopped"
+ and docstatus=1 and ifnull(qty, 0) > ifnull(produced_qty, 0)""", (item_code, warehouse))
+
+ return flt(planned_qty[0][0]) if planned_qty else 0
+
+
+def update_bin(item_code, warehouse, qty_dict=None):
+ from stock.utils import get_bin
+ bin = get_bin(item_code, warehouse)
+ mismatch = False
+ for fld, val in qty_dict.items():
+ if flt(bin.doc.fields.get(fld)) != flt(val):
+ bin.doc.fields[fld] = flt(val)
+ mismatch = True
+
+ if mismatch:
+ bin.doc.projected_qty = flt(bin.doc.actual_qty) + flt(bin.doc.ordered_qty) + \
+ flt(bin.doc.indented_qty) + flt(bin.doc.planned_qty) - flt(bin.doc.reserved_qty)
+
+ bin.doc.save()
\ No newline at end of file
diff --git a/utilities/transaction_base.py b/utilities/transaction_base.py
index 0655567..0f4d6bc 100644
--- a/utilities/transaction_base.py
+++ b/utilities/transaction_base.py
@@ -144,7 +144,6 @@
def get_customer_address(self, args):
args = load_json(args)
- webnotes.errprint(args)
ret = {
'customer_address' : args["address"],
'address_display' : get_address_display(args["address"]),
@@ -426,19 +425,13 @@
def validate_conversion_rate(currency, conversion_rate, conversion_rate_label, company):
"""common validation for currency and price list currency"""
+
if conversion_rate == 0:
msgprint(conversion_rate_label + _(' cannot be 0'), raise_exception=True)
company_currency = webnotes.conn.get_value("Company", company, "default_currency")
- # parenthesis for 'OR' are necessary as we want it to evaluate as
- # mandatory valid condition and (1st optional valid condition
- # or 2nd optional valid condition)
- valid_conversion_rate = (conversion_rate and
- ((currency == company_currency and conversion_rate == 1.00)
- or (currency != company_currency and conversion_rate != 1.00)))
-
- if not valid_conversion_rate:
+ if not conversion_rate:
msgprint(_('Please enter valid ') + conversion_rate_label + (': ')
+ ("1 %s = [?] %s" % (currency, company_currency)),
raise_exception=True)