[merge conflict]
This commit is contained in:
commit
90202aec2b
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
13
accounts/README.md
Normal file
13
accounts/README.md
Normal file
@ -0,0 +1,13 @@
|
||||
Accounts module contains masters and transactions to manage a traditional
|
||||
double entry accounting system.
|
||||
|
||||
Accounting heads are called "Accounts" and they can be groups in a tree like
|
||||
"Chart of Accounts"
|
||||
|
||||
Entries are:
|
||||
|
||||
- Journal Vouchers
|
||||
- Sales Invoice (Itemised)
|
||||
- Purchase Invoice (Itemised)
|
||||
|
||||
All accounting entries are stored in the `General Ledger`
|
11
accounts/doctype/account/README.md
Normal file
11
accounts/doctype/account/README.md
Normal file
@ -0,0 +1,11 @@
|
||||
Account DocType represents an Accounting Ledger or Group.
|
||||
|
||||
Follows a composite model. `parent_account` represents the parent of an Account except
|
||||
a root account.
|
||||
There can be only 4 root accounts: Income, Expense, Assets and Liabilities in a company.
|
||||
|
||||
Other features:
|
||||
|
||||
- It can be of type Debit or Credit.
|
||||
- A Group is a collection of groups or ledgers
|
||||
|
@ -29,6 +29,7 @@ class DocType:
|
||||
self.nsm_parent_field = 'parent_account'
|
||||
|
||||
def autoname(self):
|
||||
"""Append abbreviation to company on naming"""
|
||||
self.doc.name = self.doc.account_name.strip() + ' - ' + \
|
||||
webnotes.conn.get_value("Company", self.doc.company, "abbr")
|
||||
|
||||
@ -37,6 +38,7 @@ class DocType:
|
||||
return {'address': address}
|
||||
|
||||
def validate_master_name(self):
|
||||
"""Remind to add master name"""
|
||||
if (self.doc.master_type == 'Customer' or self.doc.master_type == 'Supplier') \
|
||||
and not self.doc.master_name:
|
||||
msgprint("Message: Please enter Master Name once the account is created.")
|
||||
@ -62,6 +64,7 @@ class DocType:
|
||||
self.doc.debit_or_credit = par[0][3]
|
||||
|
||||
def validate_max_root_accounts(self):
|
||||
"""Raise exception if there are more than 4 root accounts"""
|
||||
if webnotes.conn.sql("""select count(*) from tabAccount where
|
||||
company=%s and ifnull(parent_account,'')='' and docstatus != 2""",
|
||||
self.doc.company)[0][0] > 4:
|
||||
@ -69,7 +72,6 @@ class DocType:
|
||||
raise_exception=1)
|
||||
|
||||
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""",
|
||||
@ -134,6 +136,7 @@ class DocType:
|
||||
self.doc.parent_account = ''
|
||||
|
||||
def update_nsm_model(self):
|
||||
"""update lft, rgt indices for nested set model"""
|
||||
import webnotes
|
||||
import webnotes.utils.nestedset
|
||||
webnotes.utils.nestedset.update_nsm(self)
|
||||
|
@ -1,8 +1,8 @@
|
||||
[
|
||||
{
|
||||
"creation": "2013-05-09 13:16:11",
|
||||
"creation": "2013-05-24 12:15:51",
|
||||
"docstatus": 0,
|
||||
"modified": "2013-05-23 12:52:09",
|
||||
"modified": "2013-05-30 12:09:39",
|
||||
"modified_by": "Administrator",
|
||||
"owner": "Administrator"
|
||||
},
|
||||
@ -18,8 +18,7 @@
|
||||
"parent": "POS Setting",
|
||||
"parentfield": "fields",
|
||||
"parenttype": "DocType",
|
||||
"permlevel": 0,
|
||||
"read_only": 0
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"doctype": "DocPerm",
|
||||
@ -44,7 +43,8 @@
|
||||
"label": "User",
|
||||
"oldfieldname": "user",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Profile"
|
||||
"options": "Profile",
|
||||
"read_only": 0
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
@ -54,6 +54,7 @@
|
||||
"oldfieldname": "territory",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Territory",
|
||||
"read_only": 0,
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
@ -64,6 +65,7 @@
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "naming_series",
|
||||
"oldfieldtype": "Select",
|
||||
"read_only": 0,
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
@ -74,6 +76,7 @@
|
||||
"oldfieldname": "currency",
|
||||
"oldfieldtype": "Select",
|
||||
"options": "Currency",
|
||||
"read_only": 0,
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
@ -84,6 +87,7 @@
|
||||
"label": "Conversion Rate",
|
||||
"oldfieldname": "conversion_rate",
|
||||
"oldfieldtype": "Currency",
|
||||
"read_only": 0,
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
@ -94,6 +98,7 @@
|
||||
"oldfieldname": "price_list_name",
|
||||
"oldfieldtype": "Select",
|
||||
"options": "link:Price List",
|
||||
"read_only": 0,
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
@ -105,13 +110,24 @@
|
||||
"oldfieldname": "company",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Company",
|
||||
"read_only": 0,
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "column_break0",
|
||||
"fieldtype": "Column Break",
|
||||
"oldfieldtype": "Column Break"
|
||||
"oldfieldtype": "Column Break",
|
||||
"read_only": 0
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"description": "Create Stock Ledger Entries when you submit a Sales Invoice",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "update_stock",
|
||||
"fieldtype": "Check",
|
||||
"label": "Update Stock",
|
||||
"reqd": 0
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
@ -121,6 +137,7 @@
|
||||
"oldfieldname": "customer_account",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Account",
|
||||
"read_only": 0,
|
||||
"reqd": 0
|
||||
},
|
||||
{
|
||||
@ -131,6 +148,7 @@
|
||||
"oldfieldname": "cash_bank_account",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Account",
|
||||
"read_only": 0,
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
@ -141,6 +159,7 @@
|
||||
"oldfieldname": "income_account",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Account",
|
||||
"read_only": 0,
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
@ -152,6 +171,7 @@
|
||||
"label": "Expense Account",
|
||||
"options": "Account",
|
||||
"print_hide": 1,
|
||||
"read_only": 0,
|
||||
"reqd": 0
|
||||
},
|
||||
{
|
||||
@ -162,6 +182,7 @@
|
||||
"oldfieldname": "warehouse",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Warehouse",
|
||||
"read_only": 0,
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
@ -172,6 +193,7 @@
|
||||
"oldfieldname": "cost_center",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Cost Center",
|
||||
"read_only": 0,
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
@ -181,7 +203,8 @@
|
||||
"label": "Charge",
|
||||
"oldfieldname": "charge",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Sales Taxes and Charges Master"
|
||||
"options": "Sales Taxes and Charges Master",
|
||||
"read_only": 0
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
@ -191,7 +214,8 @@
|
||||
"oldfieldname": "letter_head",
|
||||
"oldfieldtype": "Select",
|
||||
"options": "link:Letter Head",
|
||||
"print_hide": 1
|
||||
"print_hide": 1,
|
||||
"read_only": 0
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
@ -200,7 +224,8 @@
|
||||
"label": "Terms and Conditions",
|
||||
"oldfieldname": "tc_name",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Terms and Conditions"
|
||||
"options": "Terms and Conditions",
|
||||
"read_only": 0
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
@ -210,17 +235,10 @@
|
||||
"label": "Select Print Heading",
|
||||
"oldfieldname": "select_print_heading",
|
||||
"oldfieldtype": "Select",
|
||||
"options": "link:Print Heading"
|
||||
"options": "link:Print Heading",
|
||||
"read_only": 0
|
||||
},
|
||||
{
|
||||
"cancel": 1,
|
||||
"create": 1,
|
||||
"doctype": "DocPerm",
|
||||
"role": "System Manager",
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"cancel": 1,
|
||||
"create": 1,
|
||||
"doctype": "DocPerm",
|
||||
"role": "Accounts Manager",
|
||||
|
@ -17,86 +17,56 @@
|
||||
cur_frm.cscript.tname = "Purchase Invoice Item";
|
||||
cur_frm.cscript.fname = "entries";
|
||||
cur_frm.cscript.other_fname = "purchase_tax_details";
|
||||
|
||||
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');
|
||||
|
||||
erpnext.buying.PurchaseInvoiceController = erpnext.buying.BuyingController.extend({
|
||||
erpnext.accounts.PurchaseInvoiceController = erpnext.buying.BuyingController.extend({
|
||||
onload: function() {
|
||||
this._super();
|
||||
|
||||
if(!this.frm.doc.__islocal) {
|
||||
// show credit_to in print format
|
||||
if(!this.frm.doc.supplier && this.frm.doc.credit_to) {
|
||||
this.frm.set_df_property("credit_to", "print_hide", 0);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
refresh: function(doc) {
|
||||
this._super();
|
||||
|
||||
// Show / Hide button
|
||||
if(doc.docstatus==1 && doc.outstanding_amount > 0)
|
||||
cur_frm.add_custom_button('Make Payment Entry', cur_frm.cscript.make_bank_voucher);
|
||||
this.frm.add_custom_button('Make Payment Entry', this.make_bank_voucher);
|
||||
|
||||
if(doc.docstatus==1) {
|
||||
cur_frm.add_custom_button('View Ledger', cur_frm.cscript.view_ledger_entry);
|
||||
this.frm.add_custom_button('View Ledger', this.view_ledger_entry);
|
||||
}
|
||||
|
||||
cur_frm.cscript.is_opening(doc);
|
||||
this.is_opening(doc);
|
||||
},
|
||||
onload_post_render: function(doc, dt, dn) {
|
||||
var me = this;
|
||||
var callback1 = function(doc, dt, dn) {
|
||||
var callback2 = function(doc, dt, dn) {
|
||||
if(doc.__islocal && doc.supplier) cur_frm.cscript.supplier(doc, dt, dn);
|
||||
}
|
||||
me.update_item_details(doc, dt, dn, callback2);
|
||||
}
|
||||
|
||||
// TODO: improve this
|
||||
if(this.frm.doc.__islocal) {
|
||||
if (this.frm.fields_dict.price_list_name && this.frm.doc.price_list_name) {
|
||||
this.price_list_name(callback1);
|
||||
} else {
|
||||
callback1(doc, dt, dn);
|
||||
}
|
||||
}
|
||||
|
||||
credit_to: function() {
|
||||
this.supplier();
|
||||
},
|
||||
|
||||
write_off_amount: function() {
|
||||
this.calculate_outstanding_amount();
|
||||
this.frm.refresh_fields();
|
||||
},
|
||||
|
||||
allocated_amount: function() {
|
||||
this.calculate_total_advance("Purchase Invoice", "advance_allocation_details");
|
||||
this.frm.refresh_fields();
|
||||
}
|
||||
});
|
||||
|
||||
var new_cscript = new erpnext.buying.PurchaseInvoiceController({frm: cur_frm});
|
||||
|
||||
// for backward compatibility: combine new and previous states
|
||||
$.extend(cur_frm.cscript, new_cscript);
|
||||
$.extend(cur_frm.cscript, new erpnext.accounts.PurchaseInvoiceController({frm: cur_frm}));
|
||||
|
||||
|
||||
cur_frm.cscript.onload = function(doc,dt,dn) {
|
||||
if(!doc.posting_date) set_multiple(dt,dn,{posting_date:get_today()});
|
||||
}
|
||||
|
||||
cur_frm.cscript.supplier = function(doc,dt,dn) {
|
||||
var callback = function(r,rt) {
|
||||
var doc = locals[cur_frm.doctype][cur_frm.docname];
|
||||
get_server_fields('get_credit_to','','',doc, dt, dn, 0, callback2);
|
||||
}
|
||||
|
||||
var callback2 = function(r,rt){
|
||||
var doc = locals[cur_frm.doctype][cur_frm.docname];
|
||||
var el = getchildren('Purchase Invoice Item',doc.name,'entries');
|
||||
for(var i in el){
|
||||
if(el[i].item_code && (!el[i].expense_head || !el[i].cost_center)){
|
||||
args = {
|
||||
item_code: el[i].item_code,
|
||||
expense_head: el[i].expense_head,
|
||||
cost_center: el[i].cost_center
|
||||
};
|
||||
get_server_fields('get_default_values', JSON.stringify(args), 'entries', doc, el[i].doctype, el[i].name, 1);
|
||||
}
|
||||
}
|
||||
cur_frm.cscript.calc_amount(doc, 1);
|
||||
}
|
||||
|
||||
if (doc.supplier) {
|
||||
get_server_fields('get_default_supplier_address',
|
||||
JSON.stringify({ supplier: doc.supplier }),'', doc, dt, dn, 1, function(doc, dt, dn) {
|
||||
cur_frm.refresh();
|
||||
callback(doc, dt, dn);
|
||||
});
|
||||
unhide_field(['supplier_address','contact_person']);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
cur_frm.cscript.supplier_address = cur_frm.cscript.contact_person = function(doc,dt,dn) {
|
||||
if(doc.supplier) get_server_fields('get_supplier_address', JSON.stringify({supplier: doc.supplier, address: doc.supplier_address, contact: doc.contact_person}),'', doc, dt, dn, 1);
|
||||
}
|
||||
@ -112,23 +82,6 @@ cur_frm.fields_dict.contact_person.on_new = function(dn) {
|
||||
}
|
||||
|
||||
|
||||
cur_frm.cscript.credit_to = function(doc,dt,dn) {
|
||||
|
||||
var callback = function(doc, dt, dn) {
|
||||
var doc = locals[doc.doctype][doc.name];
|
||||
if(doc.supplier) {
|
||||
get_server_fields('get_default_supplier_address',
|
||||
JSON.stringify({ supplier: doc.supplier }), '', doc, dt, dn, 1, function() {
|
||||
cur_frm.refresh();
|
||||
});
|
||||
unhide_field(['supplier_address','contact_person']);
|
||||
}
|
||||
cur_frm.refresh();
|
||||
}
|
||||
|
||||
get_server_fields('get_cust', '', '', doc, dt, dn, 1, callback);
|
||||
}
|
||||
|
||||
cur_frm.fields_dict['entries'].grid.onrowadd = function(doc, cdt, cdn){
|
||||
|
||||
cl = getchildren('Purchase Invoice Item', doc.name, cur_frm.cscript.fname, doc.doctype);
|
||||
@ -152,17 +105,6 @@ cur_frm.cscript.is_opening = function(doc, dt, dn) {
|
||||
if (doc.is_opening == 'Yes') unhide_field('aging_date');
|
||||
}
|
||||
|
||||
cur_frm.cscript.write_off_amount = function(doc) {
|
||||
doc.total_amount_to_pay = flt(doc.grand_total) - flt(doc.write_off_amount);
|
||||
doc.outstanding_amount = flt(doc.total_amount_to_pay) - flt(doc.total_advance);
|
||||
refresh_many(['outstanding_amount', 'total_amount_to_pay']);
|
||||
}
|
||||
|
||||
cur_frm.cscript.recalculate = function(doc, cdt, cdn) {
|
||||
cur_frm.cscript.calculate_tax(doc,cdt,cdn);
|
||||
calc_total_advance(doc,cdt,cdn);
|
||||
}
|
||||
|
||||
cur_frm.cscript.get_items = function(doc, dt, dn) {
|
||||
var callback = function(r,rt) {
|
||||
unhide_field(['supplier_address', 'contact_person']);
|
||||
@ -171,11 +113,6 @@ cur_frm.cscript.get_items = function(doc, dt, dn) {
|
||||
$c_obj(make_doclist(dt,dn),'pull_details','',callback);
|
||||
}
|
||||
|
||||
cur_frm.cscript.allocated_amount = function(doc,cdt,cdn) {
|
||||
calc_total_advance(doc, cdt, cdn);
|
||||
}
|
||||
|
||||
|
||||
cur_frm.cscript.make_bank_voucher = function() {
|
||||
wn.call({
|
||||
method: "accounts.doctype.journal_voucher.journal_voucher.get_default_bank_cash_account",
|
||||
@ -261,21 +198,6 @@ cur_frm.cscript.cost_center = function(doc, cdt, cdn){
|
||||
refresh_field('entries');
|
||||
}
|
||||
|
||||
calc_total_advance = function(doc,cdt,cdn) {
|
||||
var doc = locals[doc.doctype][doc.name];
|
||||
var el = getchildren('Purchase Invoice Advance',doc.name,'advance_allocation_details')
|
||||
var total_advance = 0;
|
||||
for(var i in el) {
|
||||
if (! el[i].allocated_amount == 0) {
|
||||
total_advance += flt(el[i].allocated_amount);
|
||||
}
|
||||
}
|
||||
doc.total_amount_to_pay = flt(doc.grand_total) - flt(doc.write_off_amount);
|
||||
doc.total_advance = flt(total_advance);
|
||||
doc.outstanding_amount = flt(doc.total_amount_to_pay) - flt(total_advance);
|
||||
refresh_many(['total_advance','outstanding_amount', 'total_amount_to_pay']);
|
||||
}
|
||||
|
||||
cur_frm.cscript.make_jv = function(doc, dt, dn, bank_account) {
|
||||
var jv = wn.model.make_new_doc_and_get_name('Journal Voucher');
|
||||
jv = locals['Journal Voucher'][jv];
|
||||
|
@ -91,6 +91,11 @@ class DocType(BuyingController):
|
||||
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):
|
||||
self.doc.fields.update(self.get_cust())
|
||||
self.doc.fields.update(self.get_credit_to())
|
||||
super(DocType, self).set_supplier_defaults()
|
||||
|
||||
def get_cust(self):
|
||||
ret = {}
|
||||
if self.doc.credit_to:
|
||||
@ -100,31 +105,6 @@ class DocType(BuyingController):
|
||||
|
||||
return ret
|
||||
|
||||
def get_default_values(self, args):
|
||||
if isinstance(args, basestring):
|
||||
import json
|
||||
args = json.loads(args)
|
||||
|
||||
out = webnotes._dict()
|
||||
|
||||
item = webnotes.conn.sql("""select name, purchase_account, cost_center,
|
||||
is_stock_item from `tabItem` where name=%s""", args.get("item_code"), as_dict=1)
|
||||
|
||||
if item and item[0]:
|
||||
item = item[0]
|
||||
|
||||
if cint(webnotes.defaults.get_global_default("auto_inventory_accounting")) and \
|
||||
item.is_stock_item == "Yes":
|
||||
# unset expense head for stock item and auto inventory accounting
|
||||
out.expense_head = out.cost_center = None
|
||||
else:
|
||||
if not args.get("expense_head"):
|
||||
out.expense_head = item.purchase_account
|
||||
if not args.get("cost_center"):
|
||||
out.cost_center = item.cost_center
|
||||
|
||||
return out
|
||||
|
||||
def pull_details(self):
|
||||
if self.doc.purchase_receipt_main:
|
||||
self.validate_duplicate_docname('purchase_receipt')
|
||||
@ -466,9 +446,9 @@ class DocType(BuyingController):
|
||||
# expense will be booked in sales invoice
|
||||
stock_item_and_auto_inventory_accounting = True
|
||||
|
||||
valuation_amt = (flt(item.amount, self.precision.item.amount) +
|
||||
flt(item.item_tax_amount, self.precision.item.item_tax_amount) +
|
||||
flt(item.rm_supp_cost, self.precision.item.rm_supp_cost))
|
||||
valuation_amt = (flt(item.amount, self.precision("amount", item)) +
|
||||
flt(item.item_tax_amount, self.precision("item_tax_amount", item)) +
|
||||
flt(item.rm_supp_cost, self.precision("rm_supp_cost", item)))
|
||||
|
||||
gl_entries.append(
|
||||
self.get_gl_dict({
|
||||
|
@ -1,8 +1,8 @@
|
||||
[
|
||||
{
|
||||
"creation": "2013-05-07 13:50:30",
|
||||
"creation": "2013-05-21 16:16:39",
|
||||
"docstatus": 0,
|
||||
"modified": "2013-05-13 11:12:56",
|
||||
"modified": "2013-05-28 12:18:35",
|
||||
"modified_by": "Administrator",
|
||||
"owner": "Administrator"
|
||||
},
|
||||
@ -89,10 +89,11 @@
|
||||
"read_only": 0
|
||||
},
|
||||
{
|
||||
"depends_on": "supplier",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "supplier_name",
|
||||
"fieldtype": "Text",
|
||||
"hidden": 1,
|
||||
"hidden": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Name",
|
||||
"oldfieldname": "supplier_name",
|
||||
@ -100,34 +101,38 @@
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "supplier",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "address_display",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 1,
|
||||
"hidden": 0,
|
||||
"label": "Address",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "supplier",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "contact_display",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 1,
|
||||
"hidden": 0,
|
||||
"label": "Contact",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "supplier",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "contact_mobile",
|
||||
"fieldtype": "Text",
|
||||
"hidden": 1,
|
||||
"hidden": 0,
|
||||
"label": "Mobile No",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "supplier",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "contact_email",
|
||||
"fieldtype": "Text",
|
||||
"hidden": 1,
|
||||
"hidden": 0,
|
||||
"label": "Contact Email",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
@ -392,6 +397,7 @@
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "supplier",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "contact_section",
|
||||
"fieldtype": "Section Break",
|
||||
@ -399,7 +405,6 @@
|
||||
"read_only": 0
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.supplier",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "supplier_address",
|
||||
"fieldtype": "Link",
|
||||
@ -415,7 +420,6 @@
|
||||
"width": "50%"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.supplier",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "contact_person",
|
||||
"fieldtype": "Link",
|
||||
|
@ -24,6 +24,7 @@ from webnotes.utils import cint
|
||||
import webnotes.defaults
|
||||
|
||||
test_dependencies = ["Item", "Cost Center"]
|
||||
test_ignore = ["Serial No"]
|
||||
|
||||
class TestPurchaseInvoice(unittest.TestCase):
|
||||
def test_gl_entries_without_auto_inventory_accounting(self):
|
||||
@ -119,25 +120,6 @@ class TestPurchaseInvoice(unittest.TestCase):
|
||||
wrapper.insert()
|
||||
wrapper.load_from_db()
|
||||
|
||||
self.assertEqual(wrapper.doclist[0].net_total, 1250)
|
||||
|
||||
# tax amounts
|
||||
expected_values = [
|
||||
["_Test Account Shipping Charges - _TC", 100, 1350],
|
||||
["_Test Account Customs Duty - _TC", 125, 1350],
|
||||
["_Test Account Excise Duty - _TC", 140, 1490],
|
||||
["_Test Account Education Cess - _TC", 2.8, 1492.8],
|
||||
["_Test Account S&H Education Cess - _TC", 1.4, 1494.2],
|
||||
["_Test Account CST - _TC", 29.88, 1524.08],
|
||||
["_Test Account VAT - _TC", 156.25, 1680.33],
|
||||
["_Test Account Discount - _TC", 168.03, 1512.30],
|
||||
]
|
||||
|
||||
for i, tax in enumerate(wrapper.doclist.get({"parentfield": "purchase_tax_details"})):
|
||||
self.assertEqual(tax.account_head, expected_values[i][0])
|
||||
self.assertEqual(tax.tax_amount, expected_values[i][1])
|
||||
self.assertEqual(tax.total, expected_values[i][2])
|
||||
|
||||
expected_values = [
|
||||
["_Test Item Home Desktop 100", 90, 59],
|
||||
["_Test Item Home Desktop 200", 135, 177]
|
||||
@ -147,13 +129,41 @@ class TestPurchaseInvoice(unittest.TestCase):
|
||||
self.assertEqual(item.item_tax_amount, expected_values[i][1])
|
||||
self.assertEqual(item.valuation_rate, expected_values[i][2])
|
||||
|
||||
self.assertEqual(wrapper.doclist[0].net_total, 1250)
|
||||
|
||||
# tax amounts
|
||||
expected_values = [
|
||||
["_Test Account Shipping Charges - _TC", 100, 1350],
|
||||
["_Test Account Customs Duty - _TC", 125, 1350],
|
||||
["_Test Account Excise Duty - _TC", 140, 1490],
|
||||
["_Test Account Education Cess - _TC", 2.8, 1492.8],
|
||||
["_Test Account S&H Education Cess - _TC", 1.4, 1494.2],
|
||||
["_Test Account CST - _TC", 29.88, 1524.08],
|
||||
["_Test Account VAT - _TC", 156.25, 1680.33],
|
||||
["_Test Account Discount - _TC", 168.03, 1512.30],
|
||||
]
|
||||
|
||||
for i, tax in enumerate(wrapper.doclist.get({"parentfield": "purchase_tax_details"})):
|
||||
self.assertEqual(tax.account_head, expected_values[i][0])
|
||||
self.assertEqual(tax.tax_amount, expected_values[i][1])
|
||||
self.assertEqual(tax.total, expected_values[i][2])
|
||||
|
||||
def test_purchase_invoice_with_subcontracted_item(self):
|
||||
wrapper = webnotes.bean(copy=test_records[0])
|
||||
wrapper.doclist[1].item_code = "_Test FG Item"
|
||||
wrapper.run_method("calculate_taxes_and_totals")
|
||||
wrapper.insert()
|
||||
wrapper.load_from_db()
|
||||
|
||||
|
||||
expected_values = [
|
||||
["_Test FG Item", 90, 7059],
|
||||
["_Test Item Home Desktop 200", 135, 177]
|
||||
]
|
||||
for i, item in enumerate(wrapper.doclist.get({"parentfield": "entries"})):
|
||||
self.assertEqual(item.item_code, expected_values[i][0])
|
||||
self.assertEqual(item.item_tax_amount, expected_values[i][1])
|
||||
self.assertEqual(item.valuation_rate, expected_values[i][2])
|
||||
|
||||
self.assertEqual(wrapper.doclist[0].net_total, 1250)
|
||||
|
||||
# tax amounts
|
||||
@ -172,15 +182,6 @@ class TestPurchaseInvoice(unittest.TestCase):
|
||||
self.assertEqual(tax.account_head, expected_values[i][0])
|
||||
self.assertEqual(tax.tax_amount, expected_values[i][1])
|
||||
self.assertEqual(tax.total, expected_values[i][2])
|
||||
|
||||
expected_values = [
|
||||
["_Test FG Item", 90, 7059],
|
||||
["_Test Item Home Desktop 200", 135, 177]
|
||||
]
|
||||
for i, item in enumerate(wrapper.doclist.get({"parentfield": "entries"})):
|
||||
self.assertEqual(item.item_code, expected_values[i][0])
|
||||
self.assertEqual(item.item_tax_amount, expected_values[i][1])
|
||||
self.assertEqual(item.valuation_rate, expected_values[i][2])
|
||||
|
||||
def test_purchase_invoice_with_advance(self):
|
||||
from accounts.doctype.journal_voucher.test_journal_voucher \
|
||||
|
@ -1,8 +1,8 @@
|
||||
[
|
||||
{
|
||||
"creation": "2013-04-19 11:00:06",
|
||||
"creation": "2013-05-21 16:16:04",
|
||||
"docstatus": 0,
|
||||
"modified": "2013-05-07 11:23:56",
|
||||
"modified": "2013-05-28 12:02:02",
|
||||
"modified_by": "Administrator",
|
||||
"owner": "Administrator"
|
||||
},
|
||||
@ -101,7 +101,7 @@
|
||||
"oldfieldname": "tax_amount",
|
||||
"oldfieldtype": "Currency",
|
||||
"options": "Company:company:default_currency",
|
||||
"read_only": 0,
|
||||
"read_only": 1,
|
||||
"reqd": 0
|
||||
},
|
||||
{
|
||||
@ -159,35 +159,5 @@
|
||||
"print_hide": 1,
|
||||
"read_only": 0,
|
||||
"search_index": 0
|
||||
},
|
||||
{
|
||||
"description": "Cheating Field\nPlease do not delete ",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "total_tax_amount",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 1,
|
||||
"label": "Total +Tax",
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "total_tax_amount",
|
||||
"oldfieldtype": "Currency",
|
||||
"options": "Company:company:default_currency",
|
||||
"print_hide": 1,
|
||||
"read_only": 0,
|
||||
"report_hide": 1
|
||||
},
|
||||
{
|
||||
"description": "Cheating Field\nPlease do not delete ",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "total_amount",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 1,
|
||||
"label": "Tax Amount",
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "total_amount",
|
||||
"oldfieldtype": "Currency",
|
||||
"options": "Company:company:default_currency",
|
||||
"print_hide": 1,
|
||||
"read_only": 0,
|
||||
"report_hide": 1
|
||||
}
|
||||
]
|
@ -22,52 +22,90 @@ cur_frm.cscript.sales_team_fname = "sales_team";
|
||||
// print heading
|
||||
cur_frm.pformat.print_heading = 'Invoice';
|
||||
|
||||
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/selling/doctype/sales_common/sales_common.js');
|
||||
|
||||
// On Load
|
||||
// -------
|
||||
cur_frm.cscript.onload = function(doc,dt,dn) {
|
||||
cur_frm.cscript.manage_rounded_total();
|
||||
if(!doc.customer && doc.debit_to) wn.meta.get_docfield(dt, 'debit_to', dn).print_hide = 0;
|
||||
if (doc.__islocal) {
|
||||
if(!doc.due_date) set_multiple(dt,dn,{due_date:get_today()});
|
||||
if(!doc.posting_date) set_multiple(dt,dn,{posting_date:get_today()});
|
||||
if(!doc.currency && sys_defaults.currency) set_multiple(dt,dn,{currency:sys_defaults.currency});
|
||||
if(!doc.price_list_currency) set_multiple(dt, dn, {price_list_currency: doc.currency, plc_conversion_rate: 1});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
cur_frm.cscript.onload_post_render = function(doc, dt, dn) {
|
||||
var callback = function(doc, dt, dn) {
|
||||
// called from mapper, update the account names for items and customer
|
||||
var callback2 = function(doc, dt, dn) {
|
||||
if(doc.customer && doc.__islocal) {
|
||||
$c_obj(make_doclist(doc.doctype,doc.name),
|
||||
'load_default_accounts','',
|
||||
function(r,rt) {
|
||||
refresh_field('entries');
|
||||
cur_frm.cscript.customer(doc,dt,dn,onload=true);
|
||||
}
|
||||
);
|
||||
wn.provide("erpnext.accounts");
|
||||
erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.extend({
|
||||
onload: function() {
|
||||
this._super();
|
||||
|
||||
if(!this.frm.doc.__islocal) {
|
||||
// show debit_to in print format
|
||||
if(!this.frm.doc.customer && this.frm.doc.debit_to) {
|
||||
this.frm.set_df_property("debit_to", "print_hide", 0);
|
||||
}
|
||||
}
|
||||
// defined in sales_common.js
|
||||
var callback1 = function(doc, dt, dn) {
|
||||
//for previously created sales invoice, set required field related to pos
|
||||
cur_frm.cscript.update_item_details(doc, dt, dn, callback2);
|
||||
},
|
||||
|
||||
refresh: function(doc, dt, dn) {
|
||||
this._super();
|
||||
|
||||
cur_frm.cscript.is_opening(doc, dt, dn);
|
||||
|
||||
if(doc.docstatus==1) {
|
||||
cur_frm.add_custom_button('View Ledger', cur_frm.cscript.view_ledger_entry);
|
||||
cur_frm.add_custom_button('Send SMS', cur_frm.cscript.send_sms);
|
||||
|
||||
if(doc.is_pos==1 && doc.update_stock!=1)
|
||||
cur_frm.add_custom_button('Make Delivery', cur_frm.cscript['Make Delivery Note']);
|
||||
|
||||
if(doc.outstanding_amount!=0)
|
||||
cur_frm.add_custom_button('Make Payment Entry', cur_frm.cscript.make_bank_voucher);
|
||||
}
|
||||
cur_frm.cscript.hide_fields(doc, dt, dn);
|
||||
},
|
||||
|
||||
is_pos: function() {
|
||||
if(cint(this.frm.doc.is_pos)) {
|
||||
if(!this.frm.doc.company) {
|
||||
this.frm.set_value("is_pos", 0);
|
||||
msgprint(wn._("Please specify Company to proceed"));
|
||||
} else {
|
||||
var me = this;
|
||||
this.frm.call({
|
||||
doc: me.frm.doc,
|
||||
method: "set_missing_values",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if(doc.is_pos ==1) cur_frm.cscript.is_pos(doc, dt, dn,callback1);
|
||||
else cur_frm.cscript.update_item_details(doc, dt, dn, callback2);
|
||||
}
|
||||
|
||||
cur_frm.cscript.hide_price_list_currency(doc, dt, dn, callback);
|
||||
|
||||
}
|
||||
// TODO toggle display of fields
|
||||
},
|
||||
|
||||
debit_to: function() {
|
||||
this.customer();
|
||||
},
|
||||
|
||||
allocated_amount: function() {
|
||||
this.calculate_total_advance("Sales Invoice", "advance_adjustment_details");
|
||||
this.frm.refresh_fields();
|
||||
},
|
||||
|
||||
write_off_outstanding_amount_automatically: function() {
|
||||
if(cint(this.frm.doc.write_off_outstanding_amount_automatically)) {
|
||||
wn.model.round_floats_in(this.frm.doc, ["grand_total", "paid_amount"]);
|
||||
// this will make outstanding amount 0
|
||||
this.frm.set_value("write_off_amount",
|
||||
flt(this.frm.doc.grand_total - this.frm.doc.paid_amount), precision("write_off_amount"));
|
||||
}
|
||||
|
||||
this.frm.runclientscript("write_off_amount");
|
||||
},
|
||||
|
||||
write_off_amount: function() {
|
||||
this.calculate_outstanding_amount();
|
||||
this.frm.refresh_fields();
|
||||
},
|
||||
|
||||
paid_amount: function() {
|
||||
this.write_off_outstanding_amount_automatically();
|
||||
},
|
||||
});
|
||||
|
||||
// for backward compatibility: combine new and previous states
|
||||
$.extend(cur_frm.cscript, new erpnext.accounts.SalesInvoiceController({frm: cur_frm}));
|
||||
|
||||
// Hide Fields
|
||||
// ------------
|
||||
@ -87,15 +125,12 @@ cur_frm.cscript.hide_fields = function(doc, cdt, cdn) {
|
||||
hide_field(par_flds);
|
||||
unhide_field('payments_section');
|
||||
for(f in item_flds_normal) cur_frm.fields_dict['entries'].grid.set_column_disp(item_flds_normal[f], false);
|
||||
for(f in item_flds_pos) cur_frm.fields_dict['entries'].grid.set_column_disp(item_flds_pos[f], (doc.update_stock==1?true:false));
|
||||
} else {
|
||||
hide_field('payments_section');
|
||||
unhide_field(par_flds);
|
||||
for(f in item_flds_normal) cur_frm.fields_dict['entries'].grid.set_column_disp(item_flds_normal[f], true);
|
||||
for(f in item_flds_pos) cur_frm.fields_dict['entries'].grid.set_column_disp(item_flds_pos[f], false);
|
||||
}
|
||||
|
||||
cur_frm.toggle_display("contact_section", doc.customer);
|
||||
for(f in item_flds_pos) cur_frm.fields_dict['entries'].grid.set_column_disp(item_flds_pos[f], (cint(doc.update_stock)==1?true:false));
|
||||
|
||||
// India related fields
|
||||
var cp = wn.control_panel;
|
||||
@ -104,50 +139,6 @@ cur_frm.cscript.hide_fields = function(doc, cdt, cdn) {
|
||||
}
|
||||
|
||||
|
||||
// Refresh
|
||||
// -------
|
||||
cur_frm.cscript.refresh = function(doc, dt, dn) {
|
||||
cur_frm.cscript.is_opening(doc, dt, dn);
|
||||
erpnext.hide_naming_series();
|
||||
|
||||
// Show / Hide button
|
||||
cur_frm.clear_custom_buttons();
|
||||
if (!cur_frm.cscript.is_onload) cur_frm.cscript.hide_price_list_currency(doc, dt, dn);
|
||||
|
||||
if(doc.docstatus==1) {
|
||||
cur_frm.add_custom_button('View Ledger', cur_frm.cscript.view_ledger_entry);
|
||||
cur_frm.add_custom_button('Send SMS', cur_frm.cscript.send_sms);
|
||||
|
||||
if(doc.is_pos==1 && doc.update_stock!=1)
|
||||
cur_frm.add_custom_button('Make Delivery', cur_frm.cscript['Make Delivery Note']);
|
||||
|
||||
if(doc.outstanding_amount!=0)
|
||||
cur_frm.add_custom_button('Make Payment Entry', cur_frm.cscript.make_bank_voucher);
|
||||
}
|
||||
cur_frm.cscript.hide_fields(doc, dt, dn);
|
||||
|
||||
}
|
||||
|
||||
//fetch retail transaction related fields
|
||||
//--------------------------------------------
|
||||
cur_frm.cscript.is_pos = function(doc,dt,dn,callback){
|
||||
cur_frm.cscript.hide_fields(doc, dt, dn);
|
||||
if(doc.is_pos == 1){
|
||||
if (!doc.company) {
|
||||
msgprint("Please select company to proceed");
|
||||
doc.is_pos = 0;
|
||||
refresh_field('is_pos');
|
||||
}
|
||||
else {
|
||||
var callback1 = function(r,rt){
|
||||
if(callback) callback(doc, dt, dn);
|
||||
cur_frm.refresh();
|
||||
}
|
||||
$c_obj(make_doclist(dt,dn),'set_pos_fields','',callback1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cur_frm.cscript.mode_of_payment = function(doc) {
|
||||
cur_frm.call({
|
||||
method: "get_bank_cash_account",
|
||||
@ -159,94 +150,10 @@ cur_frm.cscript.update_stock = function(doc, dt, dn) {
|
||||
cur_frm.cscript.hide_fields(doc, dt, dn);
|
||||
}
|
||||
|
||||
cur_frm.cscript.warehouse = function(doc, cdt , cdn) {
|
||||
var d = locals[cdt][cdn];
|
||||
if (!d.item_code) { msgprint("please enter item code first"); return };
|
||||
if (d.warehouse) {
|
||||
arg = "{'item_code':'" + d.item_code + "','warehouse':'" + d.warehouse +"'}";
|
||||
get_server_fields('get_actual_qty',arg,'entries',doc,cdt,cdn,1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//Customer
|
||||
cur_frm.cscript.customer = function(doc,dt,dn,onload) {
|
||||
cur_frm.toggle_display("contact_section", doc.customer);
|
||||
|
||||
var pl = doc.price_list_name;
|
||||
var callback = function(r,rt) {
|
||||
var callback2 = function(doc, dt, dn) {
|
||||
doc = locals[dt][dn];
|
||||
if(doc.debit_to && doc.posting_date){
|
||||
get_server_fields('get_cust_and_due_date','','',doc,dt,dn,1,
|
||||
function(doc, dt, dn) {
|
||||
cur_frm.refresh();
|
||||
if (!onload && (pl != doc.price_list_name)) cur_frm.cscript.price_list_name(doc, dt, dn);
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
var doc = locals[cur_frm.doctype][cur_frm.docname];
|
||||
get_server_fields('get_debit_to','','',doc, dt, dn, 0, callback2);
|
||||
}
|
||||
var args = onload ? 'onload':''
|
||||
if(doc.customer) $c_obj(make_doclist(doc.doctype, doc.name), 'get_default_customer_address', args, callback);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
cur_frm.cscript.customer_address = cur_frm.cscript.contact_person = function(doc,dt,dn) {
|
||||
if(doc.customer) get_server_fields('get_customer_address', JSON.stringify({customer: doc.customer, address: doc.customer_address, contact: doc.contact_person}),'', doc, dt, dn, 1);
|
||||
}
|
||||
|
||||
// Set Due Date = posting date + credit days
|
||||
cur_frm.cscript.debit_to = function(doc,dt,dn) {
|
||||
|
||||
var callback2 = function(r,rt) {
|
||||
var doc = locals[cur_frm.doctype][cur_frm.docname];
|
||||
cur_frm.refresh();
|
||||
}
|
||||
|
||||
var callback = function(r,rt) {
|
||||
var doc = locals[cur_frm.doctype][cur_frm.docname];
|
||||
if(doc.customer) $c_obj(make_doclist(dt,dn), 'get_default_customer_address', '', callback2);
|
||||
cur_frm.toggle_display("contact_section", doc.customer);
|
||||
|
||||
cur_frm.refresh();
|
||||
}
|
||||
|
||||
if(doc.debit_to && doc.posting_date){
|
||||
get_server_fields('get_cust_and_due_date','','',doc,dt,dn,1,callback);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//refresh advance amount
|
||||
//-------------------------------------------------
|
||||
|
||||
|
||||
cur_frm.cscript.write_off_outstanding_amount_automatically = function(doc) {
|
||||
if (doc.write_off_outstanding_amount_automatically == 1)
|
||||
doc.write_off_amount = flt(doc.grand_total) - flt(doc.paid_amount);
|
||||
|
||||
doc.outstanding_amount = flt(doc.grand_total) - flt(doc.paid_amount) - flt(doc.write_off_amount);
|
||||
refresh_field(['write_off_amount', 'outstanding_amount']);
|
||||
}
|
||||
|
||||
cur_frm.cscript.paid_amount = function(doc) {
|
||||
cur_frm.cscript.write_off_outstanding_amount_automatically(doc);
|
||||
}
|
||||
|
||||
cur_frm.cscript.write_off_amount = function(doc) {
|
||||
cur_frm.cscript.write_off_outstanding_amount_automatically(doc);
|
||||
}
|
||||
|
||||
|
||||
//Set debit and credit to zero on adding new row
|
||||
//----------------------------------------------
|
||||
cur_frm.fields_dict['entries'].grid.onrowadd = function(doc, cdt, cdn){
|
||||
|
||||
cl = getchildren('Sales Invoice Item', doc.name, cur_frm.cscript.fname, doc.doctype);
|
||||
@ -282,12 +189,6 @@ cur_frm.cscript.get_items = function(doc, dt, dn) {
|
||||
|
||||
|
||||
|
||||
// Allocated Amount in advances table
|
||||
// -----------------------------------
|
||||
cur_frm.cscript.allocated_amount = function(doc,cdt,cdn){
|
||||
cur_frm.cscript.calc_adjustment_amount(doc,cdt,cdn);
|
||||
}
|
||||
|
||||
//Make Delivery Note Button
|
||||
//-----------------------------
|
||||
|
||||
@ -452,19 +353,6 @@ cur_frm.cscript.cost_center = function(doc, cdt, cdn){
|
||||
refresh_field(cur_frm.cscript.fname);
|
||||
}
|
||||
|
||||
cur_frm.cscript.calc_adjustment_amount = function(doc,cdt,cdn) {
|
||||
var doc = locals[doc.doctype][doc.name];
|
||||
var el = getchildren('Sales Invoice Advance',doc.name,'advance_adjustment_details');
|
||||
var total_adjustment_amt = 0
|
||||
for(var i in el) {
|
||||
total_adjustment_amt += flt(el[i].allocated_amount)
|
||||
}
|
||||
doc.total_advance = flt(total_adjustment_amt);
|
||||
doc.outstanding_amount = flt(doc.grand_total) - flt(total_adjustment_amt) - flt(doc.paid_amount) - flt(doc.write_off_amount);
|
||||
refresh_many(['total_advance','outstanding_amount']);
|
||||
}
|
||||
|
||||
|
||||
// Make Journal Voucher
|
||||
// --------------------
|
||||
cur_frm.cscript.make_jv = function(doc, dt, dn, bank_account) {
|
||||
|
@ -41,7 +41,6 @@ class DocType(SellingController):
|
||||
|
||||
def validate(self):
|
||||
super(DocType, self).validate()
|
||||
self.fetch_missing_values()
|
||||
self.validate_posting_time()
|
||||
self.so_dn_required()
|
||||
self.validate_proj_cust()
|
||||
@ -50,7 +49,6 @@ class DocType(SellingController):
|
||||
sales_com_obj.check_active_sales_items(self)
|
||||
sales_com_obj.check_conversion_rate(self)
|
||||
sales_com_obj.validate_max_discount(self, 'entries')
|
||||
sales_com_obj.get_allocated_sum(self)
|
||||
sales_com_obj.validate_fiscal_year(self.doc.fiscal_year,
|
||||
self.doc.posting_date,'Posting Date')
|
||||
self.validate_customer()
|
||||
@ -59,18 +57,22 @@ class DocType(SellingController):
|
||||
self.validate_fixed_asset_account()
|
||||
self.clear_unallocated_advances("Sales Invoice Advance", "advance_adjustment_details")
|
||||
self.add_remarks()
|
||||
|
||||
if cint(self.doc.is_pos):
|
||||
self.validate_pos()
|
||||
self.validate_write_off_account()
|
||||
if cint(self.doc.update_stock):
|
||||
sl = get_obj('Stock Ledger')
|
||||
sl.validate_serial_no(self, 'entries')
|
||||
sl.validate_serial_no(self, 'packing_details')
|
||||
self.validate_item_code()
|
||||
self.update_current_stock()
|
||||
self.validate_delivery_note()
|
||||
|
||||
if cint(self.doc.update_stock):
|
||||
sl = get_obj('Stock Ledger')
|
||||
sl.validate_serial_no(self, 'entries')
|
||||
sl.validate_serial_no(self, 'packing_details')
|
||||
self.validate_item_code()
|
||||
self.update_current_stock()
|
||||
self.validate_delivery_note()
|
||||
|
||||
if not self.doc.is_opening:
|
||||
self.doc.is_opening = 'No'
|
||||
|
||||
self.set_aging_date()
|
||||
self.set_against_income_account()
|
||||
self.validate_c_form()
|
||||
@ -80,16 +82,15 @@ class DocType(SellingController):
|
||||
|
||||
|
||||
def on_submit(self):
|
||||
if cint(self.doc.is_pos) == 1:
|
||||
if cint(self.doc.update_stock) == 1:
|
||||
sl_obj = get_obj("Stock Ledger")
|
||||
sl_obj.validate_serial_no_warehouse(self, 'entries')
|
||||
sl_obj.validate_serial_no_warehouse(self, 'packing_details')
|
||||
|
||||
sl_obj.update_serial_record(self, 'entries', is_submit = 1, is_incoming = 0)
|
||||
sl_obj.update_serial_record(self, 'packing_details', is_submit = 1, is_incoming = 0)
|
||||
|
||||
self.update_stock_ledger(update_stock=1)
|
||||
if cint(self.doc.update_stock) == 1:
|
||||
sl_obj = get_obj("Stock Ledger")
|
||||
sl_obj.validate_serial_no_warehouse(self, 'entries')
|
||||
sl_obj.validate_serial_no_warehouse(self, 'packing_details')
|
||||
|
||||
sl_obj.update_serial_record(self, 'entries', is_submit = 1, is_incoming = 0)
|
||||
sl_obj.update_serial_record(self, 'packing_details', is_submit = 1, is_incoming = 0)
|
||||
|
||||
self.update_stock_ledger(update_stock=1)
|
||||
else:
|
||||
# Check for Approving Authority
|
||||
if not self.doc.recurring_id:
|
||||
@ -114,13 +115,12 @@ class DocType(SellingController):
|
||||
self.update_time_log_batch(None)
|
||||
|
||||
def on_cancel(self):
|
||||
if cint(self.doc.is_pos) == 1:
|
||||
if cint(self.doc.update_stock) == 1:
|
||||
sl = get_obj('Stock Ledger')
|
||||
sl.update_serial_record(self, 'entries', is_submit = 0, is_incoming = 0)
|
||||
sl.update_serial_record(self, 'packing_details', is_submit = 0, is_incoming = 0)
|
||||
|
||||
self.update_stock_ledger(update_stock = -1)
|
||||
if cint(self.doc.update_stock) == 1:
|
||||
sl = get_obj('Stock Ledger')
|
||||
sl.update_serial_record(self, 'entries', is_submit = 0, is_incoming = 0)
|
||||
sl.update_serial_record(self, 'packing_details', is_submit = 0, is_incoming = 0)
|
||||
|
||||
self.update_stock_ledger(update_stock = -1)
|
||||
|
||||
sales_com_obj = get_obj(dt = 'Sales Common')
|
||||
sales_com_obj.check_stop_sales_order(self)
|
||||
@ -136,25 +136,16 @@ class DocType(SellingController):
|
||||
self.validate_recurring_invoice()
|
||||
self.convert_to_recurring()
|
||||
|
||||
def fetch_missing_values(self):
|
||||
# fetch contact and address details for customer, if they are not mentioned
|
||||
if not (self.doc.contact_person and self.doc.customer_address):
|
||||
for fieldname, val in self.get_default_address_and_contact("customer").items():
|
||||
if not self.doc.fields.get(fieldname) and self.meta.get_field(fieldname):
|
||||
self.doc.fields[fieldname] = val
|
||||
|
||||
# fetch missing item values
|
||||
for item in self.doclist.get({"parentfield": "entries"}):
|
||||
if item.fields.get("item_code"):
|
||||
ret = get_obj('Sales Common').get_item_details(item.fields, self)
|
||||
for fieldname, value in ret.items():
|
||||
if self.meta.get_field(fieldname, parentfield="entries") and \
|
||||
not item.fields.get(fieldname):
|
||||
item.fields[fieldname] = value
|
||||
def set_missing_values(self, for_validate=False):
|
||||
super(DocType, self).set_missing_values(for_validate)
|
||||
self.set_pos_fields(for_validate)
|
||||
|
||||
# fetch pos details, if they are not fetched
|
||||
if cint(self.doc.is_pos):
|
||||
self.set_pos_fields(for_validate=True)
|
||||
def set_customer_defaults(self):
|
||||
# TODO cleanup these methods
|
||||
self.doc.fields.update(self.get_debit_to())
|
||||
self.get_cust_and_due_date()
|
||||
|
||||
super(DocType, self).set_customer_defaults()
|
||||
|
||||
def update_time_log_batch(self, sales_invoice):
|
||||
for d in self.doclist.get({"doctype":"Sales Invoice Item"}):
|
||||
@ -175,10 +166,11 @@ class DocType(SellingController):
|
||||
"""Set retail related fields from pos settings"""
|
||||
if cint(self.doc.is_pos) != 1:
|
||||
return
|
||||
|
||||
from selling.utils import get_pos_settings, apply_pos_settings
|
||||
pos = get_pos_settings(self.doc.company)
|
||||
|
||||
if self.pos_settings:
|
||||
pos = self.pos_settings[0]
|
||||
|
||||
if pos:
|
||||
self.doc.conversion_rate = flt(pos.conversion_rate)
|
||||
|
||||
if not self.doc.debit_to:
|
||||
@ -191,11 +183,14 @@ class DocType(SellingController):
|
||||
'price_list_name', 'company', 'select_print_heading', 'cash_bank_account'):
|
||||
if (not for_validate) or (for_validate and not self.doc.fields.get(fieldname)):
|
||||
self.doc.fields[fieldname] = pos.get(fieldname)
|
||||
|
||||
if not for_validate:
|
||||
self.doc.update_stock = cint(pos.get("update_stock"))
|
||||
|
||||
# set pos values in items
|
||||
for item in self.doclist.get({"parentfield": "entries"}):
|
||||
if item.fields.get('item_code'):
|
||||
for fieldname, val in self.apply_pos_settings(item.fields).items():
|
||||
for fieldname, val in apply_pos_settings(pos, item.fields).items():
|
||||
if (not for_validate) or (for_validate and not item.fields.get(fieldname)):
|
||||
item.fields[fieldname] = val
|
||||
|
||||
@ -205,7 +200,7 @@ class DocType(SellingController):
|
||||
|
||||
# fetch charges
|
||||
if self.doc.charge and not len(self.doclist.get({"parentfield": "other_charges"})):
|
||||
self.get_other_charges()
|
||||
self.set_taxes()
|
||||
|
||||
def get_customer_account(self):
|
||||
"""Get Account Head to which amount needs to be Debited based on Customer"""
|
||||
@ -275,86 +270,6 @@ class DocType(SellingController):
|
||||
ret = self.get_debit_to()
|
||||
self.doc.debit_to = ret.get('debit_to')
|
||||
|
||||
|
||||
def load_default_accounts(self):
|
||||
"""
|
||||
Loads default accounts from items, customer when called from mapper
|
||||
"""
|
||||
self.get_income_expense_account('entries')
|
||||
|
||||
|
||||
def get_income_expense_account(self,doctype):
|
||||
for d in getlist(self.doclist, doctype):
|
||||
if d.item_code:
|
||||
item = webnotes.conn.get_value("Item", d.item_code, ["default_income_account",
|
||||
"default_sales_cost_center", "purchase_account"], as_dict=True)
|
||||
d.income_account = item['default_income_account'] or ""
|
||||
d.cost_center = item['default_sales_cost_center'] or ""
|
||||
|
||||
if cint(webnotes.defaults.get_global_default("auto_inventory_accounting")) \
|
||||
and cint(self.doc.is_pos) and cint(self.doc.update_stock):
|
||||
d.expense_account = item['purchase_account'] or ""
|
||||
|
||||
def get_item_details(self, args=None):
|
||||
import json
|
||||
args = args and json.loads(args) or {}
|
||||
if args.get('item_code'):
|
||||
ret = get_obj('Sales Common').get_item_details(args, self)
|
||||
|
||||
if cint(self.doc.is_pos) == 1 and self.pos_settings:
|
||||
ret = self.apply_pos_settings(args, ret)
|
||||
|
||||
return ret
|
||||
|
||||
elif cint(self.doc.is_pos) == 1 and self.pos_settings:
|
||||
for doc in self.doclist.get({"parentfield": "entries"}):
|
||||
if doc.fields.get('item_code'):
|
||||
ret = self.apply_pos_settings(doc.fields)
|
||||
for r in ret:
|
||||
if not doc.fields.get(r):
|
||||
doc.fields[r] = ret[r]
|
||||
|
||||
@property
|
||||
def pos_settings(self):
|
||||
if not hasattr(self, "_pos_settings"):
|
||||
dtl = webnotes.conn.sql("""select * from `tabPOS Setting` where user = %s
|
||||
and company = %s""", (webnotes.session['user'], self.doc.company), as_dict=1)
|
||||
if not dtl:
|
||||
dtl = webnotes.conn.sql("""select * from `tabPOS Setting`
|
||||
where ifnull(user,'') = '' and company = %s""", self.doc.company, as_dict=1)
|
||||
self._pos_settings = dtl
|
||||
|
||||
return self._pos_settings
|
||||
|
||||
def apply_pos_settings(self, args, ret=None):
|
||||
if not ret: ret = {}
|
||||
|
||||
pos = self.pos_settings[0]
|
||||
|
||||
item = webnotes.conn.sql("""select default_income_account, default_sales_cost_center,
|
||||
default_warehouse, purchase_account from tabItem where name = %s""",
|
||||
args.get('item_code'), as_dict=1)
|
||||
|
||||
if item:
|
||||
item = item[0]
|
||||
|
||||
ret.update({
|
||||
"income_account": item.get("default_income_account") \
|
||||
or pos.get("income_account") or args.get("income_account"),
|
||||
"cost_center": item.get("default_sales_cost_center") \
|
||||
or pos.get("cost_center") or args.get("cost_center"),
|
||||
"warehouse": item.get("default_warehouse") \
|
||||
or pos.get("warehouse") or args.get("warehouse"),
|
||||
"expense_account": item.get("purchase_account") \
|
||||
or pos.get("expense_account") or args.get("expense_account")
|
||||
})
|
||||
|
||||
if ret.get("warehouse"):
|
||||
ret["actual_qty"] = flt(webnotes.conn.get_value("Bin",
|
||||
{"item_code": args.get("item_code"), "warehouse": ret.get("warehouse")},
|
||||
"actual_qty"))
|
||||
return ret
|
||||
|
||||
def get_barcode_details(self, barcode):
|
||||
return get_obj('Sales Common').get_barcode_details(barcode)
|
||||
|
||||
@ -378,14 +293,6 @@ class DocType(SellingController):
|
||||
return get_obj('Sales Common').get_tc_details(self)
|
||||
|
||||
|
||||
def load_default_taxes(self):
|
||||
self.doclist = get_obj('Sales Common').load_default_taxes(self)
|
||||
|
||||
|
||||
def get_other_charges(self):
|
||||
self.doclist = get_obj('Sales Common').get_other_charges(self)
|
||||
|
||||
|
||||
def get_advances(self):
|
||||
super(DocType, self).get_advances(self.doc.debit_to,
|
||||
"Sales Invoice Advance", "advance_adjustment_details", "credit")
|
||||
@ -534,13 +441,13 @@ class DocType(SellingController):
|
||||
def validate_item_code(self):
|
||||
for d in getlist(self.doclist, 'entries'):
|
||||
if not d.item_code:
|
||||
msgprint("Please enter Item Code at line no : %s to update stock for POS or remove check from Update Stock in Basic Info Tab." % (d.idx))
|
||||
raise Exception
|
||||
msgprint("Please enter Item Code at line no : %s to update stock or remove check from Update Stock in Basic Info Tab." % (d.idx),
|
||||
raise_exception=True)
|
||||
|
||||
def validate_delivery_note(self):
|
||||
for d in self.doclist.get({"parentfield": "entries"}):
|
||||
if d.delivery_note:
|
||||
msgprint("""POS can not be made against Delivery Note""", raise_exception=1)
|
||||
msgprint("""Stock update can not be made against Delivery Note""", raise_exception=1)
|
||||
|
||||
|
||||
def validate_write_off_account(self):
|
||||
@ -606,36 +513,28 @@ class DocType(SellingController):
|
||||
|
||||
|
||||
def on_update(self):
|
||||
# Set default warehouse from pos setting
|
||||
if cint(self.doc.is_pos) == 1:
|
||||
if cint(self.doc.update_stock) == 1:
|
||||
if cint(self.doc.update_stock) == 1:
|
||||
# Set default warehouse from pos setting
|
||||
if cint(self.doc.is_pos) == 1:
|
||||
w = self.get_warehouse()
|
||||
if w:
|
||||
for d in getlist(self.doclist, 'entries'):
|
||||
if not d.warehouse:
|
||||
d.warehouse = cstr(w)
|
||||
|
||||
self.make_packing_list()
|
||||
else:
|
||||
self.doclist = self.doc.clear_table(self.doclist, 'packing_details')
|
||||
|
||||
if flt(self.doc.paid_amount) == 0:
|
||||
if self.doc.cash_bank_account:
|
||||
webnotes.conn.set(self.doc, 'paid_amount',
|
||||
(flt(self.doc.grand_total) - flt(self.doc.write_off_amount)))
|
||||
else:
|
||||
# show message that the amount is not paid
|
||||
webnotes.conn.set(self.doc,'paid_amount',0)
|
||||
webnotes.msgprint("Note: Payment Entry will not be created since 'Cash/Bank Account' was not specified.")
|
||||
if flt(self.doc.paid_amount) == 0:
|
||||
if self.doc.cash_bank_account:
|
||||
webnotes.conn.set(self.doc, 'paid_amount',
|
||||
(flt(self.doc.grand_total) - flt(self.doc.write_off_amount)))
|
||||
else:
|
||||
# show message that the amount is not paid
|
||||
webnotes.conn.set(self.doc,'paid_amount',0)
|
||||
webnotes.msgprint("Note: Payment Entry will not be created since 'Cash/Bank Account' was not specified.")
|
||||
|
||||
self.make_packing_list()
|
||||
else:
|
||||
self.doclist = self.doc.clear_table(self.doclist, 'packing_details')
|
||||
webnotes.conn.set(self.doc,'paid_amount',0)
|
||||
|
||||
webnotes.conn.set(self.doc, 'outstanding_amount',
|
||||
flt(self.doc.grand_total) - flt(self.doc.total_advance) -
|
||||
flt(self.doc.paid_amount) - flt(self.doc.write_off_amount))
|
||||
|
||||
|
||||
def check_prev_docstatus(self):
|
||||
for d in getlist(self.doclist,'entries'):
|
||||
@ -689,15 +588,6 @@ class DocType(SellingController):
|
||||
|
||||
get_obj('Stock Ledger', 'Stock Ledger').update_stock(self.values)
|
||||
|
||||
def get_actual_qty(self,args):
|
||||
args = eval(args)
|
||||
actual_qty = webnotes.conn.sql("select actual_qty from `tabBin` where item_code = '%s' and warehouse = '%s'" % (args['item_code'], args['warehouse']), as_dict=1)
|
||||
ret = {
|
||||
'actual_qty' : actual_qty and flt(actual_qty[0]['actual_qty']) or 0
|
||||
}
|
||||
return ret
|
||||
|
||||
|
||||
def make_gl_entries(self):
|
||||
from accounts.general_ledger import make_gl_entries, merge_similar_entries
|
||||
|
||||
@ -742,7 +632,7 @@ class DocType(SellingController):
|
||||
"against": self.doc.debit_to,
|
||||
"credit": flt(tax.tax_amount),
|
||||
"remarks": self.doc.remarks,
|
||||
"cost_center": tax.cost_center_other_charges
|
||||
"cost_center": tax.cost_center
|
||||
})
|
||||
)
|
||||
|
||||
@ -762,7 +652,7 @@ class DocType(SellingController):
|
||||
|
||||
# expense account gl entries
|
||||
if cint(webnotes.defaults.get_global_default("auto_inventory_accounting")) \
|
||||
and cint(self.doc.is_pos) and cint(self.doc.update_stock):
|
||||
and cint(self.doc.update_stock):
|
||||
|
||||
for item in self.doclist.get({"parentfield": "entries"}):
|
||||
self.check_expense_account(item)
|
||||
|
@ -1,8 +1,8 @@
|
||||
[
|
||||
{
|
||||
"creation": "2013-04-19 11:00:14",
|
||||
"creation": "2013-05-24 19:29:05",
|
||||
"docstatus": 0,
|
||||
"modified": "2013-04-22 11:59:28",
|
||||
"modified": "2013-05-29 18:53:26",
|
||||
"modified_by": "Administrator",
|
||||
"owner": "Administrator"
|
||||
},
|
||||
@ -10,6 +10,7 @@
|
||||
"allow_attach": 1,
|
||||
"autoname": "naming_series:",
|
||||
"doctype": "DocType",
|
||||
"document_type": "Transaction",
|
||||
"is_submittable": 1,
|
||||
"module": "Accounts",
|
||||
"name": "__common__",
|
||||
@ -79,7 +80,6 @@
|
||||
"read_only": 0
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.is_pos==1",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "update_stock",
|
||||
"fieldtype": "Check",
|
||||
@ -118,10 +118,11 @@
|
||||
"read_only": 0
|
||||
},
|
||||
{
|
||||
"depends_on": "customer",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "customer_name",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"hidden": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Name",
|
||||
"oldfieldname": "customer_name",
|
||||
@ -129,34 +130,38 @@
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "customer",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "address_display",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 1,
|
||||
"hidden": 0,
|
||||
"label": "Address",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "customer",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "contact_display",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 1,
|
||||
"hidden": 0,
|
||||
"label": "Contact",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "customer",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "contact_mobile",
|
||||
"fieldtype": "Text",
|
||||
"hidden": 1,
|
||||
"hidden": 0,
|
||||
"label": "Mobile No",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "customer",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "contact_email",
|
||||
"fieldtype": "Text",
|
||||
"hidden": 1,
|
||||
"hidden": 0,
|
||||
"label": "Contact Email",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
@ -222,11 +227,12 @@
|
||||
"doctype": "DocField",
|
||||
"fieldname": "entries",
|
||||
"fieldtype": "Table",
|
||||
"label": "Entries",
|
||||
"label": "Sales Invoice Items",
|
||||
"oldfieldname": "entries",
|
||||
"oldfieldtype": "Table",
|
||||
"options": "Sales Invoice Item",
|
||||
"read_only": 0
|
||||
"read_only": 0,
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
@ -250,35 +256,6 @@
|
||||
"read_only": 0,
|
||||
"width": "50%"
|
||||
},
|
||||
{
|
||||
"description": "Will be calculated automatically when you enter the details",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "net_total",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Net Total*",
|
||||
"oldfieldname": "net_total",
|
||||
"oldfieldtype": "Currency",
|
||||
"options": "Company:company:default_currency",
|
||||
"print_hide": 0,
|
||||
"read_only": 1,
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "recalculate_values",
|
||||
"fieldtype": "Button",
|
||||
"label": "Re-Calculate Values",
|
||||
"oldfieldtype": "Button",
|
||||
"print_hide": 1,
|
||||
"read_only": 0
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "col_break25",
|
||||
"fieldtype": "Column Break",
|
||||
"read_only": 0,
|
||||
"width": "50%"
|
||||
},
|
||||
{
|
||||
"description": "Select Items from Sales Order",
|
||||
"doctype": "DocField",
|
||||
@ -312,6 +289,43 @@
|
||||
"print_hide": 1,
|
||||
"read_only": 0
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "col_break25",
|
||||
"fieldtype": "Column Break",
|
||||
"read_only": 0,
|
||||
"width": "50%"
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "recalculate_values",
|
||||
"fieldtype": "Button",
|
||||
"label": "Re-Calculate Values",
|
||||
"oldfieldtype": "Button",
|
||||
"print_hide": 1,
|
||||
"read_only": 0
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "net_total",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Net Total*",
|
||||
"oldfieldname": "net_total",
|
||||
"oldfieldtype": "Currency",
|
||||
"options": "Company:company:default_currency",
|
||||
"print_hide": 1,
|
||||
"read_only": 1,
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "net_total_export",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Net Total (Export)",
|
||||
"options": "currency",
|
||||
"print_hide": 0,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "currency_section",
|
||||
@ -450,6 +464,15 @@
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "other_charges_total_export",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Total Taxes and Charges (Export)",
|
||||
"options": "currency",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "other_charges_calculation",
|
||||
@ -728,6 +751,7 @@
|
||||
"read_only": 0
|
||||
},
|
||||
{
|
||||
"depends_on": "customer",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "contact_section",
|
||||
"fieldtype": "Section Break",
|
||||
@ -1283,6 +1307,17 @@
|
||||
"read_only": 0,
|
||||
"report_hide": 1
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 0,
|
||||
"doctype": "DocPerm",
|
||||
"permlevel": 1,
|
||||
"report": 0,
|
||||
"role": "Accounts Manager",
|
||||
"submit": 0,
|
||||
"write": 0
|
||||
},
|
||||
{
|
||||
"amend": 1,
|
||||
"cancel": 1,
|
||||
@ -1290,10 +1325,32 @@
|
||||
"doctype": "DocPerm",
|
||||
"permlevel": 0,
|
||||
"report": 1,
|
||||
"role": "Accounts Manager",
|
||||
"submit": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"amend": 1,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"doctype": "DocPerm",
|
||||
"permlevel": 0,
|
||||
"report": 1,
|
||||
"role": "Accounts User",
|
||||
"submit": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 0,
|
||||
"doctype": "DocPerm",
|
||||
"permlevel": 1,
|
||||
"report": 0,
|
||||
"role": "Accounts User",
|
||||
"submit": 0,
|
||||
"write": 0
|
||||
},
|
||||
{
|
||||
"doctype": "DocPerm",
|
||||
"match": "customer",
|
||||
@ -1301,4 +1358,9 @@
|
||||
"report": 1,
|
||||
"role": "Customer"
|
||||
},
|
||||
{
|
||||
"doctype": "DocPerm",
|
||||
"permlevel": 0,
|
||||
"role": "Retail User"
|
||||
}
|
||||
]
|
@ -1,12 +1,226 @@
|
||||
import webnotes
|
||||
import unittest
|
||||
import unittest, json
|
||||
from webnotes.utils import flt, cint
|
||||
|
||||
class TestSalesInvoice(unittest.TestCase):
|
||||
def make(self):
|
||||
w = webnotes.bean(webnotes.copy_doclist(test_records[0]))
|
||||
w = webnotes.bean(copy=test_records[0])
|
||||
w.insert()
|
||||
w.submit()
|
||||
return w
|
||||
|
||||
def test_sales_invoice_calculation_base_currency(self):
|
||||
si = webnotes.bean(copy=test_records[2])
|
||||
si.run_method("calculate_taxes_and_totals")
|
||||
si.insert()
|
||||
|
||||
expected_values = {
|
||||
"keys": ["ref_rate", "adj_rate", "export_rate", "export_amount",
|
||||
"base_ref_rate", "basic_rate", "amount"],
|
||||
"_Test Item Home Desktop 100": [50, 0, 50, 500, 50, 50, 500],
|
||||
"_Test Item Home Desktop 200": [150, 0, 150, 750, 150, 150, 750],
|
||||
}
|
||||
|
||||
# check if children are saved
|
||||
self.assertEquals(len(si.doclist.get({"parentfield": "entries"})),
|
||||
len(expected_values)-1)
|
||||
|
||||
# check if item values are calculated
|
||||
for d in si.doclist.get({"parentfield": "entries"}):
|
||||
for i, k in enumerate(expected_values["keys"]):
|
||||
self.assertEquals(d.fields.get(k), expected_values[d.item_code][i])
|
||||
|
||||
# check net total
|
||||
self.assertEquals(si.doc.net_total, 1250)
|
||||
self.assertEquals(si.doc.net_total_export, 1250)
|
||||
|
||||
# check tax calculation
|
||||
expected_values = {
|
||||
"keys": ["tax_amount", "total"],
|
||||
"_Test Account Shipping Charges - _TC": [100, 1350],
|
||||
"_Test Account Customs Duty - _TC": [125, 1475],
|
||||
"_Test Account Excise Duty - _TC": [140, 1615],
|
||||
"_Test Account Education Cess - _TC": [2.8, 1617.8],
|
||||
"_Test Account S&H Education Cess - _TC": [1.4, 1619.2],
|
||||
"_Test Account CST - _TC": [32.38, 1651.58],
|
||||
"_Test Account VAT - _TC": [156.25, 1807.83],
|
||||
"_Test Account Discount - _TC": [-180.78, 1627.05]
|
||||
}
|
||||
|
||||
for d in si.doclist.get({"parentfield": "other_charges"}):
|
||||
for i, k in enumerate(expected_values["keys"]):
|
||||
self.assertEquals(d.fields.get(k), expected_values[d.account_head][i])
|
||||
|
||||
self.assertEquals(si.doc.grand_total, 1627.05)
|
||||
self.assertEquals(si.doc.grand_total_export, 1627.05)
|
||||
|
||||
def test_sales_invoice_calculation_export_currency(self):
|
||||
si = webnotes.bean(copy=test_records[2])
|
||||
si.doc.currency = "USD"
|
||||
si.doc.conversion_rate = 50
|
||||
si.doclist[1].export_rate = 1
|
||||
si.doclist[1].ref_rate = 1
|
||||
si.doclist[2].export_rate = 3
|
||||
si.doclist[2].ref_rate = 3
|
||||
si.run_method("calculate_taxes_and_totals")
|
||||
si.insert()
|
||||
|
||||
expected_values = {
|
||||
"keys": ["ref_rate", "adj_rate", "export_rate", "export_amount",
|
||||
"base_ref_rate", "basic_rate", "amount"],
|
||||
"_Test Item Home Desktop 100": [1, 0, 1, 10, 50, 50, 500],
|
||||
"_Test Item Home Desktop 200": [3, 0, 3, 15, 150, 150, 750],
|
||||
}
|
||||
|
||||
# check if children are saved
|
||||
self.assertEquals(len(si.doclist.get({"parentfield": "entries"})),
|
||||
len(expected_values)-1)
|
||||
|
||||
# check if item values are calculated
|
||||
for d in si.doclist.get({"parentfield": "entries"}):
|
||||
for i, k in enumerate(expected_values["keys"]):
|
||||
self.assertEquals(d.fields.get(k), expected_values[d.item_code][i])
|
||||
|
||||
# check net total
|
||||
self.assertEquals(si.doc.net_total, 1250)
|
||||
self.assertEquals(si.doc.net_total_export, 25)
|
||||
|
||||
# check tax calculation
|
||||
expected_values = {
|
||||
"keys": ["tax_amount", "total"],
|
||||
"_Test Account Shipping Charges - _TC": [100, 1350],
|
||||
"_Test Account Customs Duty - _TC": [125, 1475],
|
||||
"_Test Account Excise Duty - _TC": [140, 1615],
|
||||
"_Test Account Education Cess - _TC": [2.8, 1617.8],
|
||||
"_Test Account S&H Education Cess - _TC": [1.4, 1619.2],
|
||||
"_Test Account CST - _TC": [32.38, 1651.58],
|
||||
"_Test Account VAT - _TC": [156.25, 1807.83],
|
||||
"_Test Account Discount - _TC": [-180.78, 1627.05]
|
||||
}
|
||||
|
||||
for d in si.doclist.get({"parentfield": "other_charges"}):
|
||||
for i, k in enumerate(expected_values["keys"]):
|
||||
self.assertEquals(d.fields.get(k), expected_values[d.account_head][i])
|
||||
|
||||
self.assertEquals(si.doc.grand_total, 1627.05)
|
||||
self.assertEquals(si.doc.grand_total_export, 32.54)
|
||||
|
||||
def test_inclusive_rate_validations(self):
|
||||
si = webnotes.bean(copy=test_records[2])
|
||||
for i, tax in enumerate(si.doclist.get({"parentfield": "other_charges"})):
|
||||
tax.idx = i+1
|
||||
|
||||
si.doclist[1].ref_rate = 62.5
|
||||
si.doclist[1].ref_rate = 191
|
||||
for i in [3, 5, 6, 7, 8, 9]:
|
||||
si.doclist[i].included_in_print_rate = 1
|
||||
|
||||
# tax type "Actual" cannot be inclusive
|
||||
self.assertRaises(webnotes.ValidationError, si.run_method, "calculate_taxes_and_totals")
|
||||
|
||||
# taxes above included type 'On Previous Row Total' should also be included
|
||||
si.doclist[3].included_in_print_rate = 0
|
||||
self.assertRaises(webnotes.ValidationError, si.run_method, "calculate_taxes_and_totals")
|
||||
|
||||
def test_sales_invoice_calculation_base_currency_with_tax_inclusive_price(self):
|
||||
# prepare
|
||||
si = webnotes.bean(copy=test_records[3])
|
||||
si.run_method("calculate_taxes_and_totals")
|
||||
si.insert()
|
||||
|
||||
expected_values = {
|
||||
"keys": ["ref_rate", "adj_rate", "export_rate", "export_amount",
|
||||
"base_ref_rate", "basic_rate", "amount"],
|
||||
"_Test Item Home Desktop 100": [62.5, 0, 62.5, 625.0, 50, 50, 500],
|
||||
"_Test Item Home Desktop 200": [190.66, 0, 190.66, 953.3, 150, 150, 750],
|
||||
}
|
||||
|
||||
# check if children are saved
|
||||
self.assertEquals(len(si.doclist.get({"parentfield": "entries"})),
|
||||
len(expected_values)-1)
|
||||
|
||||
# check if item values are calculated
|
||||
for d in si.doclist.get({"parentfield": "entries"}):
|
||||
for i, k in enumerate(expected_values["keys"]):
|
||||
self.assertEquals(d.fields.get(k), expected_values[d.item_code][i])
|
||||
|
||||
# check net total
|
||||
self.assertEquals(si.doc.net_total, 1250)
|
||||
self.assertEquals(si.doc.net_total_export, 1578.3)
|
||||
|
||||
# check tax calculation
|
||||
expected_values = {
|
||||
"keys": ["tax_amount", "total"],
|
||||
"_Test Account Excise Duty - _TC": [140, 1390],
|
||||
"_Test Account Education Cess - _TC": [2.8, 1392.8],
|
||||
"_Test Account S&H Education Cess - _TC": [1.4, 1394.2],
|
||||
"_Test Account CST - _TC": [27.88, 1422.08],
|
||||
"_Test Account VAT - _TC": [156.25, 1578.33],
|
||||
"_Test Account Customs Duty - _TC": [125, 1703.33],
|
||||
"_Test Account Shipping Charges - _TC": [100, 1803.33],
|
||||
"_Test Account Discount - _TC": [-180.33, 1623]
|
||||
}
|
||||
|
||||
for d in si.doclist.get({"parentfield": "other_charges"}):
|
||||
for i, k in enumerate(expected_values["keys"]):
|
||||
self.assertEquals(flt(d.fields.get(k), 6), expected_values[d.account_head][i])
|
||||
|
||||
self.assertEquals(si.doc.grand_total, 1623)
|
||||
self.assertEquals(si.doc.grand_total_export, 1623)
|
||||
|
||||
def test_sales_invoice_calculation_export_currency_with_tax_inclusive_price(self):
|
||||
# prepare
|
||||
si = webnotes.bean(copy=test_records[3])
|
||||
si.doc.currency = "USD"
|
||||
si.doc.conversion_rate = 50
|
||||
si.doclist[1].ref_rate = 55.56
|
||||
si.doclist[1].adj_rate = 10
|
||||
si.doclist[2].ref_rate = 187.5
|
||||
si.doclist[2].adj_rate = 20
|
||||
si.doclist[9].rate = 5000
|
||||
|
||||
si.run_method("calculate_taxes_and_totals")
|
||||
si.insert()
|
||||
|
||||
expected_values = {
|
||||
"keys": ["ref_rate", "adj_rate", "export_rate", "export_amount",
|
||||
"base_ref_rate", "basic_rate", "amount"],
|
||||
"_Test Item Home Desktop 100": [55.56, 10, 50, 500, 2222.11, 1999.9, 19999.0],
|
||||
"_Test Item Home Desktop 200": [187.5, 20, 150, 750, 7375.66, 5900.53, 29502.65],
|
||||
}
|
||||
|
||||
# check if children are saved
|
||||
self.assertEquals(len(si.doclist.get({"parentfield": "entries"})),
|
||||
len(expected_values)-1)
|
||||
|
||||
# check if item values are calculated
|
||||
for d in si.doclist.get({"parentfield": "entries"}):
|
||||
for i, k in enumerate(expected_values["keys"]):
|
||||
self.assertEquals(d.fields.get(k), expected_values[d.item_code][i])
|
||||
|
||||
# check net total
|
||||
self.assertEquals(si.doc.net_total, 49501.65)
|
||||
self.assertEquals(si.doc.net_total_export, 1250)
|
||||
|
||||
# check tax calculation
|
||||
expected_values = {
|
||||
"keys": ["tax_amount", "total"],
|
||||
"_Test Account Excise Duty - _TC": [5540.22, 55041.87],
|
||||
"_Test Account Education Cess - _TC": [110.81, 55152.68],
|
||||
"_Test Account S&H Education Cess - _TC": [55.4, 55208.08],
|
||||
"_Test Account CST - _TC": [1104.16, 56312.24],
|
||||
"_Test Account VAT - _TC": [6187.71, 62499.95],
|
||||
"_Test Account Customs Duty - _TC": [4950.17, 67450.12],
|
||||
"_Test Account Shipping Charges - _TC": [5000, 72450.12],
|
||||
"_Test Account Discount - _TC": [-7245.01, 65205.11]
|
||||
}
|
||||
|
||||
for d in si.doclist.get({"parentfield": "other_charges"}):
|
||||
for i, k in enumerate(expected_values["keys"]):
|
||||
self.assertEquals(flt(d.fields.get(k), 6), expected_values[d.account_head][i])
|
||||
|
||||
self.assertEquals(si.doc.grand_total, 65205.11)
|
||||
self.assertEquals(si.doc.grand_total_export, 1304.1)
|
||||
|
||||
def test_outstanding(self):
|
||||
w = self.make()
|
||||
@ -103,7 +317,7 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
pos[0]["cash_bank_account"] = "_Test Account Bank Account - _TC"
|
||||
pos[0]["paid_amount"] = 600.0
|
||||
|
||||
si = webnotes.bean(pos)
|
||||
si = webnotes.bean(copy=pos)
|
||||
si.insert()
|
||||
si.submit()
|
||||
|
||||
@ -113,7 +327,7 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
si.doc.name, as_dict=1)[0]
|
||||
self.assertTrue(sle)
|
||||
self.assertEquals([sle.item_code, sle.warehouse, sle.actual_qty],
|
||||
["_Test Item", "_Test Warehouse", -5.0])
|
||||
["_Test Item", "_Test Warehouse", -1.0])
|
||||
|
||||
# check gl entries
|
||||
stock_in_hand_account = webnotes.conn.get_value("Company", "_Test Company",
|
||||
@ -126,11 +340,11 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
|
||||
expected_gl_entries = sorted([
|
||||
[si.doc.debit_to, 630.0, 0.0],
|
||||
[test_records[1][1]["income_account"], 0.0, 500.0],
|
||||
[test_records[1][2]["account_head"], 0.0, 80.0],
|
||||
[test_records[1][3]["account_head"], 0.0, 50.0],
|
||||
[stock_in_hand_account, 0.0, 375.0],
|
||||
[test_records[1][1]["expense_account"], 375.0, 0.0],
|
||||
[pos[1]["income_account"], 0.0, 500.0],
|
||||
[pos[2]["account_head"], 0.0, 80.0],
|
||||
[pos[3]["account_head"], 0.0, 50.0],
|
||||
[stock_in_hand_account, 0.0, 75.0],
|
||||
[pos[1]["expense_account"], 75.0, 0.0],
|
||||
[si.doc.debit_to, 0.0, 600.0],
|
||||
["_Test Account Bank Account - _TC", 600.0, 0.0]
|
||||
])
|
||||
@ -444,7 +658,7 @@ test_records = [
|
||||
"description": "VAT",
|
||||
"doctype": "Sales Taxes and Charges",
|
||||
"parentfield": "other_charges",
|
||||
"tax_amount": 30.0,
|
||||
"rate": 6,
|
||||
},
|
||||
{
|
||||
"account_head": "_Test Account Service Tax - _TC",
|
||||
@ -452,7 +666,7 @@ test_records = [
|
||||
"description": "Service Tax",
|
||||
"doctype": "Sales Taxes and Charges",
|
||||
"parentfield": "other_charges",
|
||||
"tax_amount": 31.8,
|
||||
"rate": 6.36,
|
||||
},
|
||||
{
|
||||
"parentfield": "sales_team",
|
||||
@ -494,7 +708,7 @@ test_records = [
|
||||
"description": "_Test Item",
|
||||
"doctype": "Sales Invoice Item",
|
||||
"parentfield": "entries",
|
||||
"qty": 5.0,
|
||||
"qty": 1.0,
|
||||
"basic_rate": 500.0,
|
||||
"amount": 500.0,
|
||||
"export_rate": 500.0,
|
||||
@ -509,7 +723,7 @@ test_records = [
|
||||
"description": "VAT",
|
||||
"doctype": "Sales Taxes and Charges",
|
||||
"parentfield": "other_charges",
|
||||
"tax_amount": 80.0,
|
||||
"rate": 16,
|
||||
},
|
||||
{
|
||||
"account_head": "_Test Account Service Tax - _TC",
|
||||
@ -517,7 +731,270 @@ test_records = [
|
||||
"description": "Service Tax",
|
||||
"doctype": "Sales Taxes and Charges",
|
||||
"parentfield": "other_charges",
|
||||
"tax_amount": 50.0,
|
||||
"rate": 10
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"naming_series": "_T-Sales Invoice-",
|
||||
"company": "_Test Company",
|
||||
"conversion_rate": 1.0,
|
||||
"currency": "INR",
|
||||
"debit_to": "_Test Customer - _TC",
|
||||
"customer": "_Test Customer",
|
||||
"customer_name": "_Test Customer",
|
||||
"doctype": "Sales Invoice",
|
||||
"due_date": "2013-01-23",
|
||||
"fiscal_year": "_Test Fiscal Year 2013",
|
||||
"grand_total_export": 0,
|
||||
"plc_conversion_rate": 1.0,
|
||||
"posting_date": "2013-01-23",
|
||||
"price_list_currency": "INR",
|
||||
"price_list_name": "_Test Price List",
|
||||
"territory": "_Test Territory",
|
||||
},
|
||||
# items
|
||||
{
|
||||
"doctype": "Sales Invoice Item",
|
||||
"parentfield": "entries",
|
||||
"item_code": "_Test Item Home Desktop 100",
|
||||
"item_name": "_Test Item Home Desktop 100",
|
||||
"qty": 10,
|
||||
"ref_rate": 50,
|
||||
"export_rate": 50,
|
||||
"stock_uom": "_Test UOM",
|
||||
"item_tax_rate": json.dumps({"_Test Account Excise Duty - _TC": 10}),
|
||||
"income_account": "Sales - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
|
||||
},
|
||||
{
|
||||
"doctype": "Sales Invoice Item",
|
||||
"parentfield": "entries",
|
||||
"item_code": "_Test Item Home Desktop 200",
|
||||
"item_name": "_Test Item Home Desktop 200",
|
||||
"qty": 5,
|
||||
"ref_rate": 150,
|
||||
"export_rate": 150,
|
||||
"stock_uom": "_Test UOM",
|
||||
"income_account": "Sales - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
|
||||
},
|
||||
# taxes
|
||||
{
|
||||
"doctype": "Sales Taxes and Charges",
|
||||
"parentfield": "other_charges",
|
||||
"charge_type": "Actual",
|
||||
"account_head": "_Test Account Shipping Charges - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"description": "Shipping Charges",
|
||||
"rate": 100
|
||||
},
|
||||
{
|
||||
"doctype": "Sales Taxes and Charges",
|
||||
"parentfield": "other_charges",
|
||||
"charge_type": "On Net Total",
|
||||
"account_head": "_Test Account Customs Duty - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"description": "Customs Duty",
|
||||
"rate": 10
|
||||
},
|
||||
{
|
||||
"doctype": "Sales Taxes and Charges",
|
||||
"parentfield": "other_charges",
|
||||
"charge_type": "On Net Total",
|
||||
"account_head": "_Test Account Excise Duty - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"description": "Excise Duty",
|
||||
"rate": 12
|
||||
},
|
||||
{
|
||||
"doctype": "Sales Taxes and Charges",
|
||||
"parentfield": "other_charges",
|
||||
"charge_type": "On Previous Row Amount",
|
||||
"account_head": "_Test Account Education Cess - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"description": "Education Cess",
|
||||
"rate": 2,
|
||||
"row_id": 3
|
||||
},
|
||||
{
|
||||
"doctype": "Sales Taxes and Charges",
|
||||
"parentfield": "other_charges",
|
||||
"charge_type": "On Previous Row Amount",
|
||||
"account_head": "_Test Account S&H Education Cess - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"description": "S&H Education Cess",
|
||||
"rate": 1,
|
||||
"row_id": 3
|
||||
},
|
||||
{
|
||||
"doctype": "Sales Taxes and Charges",
|
||||
"parentfield": "other_charges",
|
||||
"charge_type": "On Previous Row Total",
|
||||
"account_head": "_Test Account CST - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"description": "CST",
|
||||
"rate": 2,
|
||||
"row_id": 5
|
||||
},
|
||||
{
|
||||
"doctype": "Sales Taxes and Charges",
|
||||
"parentfield": "other_charges",
|
||||
"charge_type": "On Net Total",
|
||||
"account_head": "_Test Account VAT - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"description": "VAT",
|
||||
"rate": 12.5
|
||||
},
|
||||
{
|
||||
"doctype": "Sales Taxes and Charges",
|
||||
"parentfield": "other_charges",
|
||||
"charge_type": "On Previous Row Total",
|
||||
"account_head": "_Test Account Discount - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"description": "Discount",
|
||||
"rate": -10,
|
||||
"row_id": 7
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
"naming_series": "_T-Sales Invoice-",
|
||||
"company": "_Test Company",
|
||||
"conversion_rate": 1.0,
|
||||
"currency": "INR",
|
||||
"debit_to": "_Test Customer - _TC",
|
||||
"customer": "_Test Customer",
|
||||
"customer_name": "_Test Customer",
|
||||
"doctype": "Sales Invoice",
|
||||
"due_date": "2013-01-23",
|
||||
"fiscal_year": "_Test Fiscal Year 2013",
|
||||
"grand_total_export": 0,
|
||||
"plc_conversion_rate": 1.0,
|
||||
"posting_date": "2013-01-23",
|
||||
"price_list_currency": "INR",
|
||||
"price_list_name": "_Test Price List",
|
||||
"territory": "_Test Territory",
|
||||
},
|
||||
# items
|
||||
{
|
||||
"doctype": "Sales Invoice Item",
|
||||
"parentfield": "entries",
|
||||
"item_code": "_Test Item Home Desktop 100",
|
||||
"item_name": "_Test Item Home Desktop 100",
|
||||
"qty": 10,
|
||||
"ref_rate": 62.5,
|
||||
"export_rate": 62.5,
|
||||
"stock_uom": "_Test UOM",
|
||||
"item_tax_rate": json.dumps({"_Test Account Excise Duty - _TC": 10}),
|
||||
"income_account": "Sales - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
|
||||
},
|
||||
{
|
||||
"doctype": "Sales Invoice Item",
|
||||
"parentfield": "entries",
|
||||
"item_code": "_Test Item Home Desktop 200",
|
||||
"item_name": "_Test Item Home Desktop 200",
|
||||
"qty": 5,
|
||||
"ref_rate": 190.66,
|
||||
"export_rate": 190.66,
|
||||
"stock_uom": "_Test UOM",
|
||||
"income_account": "Sales - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
|
||||
},
|
||||
# taxes
|
||||
{
|
||||
"doctype": "Sales Taxes and Charges",
|
||||
"parentfield": "other_charges",
|
||||
"charge_type": "On Net Total",
|
||||
"account_head": "_Test Account Excise Duty - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"description": "Excise Duty",
|
||||
"rate": 12,
|
||||
"included_in_print_rate": 1,
|
||||
"idx": 1
|
||||
},
|
||||
{
|
||||
"doctype": "Sales Taxes and Charges",
|
||||
"parentfield": "other_charges",
|
||||
"charge_type": "On Previous Row Amount",
|
||||
"account_head": "_Test Account Education Cess - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"description": "Education Cess",
|
||||
"rate": 2,
|
||||
"row_id": 1,
|
||||
"included_in_print_rate": 1,
|
||||
"idx": 2
|
||||
},
|
||||
{
|
||||
"doctype": "Sales Taxes and Charges",
|
||||
"parentfield": "other_charges",
|
||||
"charge_type": "On Previous Row Amount",
|
||||
"account_head": "_Test Account S&H Education Cess - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"description": "S&H Education Cess",
|
||||
"rate": 1,
|
||||
"row_id": 1,
|
||||
"included_in_print_rate": 1,
|
||||
"idx": 3
|
||||
},
|
||||
{
|
||||
"doctype": "Sales Taxes and Charges",
|
||||
"parentfield": "other_charges",
|
||||
"charge_type": "On Previous Row Total",
|
||||
"account_head": "_Test Account CST - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"description": "CST",
|
||||
"rate": 2,
|
||||
"row_id": 3,
|
||||
"included_in_print_rate": 1,
|
||||
"idx": 4
|
||||
},
|
||||
{
|
||||
"doctype": "Sales Taxes and Charges",
|
||||
"parentfield": "other_charges",
|
||||
"charge_type": "On Net Total",
|
||||
"account_head": "_Test Account VAT - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"description": "VAT",
|
||||
"rate": 12.5,
|
||||
"included_in_print_rate": 1,
|
||||
"idx": 5
|
||||
},
|
||||
{
|
||||
"doctype": "Sales Taxes and Charges",
|
||||
"parentfield": "other_charges",
|
||||
"charge_type": "On Net Total",
|
||||
"account_head": "_Test Account Customs Duty - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"description": "Customs Duty",
|
||||
"rate": 10,
|
||||
"idx": 6
|
||||
},
|
||||
{
|
||||
"doctype": "Sales Taxes and Charges",
|
||||
"parentfield": "other_charges",
|
||||
"charge_type": "Actual",
|
||||
"account_head": "_Test Account Shipping Charges - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"description": "Shipping Charges",
|
||||
"rate": 100,
|
||||
"idx": 7
|
||||
},
|
||||
{
|
||||
"doctype": "Sales Taxes and Charges",
|
||||
"parentfield": "other_charges",
|
||||
"charge_type": "On Previous Row Total",
|
||||
"account_head": "_Test Account Discount - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"description": "Discount",
|
||||
"rate": -10,
|
||||
"row_id": 7,
|
||||
"idx": 8
|
||||
},
|
||||
],
|
||||
]
|
@ -2,7 +2,7 @@
|
||||
{
|
||||
"creation": "2013-04-19 11:00:07",
|
||||
"docstatus": 0,
|
||||
"modified": "2013-05-22 12:06:15",
|
||||
"modified": "2013-05-22 12:07:00",
|
||||
"modified_by": "Administrator",
|
||||
"owner": "Administrator"
|
||||
},
|
||||
@ -107,7 +107,7 @@
|
||||
"oldfieldtype": "Currency",
|
||||
"options": "currency",
|
||||
"print_hide": 1,
|
||||
"read_only": 0,
|
||||
"read_only": 1,
|
||||
"reqd": 0
|
||||
},
|
||||
{
|
||||
@ -163,7 +163,7 @@
|
||||
"oldfieldtype": "Currency",
|
||||
"options": "Company:company:default_currency",
|
||||
"print_hide": 1,
|
||||
"read_only": 0,
|
||||
"read_only": 1,
|
||||
"reqd": 1,
|
||||
"search_index": 0
|
||||
},
|
||||
|
@ -1,8 +1,8 @@
|
||||
[
|
||||
{
|
||||
"creation": "2013-02-22 01:27:41",
|
||||
"creation": "2013-04-24 11:39:32",
|
||||
"docstatus": 0,
|
||||
"modified": "2013-04-17 14:05:18",
|
||||
"modified": "2013-05-28 11:59:02",
|
||||
"modified_by": "Administrator",
|
||||
"owner": "Administrator"
|
||||
},
|
||||
@ -50,7 +50,7 @@
|
||||
{
|
||||
"default": ":Company",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "cost_center_other_charges",
|
||||
"fieldname": "cost_center",
|
||||
"fieldtype": "Link",
|
||||
"label": "Cost Center",
|
||||
"oldfieldname": "cost_center_other_charges",
|
||||
@ -85,6 +85,7 @@
|
||||
"oldfieldname": "tax_amount",
|
||||
"oldfieldtype": "Currency",
|
||||
"options": "Company:company:default_currency",
|
||||
"read_only": 1,
|
||||
"reqd": 0
|
||||
},
|
||||
{
|
||||
@ -128,34 +129,6 @@
|
||||
"print_hide": 1,
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"description": "Cheating Field\nPlease do not delete ",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "total_tax_amount",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 1,
|
||||
"label": "Total Tax Amount",
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "total_tax_amount",
|
||||
"oldfieldtype": "Currency",
|
||||
"options": "Company:company:default_currency",
|
||||
"print_hide": 1,
|
||||
"report_hide": 1
|
||||
},
|
||||
{
|
||||
"description": "Cheating Field\nPlease do not delete ",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "total_amount",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 1,
|
||||
"label": "Total Amount",
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "total_amount",
|
||||
"oldfieldtype": "Currency",
|
||||
"options": "Company:company:default_currency",
|
||||
"print_hide": 1,
|
||||
"report_hide": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"description": "If checked, the tax amount will be considered as already included in the Print Rate / Print Amount",
|
||||
|
@ -140,7 +140,7 @@ cur_frm.fields_dict['other_charges'].grid.get_field("account_head").get_query =
|
||||
return 'SELECT tabAccount.name FROM tabAccount WHERE tabAccount.group_or_ledger="Ledger" AND tabAccount.docstatus != 2 AND tabAccount.account_type in ("Tax", "Chargeable", "Income Account") AND tabAccount.company = "'+doc.company+'" AND tabAccount.name LIKE "%s"'
|
||||
}
|
||||
|
||||
cur_frm.fields_dict['other_charges'].grid.get_field("cost_center_other_charges").get_query = function(doc) {
|
||||
cur_frm.fields_dict['other_charges'].grid.get_field("cost_center").get_query = function(doc) {
|
||||
return 'SELECT `tabCost Center`.`name` FROM `tabCost Center` WHERE `tabCost Center`.`company_name` = "' +doc.company+'" AND `tabCost Center`.%(key)s LIKE "%s" AND `tabCost Center`.`group_or_ledger` = "Ledger" AND `tabCost Center`.`docstatus`!= 2 ORDER BY `tabCost Center`.`name` ASC LIMIT 50';
|
||||
}
|
||||
|
||||
|
1
accounts/page/accounts_browser/README.md
Normal file
1
accounts/page/accounts_browser/README.md
Normal file
@ -0,0 +1 @@
|
||||
Tree view browser for Chart of Accounts and Chart of Cost Centers
|
@ -1,18 +0,0 @@
|
||||
<div class="layout-wrapper layout-wrapper-background">
|
||||
<div class="appframe-area"></div>
|
||||
<div class="layout-main">
|
||||
<div class="tree-area"></div>
|
||||
<hr>
|
||||
<div class="well">
|
||||
<h4>Quick Help</h4>
|
||||
<ol>
|
||||
<li>To add child nodes, explore tree and click on the node under which you want to add more nodes.
|
||||
<li>Accounting Entries can be made against leaf nodes, called <b>Ledgers</b>. Entries against <b>Groups</b> are not allowed.
|
||||
<li>Please do NOT create Account (Ledgers) for Customers and Suppliers. They are created directly from the Customer / Supplier masters.
|
||||
<li><b>To create a Bank Account:</b> Go to the appropriate group (usually Application of Funds > Current Assets > Bank Accounts) and create a new Account Ledger (by clicking on Add Child) of type "Bank or Cash"
|
||||
<li><b>To create a Tax Account:</b> Go to the appropriate group (usually Source of Funds > Current Liabilities > Taxes and Duties) and create a new Account Ledger (by clicking on Add Child) of type "Tax" and do mention the Tax rate.
|
||||
</ol>
|
||||
<p>Please setup your chart of accounts before you start Accounting Entries</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -21,9 +21,37 @@
|
||||
// see ledger
|
||||
|
||||
pscript['onload_Accounts Browser'] = function(wrapper){
|
||||
wrapper.appframe = new wn.ui.AppFrame($(wrapper).find('.appframe-area'));
|
||||
wrapper.appframe.add_home_breadcrumb()
|
||||
wrapper.appframe.add_module_breadcrumb("Accounts")
|
||||
wn.ui.make_app_page({
|
||||
parent: wrapper,
|
||||
single_column: true
|
||||
})
|
||||
|
||||
wrapper.appframe.add_module_icon("Accounts");
|
||||
|
||||
var main = $(wrapper).find(".layout-main"),
|
||||
chart_area = $("<div>")
|
||||
.css({"margin-bottom": "15px"})
|
||||
.appendTo(main),
|
||||
help_area = $('<div class="well">\
|
||||
<h4>Quick Help</h4>\
|
||||
<ol>\
|
||||
<li>To add child nodes, explore tree and click on the node under which you \
|
||||
want to add more nodes.\
|
||||
<li>Accounting Entries can be made against leaf nodes, called <b>Ledgers</b>.\
|
||||
Entries against <b>Groups</b> are not allowed.\
|
||||
<li>Please do NOT create Account (Ledgers) for Customers and Suppliers. \
|
||||
They are created directly from the Customer / Supplier masters.\
|
||||
<li><b>To create a Bank Account:</b> Go to the appropriate group \
|
||||
(usually Application of Funds > Current Assets > Bank Accounts)\
|
||||
and create a new Account Ledger (by clicking on Add Child) of \
|
||||
type "Bank or Cash"\
|
||||
<li><b>To create a Tax Account:</b> Go to the appropriate group \
|
||||
(usually Source of Funds > Current Liabilities > Taxes and Duties) \
|
||||
and create a new Account Ledger (by clicking on Add Child) of type\
|
||||
"Tax" and do mention the Tax rate.\
|
||||
</ol>\
|
||||
<p>Please setup your chart of accounts before you start Accounting Entries</p>\
|
||||
</div>').appendTo(main);
|
||||
|
||||
if (wn.boot.profile.can_create.indexOf("Company") !== -1) {
|
||||
wrapper.appframe.add_button('New Company', function() { newdoc('Company'); },
|
||||
@ -35,13 +63,13 @@ pscript['onload_Accounts Browser'] = function(wrapper){
|
||||
}, 'icon-refresh');
|
||||
|
||||
// company-select
|
||||
wrapper.$company_select = $('<select class="accbrowser-company-select"></select>')
|
||||
wrapper.$company_select = wrapper.appframe.add_select("Company", [])
|
||||
.change(function() {
|
||||
var ctype = wn.get_route()[1] || 'Account';
|
||||
erpnext.account_chart = new erpnext.AccountsChart(ctype, $(this).val(), wrapper);
|
||||
erpnext.account_chart = new erpnext.AccountsChart(ctype, $(this).val(),
|
||||
chart_area.get(0));
|
||||
pscript.set_title(wrapper, ctype, $(this).val());
|
||||
})
|
||||
.appendTo(wrapper.appframe.$w.find('.appframe-toolbar'));
|
||||
|
||||
// load up companies
|
||||
wn.call({
|
||||
@ -77,7 +105,7 @@ pscript['onshow_Accounts Browser'] = function(wrapper){
|
||||
|
||||
erpnext.AccountsChart = Class.extend({
|
||||
init: function(ctype, company, wrapper) {
|
||||
$(wrapper).find('.tree-area').empty();
|
||||
$(wrapper).empty();
|
||||
var me = this;
|
||||
me.ctype = ctype;
|
||||
me.can_create = wn.model.can_create(this.ctype);
|
||||
@ -87,7 +115,7 @@ erpnext.AccountsChart = Class.extend({
|
||||
|
||||
me.company = company;
|
||||
this.tree = new wn.ui.Tree({
|
||||
parent: $(wrapper).find('.tree-area'),
|
||||
parent: $(wrapper),
|
||||
label: company,
|
||||
args: {ctype: ctype, comp: company},
|
||||
method: 'accounts.page.accounts_browser.accounts_browser.get_children',
|
||||
|
@ -25,7 +25,7 @@ wn.pages['financial-analytics'].onload = function(wrapper) {
|
||||
erpnext.trial_balance = new erpnext.FinancialAnalytics(wrapper, 'Financial Analytics');
|
||||
|
||||
wrapper.appframe.add_home_breadcrumb()
|
||||
wrapper.appframe.add_module_breadcrumb("Accounts")
|
||||
wrapper.appframe.add_module_icon("Accounts")
|
||||
wrapper.appframe.add_breadcrumb("icon-bar-chart")
|
||||
}
|
||||
|
||||
|
@ -1,28 +1,5 @@
|
||||
<div class="layout_wrapper">
|
||||
<div class="appframe col col-lg-12">
|
||||
<div id="fs_header"></div>
|
||||
<!-- table.statement td { vertical-align: middle; } table.statement td select { width: 100px; } table.stmt_table { table-layout: fixed; border-collapse: collapse; } table.stmt_table td { vertical-align: middle; padding: 2px; } td.stmt_level0 { font-weight: bold; font-size: 14px; border-bottom: 1px solid #AAA; } td.stmt_level1 { font-weight: bold; font-size: 12px; } td.stmt_level2 { font-size: 11px; } td.stmt_level3 { font-size: 11px; } td.stmt_level4 { font-size: 12px; font-weight: bold; border-bottom: 1px solid #000; } td.stmt_level5 { color: BLUE; font-size: 11px; } --> <!--
|
||||
<div style="border: 1px solid #cccccc; padding: 4px; margin-top: 8px; background-color: #eeeeee; width: 98%;">
|
||||
<table class="statement" border="0" cellspacing="2px">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Statement:</td>
|
||||
<td style="padding-right: 8px;" mce_style="padding-right: 8px;"><select id="stmt_type"></select></td>
|
||||
<td>Company:</td>
|
||||
<td style="padding-right: 8px;" mce_style="padding-right: 8px;"><select id="stmt_company"></select></td>
|
||||
<td>Period Type:</td>
|
||||
<td style="padding-right: 8px;" mce_style="padding-right: 8px;"><select id="stmt_period"></select></td>
|
||||
<td>Fiscal Year:</td>
|
||||
<td style="padding-right: 8px;" mce_style="padding-right: 8px;"><select id="stmt_fiscal_year"></select></td>
|
||||
<td style="padding-right: 8px;" mce_style="padding-right: 8px;">
|
||||
<div id="stmt_new"></div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
--> <!--
|
||||
<div style="margin:10px 0px 10px 0px" mce_style="margin:10px 0px 10px 0px"><button class="button" onclick="pscript.print_statement();">Print</button></div>
|
||||
-->
|
||||
<div id="print_html">
|
||||
<div id="stmt_title1" style="margin:16px 0px 4px 0px; font-size: 16px; font-weight: bold; color: #888;"></div>
|
||||
<div id="stmt_title2" style="margin:0px 0px 8px 0px; font-size: 16px; font-weight: bold;"></div>
|
||||
|
@ -24,7 +24,7 @@ wn.pages['general-ledger'].onload = function(wrapper) {
|
||||
erpnext.general_ledger = new erpnext.GeneralLedger(wrapper);
|
||||
|
||||
wrapper.appframe.add_home_breadcrumb()
|
||||
wrapper.appframe.add_module_breadcrumb("Accounts")
|
||||
wrapper.appframe.add_module_icon("Accounts")
|
||||
wrapper.appframe.add_breadcrumb("icon-bar-chart")
|
||||
|
||||
}
|
||||
|
@ -59,6 +59,6 @@ wn.pages['trial-balance'].onload = function(wrapper) {
|
||||
erpnext.trial_balance = new TrialBalance(wrapper, 'Trial Balance');
|
||||
|
||||
wrapper.appframe.add_home_breadcrumb()
|
||||
wrapper.appframe.add_module_breadcrumb("Accounts")
|
||||
wrapper.appframe.add_module_icon("Accounts")
|
||||
wrapper.appframe.add_breadcrumb("icon-bar-chart")
|
||||
}
|
@ -92,7 +92,7 @@ def get_source_data(filters):
|
||||
timestamp(si.posting_date, si.posting_time) as posting_datetime
|
||||
from `tabSales Invoice` si, `tabSales Invoice Item` item
|
||||
where item.parent = si.name and si.docstatus = 1 %s
|
||||
and si.is_pos = 1 and si.update_stock = 1
|
||||
and si.update_stock = 1
|
||||
order by si.posting_date desc, si.posting_time desc""" % (conditions,), filters, as_dict=1)
|
||||
|
||||
source = delivery_note_items + sales_invoice_items
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -69,17 +69,6 @@ class DocType(BuyingController):
|
||||
msgprint(_("Hey there! You need to put at least one item in \
|
||||
the item table."), raise_exception=True)
|
||||
|
||||
|
||||
def get_default_schedule_date( self, obj):
|
||||
for d in getlist( obj.doclist, obj.fname):
|
||||
item = sql("select lead_time_days from `tabItem` where name = '%s' and (ifnull(end_of_life,'')='' or end_of_life = '0000-00-00' or end_of_life > now())" % cstr(d.item_code) , as_dict = 1)
|
||||
ltd = item and cint(item[0]['lead_time_days']) or 0
|
||||
if ltd and obj.doc.transaction_date:
|
||||
if d.fields.has_key('lead_time_date') or obj.doc.doctype == 'Material Request':
|
||||
d.lead_time_date = cstr(add_days( obj.doc.transaction_date, cint(ltd)))
|
||||
if not d.fields.has_key('prevdoc_docname') or (d.fields.has_key('prevdoc_docname') and not d.prevdoc_docname):
|
||||
d.schedule_date = cstr( add_days( obj.doc.transaction_date, cint(ltd)))
|
||||
|
||||
# Client Trigger functions
|
||||
#------------------------------------------------------------------------------------------------
|
||||
|
||||
@ -447,28 +436,4 @@ class DocType(BuyingController):
|
||||
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, d.prevdoc_docname))
|
||||
d.prevdoc_date = dt and dt[0][0].strftime('%Y-%m-%d') or ''
|
||||
|
||||
@webnotes.whitelist()
|
||||
def get_uom_details(args=None):
|
||||
"""fetches details on change of UOM"""
|
||||
if not args:
|
||||
return {}
|
||||
|
||||
if isinstance(args, basestring):
|
||||
import json
|
||||
args = json.loads(args)
|
||||
|
||||
uom = webnotes.conn.sql("""select conversion_factor
|
||||
from `tabUOM Conversion Detail` where parent = %s and uom = %s""",
|
||||
(args['item_code'], args['uom']), as_dict=1)
|
||||
|
||||
if not uom: return {}
|
||||
|
||||
conversion_factor = args.get("conversion_factor") or \
|
||||
flt(uom[0]["conversion_factor"])
|
||||
|
||||
return {
|
||||
"conversion_factor": conversion_factor,
|
||||
"qty": flt(args["stock_qty"]) / conversion_factor,
|
||||
}
|
||||
d.prevdoc_date = dt and dt[0][0].strftime('%Y-%m-%d') or ''
|
@ -21,8 +21,8 @@ cur_frm.cscript.fname = "po_details";
|
||||
cur_frm.cscript.other_fname = "purchase_tax_details";
|
||||
|
||||
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/utilities/doctype/sms_control/sms_control.js');
|
||||
wn.require('app/buying/doctype/purchase_common/purchase_common.js');
|
||||
|
||||
erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend({
|
||||
refresh: function(doc, cdt, cdn) {
|
||||
@ -39,54 +39,10 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
|
||||
cur_frm.add_custom_button('Unstop Purchase Order', cur_frm.cscript['Unstop Purchase Order']);
|
||||
|
||||
},
|
||||
|
||||
onload_post_render: function(doc, dt, dn) {
|
||||
var me = this;
|
||||
var callback1 = function(doc, dt, dn) {
|
||||
var callback2 = function(doc, dt, dn) {
|
||||
if(doc.__islocal) cur_frm.cscript.get_default_schedule_date(doc);
|
||||
}
|
||||
me.update_item_details(doc, dt, dn, callback2);
|
||||
}
|
||||
|
||||
// TODO: improve this
|
||||
if(this.frm.doc.__islocal) {
|
||||
if (this.frm.fields_dict.price_list_name && this.frm.doc.price_list_name) {
|
||||
this.price_list_name(callback1);
|
||||
} else {
|
||||
callback1(doc, dt, dn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
var new_cscript = new erpnext.buying.PurchaseOrderController({frm: cur_frm});
|
||||
|
||||
// for backward compatibility: combine new and previous states
|
||||
$.extend(cur_frm.cscript, new_cscript);
|
||||
|
||||
cur_frm.cscript.onload = function(doc, cdt, cdn) {
|
||||
// set missing values in parent doc
|
||||
set_missing_values(doc, {
|
||||
fiscal_year: sys_defaults.fiscal_year,
|
||||
conversion_rate: 1,
|
||||
currency: sys_defaults.currency,
|
||||
status: "Draft",
|
||||
transaction_date: get_today(),
|
||||
is_subcontracted: "No"
|
||||
});
|
||||
}
|
||||
|
||||
cur_frm.cscript.supplier = function(doc,dt,dn) {
|
||||
if (doc.supplier) {
|
||||
get_server_fields('get_default_supplier_address',
|
||||
JSON.stringify({ supplier: doc.supplier }),'', doc, dt, dn, 1, function() {
|
||||
cur_frm.refresh();
|
||||
});
|
||||
$(cur_frm.fields_dict.contact_section.row.wrapper).toggle(true);
|
||||
}
|
||||
}
|
||||
$.extend(cur_frm.cscript, new erpnext.buying.PurchaseOrderController({frm: cur_frm}));
|
||||
|
||||
cur_frm.cscript.supplier_address = cur_frm.cscript.contact_person = function(doc,dt,dn) {
|
||||
if(doc.supplier) get_server_fields('get_supplier_address', JSON.stringify({supplier: doc.supplier, address: doc.supplier_address, contact: doc.contact_person}),'', doc, dt, dn, 1);
|
||||
@ -111,10 +67,6 @@ cur_frm.fields_dict.contact_person.on_new = function(dn) {
|
||||
locals['Contact'][dn].supplier_name = locals[cur_frm.doctype][cur_frm.docname].supplier_name;
|
||||
}
|
||||
|
||||
cur_frm.cscript.transaction_date = function(doc,cdt,cdn){
|
||||
if(doc.__islocal){ cur_frm.cscript.get_default_schedule_date(doc); }
|
||||
}
|
||||
|
||||
cur_frm.fields_dict['po_details'].grid.get_field('project_name').get_query = function(doc, cdt, cdn) {
|
||||
return 'SELECT `tabProject`.name FROM `tabProject` \
|
||||
WHERE `tabProject`.status not in ("Completed", "Cancelled") \
|
||||
|
@ -65,10 +65,6 @@ class DocType(BuyingController):
|
||||
self.validate_for_subcontracting()
|
||||
self.update_raw_materials_supplied("po_raw_material_details")
|
||||
|
||||
|
||||
def get_default_schedule_date(self):
|
||||
get_obj(dt = 'Purchase Common').get_default_schedule_date(self)
|
||||
|
||||
def validate_fiscal_year(self):
|
||||
get_obj(dt = 'Purchase Common').validate_fiscal_year(self.doc.fiscal_year,self.doc.transaction_date,'PO Date')
|
||||
|
||||
@ -101,7 +97,6 @@ class DocType(BuyingController):
|
||||
"""[['Supplier Quotation', 'Purchase Order'],
|
||||
['Supplier Quotation Item', 'Purchase Order Item'],
|
||||
['Purchase Taxes and Charges', 'Purchase Taxes and Charges']]""")
|
||||
self.get_default_schedule_date()
|
||||
for d in getlist(self.doclist, 'po_details'):
|
||||
if d.prevdoc_detail_docname and not d.schedule_date:
|
||||
d.schedule_date = webnotes.conn.get_value("Material Request Item",
|
||||
|
@ -1,8 +1,8 @@
|
||||
[
|
||||
{
|
||||
"creation": "2013-03-25 16:01:24",
|
||||
"creation": "2013-05-21 16:16:39",
|
||||
"docstatus": 0,
|
||||
"modified": "2013-02-18 13:37:11",
|
||||
"modified": "2013-05-28 12:20:33",
|
||||
"modified_by": "Administrator",
|
||||
"owner": "Administrator"
|
||||
},
|
||||
@ -65,43 +65,48 @@
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "supplier",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "supplier_name",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"hidden": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Name",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "supplier",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "address_display",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 1,
|
||||
"hidden": 0,
|
||||
"label": "Address",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "supplier",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "contact_display",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 1,
|
||||
"hidden": 0,
|
||||
"label": "Contact",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "supplier",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "contact_mobile",
|
||||
"fieldtype": "Text",
|
||||
"hidden": 1,
|
||||
"hidden": 0,
|
||||
"label": "Mobile No",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "supplier",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "contact_email",
|
||||
"fieldtype": "Text",
|
||||
"hidden": 1,
|
||||
"hidden": 0,
|
||||
"label": "Contact Email",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
@ -554,6 +559,7 @@
|
||||
"oldfieldtype": "Text Editor"
|
||||
},
|
||||
{
|
||||
"depends_on": "supplier",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "contact_section",
|
||||
"fieldtype": "Section Break",
|
||||
@ -808,7 +814,6 @@
|
||||
"cancel": 0,
|
||||
"create": 0,
|
||||
"doctype": "DocPerm",
|
||||
"match": "",
|
||||
"permlevel": 1,
|
||||
"report": 0,
|
||||
"role": "Material User",
|
||||
@ -831,7 +836,6 @@
|
||||
"cancel": 0,
|
||||
"create": 0,
|
||||
"doctype": "DocPerm",
|
||||
"match": "",
|
||||
"permlevel": 1,
|
||||
"report": 0,
|
||||
"role": "Purchase Manager",
|
||||
@ -854,7 +858,6 @@
|
||||
"cancel": 1,
|
||||
"create": 1,
|
||||
"doctype": "DocPerm",
|
||||
"match": "",
|
||||
"permlevel": 0,
|
||||
"report": 1,
|
||||
"role": "Purchase User",
|
||||
@ -866,7 +869,6 @@
|
||||
"cancel": 0,
|
||||
"create": 0,
|
||||
"doctype": "DocPerm",
|
||||
"match": "",
|
||||
"permlevel": 1,
|
||||
"role": "All",
|
||||
"submit": 0
|
||||
|
@ -1,8 +1,8 @@
|
||||
[
|
||||
{
|
||||
"creation": "2013-03-07 11:42:55",
|
||||
"creation": "2013-05-24 19:29:06",
|
||||
"docstatus": 0,
|
||||
"modified": "2013-05-22 11:59:52",
|
||||
"modified": "2013-05-28 12:13:21",
|
||||
"modified_by": "Administrator",
|
||||
"owner": "Administrator"
|
||||
},
|
||||
@ -361,7 +361,7 @@
|
||||
"oldfieldtype": "Currency",
|
||||
"print_hide": 1,
|
||||
"print_width": "100px",
|
||||
"read_only": 0,
|
||||
"read_only": 1,
|
||||
"width": "100px"
|
||||
},
|
||||
{
|
||||
|
@ -26,48 +26,15 @@ wn.require('app/buying/doctype/purchase_common/purchase_common.js');
|
||||
erpnext.buying.SupplierQuotationController = erpnext.buying.BuyingController.extend({
|
||||
refresh: function() {
|
||||
this._super();
|
||||
|
||||
|
||||
if (this.frm.doc.docstatus === 1) {
|
||||
cur_frm.add_custom_button("Make Purchase Order", cur_frm.cscript.make_purchase_order);
|
||||
}
|
||||
},
|
||||
|
||||
onload_post_render: function(doc, dt, dn) {
|
||||
var me = this;
|
||||
var callback = function(doc, dt, dn) {
|
||||
cur_frm.cscript.load_taxes(me.frm.doc);
|
||||
}
|
||||
|
||||
// TODO: improve this
|
||||
if(this.frm.doc.__islocal) {
|
||||
if (this.frm.fields_dict.price_list_name && this.frm.doc.price_list_name) {
|
||||
this.price_list_name(callback);
|
||||
} else {
|
||||
callback(doc, dt, dn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
});
|
||||
|
||||
var new_cscript = new erpnext.buying.SupplierQuotationController({frm: cur_frm});
|
||||
|
||||
// for backward compatibility: combine new and previous states
|
||||
$.extend(cur_frm.cscript, new_cscript);
|
||||
|
||||
|
||||
cur_frm.cscript.onload = function(doc, dt, dn) {
|
||||
// set missing values in parent doc
|
||||
set_missing_values(doc, {
|
||||
fiscal_year: sys_defaults.fiscal_year,
|
||||
conversion_rate: 1,
|
||||
currency: sys_defaults.currency,
|
||||
status: "Draft",
|
||||
transaction_date: get_today(),
|
||||
is_subcontracted: "No"
|
||||
});
|
||||
}
|
||||
$.extend(cur_frm.cscript, new erpnext.buying.SupplierQuotationController({frm: cur_frm}));
|
||||
|
||||
cur_frm.cscript.make_purchase_order = function() {
|
||||
var new_po_name = wn.model.make_new_doc_and_get_name("Purchase Order");
|
||||
@ -82,15 +49,6 @@ cur_frm.cscript.make_purchase_order = function() {
|
||||
}, function(r, rt) { loaddoc("Purchase Order", new_po_name) });
|
||||
}
|
||||
|
||||
cur_frm.cscript.supplier = function(doc, dt, dn) {
|
||||
if (doc.supplier) {
|
||||
get_server_fields('get_default_supplier_address',
|
||||
JSON.stringify({ supplier: doc.supplier }), '', doc, dt, dn, 1,
|
||||
function() { cur_frm.refresh(); });
|
||||
cur_frm.cscript.toggle_contact_section(doc);
|
||||
}
|
||||
}
|
||||
|
||||
cur_frm.cscript.uom = function(doc, cdt, cdn) {
|
||||
// no need to trigger updation of stock uom, as this field doesn't exist in supplier quotation
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
[
|
||||
{
|
||||
"creation": "2013-03-25 16:01:25",
|
||||
"creation": "2013-05-21 16:16:45",
|
||||
"docstatus": 0,
|
||||
"modified": "2013-02-18 13:40:17",
|
||||
"modified": "2013-05-28 12:19:41",
|
||||
"modified_by": "Administrator",
|
||||
"owner": "Administrator"
|
||||
},
|
||||
@ -65,43 +65,48 @@
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "supplier",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "supplier_name",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"hidden": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Name",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "supplier",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "address_display",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 1,
|
||||
"hidden": 0,
|
||||
"label": "Address",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "supplier",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "contact_display",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 1,
|
||||
"hidden": 0,
|
||||
"label": "Contact",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "supplier",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "contact_mobile",
|
||||
"fieldtype": "Text",
|
||||
"hidden": 1,
|
||||
"hidden": 0,
|
||||
"label": "Mobile No",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "supplier",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "contact_email",
|
||||
"fieldtype": "Text",
|
||||
"hidden": 1,
|
||||
"hidden": 0,
|
||||
"label": "Contact Email",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
@ -520,6 +525,7 @@
|
||||
"oldfieldtype": "Text Editor"
|
||||
},
|
||||
{
|
||||
"depends_on": "supplier",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "contact_section",
|
||||
"fieldtype": "Section Break",
|
||||
@ -723,7 +729,6 @@
|
||||
"cancel": 0,
|
||||
"create": 0,
|
||||
"doctype": "DocPerm",
|
||||
"match": "",
|
||||
"permlevel": 1,
|
||||
"report": 0,
|
||||
"role": "All",
|
||||
|
@ -13,7 +13,7 @@ wn.module_page["Buying"] = [
|
||||
},
|
||||
{
|
||||
label: wn._("Supplier Quotation"),
|
||||
description: wn._("Track Quotations received from Suppliers."),
|
||||
description: wn._("Quotations received from Suppliers."),
|
||||
doctype:"Supplier Quotation"
|
||||
},
|
||||
{
|
||||
|
@ -24,7 +24,7 @@ wn.pages['purchase-analytics'].onload = function(wrapper) {
|
||||
new erpnext.PurchaseAnalytics(wrapper);
|
||||
|
||||
wrapper.appframe.add_home_breadcrumb()
|
||||
wrapper.appframe.add_module_breadcrumb("Buying")
|
||||
wrapper.appframe.add_module_icon("Buying")
|
||||
wrapper.appframe.add_breadcrumb("icon-bar-chart")
|
||||
}
|
||||
|
||||
@ -49,7 +49,7 @@ erpnext.PurchaseAnalytics = wn.views.TreeGridReport.extend({
|
||||
item_key: "supplier",
|
||||
parent_field: "parent_supplier_type",
|
||||
formatter: function(item) {
|
||||
// return repl('<a href="#Report2/stock-invoices/customer=%(enc_value)s">%(value)s</a>', {
|
||||
// return repl('<a href="#Report/stock-invoices/customer=%(enc_value)s">%(value)s</a>', {
|
||||
// value: item.name,
|
||||
// enc_value: encodeURIComponent(item.name)
|
||||
// });
|
||||
|
167
buying/utils.py
167
buying/utils.py
@ -16,6 +16,7 @@
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import webnotes
|
||||
from webnotes import msgprint, _
|
||||
from webnotes.utils import getdate, flt, add_days
|
||||
import json
|
||||
|
||||
@ -29,7 +30,11 @@ def get_item_details(args):
|
||||
"warehouse": None,
|
||||
"supplier": None,
|
||||
"transaction_date": None,
|
||||
"conversion_rate": 1.0
|
||||
"conversion_rate": 1.0,
|
||||
"price_list_name": None,
|
||||
"price_list_currency": None,
|
||||
"plc_conversion_rate": 1.0,
|
||||
"is_subcontracted": "Yes" / "No"
|
||||
}
|
||||
"""
|
||||
if isinstance(args, basestring):
|
||||
@ -37,86 +42,102 @@ def get_item_details(args):
|
||||
|
||||
args = webnotes._dict(args)
|
||||
|
||||
item_wrapper = webnotes.bean("Item", args.item_code)
|
||||
item = item_wrapper.doc
|
||||
item_bean = webnotes.bean("Item", args.item_code)
|
||||
item = item_bean.doc
|
||||
|
||||
from stock.utils import validate_end_of_life
|
||||
validate_end_of_life(item.name, item.end_of_life)
|
||||
_validate_item_details(args, item)
|
||||
|
||||
# fetch basic values
|
||||
out = webnotes._dict()
|
||||
out.update({
|
||||
"item_name": item.item_name,
|
||||
"item_group": item.item_group,
|
||||
"brand": item.brand,
|
||||
"description": item.description,
|
||||
"qty": 0,
|
||||
"stock_uom": item.stock_uom,
|
||||
"uom": item.stock_uom,
|
||||
"conversion_factor": 1.0,
|
||||
"warehouse": args.warehouse or item.default_warehouse,
|
||||
"item_tax_rate": json.dumps(dict(([d.tax_type, d.tax_rate] for d in
|
||||
item_wrapper.doclist.get({"parentfield": "item_tax"})))),
|
||||
"batch_no": None,
|
||||
"expense_head": item.purchase_account,
|
||||
"cost_center": item.cost_center
|
||||
})
|
||||
out = _get_basic_details(args, item_bean)
|
||||
|
||||
if args.supplier:
|
||||
item_supplier = item_wrapper.doclist.get({"parentfield": "item_supplier_details",
|
||||
"supplier": args.supplier})
|
||||
if item_supplier:
|
||||
out["supplier_part_no"] = item_supplier[0].supplier_part_no
|
||||
out.supplier_part_no = _get_supplier_part_no(args, item_bean)
|
||||
|
||||
if out.warehouse:
|
||||
out.projected_qty = webnotes.conn.get_value("Bin", {"item_code": item.name,
|
||||
"warehouse": out.warehouse}, "projected_qty")
|
||||
out.projected_qty = get_projected_qty(item.name, out.warehouse)
|
||||
|
||||
if args.transaction_date and item.lead_time_days:
|
||||
out.schedule_date = out.lead_time_date = add_days(args.transaction_date,
|
||||
item.lead_time_days)
|
||||
|
||||
# set zero
|
||||
out.purchase_ref_rate = out.discount_rate = out.purchase_rate = \
|
||||
out.import_ref_rate = out.import_rate = 0.0
|
||||
meta = webnotes.get_doctype(args.doctype)
|
||||
|
||||
if meta.get_field("currency"):
|
||||
out.purchase_ref_rate = out.discount_rate = out.purchase_rate = \
|
||||
out.import_ref_rate = out.import_rate = 0.0
|
||||
out.update(_get_price_list_rate(args, item_bean, meta))
|
||||
|
||||
if args.doctype == "Material Request":
|
||||
out.min_order_qty = flt(item.min_order_qty)
|
||||
|
||||
if args.doctype in ["Purchase Order", "Purchase Invoice", "Purchase Receipt",
|
||||
"Supplier Quotation"]:
|
||||
# try fetching from price list
|
||||
if args.price_list_name and args.price_list_currency:
|
||||
rates_as_per_price_list = get_rates_as_per_price_list(args, item_wrapper.doclist)
|
||||
if rates_as_per_price_list:
|
||||
out.update(rates_as_per_price_list)
|
||||
|
||||
# if not found, fetch from last purchase transaction
|
||||
if not out.purchase_rate:
|
||||
last_purchase = get_last_purchase_details(item.name, args.docname, args.conversion_rate)
|
||||
if last_purchase:
|
||||
out.update(last_purchase)
|
||||
|
||||
return out
|
||||
|
||||
def get_rates_as_per_price_list(args, item_doclist=None):
|
||||
if not item_doclist:
|
||||
item_doclist = webnotes.bean("Item", args.item_code).doclist
|
||||
|
||||
result = item_doclist.get({"parentfield": "ref_rate_details",
|
||||
"price_list_name": args.price_list_name, "ref_currency": args.price_list_currency,
|
||||
"buying": 1})
|
||||
def _get_basic_details(args, item_bean):
|
||||
item = item_bean.doc
|
||||
|
||||
out = webnotes._dict({
|
||||
"description": item.description_html or item.description,
|
||||
"qty": 0.0,
|
||||
"uom": item.stock_uom,
|
||||
"conversion_factor": 1.0,
|
||||
"warehouse": args.warehouse or item.default_warehouse,
|
||||
"item_tax_rate": json.dumps(dict(([d.tax_type, d.tax_rate] for d in
|
||||
item_bean.doclist.get({"parentfield": "item_tax"})))),
|
||||
"batch_no": None,
|
||||
"expense_head": item.purchase_account,
|
||||
"cost_center": item.cost_center
|
||||
})
|
||||
|
||||
for fieldname in ("item_name", "item_group", "brand", "stock_uom"):
|
||||
out[fieldname] = item.fields.get(fieldname)
|
||||
|
||||
return out
|
||||
|
||||
def _get_price_list_rate(args, item_bean, meta):
|
||||
from utilities.transaction_base import validate_currency
|
||||
item = item_bean.doc
|
||||
out = webnotes._dict()
|
||||
|
||||
# try fetching from price list
|
||||
if args.price_list_name and args.price_list_currency:
|
||||
price_list_rate = item_bean.doclist.get({
|
||||
"parentfield": "ref_rate_details",
|
||||
"price_list_name": args.price_list_name,
|
||||
"ref_currency": args.price_list_currency,
|
||||
"buying": 1})
|
||||
if price_list_rate:
|
||||
out.import_ref_rate = \
|
||||
flt(price_list_rate[0].ref_rate * args.plc_conversion_rate / args.conversion_rate)
|
||||
|
||||
if result:
|
||||
purchase_ref_rate = flt(result[0].ref_rate) * flt(args.plc_conversion_rate)
|
||||
conversion_rate = flt(args.conversion_rate) or 1.0
|
||||
return webnotes._dict({
|
||||
"purchase_ref_rate": purchase_ref_rate,
|
||||
"purchase_rate": purchase_ref_rate,
|
||||
"rate": purchase_ref_rate,
|
||||
"discount_rate": 0,
|
||||
"import_ref_rate": purchase_ref_rate / conversion_rate,
|
||||
"import_rate": purchase_ref_rate / conversion_rate
|
||||
})
|
||||
else:
|
||||
return webnotes._dict()
|
||||
# if not found, fetch from last purchase transaction
|
||||
if not out.import_ref_rate:
|
||||
last_purchase = get_last_purchase_details(item.name, args.docname, args.conversion_rate)
|
||||
if last_purchase:
|
||||
out.update(last_purchase)
|
||||
|
||||
if out.import_ref_rate or out.import_rate:
|
||||
validate_currency(args, item, meta)
|
||||
|
||||
return out
|
||||
|
||||
def _get_supplier_part_no(args, item_bean):
|
||||
item_supplier = item_bean.doclist.get({"parentfield": "item_supplier_details",
|
||||
"supplier": args.supplier})
|
||||
|
||||
return item_supplier and item_supplier[0].supplier_part_no or None
|
||||
|
||||
def _validate_item_details(args, item):
|
||||
from utilities.transaction_base import validate_item_fetch
|
||||
validate_item_fetch(args, item)
|
||||
|
||||
# validate if purchase item or subcontracted item
|
||||
if item.is_purchase_item != "Yes":
|
||||
msgprint(_("Item") + (" %s: " % item.name) + _("not a purchase item"),
|
||||
raise_exception=True)
|
||||
|
||||
if args.is_subcontracted == "Yes" and item.is_sub_contracted_item != "Yes":
|
||||
msgprint(_("Item") + (" %s: " % item.name) +
|
||||
_("not a sub-contracted item.") +
|
||||
_("Please select a sub-contracted item or do not sub-contract the transaction."),
|
||||
raise_exception=True)
|
||||
|
||||
def get_last_purchase_details(item_code, doc_name, conversion_rate=1.0):
|
||||
"""returns last purchase details in stock uom"""
|
||||
@ -177,4 +198,14 @@ def get_last_purchase_details(item_code, doc_name, conversion_rate=1.0):
|
||||
"rate": out.purchase_rate
|
||||
})
|
||||
|
||||
return out
|
||||
return out
|
||||
|
||||
@webnotes.whitelist()
|
||||
def get_conversion_factor(item_code, uom):
|
||||
return {"conversion_factor": webnotes.conn.get_value("UOM Conversion Detail",
|
||||
{"parent": item_code, "uom": uom})}
|
||||
|
||||
@webnotes.whitelist()
|
||||
def get_projected_qty(item_code, warehouse):
|
||||
return webnotes.conn.get_value("Bin", {"item_code": item_code,
|
||||
"warehouse": warehouse}, "projected_qty")
|
41
config.json
41
config.json
@ -2,78 +2,79 @@
|
||||
"modules": {
|
||||
"Selling": {
|
||||
"link": "selling-home",
|
||||
"color": "#3f4901",
|
||||
"color": "#1abc9c",
|
||||
"icon": "icon-tag",
|
||||
"type": "module"
|
||||
},
|
||||
"Accounts": {
|
||||
"link": "accounts-home",
|
||||
"color": "#025770",
|
||||
"color": "#3498db",
|
||||
"icon": "icon-money",
|
||||
"type": "module"
|
||||
},
|
||||
"Stock": {
|
||||
"type": "module",
|
||||
"link": "stock-home",
|
||||
"color": "#a66a02",
|
||||
"color": "#f39c12",
|
||||
"icon": "icon-truck"
|
||||
},
|
||||
"Buying": {
|
||||
"type": "module",
|
||||
"link": "buying-home",
|
||||
"color": "#8F0222",
|
||||
"color": "#c0392b",
|
||||
"icon": "icon-shopping-cart"
|
||||
},
|
||||
"Support": {
|
||||
"type": "module",
|
||||
"link": "support-home",
|
||||
"color": "#410169",
|
||||
"color": "#2c3e50",
|
||||
"icon": "icon-phone"
|
||||
},
|
||||
"Projects": {
|
||||
"type": "module",
|
||||
"link": "projects-home",
|
||||
"color": "#473b7f",
|
||||
"color": "#8e44ad",
|
||||
"icon": "icon-tasks"
|
||||
},
|
||||
"Manufacturing": {
|
||||
"type": "module",
|
||||
"link": "manufacturing-home",
|
||||
"color": "#590116",
|
||||
"icon": "icon-magic"
|
||||
"color": "#7f8c8d",
|
||||
"icon": "icon-cogs"
|
||||
},
|
||||
"Website": {
|
||||
"type": "module",
|
||||
"link": "website-home",
|
||||
"color": "#968c00",
|
||||
"color": "#16a085",
|
||||
"icon": "icon-globe"
|
||||
},
|
||||
"HR": {
|
||||
"type": "module",
|
||||
"link": "hr-home",
|
||||
"color": "#018d6c",
|
||||
"color": "#2ecc71",
|
||||
"label": "Human Resources",
|
||||
"icon": "icon-group"
|
||||
},
|
||||
"Setup": {
|
||||
"type": "setup",
|
||||
"link": "Setup",
|
||||
"color": "#484848",
|
||||
"color": "#bdc3c7",
|
||||
"icon": "icon-wrench"
|
||||
},
|
||||
"Activity": {
|
||||
"type": "page",
|
||||
"link": "activity",
|
||||
"color": "#633501",
|
||||
"color": "#e67e22",
|
||||
"icon": "icon-play",
|
||||
"label": "Activity"
|
||||
},
|
||||
"Knowledge Base": {
|
||||
"type": "page",
|
||||
"link": "questions",
|
||||
"color": "#01372b",
|
||||
"label": "Knowledge Base",
|
||||
"icon": "icon-question-sign"
|
||||
"Notes": {
|
||||
"type": "list",
|
||||
"doctype": "Note",
|
||||
"link": "List/Note",
|
||||
"color": "#95a5a6",
|
||||
"label": "Notes",
|
||||
"icon": "icon-file-alt"
|
||||
}
|
||||
},
|
||||
"web": {
|
||||
@ -126,6 +127,10 @@
|
||||
"profile": {
|
||||
"no_cache": true,
|
||||
"template": "app/website/templates/pages/profile"
|
||||
},
|
||||
"cart": {
|
||||
"no_cache": true,
|
||||
"template": "app/website/templates/pages/cart.html"
|
||||
}
|
||||
},
|
||||
"generators": {
|
||||
|
@ -16,14 +16,262 @@
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import webnotes
|
||||
from webnotes.utils import flt
|
||||
|
||||
from utilities.transaction_base import TransactionBase
|
||||
from webnotes import _, msgprint
|
||||
from webnotes.utils import flt, cint
|
||||
from setup.utils import get_company_currency, get_price_list_currency
|
||||
from utilities.transaction_base import TransactionBase, validate_conversion_rate
|
||||
import json
|
||||
|
||||
class AccountsController(TransactionBase):
|
||||
def validate(self):
|
||||
if self.meta.get_field("grand_total"):
|
||||
self.set_missing_values(for_validate=True)
|
||||
|
||||
if self.meta.get_field("currency"):
|
||||
self.company_currency = get_company_currency(self.doc.company)
|
||||
|
||||
validate_conversion_rate(self.doc.currency, self.doc.conversion_rate,
|
||||
self.meta.get_label("conversion_rate"), self.doc.company)
|
||||
|
||||
self.calculate_taxes_and_totals()
|
||||
self.validate_value("grand_total", ">=", 0)
|
||||
self.set_total_in_words()
|
||||
|
||||
def set_price_list_currency(self, buying_or_selling):
|
||||
# TODO - change this, since price list now has only one currency allowed
|
||||
if self.meta.get_field("price_list_name") and self.doc.price_list_name and \
|
||||
not self.doc.price_list_currency:
|
||||
self.doc.fields.update(get_price_list_currency({
|
||||
"price_list_name": self.doc.price_list_name,
|
||||
"use_for": buying_or_selling
|
||||
}))
|
||||
|
||||
def set_missing_item_details(self, get_item_details):
|
||||
"""set missing item values"""
|
||||
for item in self.doclist.get({"parentfield": self.fname}):
|
||||
if item.fields.get("item_code"):
|
||||
args = item.fields.copy().update(self.doc.fields)
|
||||
ret = get_item_details(args)
|
||||
for fieldname, value in ret.items():
|
||||
if self.meta.get_field(fieldname, parentfield=self.fname) and \
|
||||
not item.fields.get(fieldname):
|
||||
item.fields[fieldname] = value
|
||||
|
||||
def set_taxes(self, tax_doctype, tax_parentfield, tax_master_field):
|
||||
if not self.meta.get_field(tax_parentfield):
|
||||
return
|
||||
|
||||
if not self.doclist.get({"parentfield": tax_parentfield}):
|
||||
if not self.doc.fields.get(tax_master_field):
|
||||
# get the default tax master
|
||||
self.doc.fields[tax_master_field] = \
|
||||
webnotes.conn.get_value(tax_doctype + " Master", {"is_default": 1})
|
||||
|
||||
if self.doc.fields.get(tax_master_field):
|
||||
from webnotes.model import default_fields
|
||||
tax_master = webnotes.bean(tax_doctype, self.doc.fields.get(tax_master_field))
|
||||
|
||||
for i, tax in enumerate(tax_master.doclist.get({"parentfield": tax_parentfield})):
|
||||
for fieldname in default_fields:
|
||||
tax.fields[fieldname] = None
|
||||
|
||||
tax.fields.update({
|
||||
"doctype": tax_doctype,
|
||||
"parentfield": tax_parentfield,
|
||||
"idx": i+1
|
||||
})
|
||||
|
||||
self.doclist.append(tax)
|
||||
|
||||
def calculate_taxes_and_totals(self):
|
||||
self.doc.conversion_rate = flt(self.doc.conversion_rate)
|
||||
self.item_doclist = self.doclist.get({"parentfield": self.fname})
|
||||
self.tax_doclist = self.doclist.get({"parentfield": self.other_fname})
|
||||
|
||||
self.calculate_item_values()
|
||||
self.initialize_taxes()
|
||||
|
||||
if hasattr(self, "determine_exclusive_rate"):
|
||||
self.determine_exclusive_rate()
|
||||
|
||||
self.calculate_net_total()
|
||||
self.calculate_taxes()
|
||||
self.calculate_totals()
|
||||
self._cleanup()
|
||||
|
||||
# TODO
|
||||
# print format: show net_total_export instead of net_total
|
||||
|
||||
def initialize_taxes(self):
|
||||
for tax in self.tax_doclist:
|
||||
tax.item_wise_tax_detail = {}
|
||||
for fieldname in ["tax_amount", "total",
|
||||
"tax_amount_for_current_item", "grand_total_for_current_item",
|
||||
"tax_fraction_for_current_item", "grand_total_fraction_for_current_item"]:
|
||||
tax.fields[fieldname] = 0.0
|
||||
|
||||
self.validate_on_previous_row(tax)
|
||||
self.validate_inclusive_tax(tax)
|
||||
self.round_floats_in(tax)
|
||||
|
||||
def validate_on_previous_row(self, tax):
|
||||
"""
|
||||
validate if a valid row id is mentioned in case of
|
||||
On Previous Row Amount and On Previous Row Total
|
||||
"""
|
||||
if tax.charge_type in ["On Previous Row Amount", "On Previous Row Total"] and \
|
||||
(not tax.row_id or cint(tax.row_id) >= tax.idx):
|
||||
msgprint((_("Row") + " # %(idx)s [%(taxes_doctype)s]: " + \
|
||||
_("Please specify a valid") + " %(row_id_label)s") % {
|
||||
"idx": tax.idx,
|
||||
"taxes_doctype": tax.doctype,
|
||||
"row_id_label": self.meta.get_label("row_id",
|
||||
parentfield=self.other_fname)
|
||||
}, raise_exception=True)
|
||||
|
||||
def validate_inclusive_tax(self, tax):
|
||||
def _on_previous_row_error(row_range):
|
||||
msgprint((_("Row") + " # %(idx)s [%(doctype)s]: " +
|
||||
_("to be included in Item's rate, it is required that: ") +
|
||||
" [" + _("Row") + " # %(row_range)s] " + _("also be included in Item's rate")) % {
|
||||
"idx": tax.idx,
|
||||
"doctype": tax.doctype,
|
||||
"inclusive_label": self.meta.get_label("included_in_print_rate",
|
||||
parentfield=self.other_fname),
|
||||
"charge_type_label": self.meta.get_label("charge_type",
|
||||
parentfield=self.other_fname),
|
||||
"charge_type": tax.charge_type,
|
||||
"row_range": row_range
|
||||
}, raise_exception=True)
|
||||
|
||||
if cint(tax.included_in_print_rate):
|
||||
if tax.charge_type == "Actual":
|
||||
# inclusive tax cannot be of type Actual
|
||||
msgprint((_("Row")
|
||||
+ " # %(idx)s [%(doctype)s]: %(charge_type_label)s = \"%(charge_type)s\" "
|
||||
+ "cannot be included in Item's rate") % {
|
||||
"idx": tax.idx,
|
||||
"doctype": tax.doctype,
|
||||
"charge_type_label": self.meta.get_label("charge_type",
|
||||
parentfield=self.other_fname),
|
||||
"charge_type": tax.charge_type,
|
||||
}, raise_exception=True)
|
||||
elif tax.charge_type == "On Previous Row Amount" and \
|
||||
not cint(self.tax_doclist[tax.row_id - 1].included_in_print_rate):
|
||||
# referred row should also be inclusive
|
||||
_on_previous_row_error(tax.row_id)
|
||||
elif tax.charge_type == "On Previous Row Total" and \
|
||||
not all([cint(t.included_in_print_rate) for t in self.tax_doclist[:tax.row_id - 1]]):
|
||||
# all rows about the reffered tax should be inclusive
|
||||
_on_previous_row_error("1 - %d" % (tax.row_id,))
|
||||
|
||||
def calculate_taxes(self):
|
||||
for item in self.item_doclist:
|
||||
item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
|
||||
|
||||
for i, tax in enumerate(self.tax_doclist):
|
||||
# tax_amount represents the amount of tax for the current step
|
||||
current_tax_amount = self.get_current_tax_amount(item, tax, item_tax_map)
|
||||
|
||||
if hasattr(self, "set_item_tax_amount"):
|
||||
self.set_item_tax_amount(item, tax, current_tax_amount)
|
||||
|
||||
# case when net total is 0 but there is an actual type charge
|
||||
# in this case add the actual amount to tax.tax_amount
|
||||
# and tax.grand_total_for_current_item for the first such iteration
|
||||
if tax.charge_type=="Actual" and \
|
||||
not (current_tax_amount or self.doc.net_total or tax.tax_amount):
|
||||
zero_net_total_adjustment = flt(tax.rate, self.precision("tax_amount", tax))
|
||||
current_tax_amount += zero_net_total_adjustment
|
||||
|
||||
# store tax_amount for current item as it will be used for
|
||||
# charge type = 'On Previous Row Amount'
|
||||
tax.tax_amount_for_current_item = current_tax_amount
|
||||
|
||||
# accumulate tax amount into tax.tax_amount
|
||||
tax.tax_amount += current_tax_amount
|
||||
|
||||
if tax.category:
|
||||
# if just for valuation, do not add the tax amount in total
|
||||
# hence, setting it as 0 for further steps
|
||||
current_tax_amount = 0.0 if (tax.category == "Valuation") else current_tax_amount
|
||||
|
||||
current_tax_amount *= -1.0 if (tax.add_deduct_tax == "Deduct") else 1.0
|
||||
|
||||
# Calculate tax.total viz. grand total till that step
|
||||
# note: grand_total_for_current_item contains the contribution of
|
||||
# item's amount, previously applied tax and the current tax on that item
|
||||
if i==0:
|
||||
tax.grand_total_for_current_item = flt(item.amount +
|
||||
current_tax_amount, self.precision("total", tax))
|
||||
|
||||
else:
|
||||
tax.grand_total_for_current_item = \
|
||||
flt(self.tax_doclist[i-1].grand_total_for_current_item +
|
||||
current_tax_amount, self.precision("total", tax))
|
||||
|
||||
# in tax.total, accumulate grand total of each item
|
||||
tax.total += tax.grand_total_for_current_item
|
||||
|
||||
def get_current_tax_amount(self, item, tax, item_tax_map):
|
||||
tax_rate = self._get_tax_rate(tax, item_tax_map)
|
||||
current_tax_amount = 0.0
|
||||
|
||||
if tax.charge_type == "Actual":
|
||||
# distribute the tax amount proportionally to each item row
|
||||
actual = flt(tax.rate, self.precision("tax_amount", tax))
|
||||
current_tax_amount = (self.doc.net_total
|
||||
and ((item.amount / self.doc.net_total) * actual)
|
||||
or 0)
|
||||
elif tax.charge_type == "On Net Total":
|
||||
current_tax_amount = (tax_rate / 100.0) * item.amount
|
||||
elif tax.charge_type == "On Previous Row Amount":
|
||||
current_tax_amount = (tax_rate / 100.0) * \
|
||||
self.tax_doclist[cint(tax.row_id) - 1].tax_amount_for_current_item
|
||||
elif tax.charge_type == "On Previous Row Total":
|
||||
current_tax_amount = (tax_rate / 100.0) * \
|
||||
self.tax_doclist[cint(tax.row_id) - 1].grand_total_for_current_item
|
||||
|
||||
current_tax_amount = flt(current_tax_amount, self.precision("tax_amount", tax))
|
||||
|
||||
# store tax breakup for each item
|
||||
tax.item_wise_tax_detail[item.item_code or item.item_name] = [tax_rate, current_tax_amount]
|
||||
|
||||
return current_tax_amount
|
||||
|
||||
def _load_item_tax_rate(self, item_tax_rate):
|
||||
return json.loads(item_tax_rate) if item_tax_rate else {}
|
||||
|
||||
def _get_tax_rate(self, tax, item_tax_map):
|
||||
if item_tax_map.has_key(tax.account_head):
|
||||
return flt(item_tax_map.get(tax.account_head), self.precision("rate", tax))
|
||||
else:
|
||||
return tax.rate
|
||||
|
||||
def _cleanup(self):
|
||||
for tax in self.tax_doclist:
|
||||
for fieldname in ("grand_total_for_current_item",
|
||||
"tax_amount_for_current_item",
|
||||
"tax_fraction_for_current_item",
|
||||
"grand_total_fraction_for_current_item"):
|
||||
if fieldname in tax.fields:
|
||||
del tax.fields[fieldname]
|
||||
|
||||
tax.item_wise_tax_detail = json.dumps(tax.item_wise_tax_detail)
|
||||
|
||||
def _set_in_company_currency(self, item, print_field, base_field):
|
||||
"""set values in base currency"""
|
||||
item.fields[base_field] = flt((flt(item.fields[print_field],
|
||||
self.precision(print_field, item)) * self.doc.conversion_rate),
|
||||
self.precision(base_field, item))
|
||||
|
||||
def calculate_total_advance(self, parenttype, advance_parentfield):
|
||||
if self.doc.doctype == parenttype and self.doc.docstatus < 2:
|
||||
sum_of_allocated_amount = sum([flt(adv.allocated_amount, self.precision("allocated_amount", adv))
|
||||
for adv in self.doclist.get({"parentfield": advance_parentfield})])
|
||||
|
||||
self.doc.total_advance = flt(sum_of_allocated_amount, self.precision("total_advance"))
|
||||
|
||||
self.calculate_outstanding_amount()
|
||||
|
||||
def get_gl_dict(self, args, cancel=None):
|
||||
"""this method populates the common properties of a gl entry record"""
|
||||
@ -97,4 +345,4 @@ class AccountsController(TransactionBase):
|
||||
if not hasattr(self, "_abbr"):
|
||||
self._abbr = webnotes.conn.get_value("Company", self.doc.company, "abbr")
|
||||
|
||||
return self._abbr
|
||||
return self._abbr
|
||||
|
@ -17,35 +17,45 @@
|
||||
from __future__ import unicode_literals
|
||||
import webnotes
|
||||
from webnotes import _, msgprint
|
||||
from webnotes.utils import flt, cint
|
||||
import json
|
||||
from webnotes.utils import flt
|
||||
|
||||
from buying.utils import get_item_details
|
||||
from setup.utils import get_company_currency
|
||||
from webnotes.model.utils import round_floats_in_doc
|
||||
|
||||
from controllers.stock_controller import StockController
|
||||
|
||||
class WrongWarehouseCompany(Exception): pass
|
||||
|
||||
class BuyingController(StockController):
|
||||
def onload_post_render(self):
|
||||
self.set_price_list_currency("buying")
|
||||
|
||||
# contact, address, item details
|
||||
self.set_missing_values()
|
||||
|
||||
self.set_taxes("Purchase Taxes and Charges", "purchase_tax_details", "purchase_other_charges")
|
||||
|
||||
def validate(self):
|
||||
super(BuyingController, self).validate()
|
||||
self.validate_stock_or_nonstock_items()
|
||||
self.validate_warehouse_belongs_to_company()
|
||||
if self.meta.get_field("currency"):
|
||||
self.company_currency = get_company_currency(self.doc.company)
|
||||
self.validate_conversion_rate("currency", "conversion_rate")
|
||||
|
||||
if self.doc.price_list_name and self.doc.price_list_currency:
|
||||
self.validate_conversion_rate("price_list_currency", "plc_conversion_rate")
|
||||
|
||||
# IMPORTANT: enable this only when client side code is similar to this one
|
||||
# self.calculate_taxes_and_totals()
|
||||
|
||||
def set_missing_values(self, for_validate=False):
|
||||
# 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):
|
||||
for fieldname, val in self.get_default_address_and_contact("supplier").items():
|
||||
if not self.doc.fields.get(fieldname) and self.meta.get_field(fieldname):
|
||||
self.doc.fields[fieldname] = val
|
||||
|
||||
self.set_missing_item_details(get_item_details)
|
||||
|
||||
def set_supplier_defaults(self):
|
||||
self.get_default_supplier_address(self.doc.fields)
|
||||
|
||||
# set total in words
|
||||
self.set_total_in_words()
|
||||
|
||||
def get_purchase_tax_details(self):
|
||||
self.doclist = self.doc.clear_table(self.doclist, "purchase_tax_details")
|
||||
self.set_taxes()
|
||||
|
||||
def validate_warehouse_belongs_to_company(self):
|
||||
for warehouse, company in webnotes.conn.get_values("Warehouse",
|
||||
self.doclist.get_distinct_values("warehouse"), "company").items():
|
||||
@ -71,46 +81,6 @@ class BuyingController(StockController):
|
||||
webnotes.msgprint(_("""Tax Category can not be 'Valuation' or 'Valuation and Total'
|
||||
as all items are non-stock items"""), raise_exception=1)
|
||||
|
||||
def update_item_details(self):
|
||||
for item in self.doclist.get({"parentfield": self.fname}):
|
||||
ret = get_item_details({
|
||||
"doctype": self.doc.doctype,
|
||||
"docname": self.doc.name,
|
||||
"item_code": item.item_code,
|
||||
"warehouse": item.warehouse,
|
||||
"supplier": self.doc.supplier,
|
||||
"transaction_date": self.doc.posting_date,
|
||||
"conversion_rate": self.doc.conversion_rate,
|
||||
"price_list_name": self.doc.price_list_name,
|
||||
"price_list_currency": self.doc.price_list_currency,
|
||||
"plc_conversion_rate": self.doc.plc_conversion_rate
|
||||
})
|
||||
for r in ret:
|
||||
if not item.fields.get(r):
|
||||
item.fields[r] = ret[r]
|
||||
|
||||
def validate_conversion_rate(self, currency_field, conversion_rate_field):
|
||||
"""common validation for currency and price list currency"""
|
||||
|
||||
currency = self.doc.fields.get(currency_field)
|
||||
conversion_rate = flt(self.doc.fields.get(conversion_rate_field))
|
||||
conversion_rate_label = self.meta.get_label(conversion_rate_field)
|
||||
|
||||
if conversion_rate == 0:
|
||||
msgprint(conversion_rate_label + _(' cannot be 0'), raise_exception=True)
|
||||
|
||||
# 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 == self.company_currency and conversion_rate == 1.00)
|
||||
or (currency != self.company_currency and conversion_rate != 1.00)))
|
||||
|
||||
if not valid_conversion_rate:
|
||||
msgprint(_('Please enter valid ') + conversion_rate_label + (': ')
|
||||
+ ("1 %s = [?] %s" % (currency, self.company_currency)),
|
||||
raise_exception=True)
|
||||
|
||||
def set_total_in_words(self):
|
||||
from webnotes.utils import money_in_words
|
||||
company_currency = get_company_currency(self.doc.company)
|
||||
@ -121,154 +91,54 @@ class BuyingController(StockController):
|
||||
self.doc.currency)
|
||||
|
||||
def calculate_taxes_and_totals(self):
|
||||
self.doc.conversion_rate = flt(self.doc.conversion_rate)
|
||||
self.item_doclist = self.doclist.get({"parentfield": self.fname})
|
||||
self.tax_doclist = self.doclist.get({"parentfield": "purchase_tax_details"})
|
||||
|
||||
self.calculate_item_values()
|
||||
self.initialize_taxes()
|
||||
self.calculate_net_total()
|
||||
self.calculate_taxes()
|
||||
self.calculate_totals()
|
||||
self.calculate_outstanding_amount()
|
||||
|
||||
self._cleanup()
|
||||
self.other_fname = "purchase_tax_details"
|
||||
super(BuyingController, self).calculate_taxes_and_totals()
|
||||
self.calculate_total_advance("Purchase Invoice", "advance_allocation_details")
|
||||
|
||||
def calculate_item_values(self):
|
||||
def _set_base(item, print_field, base_field):
|
||||
"""set values in base currency"""
|
||||
item.fields[base_field] = flt((flt(item.fields[print_field],
|
||||
self.precision.item[print_field]) * self.doc.conversion_rate),
|
||||
self.precision.item[base_field])
|
||||
|
||||
for item in self.item_doclist:
|
||||
round_floats_in_doc(item, self.precision.item)
|
||||
# hack! - cleaned up in _cleanup()
|
||||
if self.doc.doctype != "Purchase Invoice":
|
||||
df = self.meta.get_field("purchase_rate", parentfield=self.fname)
|
||||
df.fieldname = "rate"
|
||||
|
||||
for item in self.item_doclist:
|
||||
# hack! - cleaned up in _cleanup()
|
||||
if self.doc.doctype != "Purchase Invoice":
|
||||
item.rate = item.purchase_rate
|
||||
self.precision.item.rate = self.precision.item.purchase_rate
|
||||
|
||||
item.discount = item.discount_rate
|
||||
self.precision.item.discount = self.precision.item.discount_rate
|
||||
self.round_floats_in(item)
|
||||
|
||||
if item.discount == 100:
|
||||
if not item.import_ref_rate:
|
||||
item.import_ref_rate = item.import_rate
|
||||
item.import_rate = 0
|
||||
else:
|
||||
if item.import_ref_rate:
|
||||
item.import_rate = flt(item.import_ref_rate *
|
||||
(1.0 - (item.discount_rate / 100.0)),
|
||||
self.precision.item.import_rate)
|
||||
else:
|
||||
# assume that print rate and discount are specified
|
||||
item.import_ref_rate = flt(item.import_rate /
|
||||
(1.0 - (item.discount_rate / 100.0)),
|
||||
self.precision.item.import_ref_rate)
|
||||
if item.discount_rate == 100.0:
|
||||
item.import_rate = 0.0
|
||||
elif item.import_ref_rate:
|
||||
item.import_rate = flt(item.import_ref_rate * (1.0 - (item.discount_rate / 100.0)),
|
||||
self.precision("import_rate", item))
|
||||
|
||||
item.import_amount = flt(item.import_rate * item.qty,
|
||||
self.precision.item.import_amount)
|
||||
self.precision("import_amount", item))
|
||||
item.item_tax_amount = 0.0;
|
||||
|
||||
_set_base(item, "import_ref_rate", "purchase_ref_rate")
|
||||
_set_base(item, "import_rate", "rate")
|
||||
_set_base(item, "import_amount", "amount")
|
||||
|
||||
def initialize_taxes(self):
|
||||
for tax in self.tax_doclist:
|
||||
# initialize totals to 0
|
||||
tax.tax_amount = tax.total = 0.0
|
||||
self._set_in_company_currency(item, "import_ref_rate", "purchase_ref_rate")
|
||||
self._set_in_company_currency(item, "import_rate", "rate")
|
||||
self._set_in_company_currency(item, "import_amount", "amount")
|
||||
|
||||
# temporary fields
|
||||
tax.tax_amount_for_current_item = tax.grand_total_for_current_item = 0.0
|
||||
|
||||
tax.item_wise_tax_detail = {}
|
||||
|
||||
self.validate_on_previous_row(tax)
|
||||
|
||||
round_floats_in_doc(tax, self.precision.tax)
|
||||
|
||||
def calculate_net_total(self):
|
||||
self.doc.net_total = 0
|
||||
self.doc.net_total_import = 0
|
||||
self.doc.net_total = self.doc.net_total_import = 0.0
|
||||
|
||||
for item in self.item_doclist:
|
||||
self.doc.net_total += item.amount
|
||||
self.doc.net_total_import += item.import_amount
|
||||
|
||||
self.doc.net_total = flt(self.doc.net_total, self.precision.main.net_total)
|
||||
self.doc.net_total_import = flt(self.doc.net_total_import,
|
||||
self.precision.main.net_total_import)
|
||||
|
||||
def calculate_taxes(self):
|
||||
for item in self.item_doclist:
|
||||
item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
|
||||
item.item_tax_amount = 0
|
||||
|
||||
for i, tax in enumerate(self.tax_doclist):
|
||||
# tax_amount represents the amount of tax for the current step
|
||||
current_tax_amount = self.get_current_tax_amount(item, tax, item_tax_map)
|
||||
|
||||
self.set_item_tax_amount(item, tax, current_tax_amount)
|
||||
|
||||
# case when net total is 0 but there is an actual type charge
|
||||
# in this case add the actual amount to tax.tax_amount
|
||||
# and tax.grand_total_for_current_item for the first such iteration
|
||||
if not (current_tax_amount or self.doc.net_total or tax.tax_amount) and \
|
||||
tax.charge_type=="Actual":
|
||||
zero_net_total_adjustment = flt(tax.rate, self.precision.tax.tax_amount)
|
||||
current_tax_amount += zero_net_total_adjustment
|
||||
|
||||
# store tax_amount for current item as it will be used for
|
||||
# charge type = 'On Previous Row Amount'
|
||||
tax.tax_amount_for_current_item = current_tax_amount
|
||||
|
||||
# accumulate tax amount into tax.tax_amount
|
||||
tax.tax_amount += tax.tax_amount_for_current_item
|
||||
|
||||
if tax.category == "Valuation":
|
||||
# if just for valuation, do not add the tax amount in total
|
||||
# hence, setting it as 0 for further steps
|
||||
current_tax_amount = 0
|
||||
else:
|
||||
current_tax_amount *= tax.add_deduct_tax == "Deduct" and -1.0 or 1.0
|
||||
|
||||
# Calculate tax.total viz. grand total till that step
|
||||
# note: grand_total_for_current_item contains the contribution of
|
||||
# item's amount, previously applied tax and the current tax on that item
|
||||
if i==0:
|
||||
tax.grand_total_for_current_item = flt(item.amount +
|
||||
current_tax_amount, self.precision.tax.total)
|
||||
|
||||
else:
|
||||
tax.grand_total_for_current_item = \
|
||||
flt(self.tax_doclist[i-1].grand_total_for_current_item +
|
||||
current_tax_amount, self.precision.tax.total)
|
||||
|
||||
# in tax.total, accumulate grand total of each item
|
||||
tax.total += tax.grand_total_for_current_item
|
||||
|
||||
# store tax_breakup for each item
|
||||
# DOUBT: should valuation type amount also be stored?
|
||||
tax.item_wise_tax_detail[item.item_code] = current_tax_amount
|
||||
self.round_floats_in(self.doc, ["net_total", "net_total_import"])
|
||||
|
||||
def calculate_totals(self):
|
||||
if self.tax_doclist:
|
||||
self.doc.grand_total = flt(self.tax_doclist[-1].total,
|
||||
self.precision.main.grand_total)
|
||||
self.doc.grand_total_import = flt(
|
||||
self.doc.grand_total / self.doc.conversion_rate,
|
||||
self.precision.main.grand_total_import)
|
||||
else:
|
||||
self.doc.grand_total = flt(self.doc.net_total,
|
||||
self.precision.main.grand_total)
|
||||
self.doc.grand_total_import = flt(
|
||||
self.doc.grand_total / self.doc.conversion_rate,
|
||||
self.precision.main.grand_total_import)
|
||||
self.doc.grand_total = flt(self.tax_doclist and \
|
||||
self.tax_doclist[-1].total or self.doc.net_total, self.precision("grand_total"))
|
||||
self.doc.grand_total_import = flt(self.doc.grand_total / self.doc.conversion_rate,
|
||||
self.precision("grand_total_import"))
|
||||
|
||||
self.doc.total_tax = \
|
||||
flt(self.doc.grand_total - self.doc.net_total,
|
||||
self.precision.main.total_tax)
|
||||
self.doc.total_tax = flt(self.doc.grand_total - self.doc.net_total,
|
||||
self.precision("total_tax"))
|
||||
|
||||
if self.meta.get_field("rounded_total"):
|
||||
self.doc.rounded_total = round(self.doc.grand_total)
|
||||
@ -277,75 +147,31 @@ class BuyingController(StockController):
|
||||
self.doc.rounded_total_import = round(self.doc.grand_total_import)
|
||||
|
||||
def calculate_outstanding_amount(self):
|
||||
if self.doc.doctype == "Purchase Invoice" and self.doc.docstatus == 0:
|
||||
if self.doc.doctype == "Purchase Invoice" and self.doc.docstatus < 2:
|
||||
self.doc.total_advance = flt(self.doc.total_advance,
|
||||
self.precision.main.total_advance)
|
||||
self.precision("total_advance"))
|
||||
self.doc.total_amount_to_pay = flt(self.doc.grand_total - flt(self.doc.write_off_amount,
|
||||
self.precision.main.write_off_amount), self.precision.main.total_amount_to_pay)
|
||||
self.precision("write_off_amount")), self.precision("total_amount_to_pay"))
|
||||
self.doc.outstanding_amount = flt(self.doc.total_amount_to_pay - self.doc.total_advance,
|
||||
self.precision.main.outstanding_amount)
|
||||
self.precision("outstanding_amount"))
|
||||
|
||||
def _cleanup(self):
|
||||
for tax in self.tax_doclist:
|
||||
del tax.fields["grand_total_for_current_item"]
|
||||
del tax.fields["tax_amount_for_current_item"]
|
||||
tax.item_wise_tax_detail = json.dumps(tax.item_wise_tax_detail)
|
||||
|
||||
# except in purchase invoice, rate field is purchase_rate
|
||||
super(BuyingController, self)._cleanup()
|
||||
|
||||
# except in purchase invoice, rate field is purchase_rate
|
||||
# reset fieldname of rate
|
||||
if self.doc.doctype != "Purchase Invoice":
|
||||
df = self.meta.get_field("rate", parentfield=self.fname)
|
||||
df.fieldname = "purchase_rate"
|
||||
|
||||
for item in self.item_doclist:
|
||||
item.purchase_rate = item.rate
|
||||
del item.fields["rate"]
|
||||
|
||||
item.discount_rate = item.discount
|
||||
del item.fields["discount"]
|
||||
|
||||
def validate_on_previous_row(self, tax):
|
||||
"""
|
||||
validate if a valid row id is mentioned in case of
|
||||
On Previous Row Amount and On Previous Row Total
|
||||
"""
|
||||
if tax.charge_type in ["On Previous Row Amount", "On Previous Row Total"] and \
|
||||
(not tax.row_id or cint(tax.row_id) >= tax.idx):
|
||||
msgprint((_("Row") + " # %(idx)s [%(taxes_doctype)s]: " + \
|
||||
_("Please specify a valid") + " %(row_id_label)s") % {
|
||||
"idx": tax.idx,
|
||||
"taxes_doctype": tax.parenttype,
|
||||
"row_id_label": self.meta.get_label("row_id",
|
||||
parentfield="purchase_tax_details")
|
||||
}, raise_exception=True)
|
||||
if not self.meta.get_field("item_tax_amount", parentfield=self.fname):
|
||||
for item in self.item_doclist:
|
||||
del item.fields["item_tax_amount"]
|
||||
|
||||
def _load_item_tax_rate(self, item_tax_rate):
|
||||
if not item_tax_rate:
|
||||
return {}
|
||||
return json.loads(item_tax_rate)
|
||||
|
||||
def get_current_tax_amount(self, item, tax, item_tax_map):
|
||||
tax_rate = self._get_tax_rate(tax, item_tax_map)
|
||||
|
||||
if tax.charge_type == "Actual":
|
||||
# distribute the tax amount proportionally to each item row
|
||||
actual = flt(tax.rate, self.precision.tax.tax_amount)
|
||||
current_tax_amount = (self.doc.net_total
|
||||
and ((item.amount / self.doc.net_total) * actual)
|
||||
or 0)
|
||||
elif tax.charge_type == "On Net Total":
|
||||
current_tax_amount = (tax_rate / 100.0) * item.amount
|
||||
elif tax.charge_type == "On Previous Row Amount":
|
||||
current_tax_amount = (tax_rate / 100.0) * \
|
||||
self.tax_doclist[cint(tax.row_id) - 1].tax_amount_for_current_item
|
||||
elif tax.charge_type == "On Previous Row Total":
|
||||
current_tax_amount = (tax_rate / 100.0) * \
|
||||
self.tax_doclist[cint(tax.row_id) - 1].grand_total_for_current_item
|
||||
|
||||
return flt(current_tax_amount, self.precision.tax.tax_amount)
|
||||
|
||||
def _get_tax_rate(self, tax, item_tax_map):
|
||||
if item_tax_map.has_key(tax.account_head):
|
||||
return flt(item_tax_map.get(tax.account_head), self.precision.tax.rate)
|
||||
else:
|
||||
return tax.rate
|
||||
|
||||
def set_item_tax_amount(self, item, tax, current_tax_amount):
|
||||
"""
|
||||
item_tax_amount is the total tax amount applied on that item
|
||||
@ -353,29 +179,28 @@ class BuyingController(StockController):
|
||||
|
||||
TODO: rename item_tax_amount to valuation_tax_amount
|
||||
"""
|
||||
if tax.category in ["Valuation", "Valuation and Total"] and \
|
||||
item.item_code in self.stock_items:
|
||||
item.item_tax_amount += flt(current_tax_amount,
|
||||
self.precision.item.item_tax_amount)
|
||||
if tax.category in ["Valuation", "Valuation and Total"]:
|
||||
item.item_tax_amount += flt(current_tax_amount, self.precision("item_tax_amount", item))
|
||||
|
||||
# update valuation rate
|
||||
def update_valuation_rate(self, parentfield):
|
||||
for d in self.doclist.get({"parentfield": parentfield}):
|
||||
d.conversion_factor = d.conversion_factor or flt(webnotes.conn.get_value(
|
||||
"UOM Conversion Detail", {"parent": d.item_code, "uom": d.uom},
|
||||
for item in self.doclist.get({"parentfield": parentfield}):
|
||||
item.conversion_factor = item.conversion_factor or flt(webnotes.conn.get_value(
|
||||
"UOM Conversion Detail", {"parent": item.item_code, "uom": item.uom},
|
||||
"conversion_factor")) or 1
|
||||
if d.item_code and d.qty:
|
||||
|
||||
if item.item_code and item.qty:
|
||||
self.round_floats_in(item)
|
||||
|
||||
purchase_rate = item.rate if self.doc.doctype == "Purchase Invoice" else item.purchase_rate
|
||||
|
||||
# if no item code, which is sometimes the case in purchase invoice,
|
||||
# then it is not possible to track valuation against it
|
||||
d.valuation_rate = flt(((flt(d.purchase_rate, self.precision.item.purchase_rate) or
|
||||
flt(d.rate, self.precision.item.rate)) +
|
||||
(flt(d.item_tax_amount, self.precision.item.item_tax_amount) +
|
||||
flt(d.rm_supp_cost, self.precision.item.rm_supp_cost)) /
|
||||
flt(d.qty, self.precision.item.qty)) /
|
||||
flt(d.conversion_factor, self.precision.item.conversion_factor),
|
||||
self.precision.item.valuation_rate)
|
||||
item.valuation_rate = flt((purchase_rate +
|
||||
(item.item_tax_amount + item.rm_supp_cost) / item.qty) / item.conversion_factor,
|
||||
self.precision("valuation_rate", item))
|
||||
else:
|
||||
d.valuation_rate = 0.0
|
||||
item.valuation_rate = 0.0
|
||||
|
||||
def validate_for_subcontracting(self):
|
||||
if not self.doc.is_subcontracted and self.sub_contracted_items:
|
||||
@ -437,18 +262,6 @@ class BuyingController(StockController):
|
||||
|
||||
return bom_items
|
||||
|
||||
|
||||
@property
|
||||
def precision(self):
|
||||
if not hasattr(self, "_precision"):
|
||||
self._precision = webnotes._dict()
|
||||
self._precision.main = self.meta.get_precision_map()
|
||||
self._precision.item = self.meta.get_precision_map(parentfield = self.fname)
|
||||
if self.meta.get_field("purchase_tax_details"):
|
||||
self._precision.tax = self.meta.get_precision_map(parentfield = \
|
||||
"purchase_tax_details")
|
||||
return self._precision
|
||||
|
||||
@property
|
||||
def sub_contracted_items(self):
|
||||
if not hasattr(self, "_sub_contracted_items"):
|
||||
|
@ -16,16 +16,40 @@
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import webnotes
|
||||
from webnotes.utils import cint
|
||||
from webnotes.utils import cint, flt, comma_or
|
||||
from setup.utils import get_company_currency
|
||||
from selling.utils import get_item_details
|
||||
from webnotes import msgprint, _
|
||||
|
||||
from controllers.stock_controller import StockController
|
||||
|
||||
class SellingController(StockController):
|
||||
def validate(self):
|
||||
super(SellingController, self).validate()
|
||||
self.set_total_in_words()
|
||||
def onload_post_render(self):
|
||||
self.set_price_list_currency("selling")
|
||||
|
||||
# contact, address, item details and pos details (if applicable)
|
||||
self.set_missing_values()
|
||||
|
||||
self.set_taxes("Sales Taxes and Charges", "other_charges", "charge")
|
||||
|
||||
def set_missing_values(self, for_validate=False):
|
||||
# set contact and address details for customer, if they are not mentioned
|
||||
if self.doc.customer and not (self.doc.contact_person and self.doc.customer_address):
|
||||
for fieldname, val in self.get_default_address_and_contact("customer").items():
|
||||
if not self.doc.fields.get(fieldname) and self.meta.get_field(fieldname):
|
||||
self.doc.fields[fieldname] = val
|
||||
|
||||
self.set_missing_item_details(get_item_details)
|
||||
|
||||
def get_other_charges(self):
|
||||
self.doclist = self.doc.clear_table(self.doclist, "other_charges")
|
||||
self.set_taxes("Sales Taxes and Charges", "other_charges", "charge")
|
||||
|
||||
def set_customer_defaults(self):
|
||||
self.get_default_customer_address()
|
||||
|
||||
if self.meta.get_field("shipping_address"):
|
||||
self.doc.fields.update(self.get_shipping_address(self.doc.customer))
|
||||
|
||||
def set_total_in_words(self):
|
||||
from webnotes.utils import money_in_words
|
||||
@ -71,4 +95,154 @@ class SellingController(StockController):
|
||||
|
||||
if item.buying_amount and not item.cost_center:
|
||||
msgprint(_("""Cost Center is mandatory for item: """) + item.item_code,
|
||||
raise_exception=1)
|
||||
raise_exception=1)
|
||||
|
||||
def calculate_taxes_and_totals(self):
|
||||
self.other_fname = "other_charges"
|
||||
|
||||
super(SellingController, self).calculate_taxes_and_totals()
|
||||
|
||||
self.calculate_total_advance("Sales Invoice", "advance_adjustment_details")
|
||||
self.calculate_commission()
|
||||
self.calculate_contribution()
|
||||
|
||||
def determine_exclusive_rate(self):
|
||||
if not any((cint(tax.included_in_print_rate) for tax in self.tax_doclist)):
|
||||
# no inclusive tax
|
||||
return
|
||||
|
||||
for item in self.item_doclist:
|
||||
item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
|
||||
cumulated_tax_fraction = 0
|
||||
for i, tax in enumerate(self.tax_doclist):
|
||||
tax.tax_fraction_for_current_item = self.get_current_tax_fraction(tax, item_tax_map)
|
||||
|
||||
if i==0:
|
||||
tax.grand_total_fraction_for_current_item = 1 + tax.tax_fraction_for_current_item
|
||||
else:
|
||||
tax.grand_total_fraction_for_current_item = \
|
||||
self.tax_doclist[i-1].grand_total_fraction_for_current_item \
|
||||
+ tax.tax_fraction_for_current_item
|
||||
|
||||
cumulated_tax_fraction += tax.tax_fraction_for_current_item
|
||||
|
||||
if cumulated_tax_fraction:
|
||||
item.basic_rate = flt((item.export_rate * self.doc.conversion_rate) /
|
||||
(1 + cumulated_tax_fraction), self.precision("basic_rate", item))
|
||||
|
||||
item.amount = flt(item.basic_rate * item.qty, self.precision("amount", item))
|
||||
|
||||
if item.adj_rate == 100:
|
||||
item.base_ref_rate = item.basic_rate
|
||||
item.basic_rate = 0.0
|
||||
else:
|
||||
item.base_ref_rate = flt(item.basic_rate / (1 - (item.adj_rate / 100.0)),
|
||||
self.precision("base_ref_rate", item))
|
||||
|
||||
def get_current_tax_fraction(self, tax, item_tax_map):
|
||||
"""
|
||||
Get tax fraction for calculating tax exclusive amount
|
||||
from tax inclusive amount
|
||||
"""
|
||||
current_tax_fraction = 0
|
||||
|
||||
if cint(tax.included_in_print_rate):
|
||||
tax_rate = self._get_tax_rate(tax, item_tax_map)
|
||||
|
||||
if tax.charge_type == "On Net Total":
|
||||
current_tax_fraction = tax_rate / 100.0
|
||||
|
||||
elif tax.charge_type == "On Previous Row Amount":
|
||||
current_tax_fraction = (tax_rate / 100.0) * \
|
||||
self.tax_doclist[cint(tax.row_id) - 1].tax_fraction_for_current_item
|
||||
|
||||
elif tax.charge_type == "On Previous Row Total":
|
||||
current_tax_fraction = (tax_rate / 100.0) * \
|
||||
self.tax_doclist[cint(tax.row_id) - 1].grand_total_fraction_for_current_item
|
||||
|
||||
return current_tax_fraction
|
||||
|
||||
def calculate_item_values(self):
|
||||
for item in self.item_doclist:
|
||||
self.round_floats_in(item)
|
||||
|
||||
if item.adj_rate == 100:
|
||||
item.export_rate = 0
|
||||
elif item.ref_rate:
|
||||
item.export_rate = flt(item.ref_rate * (1.0 - (item.adj_rate / 100.0)),
|
||||
self.precision("export_rate", item))
|
||||
|
||||
item.export_amount = flt(item.export_rate * item.qty,
|
||||
self.precision("export_amount", item))
|
||||
|
||||
self._set_in_company_currency(item, "ref_rate", "base_ref_rate")
|
||||
self._set_in_company_currency(item, "export_rate", "basic_rate")
|
||||
self._set_in_company_currency(item, "export_amount", "amount")
|
||||
|
||||
def calculate_net_total(self):
|
||||
self.doc.net_total = self.doc.net_total_export = 0.0
|
||||
|
||||
for item in self.item_doclist:
|
||||
self.doc.net_total += item.amount
|
||||
self.doc.net_total_export += item.export_amount
|
||||
|
||||
self.round_floats_in(self.doc, ["net_total", "net_total_export"])
|
||||
|
||||
def calculate_totals(self):
|
||||
self.doc.grand_total = flt(self.tax_doclist and \
|
||||
self.tax_doclist[-1].total or self.doc.net_total, self.precision("grand_total"))
|
||||
self.doc.grand_total_export = flt(self.doc.grand_total / self.doc.conversion_rate,
|
||||
self.precision("grand_total_export"))
|
||||
|
||||
self.doc.other_charges_total = flt(self.doc.grand_total - self.doc.net_total,
|
||||
self.precision("other_charges_total"))
|
||||
self.doc.other_charges_total_export = flt(self.doc.grand_total_export - self.doc.net_total_export,
|
||||
self.precision("other_charges_total_export"))
|
||||
|
||||
self.doc.rounded_total = round(self.doc.grand_total)
|
||||
self.doc.rounded_total_export = round(self.doc.grand_total_export)
|
||||
|
||||
def calculate_outstanding_amount(self):
|
||||
# NOTE:
|
||||
# write_off_amount is only for POS Invoice
|
||||
# total_advance is only for non POS Invoice
|
||||
if self.doc.doctype == "Sales Invoice" and self.doc.docstatus < 2:
|
||||
self.round_floats_in(self.doc, ["grand_total", "total_advance", "write_off_amount",
|
||||
"paid_amount"])
|
||||
total_amount_to_pay = self.doc.grand_total - self.doc.write_off_amount
|
||||
self.doc.outstanding_amount = flt(total_amount_to_pay - self.doc.total_advance - self.doc.paid_amount,
|
||||
self.precision("outstanding_amount"))
|
||||
|
||||
def calculate_commission(self):
|
||||
if self.meta.get_field("commission_rate"):
|
||||
self.round_floats_in(self.doc, ["net_total", "commission_rate"])
|
||||
if self.doc.commission_rate > 100.0:
|
||||
msgprint(_(self.meta.get_label("commission_rate")) + " " +
|
||||
_("cannot be greater than 100"), raise_exception=True)
|
||||
|
||||
self.doc.total_commission = flt(self.doc.net_total * self.doc.commission_rate / 100.0,
|
||||
self.precision("total_commission"))
|
||||
|
||||
def calculate_contribution(self):
|
||||
total = 0.0
|
||||
sales_team = self.doclist.get({"parentfield": "sales_team"})
|
||||
for sales_person in sales_team:
|
||||
self.round_floats_in(sales_person)
|
||||
|
||||
sales_person.allocated_amount = flt(
|
||||
self.doc.net_total * sales_person.allocated_percentage / 100.0,
|
||||
self.precision("allocated_amount", sales_person))
|
||||
|
||||
total += sales_person.allocated_percentage
|
||||
|
||||
if sales_team and total != 100.0:
|
||||
msgprint(_("Total") + " " +
|
||||
_(self.meta.get_label("allocated_percentage", parentfield="sales_team")) +
|
||||
" " + _("should be 100%"), raise_exception=True)
|
||||
|
||||
def validate_order_type(self):
|
||||
valid_types = ["Sales", "Maintenance"]
|
||||
if 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)
|
||||
|
15
docs/docs.dev.md
Normal file
15
docs/docs.dev.md
Normal file
@ -0,0 +1,15 @@
|
||||
---
|
||||
{
|
||||
"_label": "Developer API",
|
||||
"_toc": [
|
||||
"docs.dev.quickstart",
|
||||
"docs.dev.framework",
|
||||
"docs.dev.modules"
|
||||
]
|
||||
}
|
||||
---
|
||||
### Is this for me?
|
||||
|
||||
To starting hacking into ERPNext, you must have some understanding of how a dynamic web application works. There are hundreds of architectures and frameworks to make web development easier, but at the core there are a few elements that are important to understand.
|
||||
|
||||
ERPNext is built on `wnframework` which is primarily developed for ERPNext but can be extended to make similar database driven applications. wnframework uses Python on the server-side and has a javascript based client for entering data, managing workflow and making reports.
|
6
docs/docs.dev.modules.md
Normal file
6
docs/docs.dev.modules.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
{
|
||||
"_label": "Modules"
|
||||
}
|
||||
---
|
||||
The models used in both `wnframework` (**Core** module) and ERPNext are listed here. The basic element of the model is a `DocType`, which is most often also a database table. The model properties are called `DocFields`, which describe the view and the database columns.
|
24
docs/docs.md
Normal file
24
docs/docs.md
Normal file
@ -0,0 +1,24 @@
|
||||
---
|
||||
{
|
||||
"_label": "ERPNext",
|
||||
"_no_title": 1,
|
||||
"_toc": [
|
||||
"docs.user",
|
||||
"docs.dev",
|
||||
"docs.download",
|
||||
"docs.community",
|
||||
"docs.blog",
|
||||
"docs.about"
|
||||
]
|
||||
}
|
||||
---
|
||||
<div class="jumbotron">
|
||||
<h1>ERPNext</h1>
|
||||
<p>Open Source Organization Management Platform</p>
|
||||
</div>
|
||||
|
||||
### Is this for me?
|
||||
|
||||
To starting hacking into ERPNext, you must have some understanding of how a dynamic web application works. There are hundreds of architectures and frameworks to make web development easier, but at the core there are a few elements that are important to understand.
|
||||
|
||||
ERPNext is built on `wnframework` which is primarily developed for ERPNext but can be extended to make similar database driven applications. wnframework uses Python on the server-side and has a javascript based client for entering data, managing workflow and making reports.
|
54
docs/docs.user.intro.md
Normal file
54
docs/docs.user.intro.md
Normal file
@ -0,0 +1,54 @@
|
||||
---
|
||||
{
|
||||
"_label": "Introduction",
|
||||
"_title_image": "img/why-erpnext.png"
|
||||
}
|
||||
---
|
||||
## What is an ERP and why should I care?
|
||||
|
||||
Small business are not so different from large ones. They contain most of the complexities of a large business but with many more constraints. Small businesses have to communicate with customers, do accounts, pay taxes, do payroll, manage timelines, deliver quality, answer questions and keep everyone happy just like large businesses.
|
||||
|
||||
And to it *efficiently*.
|
||||
|
||||
Large businesses have the advantage of using advanced data systems to manage their process efficiently. Small businesses typically struggle to keep things organized. They are often using a mix of apps like spreadsheets, accounting software, web CRM etc to manage but not everyone is on the same page. An ERP changes that.
|
||||
|
||||
---
|
||||
|
||||
## What is ERPNext?
|
||||
|
||||
ERPNext helps you to manage all your business information in one application and use it to manage operations and take decisions based on data.
|
||||
|
||||
Among other things, ERPNext will help you to:
|
||||
|
||||
- Track all Invoices and Payments.
|
||||
- Know what quantity of what product is available in stock.
|
||||
- Identify open customer queries.
|
||||
- Manage payroll.
|
||||
- Assign tasks and follow up on them.
|
||||
- Maintain a database of all your customers, suppliers and their contacts.
|
||||
- Prepare quotes.
|
||||
- Get reminders on maintenance schedules.
|
||||
- Publish you website.
|
||||
|
||||
And a lot lot lot more.
|
||||
|
||||
---
|
||||
|
||||
## Why Should I Use ERPNext?
|
||||
|
||||
ERPNext is a modern accounting plus everything system and has many benefits over both traditional accounting as well as ERP applications.
|
||||
|
||||
### Benefits over traditional accounting software:
|
||||
|
||||
- Do a lot more than just accounting! Manage inventory, billing, quotes, leads, payroll and much much more.
|
||||
- All data safe and in one place. Don’t keep hunting for data when you need it across spreadsheets and different computers.
|
||||
- Everyone on the same page. All users get the same update data.
|
||||
- Stop repetitive work. Don’t enter the same information from your word processor to your accounting tool. Its all integrated.
|
||||
- Keep track. Get the entire history of a customer or a deal in one place.
|
||||
|
||||
### Benefits over big ERPs
|
||||
|
||||
- $$$ - Save money.
|
||||
- Easier to configure. Big ERPs are notoriously hard to setup and will ask you a zillion questions before you can do something meaningful.
|
||||
- Easier to use. Modern web like user interface will keep your users happy and in familiar territory.
|
||||
- Open Source. This software is always free and you can host it anywhere you like.
|
10
docs/docs.user.md
Normal file
10
docs/docs.user.md
Normal file
@ -0,0 +1,10 @@
|
||||
---
|
||||
{
|
||||
"_label": "User Guide",
|
||||
"_toc": [
|
||||
"docs.user.intro",
|
||||
"docs.user.setup"
|
||||
]
|
||||
}
|
||||
---
|
||||
This manual covers all the major processes in setting up and using ERPNext. The manual is written in a way that the user can manage a self-implementation of the ERP. We recommend that the manual be read first before starting implementation.
|
129
docs/docs.user.setup.accounting.md
Normal file
129
docs/docs.user.setup.accounting.md
Normal file
@ -0,0 +1,129 @@
|
||||
---
|
||||
{
|
||||
"_label": "Accounting Setup",
|
||||
"_title_image": "img/setup-accounting.png"
|
||||
}
|
||||
---
|
||||
## Chart of Accounts
|
||||
|
||||
The Chart of Accounts forms the blueprint of your organization. The overall structure of your Chart of Accounts is based on a system of double entry accounting that has become a standard all over the world to quantify how a company is doing financially.
|
||||
|
||||
The Chart of Accounts helps you answer:
|
||||
|
||||
- What is your organization worth?
|
||||
- How much debt have you taken?
|
||||
- How much profit you are making (and hence paying tax)?
|
||||
- How much is are you selling?
|
||||
- How are your expenses broken up?
|
||||
|
||||
As you can see, it is very valuable to you as a business manager to see how well your business is doing.
|
||||
|
||||
> Tip: If you can’t read a Balance Sheet (I confess it took me a long time to figure this out) its a good opportunity to start learning about this. It will be worth the effort. You can also take the help of your accountant to setup your Chart of Accounts.
|
||||
|
||||
To edit your Chart of Accounts in ERPNext go to:
|
||||
|
||||
> Accounts > Chart of Accounts
|
||||
|
||||
Chart of Accounts is a tree view of the names of the Accounts (Ledgers and Groups) that a Company requires to manage its books of accounts. ERPNext sets up a simple chart of accounts for each Company you create, but you have to modify it according to your needs and legal requirements.
|
||||

|
||||
For each company, Chart of Accounts signifies the way to classify the accounting entries, mostly based on statutory (tax, compliance to government regulations) requirements.
|
||||
|
||||
Let us understand the main groups of the Chart of Accounts.
|
||||
|
||||
### Balance Sheet Accounts
|
||||
|
||||
The Balance Sheet has Application of Funds (Assets) and Sources of Funds (Liabilities) that signify the net-worth of your company at any given time. When you begin or end a financial period, all the Assets are equal to the Liabilities.
|
||||
|
||||
> Accounting: If you are new to accounting, you might be wondering, how can Assets be equal to Liabilities? That would mean the company has nothing of its own. Thats right. All the “investment” made in the company to buy assets (like land, furniture, machines) is made by the owners and is a liability to the company. If the company would to shut down, it would need to sell all the assets and pay back all the liabilities (including profits) to the owners, leaving itself with nothing.
|
||||
|
||||
All the accounts under this represent an asset owned by company like "Bank Account", "Land and Property", "Furniture" or a liability (funds that the company owes to others) like "Owners funds", "Debt" etc.
|
||||
|
||||
Two special accounts to note here are Accounts Receivable (money you have to collect from your customers) and Accounts Payable (money you have to pay to your suppliers) under Assets and Liabilities respectively.
|
||||
|
||||
### Profit and Loss Accounts
|
||||
|
||||
Profit and Loss is the group of Income and Expense accounts that represent your accounting transactions over a period.
|
||||
|
||||
Unlike Balance sheet accounts, Profit and Loss accounts (or PL accounts) do not represent net worth (assets), but rather the amount of money spent and collected in servicing customers during the period. Hence at the beginning and end of your Fiscal Year, they become zero.
|
||||
|
||||
(On the first day of the year you have not made any profit or loss, but you still have assets, hence balance sheet accounts never become zero at the beginning or end of a period)
|
||||
|
||||
### Groups and Ledgers
|
||||
|
||||
There are two main kinds of Accounts in ERPNext - Group and Ledger. Groups can have sub-groups and ledgers within them, whereas ledgers are the leaf nodes of your chart and cannot be further classified.
|
||||
|
||||
Accounting Transactions can only be made against Ledger Accounts (not Groups)
|
||||
|
||||
> Info: The term "Ledger" means a page in an accounting book where entries are made. There is usually one ledger for each account (like a Customer or a Supplier).
|
||||
|
||||
> Note: An Account “Ledger” is also sometimes called as Account “Head”.
|
||||
|
||||
### Other Account Types
|
||||
|
||||
In ERPNext, you can also specify more information when you create a new Account, this is there to help you select that particular account in a scenario like Bank Account or a Tax Account and has no affect on the Chart itself.
|
||||
|
||||
You can also tag if an account represents a Customer, Supplier or Employee in "Master Type".
|
||||
|
||||
### Creating / Editing Accounts
|
||||
|
||||
To create new Accounts, explore your Chart of Accounts and click on an Account group under which you want to create the new Account. On the right side, you will see a options to “Edit” or “Add” a new Account.
|
||||
|
||||
Option to create will only appear if you click on a Group (folder) type Account.
|
||||
|
||||
ERPNext creates a standard structure for you when the Company is created but it is up to you to modify or add or remove accounts.
|
||||
|
||||
Typically, you might want to create Accounts for
|
||||
|
||||
- Types of Expenses (travel, salaries, telephone etc) under Expenses.
|
||||
- Taxes (VAT, Sales Tax etc based on your country) under Current Liabilities.
|
||||
- Types of Sales (for example, Product Sales, Service Sales etc.) under Income.
|
||||
- Types of Assets (building, machinery, furniture etc.) under Fixed Assets.
|
||||
|
||||
---
|
||||
|
||||
## Chart of Cost Centers
|
||||
|
||||
Your Chart of Accounts is mainly for reporting your information for governmental purposes and less for how you business actually performs. Though you can tweak it a bit to resemble your business.
|
||||

|
||||
Most businesses have multiple activities like different product lines, market segments, areas of business that share some common overheads but should ideally have their own structure to report whether they are profitable or not. For this purpose, there is an alternate structure, called the Chart of Cost Centers.
|
||||
|
||||
You can create a tree of Cost Centers to represent your business better. Each Income / Expense entry is also tagged against a Cost Center.
|
||||
|
||||
For example, if you have two types of sales:
|
||||
|
||||
- Walk-in Sales
|
||||
- Online Sales
|
||||
|
||||
You may not have shipping expenses for your walk-in customers, and no shop-rent for your online customers. If you want to get the profitability of each of these separately, you create the two as Cost Centers and you can mark all sales as either "Walk-in" or "Online" and also all your purchases in the same way.
|
||||
|
||||
So when you do your analysis you can get a better idea which side of your business is doing better. Since ERPNext has option to add multiple Companies, you can create Cost Centers for each Company and manage it separately.
|
||||
|
||||
To setup your Chart of Cost Centers go to:
|
||||
|
||||
> Accounts > Chart of Cost Centers
|
||||
|
||||
Cost centers help you in one more activity, budgeting.
|
||||
|
||||
### Budgeting
|
||||
|
||||
ERPNext will help you set and manage budgets on your Cost Centers. This is useful when, for example, you are doing online sales and you have a budget for search ads and you want ERPNext to stop or warn you from over spending based on that budget.
|
||||
|
||||
Budgets are also great for planning purposes. When you are making your plans for the next financial year, you would typically target a revenue and based on that you would set your expenses. Setting a budget will ensure that your expenses do not get out of hand at any point based on 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.
|
||||

|
||||
#### Budget Actions
|
||||
|
||||
ERPNext allows you to either:
|
||||
|
||||
- Stop.
|
||||
- Warn or,
|
||||
- Ignore
|
||||
|
||||
if you exceed budgets.
|
||||
|
||||
These can be defined from the Company record.
|
||||
|
||||
Even if you choose to “ignore” budget overruns, you will get a wealth of information from the “Budget vs Actual” variance report.
|
||||
|
||||
> Note: When you set a budget, it has to be set per Account under the Cost Center. For example if you have a Cost Center “Online Sales”, you can restrict “Advertising Budget” by creating a row with that Account and defining the amount.
|
35
docs/docs.user.setup.before.md
Normal file
35
docs/docs.user.setup.before.md
Normal file
@ -0,0 +1,35 @@
|
||||
---
|
||||
{
|
||||
"_label": "Before We Start",
|
||||
"_title_image": "img/before-we-start.png"
|
||||
}
|
||||
---
|
||||
## Before We Start
|
||||
|
||||
We have seem dozens of ERP implementations over the past few years and we realize that successful implementations are a lot about intangibles and attitude.
|
||||
|
||||
> The Benefits come Later
|
||||
|
||||
ERPs are not required.
|
||||
|
||||
Like exercise.
|
||||
|
||||
Human body does not require to be exercised today or maybe tomorrow, but in the long run, if you wish to maintain your body and its health, you should get on the treadmill.
|
||||
|
||||
In the same way, ERPs improve the health of your organization over a long run by keep it fit and efficient. The more you delay putting things in order, the more time you lose and the closer you get to a major disaster.
|
||||
|
||||
So when you start implementing an ERP, keep your sight on the long term benefits. Like exercise, its painful in the short run, but will do wonders if you stay on course.
|
||||
|
||||
---
|
||||
|
||||
## The Champion
|
||||
|
||||
ERP means organization wide change and it does not happen without effort. Every change requires a champion and its the duty of the champion to organize and energize the entire team towards implementation. The champion is all the fall guy (or fall gal) incase something goes wrong and hence needs to be resilient. Who becomes a champion without putting effort anyways?
|
||||
|
||||
In many organizations we have seen, the champion is most often the owner or a senior manager. Occasionally, the champion is an outsider who is hired for the purpose.
|
||||
|
||||
In either case, you must identify your champion first.
|
||||
|
||||
Most likely its **you!**
|
||||
|
||||
Lets Begin!
|
64
docs/docs.user.setup.codification.md
Normal file
64
docs/docs.user.setup.codification.md
Normal file
@ -0,0 +1,64 @@
|
||||
---
|
||||
{
|
||||
"_label": "Item Codification"
|
||||
}
|
||||
---
|
||||
|
||||
> To Codify or Not To Codify, is the question.
|
||||
|
||||
If you already have a running business with a number of physical items, you would have probably coded your items. If you have not, you have a choice. We recommend you should codify, but its your call.
|
||||
|
||||
Item codification is always a sensitive topic and wars have been fought on this (not joking). In our experience, when you have items that cross a certain size, life without codification is a nightmare.
|
||||
|
||||
### Benefits
|
||||
|
||||
- Standard way of naming things.
|
||||
- Less likely to have duplicates.
|
||||
- Explicit definition.
|
||||
- Help you quickly find if a similar item exists.
|
||||
- Item names get longer and longer as more types get introduced. Codes are shorter.
|
||||
|
||||
### Pain
|
||||
|
||||
- You have to remember the codes!
|
||||
- Harder for new team members to pick up.
|
||||
- You have to create new codes all the time.
|
||||
|
||||
### Example
|
||||
|
||||
You should have a simple manual / cheat-sheet to codify your items instead of just numbering them sequentially. Each letter should mean something. Here is an example:
|
||||
|
||||
If your business involves wooden furniture, then you may codify as follows:
|
||||
|
||||
Item Codification Summary Sheet
|
||||
(SAMPLE)
|
||||
|
||||
First letter: "Material" Third letter: "Size"
|
||||
|
||||
- W - Wood - 0 - less than 1mm
|
||||
- H - Hardware - 1 - 1mm - 5mm
|
||||
- G - Glass - 2 - 5mm - 10mm
|
||||
- U - Upholstery - 3 - 10mm - 10cm
|
||||
- P - Plastic
|
||||
|
||||
Second Letter: "Type"
|
||||
|
||||
For Wood: For Hardware:
|
||||
|
||||
- S - Sheet - S - Screw
|
||||
- B - Bar - N - Nut
|
||||
- L - L-section - W - Washer
|
||||
- M - Molded - B - Bracket
|
||||
- R - Round
|
||||
|
||||
The last few letters could be sequential. So by looking at code **WM304** - you know its a wooden molding less than 10cm in size
|
||||
|
||||
### Standardization
|
||||
|
||||
If you have more than one person naming items, the style of naming items will change for everyone. Sometimes, even for one person, he or she may forget how did they name the item and may create a duplicate name _"Wooden Sheet 3mm" or "3mm Sheet of Wood"?_
|
||||
|
||||
### Rationalizing
|
||||
|
||||
It is a good practice to have minimum varieties of items so that you keep minimum stock, housekeeping is simpler etc. When you are planning a new product and you want to know if you are already purchasing a part in some other product, the item codes will help you quickly determine if you are using a similar raw material in another product.
|
||||
|
||||
We believe if you do this small investment, it will help you rationalize things as your business grows, though its okay not to codify if you have less items.
|
65
docs/docs.user.setup.customer.md
Normal file
65
docs/docs.user.setup.customer.md
Normal file
@ -0,0 +1,65 @@
|
||||
---
|
||||
{
|
||||
"_label": "Foundation: Customer",
|
||||
"_title_image": "img/customers.png"
|
||||
}
|
||||
---
|
||||
You can either directly create your Customers via
|
||||
|
||||
> Selling > Customer
|
||||
|
||||
or upload it via the Data Import Tool.
|
||||
|
||||
In your normal operations, you can also create Customers from Leads.
|
||||
|
||||
> Note: Customers are separate from Contacts and Addresses. A Customer can have multiple Contacts and Addresses.
|
||||
|
||||
### Contacts and Addresses
|
||||

|
||||
Contacts and Addresses in ERPNext are stored separately so that you can attach multiple Contacts or Addresses to Customers and Suppliers.
|
||||
|
||||
To add a Contact or Address directly from the Customer record, click on “New Contact” or “New Address”.
|
||||
|
||||
> Tip: When you select a Customer 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!
|
||||
|
||||
To Import multiple Contacts and Addresses from a spreadsheet, use the Data Import Tool.
|
||||
|
||||
### Integration with Accounts
|
||||
|
||||
In ERPNext, there is a separate Account record for each Customer, for each Company.
|
||||
|
||||
When you create a new Customer, ERPNext will automatically create an Account Ledger for the Customer under “Accounts Receivable” in the Company set in the Customer record.
|
||||
|
||||
> Advanced Tip: If you want to change the Account Group under which the Customer 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 Customer again.
|
||||
|
||||
### Customer Settings
|
||||
|
||||
You can link a Price List to a Customer (select “Default Price List”), so that when you select that Customer, the Price List will be automatically selected.
|
||||
|
||||
You can set “Credit Days” so that it is automatically set in the Sales Invoices made against this Customer.
|
||||
|
||||
You can set how much credit you want to allow for a Customer by adding the “Credit Limit”. You can also set a global “Credit Limit” in the Company master.Classifying Customers
|
||||
|
||||
ERPNext allows you to group your Customers and also divide them into Territories. Grouping will help you get better analysis of your data and identify what Customers are profitable and which are not and Territories will help you set sales targets for the territories.
|
||||
|
||||
### Customer Group
|
||||
|
||||
You can group your Customers so that you can get trend analysis for each group. Typically Customers are grouped by market segment (that is usually based on your domain).
|
||||
|
||||
> Tip: If you think all this is too much effort, you can leave it at “Default Customer Group”. But all this effort, will pay off when you start getting reports.
|
||||

|
||||
### Territory
|
||||
|
||||
If your business operates in multiple Territories (could be countries, states or cities) it is usually a great idea to build your structure in the system. Once you group your Customers by Territories, you can set annual targets for each Item Group and get reports that will show your actual performance in the territory v/s what you had planned.
|
||||
|
||||
### Sales Person
|
||||
|
||||
Sales Persons behave exactly like Territories. You can great an organization chart of Sales Persons where each Sales Person’s target can be set individually. Again as in Territory, the target has to be set against Item Group.
|
||||
|
||||
### Sales Partner
|
||||
|
||||
A Sales Partner is a third party distributor / dealer / commission agent / affiliate / reseller who sells the companies products, for a commission. This is useful if you make the end sale to the Customer, involving your Sales Partner.
|
||||
|
||||
If you sell to your Sales Partner who in-turn sells it to the Customer, then you must make a Customer instead.
|
14
docs/docs.user.setup.first.md
Normal file
14
docs/docs.user.setup.first.md
Normal file
@ -0,0 +1,14 @@
|
||||
---
|
||||
{
|
||||
"_label": "Initial Setup"
|
||||
}
|
||||
---
|
||||
After a successful sign-up / installation of ERPNext, on your first sign-in, you will be shown a form to fill.
|
||||

|
||||
This form will create your first **Company** and **Fiscal Year** (accounting or financial year) record. You can create other Companies later.
|
||||
|
||||
This will also set the default **Currency** and time zone for your account. Once your complete this, your first **Company** and **Chart of Accounts** will be created.
|
||||
|
||||
Congrats! You are already on your way.
|
||||
|
||||
The next step is to configure your Chart of Accounts or start adding users and setting their permissions.
|
106
docs/docs.user.setup.item.md
Normal file
106
docs/docs.user.setup.item.md
Normal file
@ -0,0 +1,106 @@
|
||||
---
|
||||
{
|
||||
"_label": "Foundation: Item",
|
||||
"_title_image": "img/items.png"
|
||||
}
|
||||
---
|
||||
Items, Customers and Suppliers form the foundation of any ERP system.
|
||||
|
||||
It is very likely you will have your masters ready in another system or a spread sheet and you would just need to import them. Before importing, it might be a good idea to understand how ERPNext treats them a bit first.
|
||||
|
||||
---
|
||||
|
||||
## Items
|
||||
|
||||
An Item is simply a product or service you sell or buy from your Customers or Suppliers. ERPNext is optimized for itemized management of your sales and purchase though you can skip creating Items. If you are in services, you can create an Item for each service that your offer.
|
||||
|
||||
> Items are mandatory if you want to track inventory.
|
||||
|
||||
There are two main categories of Items in ERPNext
|
||||
|
||||
- Stock Items
|
||||
- Non Stock Items
|
||||
|
||||
As you may have guessed, inventory balances are tracked for stock items and not for
|
||||
non-stock items. Non-stock items could be services or consumables that are not tracked.
|
||||
|
||||
### Naming Items
|
||||
|
||||
This is a complex topic [coming up next]. In ERPNext you can use item codes or names. If you do not want to codify, you can keep the item name and item code as the same.
|
||||
|
||||
### Item Groups
|
||||
|
||||
ERPNext allows you to classify items into groups. This will help you in getting reports about various classes of items and also help in cataloging your items for the website.
|
||||
|
||||
### Warehouses
|
||||
|
||||
In ERPNext you can create Warehouses to identify where your Items reside.
|
||||
|
||||
There are two main Warehouse Types that are significant in ERPNext.
|
||||
|
||||
Stores: These are where your incoming Items are kept before they are consumed or sold. You can have as many “Stores” type Warehouses as you wish. Stores type warehouses are significant because if you set an Item for automatic re-order, ERPNext will check its quantities in all “Stores” type Warehouses when deciding whether to re-order or not.
|
||||
|
||||
Asset: Items marked as type “Fixed Asset” are maintained in Asset Type Warehouses. This helps you separate them for the Items that are consumed as a part of your regular operations or “Cost of Goods Sold”.
|
||||
|
||||
### Item Taxes
|
||||
|
||||
These settings are only required if this particular Item has a different tax rate than what is the rate defined in the standard tax Account.
|
||||
|
||||
For example, you have a tax Account, “VAT 10%” and this particular item is exempted from this tax, then you select “VAT 10%” in the first column, and set “0” as the tax rate in the second column.
|
||||
|
||||
### Inspection
|
||||
|
||||
Inspection Required: If an incoming inspection (at the time of delivery from the Supplier) is mandatory for this Item, mention “Inspection Required” as “Yes”. The system will ensure that a Quality Inspection will be prepared and approved before a Purchase Receipt is submitted.
|
||||
|
||||
Inspection Criterial: If a Quality Inspection is prepared for this Item, then this template of criteria can will automatically be updated in the Quality Inspection table of the Quality Inspection.
Examples of Criteria are: Weight, Length, Finish etc.
|
||||
|
||||
### Item Pricing and Price Lists
|
||||
|
||||
ERPNext lets you maintain multiple selling prices for an Item using Price Lists. A Price List is a name you can give to a set of Item prices.
|
||||

|
||||
Why would you want Price Lists? You have different prices for different zones (based on the shipping costs), for different currencies, regions etc.
|
||||
|
||||
### Item Valuation
|
||||
|
||||
How are Items Valued?
|
||||
|
||||
One of the major features of any inventory system is that you can find out the value of any item based on its historic or average price. You can also find the value of all your items for your balance sheet. Why is valuation important?
|
||||
|
||||
- The buying price fluctuates.
|
||||
- The value changes because of some process (value add).
|
||||
- The value changes because of decay, loss etc.
|
||||
|
||||
You may encounter these terms, so lets clarify:
|
||||
|
||||
- Rate: Rate at which the transaction takes place.
|
||||
- Valuation Rate: Rate at which the items value is set for your valuation.
|
||||
|
||||
There are two major ways in which ERPNext values your items.
|
||||
|
||||
- **FIFO (First In First Out):** In this system, ERPNext assumes that you will consume / sell those Items first that you bought first. For example, if you buy an Item at price X and then after a few days at price Y. So when sell your Item, ERPNext will reduce the quantity of the Item priced at X first and then Y.
|
||||
|
||||

|
||||
|
||||
- **Moving Average:** In this method, ERPNext assumes that the value of the item at any point is the average price of the units of that Item in stock. For example, if the value of an Item is X in a Warehouse with quantity Y and another quantity Y1 is added to the Warehouse at cost X1, the new value X2 would be:
|
||||
|
||||
> New Value X2 = (X * Y + X1 * Y1) / (Y + Y1)
|
||||
|
||||
#### Negative Stock
|
||||
|
||||
FIFO is the more accurate system of the two but has a disadvantage. You cannot have negative stock in FIFO. This means that you cannot make forward transactions that would make your stock negative. Why is this? Because sequences are so important to FIFO, you cannot track the value of the stock if it does not exist!
|
||||
|
||||
In Moving Average, since each item has an “average” value, the value of the negative stock is also based on this “average”.
|
||||
|
||||
### Serial Numbers and Batches
|
||||
|
||||
In scenarios where you may have to track individual units or batches of Items you sell, ERPNext allows you to manage Serial Numbers and Batches.
|
||||

|
||||
Why is this useful?
|
||||
|
||||
- To track warranty and returns.
|
||||
- To trace individual Items incase they are recalled by the Supplier.
|
||||
- To manage expiry.
|
||||
|
||||
In ERPNext, Serial Number and Batch are separate entities and all stock transactions for Items that serialized or batches must be tagged with either the Batch or Serial Number.
|
||||
|
||||
> Important: Once you mark an item as serialized or batched or neither, you cannot change it after you have make any stock entry.
|
19
docs/docs.user.setup.md
Normal file
19
docs/docs.user.setup.md
Normal file
@ -0,0 +1,19 @@
|
||||
---
|
||||
{
|
||||
"_label": "Setting Up",
|
||||
"_toc": [
|
||||
"docs.user.setup.before",
|
||||
"docs.user.setup.strategy",
|
||||
"docs.user.setup.first",
|
||||
"docs.user.setup.accounting",
|
||||
"docs.user.setup.item",
|
||||
"docs.user.setup.customer",
|
||||
"docs.user.setup.supplier"
|
||||
]
|
||||
}
|
||||
---
|
||||
Setting up an ERP system is like starting your business all over again, but in the virtual
|
||||
world. Thankfully its not as hard as the real thing and you get to do a trial too.
|
||||
|
||||
The important thing to note is that if you really want to take benefit out of this project, then you must take a step back and make sometime to do this right. This is usually not couple-of-hours after work kind of a project.
|
||||
|
30
docs/docs.user.setup.strategy.md
Normal file
30
docs/docs.user.setup.strategy.md
Normal file
@ -0,0 +1,30 @@
|
||||
---
|
||||
{
|
||||
"_label": "Implementation Strategy"
|
||||
}
|
||||
---
|
||||
Before you start managing your Operations in EPRNext, you must first become familiar with the system and the terms used. For this we recommend implementation should happen in two phases.
|
||||
|
||||
- A Test Phase, where you enter dummy records representing your day to day transactions and a - Live Phase, where we start entering live data.
|
||||
|
||||
### Test Phase
|
||||
|
||||
- Read the Manual
|
||||
- Create your first Customer, Supplier and Item. Add a few more so you get familiar.
|
||||
- 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)
|
||||
|
||||
> Tip: Use the 30-day free trial at [erpnext.com](https://erpnext.com) to do your test drive.
|
||||
|
||||
### Live Phase
|
||||
|
||||
Once you have made yourself familiar with ERPNext, start entering your live data!
|
||||
|
||||
- Clean up the account of test data or better, start a fresh install.
|
||||
- Setup all the modules with Customer Groups, Item Groups, Warehouses, BOMs etc.
|
||||
- Import Customers, Suppliers, Items, Contacts and Addresses using Data Import Tool.
|
||||
- Import opening stock using Stock Reconciliation Tool.
|
||||
- Create opening accounting entries via Journal Voucher and create outstanding Sales Invoices and Purchase Invoices.
|
||||
|
@ -1,20 +1,20 @@
|
||||
#activity-list .label {
|
||||
#page-activity .label {
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
margin-right: 7px;
|
||||
}
|
||||
|
||||
#activity-list .label-info {
|
||||
#page-activity .label-info {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#activity-list .user-info {
|
||||
#page-activity .user-info {
|
||||
float: right;
|
||||
color: #777;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
#activity-list .date-sep {
|
||||
#page-activity .date-sep {
|
||||
margin-bottom: 11px;
|
||||
padding: 5px 0px;
|
||||
border-bottom: 1px solid #aaa;
|
||||
|
@ -1,7 +0,0 @@
|
||||
<div class="layout-wrapper layout-wrapper-appframe">
|
||||
<div class="layout-appframe"></div>
|
||||
<div class="layout-main">
|
||||
<div id="activity-list">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -1,13 +1,15 @@
|
||||
wn.pages['activity'].onload = function(wrapper) {
|
||||
wrapper.appframe = new wn.ui.AppFrame($(wrapper).find('.layout-appframe'));
|
||||
wrapper.appframe.add_home_breadcrumb();
|
||||
wrapper.appframe.add_breadcrumb(wn.modules["Activity"].icon);
|
||||
wrapper.appframe.title('Activity');
|
||||
wn.ui.make_app_page({
|
||||
parent: wrapper,
|
||||
title: "Activity",
|
||||
single_column: true
|
||||
})
|
||||
wrapper.appframe.add_module_icon("Activity");
|
||||
|
||||
var list = new wn.ui.Listing({
|
||||
appframe: wrapper.appframe,
|
||||
method: 'home.page.activity.activity.get_feed',
|
||||
parent: $('#activity-list'),
|
||||
parent: $(wrapper).find(".layout-main"),
|
||||
render_row: function(row, data) {
|
||||
new erpnext.ActivityFeed(row, data);
|
||||
}
|
||||
@ -17,7 +19,7 @@ wn.pages['activity'].onload = function(wrapper) {
|
||||
// Build Report Button
|
||||
if(wn.boot.profile.can_get_report.indexOf("Feed")!=-1) {
|
||||
wrapper.appframe.add_button('Build Report', function() {
|
||||
wn.set_route('Report2', "Feed");
|
||||
wn.set_route('Report', "Feed");
|
||||
}, 'icon-th')
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
<div class="layout-wrapper layout-wrapper-appframe layout-attributions">
|
||||
<div class="appframe col col-lg-12 layout-attributions">
|
||||
<div class="layout-appframe"></div>
|
||||
<div class="layout-main">
|
||||
<h3>ERPNext is made using these Awesome Open Source Projects <i class="icon-heart" style="color: red"></i></h3>
|
||||
@ -94,6 +94,10 @@
|
||||
<td><a href="http://taitems.github.com/jQuery.Gantt/">JQuery.Gantt - Gantt Charts</a></td>
|
||||
<td>Draw Gantt charts with the famous jQuery ease of development.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="http://aehlke.github.io/tag-it/">JQuery Tag-it</a></td>
|
||||
<td>Simple and configurable tag editing widget with autocomplete support.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="http://jscolor.com/">JSColor - Color Picker</a></td>
|
||||
<td>HTML/Javascript Color Picker.</td>
|
||||
|
@ -3,7 +3,7 @@
|
||||
cur_frm.cscript = {
|
||||
onload: function(doc, dt, dn) {
|
||||
if(in_list(user_roles,'System Manager')) {
|
||||
cur_frm.page_layout.footer.help_area.innerHTML = '<hr>\
|
||||
cur_frm.footer.help_area.innerHTML = '<hr>\
|
||||
<p><a href="#Form/Jobs Email Settings">Jobs Email Settings</a><br>\
|
||||
<span class="help">Automatically extract Job Applicants from a mail box e.g. "jobs@example.com"</span></p>';
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ wn.module_page["HR"] = [
|
||||
},
|
||||
{
|
||||
label: wn._("Expense Claim"),
|
||||
description: wn._("Claims for expenses made on behalf of the organization."),
|
||||
description: wn._("Claims for company expense."),
|
||||
doctype:"Expense Claim"
|
||||
},
|
||||
{
|
||||
@ -33,7 +33,7 @@ wn.module_page["HR"] = [
|
||||
},
|
||||
{
|
||||
label: wn._("Job Applicant"),
|
||||
description: wn._("Applicant for a Job (extracted from jobs email)."),
|
||||
description: wn._("Applicant for a Job."),
|
||||
doctype:"Job Applicant"
|
||||
},
|
||||
]
|
||||
@ -175,7 +175,7 @@ wn.module_page["HR"] = [
|
||||
},
|
||||
{
|
||||
"label":wn._("Employee Information"),
|
||||
route: "Report2/Employee/Employee Information"
|
||||
route: "Report/Employee/Employee Information"
|
||||
},
|
||||
{
|
||||
"label":wn._("Monthly Salary Register"),
|
||||
|
@ -1,5 +1,4 @@
|
||||
import webnotes, webnotes.utils, os
|
||||
from webnotes.modules.export_file import export_to_files
|
||||
|
||||
def execute():
|
||||
webnotes.reload_doc("core", "doctype", "file_data")
|
||||
@ -71,4 +70,4 @@ def update_for_doc(doctype, doc):
|
||||
pass
|
||||
else:
|
||||
webnotes.conn.sql("""delete from `tabFile Data` where name=%s""",
|
||||
fileid)
|
||||
fileid)
|
||||
|
@ -1,10 +1,10 @@
|
||||
import webnotes
|
||||
|
||||
def execute():
|
||||
for dt, fieldname in \
|
||||
(("Journal Voucher Detail", "cost_center"),
|
||||
("Sales Taxes and Charges", "cost_center_other_charges"),
|
||||
("Purchase Taxes and Charges", "cost_center"), ("Delivery Note Item", "cost_center"),
|
||||
("Purchase Invoice Item", "cost_center"), ("Sales Invoice Item", "cost_center")):
|
||||
webnotes.conn.sql_ddl("""alter table `tab%s` alter `%s` drop default""" % (dt, fieldname))
|
||||
webnotes.reload_doc("Stock", "DocType", "Delivery Note Item")
|
||||
for dt in ("Journal Voucher Detail", "Sales Taxes and Charges",
|
||||
"Purchase Taxes and Charges", "Delivery Note Item",
|
||||
"Purchase Invoice Item", "Sales Invoice Item"):
|
||||
webnotes.conn.sql_ddl("""alter table `tab%s` alter `cost_center` drop default""" \
|
||||
% (dt,))
|
||||
webnotes.reload_doc(webnotes.conn.get_value("DocType", dt, "module"), "DocType", dt)
|
||||
|
@ -0,0 +1,9 @@
|
||||
import webnotes
|
||||
|
||||
def execute():
|
||||
webnotes.reload_doc("Accounts", "DocType", "Sales Taxes and Charges")
|
||||
webnotes.conn.sql("""update `tabSales Taxes and Charges`
|
||||
set cost_center = cost_center_other_charges""")
|
||||
webnotes.conn.sql_ddl("""alter table `tabSales Taxes and Charges`
|
||||
drop column cost_center_other_charges""")
|
||||
|
16
patches/may_2013/p01_selling_net_total_export.py
Normal file
16
patches/may_2013/p01_selling_net_total_export.py
Normal file
@ -0,0 +1,16 @@
|
||||
from __future__ import unicode_literals
|
||||
import webnotes
|
||||
from webnotes.utils import cint
|
||||
|
||||
def execute():
|
||||
for module, doctype in (("Accounts", "Sales Invoice"), ("Selling", "Sales Order"), ("Selling", "Quotation"),
|
||||
("Stock", "Delivery Note")):
|
||||
webnotes.reload_doc(module, "DocType", doctype)
|
||||
webnotes.conn.sql("""update `tab%s`
|
||||
set net_total_export = round(net_total / if(conversion_rate=0, 1, ifnull(conversion_rate, 1)), 2),
|
||||
other_charges_total_export = round(grand_total_export - net_total_export, 2)""" %
|
||||
(doctype,))
|
||||
|
||||
for module, doctype in (("Accounts", "Sales Invoice Item"), ("Selling", "Sales Order Item"), ("Selling", "Quotation Item"),
|
||||
("Stock", "Delivery Note Item")):
|
||||
webnotes.reload_doc(module, "DocType", doctype)
|
28
patches/may_2013/p06_make_notes.py
Normal file
28
patches/may_2013/p06_make_notes.py
Normal file
@ -0,0 +1,28 @@
|
||||
import webnotes, markdown2
|
||||
|
||||
def execute():
|
||||
webnotes.reload_doc("utilities", "doctype", "note")
|
||||
webnotes.reload_doc("utilities", "doctype", "note_user")
|
||||
|
||||
for question in webnotes.conn.sql("""select * from tabQuestion""", as_dict=True):
|
||||
if question.question:
|
||||
name = question.question[:180]
|
||||
if webnotes.conn.exists("Note", name):
|
||||
webnotes.delete_doc("Note", name)
|
||||
note = webnotes.bean({
|
||||
"doctype":"Note",
|
||||
"title": name,
|
||||
"content": "<hr>".join([markdown2.markdown(c) for c in webnotes.conn.sql_list("""
|
||||
select answer from tabAnswer where question=%s""", question.name)]),
|
||||
"owner": question.owner,
|
||||
"creation": question.creation,
|
||||
"public": 1
|
||||
}).insert()
|
||||
|
||||
webnotes.delete_doc("DocType", "Question")
|
||||
webnotes.delete_doc("DocType", "Answer")
|
||||
webnotes.bean("Style Settings").save()
|
||||
|
||||
# update comment delete
|
||||
webnotes.conn.sql("""update tabDocPerm \
|
||||
set cancel=1 where parent='Comment' and role='System Manager'""")
|
23
patches/may_2013/p07_move_update_stock_to_pos.py
Normal file
23
patches/may_2013/p07_move_update_stock_to_pos.py
Normal file
@ -0,0 +1,23 @@
|
||||
import webnotes, webnotes.defaults
|
||||
from webnotes.utils import cint
|
||||
|
||||
def execute():
|
||||
webnotes.reload_doc("accounts", "doctype", "pos_setting")
|
||||
|
||||
webnotes.conn.sql("""update `tabPOS Setting` set update_stock=%s""",
|
||||
cint(webnotes.defaults.get_global_default("update_stock")))
|
||||
|
||||
webnotes.conn.sql("""delete from `tabSingles`
|
||||
where doctype='Global Defaults' and field='update_stock'""")
|
||||
|
||||
webnotes.conn.sql("""delete from `tabDefaultValue`
|
||||
where parent='Control Panel' and defkey="update_stock" """)
|
||||
|
||||
webnotes.defaults.clear_cache("Control Panel")
|
||||
|
||||
webnotes.reload_doc("setup", "doctype", "global_defaults")
|
||||
|
||||
# previously, update_stock was valid only when is_pos was checked
|
||||
# henceforth it is valid, and hence the patch
|
||||
webnotes.conn.sql("""update `tabSales Invoice` set update_stock=0
|
||||
where ifnull(is_pos, 0)=0""")
|
23
patches/may_2013/p08_change_item_wise_tax.py
Normal file
23
patches/may_2013/p08_change_item_wise_tax.py
Normal file
@ -0,0 +1,23 @@
|
||||
import webnotes
|
||||
import json
|
||||
from webnotes.utils import flt
|
||||
|
||||
def execute():
|
||||
for doctype in ["Purchase Taxes and Charges", "Sales Taxes and Charges"]:
|
||||
for tax_name, item_wise_tax_detail in \
|
||||
webnotes.conn.sql("""select name, item_wise_tax_detail from `tab%s`""" % doctype):
|
||||
if not item_wise_tax_detail or not isinstance(item_wise_tax_detail, basestring):
|
||||
continue
|
||||
|
||||
try:
|
||||
json.loads(item_wise_tax_detail)
|
||||
except ValueError:
|
||||
out = {}
|
||||
for t in item_wise_tax_detail.split("\n"):
|
||||
if ":" in t:
|
||||
account_head, amount = t.split(":")
|
||||
out[account_head.strip()] = flt(amount.strip())
|
||||
|
||||
if out:
|
||||
webnotes.conn.sql("""update `tab%s` set item_wise_tax_detail=%s
|
||||
where name=%s""" % (doctype, "%s", "%s"), (json.dumps(out), tax_name))
|
@ -240,17 +240,21 @@ patch_list = [
|
||||
"patches.april_2013.p06_update_file_size",
|
||||
"patches.april_2013.p05_fixes_in_reverse_modules",
|
||||
"execute:webnotes.delete_doc('DocType Mapper', 'Delivery Note-Packing Slip')",
|
||||
"execute:webnotes.reload_doc('Stock', 'DocType', 'Delivery Note Item')",
|
||||
"patches.april_2013.p07_rename_cost_center_other_charges",
|
||||
"patches.april_2013.p06_default_cost_center",
|
||||
"execute:webnotes.reset_perms('File Data')",
|
||||
"patches.april_2013.p07_update_file_data_2",
|
||||
"patches.april_2013.rebuild_sales_browser",
|
||||
"patches.april_2013.p08_price_list_country",
|
||||
"patches.may_2013.p01_selling_net_total_export",
|
||||
"patches.may_2013.repost_stock_for_no_posting_time",
|
||||
"patches.may_2013.p01_conversion_factor_and_aii",
|
||||
"patches.may_2013.p02_update_valuation_rate",
|
||||
"patches.may_2013.p03_update_support_ticket",
|
||||
"patches.may_2013.p04_reorder_level",
|
||||
"patches.may_2013.p05_update_cancelled_gl_entries",
|
||||
"patches.may_2013.p06_make_notes",
|
||||
"patches.may_2013.p07_move_update_stock_to_pos",
|
||||
"patches.may_2013.p08_change_item_wise_tax",
|
||||
"patches.june_2013.p01_update_bom_exploded_items",
|
||||
]
|
@ -1,8 +1,8 @@
|
||||
[
|
||||
{
|
||||
"creation": "2013-01-29 19:25:50",
|
||||
"creation": "2013-03-07 11:55:07",
|
||||
"docstatus": 0,
|
||||
"modified": "2013-02-22 11:06:22",
|
||||
"modified": "2013-06-03 17:03:08",
|
||||
"modified_by": "Administrator",
|
||||
"owner": "Administrator"
|
||||
},
|
||||
@ -25,13 +25,20 @@
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 1,
|
||||
"create": 1,
|
||||
"doctype": "DocPerm",
|
||||
"name": "__common__",
|
||||
"parent": "Project",
|
||||
"parentfield": "permissions",
|
||||
"parenttype": "DocType",
|
||||
"permlevel": 0,
|
||||
"read": 1,
|
||||
"role": "Projects User"
|
||||
"report": 1,
|
||||
"role": "Projects User",
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"doctype": "DocType",
|
||||
@ -138,6 +145,19 @@
|
||||
"options": "Internal\nExternal\nOther",
|
||||
"search_index": 0
|
||||
},
|
||||
{
|
||||
"description": "Tasks belonging to this Project.",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "sb_tasks",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Tasks"
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "project_tasks",
|
||||
"fieldtype": "HTML",
|
||||
"label": "Project Tasks"
|
||||
},
|
||||
{
|
||||
"description": "Important dates and commitments in your project life cycle",
|
||||
"doctype": "DocField",
|
||||
@ -158,19 +178,6 @@
|
||||
"options": "Project Milestone",
|
||||
"search_index": 0
|
||||
},
|
||||
{
|
||||
"description": "Tasks belonging to this Project.",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "sb_tasks",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Tasks"
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "project_tasks",
|
||||
"fieldtype": "HTML",
|
||||
"label": "Project Tasks"
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "section_break0",
|
||||
@ -283,38 +290,6 @@
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "trash_reason",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 1,
|
||||
"label": "Trash Reason",
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "trash_reason",
|
||||
"oldfieldtype": "Small Text",
|
||||
"print_hide": 1,
|
||||
"search_index": 0
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "file_list",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 1,
|
||||
"label": "File List",
|
||||
"no_copy": 1,
|
||||
"print_hide": 1,
|
||||
"search_index": 0
|
||||
},
|
||||
{
|
||||
"cancel": 1,
|
||||
"create": 1,
|
||||
"doctype": "DocPerm",
|
||||
"permlevel": 0,
|
||||
"report": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"doctype": "DocPerm",
|
||||
"permlevel": 1
|
||||
"doctype": "DocPerm"
|
||||
}
|
||||
]
|
@ -34,7 +34,7 @@ erpnext.projects.Task = wn.ui.form.Controller.extend({
|
||||
}
|
||||
},
|
||||
|
||||
after_save: function() {
|
||||
validate: function() {
|
||||
this.frm.doc.project && wn.model.remove_from_locals("Project",
|
||||
this.frm.doc.project);
|
||||
},
|
||||
|
52
public/css/splash.css
Normal file
52
public/css/splash.css
Normal file
@ -0,0 +1,52 @@
|
||||
@-webkit-keyframes pulse {
|
||||
from {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
from {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.splash {
|
||||
margin: auto;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
bottom: 0px;
|
||||
width: 160px;
|
||||
height: 160px;
|
||||
text-align: center;
|
||||
color: #888;
|
||||
|
||||
animation-duration: 1s;
|
||||
animation-name: pulse;
|
||||
animation-iteration-count: infinite;
|
||||
animation-direction: alternate;
|
||||
|
||||
-webkit-animation-duration: 1s;
|
||||
-webkit-animation-name: pulse;
|
||||
-webkit-animation-iteration-count: infinite;
|
||||
-webkit-animation-direction: alternate;
|
||||
}
|
||||
|
||||
.splash:after {
|
||||
content: "erpnext";
|
||||
font-size: 30px;
|
||||
font-weight: 700;
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
position: relative;
|
||||
top: -20%;
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
171
public/images/splash.svg
Normal file
171
public/images/splash.svg
Normal file
@ -0,0 +1,171 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.2 r9819"
|
||||
sodipodi:docname="erpnext-logo-2013 - flat-ui-colors.svg"
|
||||
inkscape:export-filename="/Users/anandpdoshi/Dropbox/erpnext/logo 2013/erpnext-logo-2013 other colors 3.png"
|
||||
inkscape:export-xdpi="167.56363"
|
||||
inkscape:export-ydpi="167.56363"
|
||||
viewBox="0 0 680 820"
|
||||
preserveAspectRatio="xMidyMid meet">
|
||||
<defs
|
||||
id="defs4" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.53740115"
|
||||
inkscape:cx="523.82055"
|
||||
inkscape:cy="650.03526"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer5"
|
||||
showgrid="false"
|
||||
inkscape:snap-bbox="false"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true"
|
||||
inkscape:snap-global="true"
|
||||
inkscape:bbox-paths="false"
|
||||
inkscape:bbox-nodes="false"
|
||||
inkscape:snap-page="false"
|
||||
inkscape:snap-grids="true"
|
||||
inkscape:window-width="721"
|
||||
inkscape:window-height="690"
|
||||
inkscape:window-x="559"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="0">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid3836" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
style="display:inline">
|
||||
<rect
|
||||
style="opacity:0.90000000000000002;fill:#f39c12;fill-opacity:1"
|
||||
id="rect3800"
|
||||
width="150"
|
||||
height="150"
|
||||
x="60.000008"
|
||||
y="-472.36218"
|
||||
rx="20"
|
||||
ry="20"
|
||||
transform="scale(1,-1)" />
|
||||
</g>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer2"
|
||||
inkscape:label="Layer 2">
|
||||
<path
|
||||
transform="scale(1,-1)"
|
||||
style="display:inline;opacity:0.90000000000000002;fill:#f1c40f;fill-opacity:1"
|
||||
d="m 180,-372.36218 110,0 20,0 0,20 0,110 c 0,11.08 -8.92,20 -20,20 l -110,0 c -11.08,0 -20,-8.92 -20,-20 l 0,-110 c 0,-11.08 8.92,-20 20,-20 z"
|
||||
id="rect3051"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="scccssssss" />
|
||||
</g>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer3"
|
||||
inkscape:label="Layer 3">
|
||||
<rect
|
||||
style="display:inline;opacity:0.90000000000000002;fill:#2ecc71;fill-opacity:0.94117647"
|
||||
id="rect3840"
|
||||
width="150"
|
||||
height="150"
|
||||
x="260"
|
||||
y="-272.36218"
|
||||
rx="20"
|
||||
ry="20"
|
||||
transform="scale(1,-1)" />
|
||||
</g>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer4"
|
||||
inkscape:label="Layer 4">
|
||||
<path
|
||||
sodipodi:nodetypes="scccssssss"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3054"
|
||||
d="m 490,372.36218 -110,0 -20,0 0,-20 0,-110 c 0,-11.08 8.92,-20 20,-20 l 110,0 c 11.08,0 20,8.92 20,20 l 0,110 c 0,11.08 -8.92,20 -20,20 z"
|
||||
style="display:inline;opacity:0.90000000000000002;fill:#27ae60;fill-opacity:1" />
|
||||
</g>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer5"
|
||||
inkscape:label="Layer 5">
|
||||
<rect
|
||||
style="display:inline;opacity:0.90000000000000002;fill:#3498db;fill-opacity:0.94117647"
|
||||
id="rect3844"
|
||||
width="150"
|
||||
height="150"
|
||||
x="460"
|
||||
y="-472.36218"
|
||||
rx="20"
|
||||
ry="20"
|
||||
transform="scale(1,-1)" />
|
||||
</g>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer6"
|
||||
inkscape:label="Layer 6">
|
||||
<path
|
||||
style="display:inline;opacity:0.90000000000000002;fill:#2980b9;fill-opacity:0.94117647"
|
||||
d="m 490,422.36218 -110,0 -20,0 0,20 0,110 c 0,11.08 8.92,20 20,20 l 110,0 c 11.08,0 20,-8.92 20,-20 l 0,-110 c 0,-11.08 -8.92,-20 -20,-20 z"
|
||||
id="path3058"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="scccssssss" />
|
||||
</g>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer7"
|
||||
inkscape:label="Layer 7">
|
||||
<rect
|
||||
style="display:inline;opacity:0.90000000000000002;fill:#c0392b;fill-opacity:1"
|
||||
id="rect3848"
|
||||
width="150"
|
||||
height="150"
|
||||
x="260"
|
||||
y="-672.36218"
|
||||
rx="20"
|
||||
ry="20"
|
||||
transform="scale(1,-1)" />
|
||||
</g>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer8"
|
||||
inkscape:label="Layer 8">
|
||||
<path
|
||||
sodipodi:nodetypes="scccssssss"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3056"
|
||||
d="m 180,422.36218 110,0 20,0 0,20 0,110 c 0,11.08 -8.92,20 -20,20 l -110,0 c -11.08,0 -20,-8.92 -20,-20 l 0,-110 c 0,-11.08 8.92,-20 20,-20 z"
|
||||
style="display:inline;opacity:0.90000000000000002;fill:#e74c3c;fill-opacity:1" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 5.1 KiB |
@ -15,8 +15,8 @@ wn.modules_path = 'erpnext';
|
||||
|
||||
// add toolbar icon
|
||||
$(document).bind('toolbar_setup', function() {
|
||||
$('.brand').html((wn.boot.website_settings.brand_html || 'erpnext') +
|
||||
' <i class="icon-home icon-white navbar-icon-home" ></i>')
|
||||
$('.navbar-brand').html((wn.boot.website_settings.brand_html || 'erpnext') +
|
||||
' <i class="icon-home icon-white navbar-icon-home" ></i>')
|
||||
.css('max-width', '200px').css('overflow', 'hidden')
|
||||
.hover(function() {
|
||||
$(this).find('.icon-home').addClass('navbar-icon-home-hover');
|
||||
|
@ -1,158 +0,0 @@
|
||||
// ERPNext - web based ERP (http://erpnext.com)
|
||||
// Copyright (C) 2012 Web Notes Technologies Pvt Ltd
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// question toolbar
|
||||
// contains - voting widget / tag list and user info / timestamp
|
||||
// By XXXXXX on YYYYY
|
||||
|
||||
KBItemToolbar = function(args, kb) {
|
||||
$.extend(this, args);
|
||||
var me = this;
|
||||
this.make = function() {
|
||||
this.wrapper = $a(this.parent, 'div', '', {});
|
||||
this.line1 = $a(this.wrapper, 'div', '', {color: '#888', fontSize:'11px', margin:'7px 0px'});
|
||||
this.make_timestamp();
|
||||
this.make_answers();
|
||||
if(this.with_tags)
|
||||
this.make_tags();
|
||||
this.setup_del();
|
||||
}
|
||||
|
||||
this.make_timestamp = function() {
|
||||
this.line1.innerHTML = repl('By %(name)s | %(when)s', {
|
||||
name: wn.user_info(this.det.owner).fullname,
|
||||
when: wn.datetime.comment_when(this.det.modified)
|
||||
});
|
||||
|
||||
// allow system manager to delete questions / answers
|
||||
if(has_common(user_roles, ['Administrator', 'System Manager'])) {
|
||||
this.line1.innerHTML += ' | <a style="cursor:pointer;"\
|
||||
class="del-link">delete</a>';
|
||||
}
|
||||
}
|
||||
|
||||
this.make_answers = function() {
|
||||
if(this.doctype=='Question') {
|
||||
if(this.det.answers==0) {
|
||||
this.line1.innerHTML += ' | no answers';
|
||||
} else if(this.det.answers==1) {
|
||||
this.line1.innerHTML += ' | 1 answer';
|
||||
} else {
|
||||
this.line1.innerHTML += ' | '+this.det.answers+' answers';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.make_tags = function() {
|
||||
this.line1.innerHTML += ' | '
|
||||
this.tags_area = $a(this.line1, 'span', 'kb-tags')
|
||||
this.tags = new TagList(this.tags_area,
|
||||
this.det._user_tags && (this.det._user_tags.split(',')),
|
||||
this.doctype, this.det.name, 0, kb.set_tag_filter)
|
||||
}
|
||||
|
||||
this.setup_del = function() {
|
||||
$(this.line1).find('.del-link').click(function() {
|
||||
this.innerHTML = 'deleting...';
|
||||
this.disabled = 1;
|
||||
$c_page('utilities', 'questions', 'delete', {
|
||||
dt: me.doctype, dn: me.det.name}, function(r,rt) {
|
||||
// reload the list
|
||||
kb.list.run()
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
this.make();
|
||||
}
|
||||
|
||||
|
||||
// displays an editable text,
|
||||
// needs parent, text, disp_class, inp_class
|
||||
// dt, dn
|
||||
|
||||
EditableText = function(args) {
|
||||
$.extend(this, args);
|
||||
var me = this;
|
||||
|
||||
me.$w = $(repl('<div class="ed-text">\
|
||||
<div class="ed-text-display %(disp_class)s"></div>\
|
||||
<a class="ed-text-edit" style="cursor: pointer; float: right; margin-top: -16px;">[edit]</a>\
|
||||
<textarea class="ed-text-input %(inp_class)s hide"></textarea>\
|
||||
<div class="help hide"><br>Formatted as <a href="#markdown-reference"\
|
||||
target="_blank">markdown</a></div>\
|
||||
<button class="btn btn-info hide ed-text-save">Save</button>\
|
||||
<a class="ed-text-cancel hide" style="cursor: pointer;">Cancel</a>\
|
||||
</div>', args)).appendTo(me.parent);
|
||||
|
||||
this.set_display = function(txt) {
|
||||
var display_wrapper = me.$w.find('.ed-text-display');
|
||||
display_wrapper.html(wn.markdown(txt));
|
||||
display_wrapper.find("a").attr("target", "blank");
|
||||
me.text = txt;
|
||||
}
|
||||
|
||||
this.set_display(me.text);
|
||||
|
||||
if(me.height) me.$w.find('.ed-text-input').css('height', me.height);
|
||||
if(me.width) me.$w.find('.ed-text-input').css('width', me.width);
|
||||
|
||||
// edit
|
||||
me.$w.find('.ed-text-edit').click(function() {
|
||||
me.$w.find('.ed-text-input').val(me.text);
|
||||
me.show_as_input();
|
||||
})
|
||||
|
||||
// save button - save the new text
|
||||
me.$w.find('.ed-text-save').click(
|
||||
function() {
|
||||
var v = me.$w.find('.ed-text-input').val();
|
||||
// check if text is written
|
||||
if(!v) {
|
||||
msgprint('Please write something!');
|
||||
return;
|
||||
}
|
||||
var btn = this;
|
||||
$(btn).set_working();
|
||||
$c_page('utilities', 'question_view', 'update_item', {
|
||||
dt: me.dt, dn: me.dn, fn: me.fieldname, text: v
|
||||
},
|
||||
function(r) {
|
||||
$(btn).done_working();
|
||||
if(r.exc) {msgprint(r.exc); return; }
|
||||
me.set_display(v);
|
||||
me.show_as_text();
|
||||
});
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
// cancel button
|
||||
me.$w.find('.ed-text-cancel').click(function() {
|
||||
me.show_as_text();
|
||||
})
|
||||
|
||||
this.show_as_text = function() {
|
||||
me.$w.find('.ed-text-display, .ed-text-edit').toggle(true);
|
||||
me.$w.find('.ed-text-input, .ed-text-save, .ed-text-cancel, .help').toggle(false);
|
||||
}
|
||||
|
||||
this.show_as_input = function() {
|
||||
me.$w.find('.ed-text-display, .ed-text-edit').toggle(false);
|
||||
me.$w.find('.ed-text-input, .ed-text-save, .ed-text-cancel, .help').toggle(true);
|
||||
}
|
||||
|
||||
}
|
@ -4,7 +4,6 @@ h1, h2, h3, h4, h5 {
|
||||
|
||||
body {
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
span, div, td, input, textarea, button, select {
|
||||
|
@ -18,75 +18,26 @@
|
||||
wn.provide('erpnext.toolbar');
|
||||
|
||||
erpnext.toolbar.setup = function() {
|
||||
// modules
|
||||
erpnext.toolbar.add_modules();
|
||||
|
||||
// profile
|
||||
$('#toolbar-user').append('<li><a href="#Form/Profile/'+user+'">'
|
||||
var $user = $('#toolbar-user');
|
||||
$user.append('<li><a href="#Form/Profile/'+user+'">'
|
||||
+wn._("My Settings")+'...</a></li>');
|
||||
$user.append('<li class="divider"></li>');
|
||||
$user.append('<li><a href="https://erpnext.com/manual" target="_blank">'
|
||||
+wn._('Documentation')+'</a></li>')
|
||||
$user.append('<li><a href="http://groups.google.com/group/erpnext-user-forum" target="_blank">'
|
||||
+wn._('Forum')+'</a></li>')
|
||||
$user.append('<li><a href="http://www.providesupport.com?messenger=iwebnotes" target="_blank">\
|
||||
'+wn._('Live Chat')+'</a></li>')
|
||||
|
||||
|
||||
$('.navbar .pull-right').append('\
|
||||
<li><a href="#!messages" title="'+wn._('Unread Messages')
|
||||
+'"><span class="navbar-new-comments"></span></a></li>');
|
||||
|
||||
// help
|
||||
$('.navbar .pull-right').prepend('<li class="dropdown">\
|
||||
<a class="dropdown-toggle" data-toggle="dropdown" href="#" \
|
||||
onclick="return false;">'+wn._('Help')+'<b class="caret"></b></a>\
|
||||
<ul class="dropdown-menu" id="toolbar-help">\
|
||||
</ul></li>')
|
||||
|
||||
$('#toolbar-help').append('<li><a href="https://erpnext.com/manual" target="_blank">'
|
||||
+wn._('Documentation')+'</a></li>')
|
||||
|
||||
$('#toolbar-help').append('<li><a href="http://groups.google.com/group/erpnext-user-forum" target="_blank">'
|
||||
+wn._('Forum')+'</a></li>')
|
||||
|
||||
$('#toolbar-help').append('<li><a href="http://www.providesupport.com?messenger=iwebnotes" target="_blank">\
|
||||
'+wn._('Live Chat')+'</a></li>')
|
||||
|
||||
erpnext.toolbar.set_new_comments();
|
||||
}
|
||||
|
||||
erpnext.toolbar.add_modules = function() {
|
||||
$('<li class="dropdown">\
|
||||
<a class="dropdown-toggle" data-toggle="dropdown" href="#"\
|
||||
title="'+wn._("Modules")+'"\
|
||||
onclick="return false;"><i class="icon-th"></i></a>\
|
||||
<ul class="dropdown-menu modules">\
|
||||
</ul>\
|
||||
</li>').prependTo('.navbar .nav:first');
|
||||
|
||||
var modules_list = wn.user.get_desktop_items().sort();
|
||||
|
||||
var _get_list_item = function(m) {
|
||||
args = {
|
||||
module: m,
|
||||
module_page: wn.modules[m].link,
|
||||
module_label: wn._(wn.modules[m].label || m),
|
||||
icon: wn.modules[m].icon
|
||||
}
|
||||
|
||||
return repl('<li><a href="#!%(module_page)s" \
|
||||
data-module="%(module)s"><i class="%(icon)s" style="display: inline-block; \
|
||||
width: 21px; margin-top: -2px; margin-left: -7px;"></i>\
|
||||
%(module_label)s</a></li>', args);
|
||||
}
|
||||
|
||||
// add to dropdown
|
||||
$.each(modules_list,function(i, m) {
|
||||
if(m!='Setup') {
|
||||
$('.navbar .modules').append(_get_list_item(m));
|
||||
}
|
||||
})
|
||||
|
||||
// setup for system manager
|
||||
if(user_roles.indexOf("System Manager")!=-1) {
|
||||
$('.navbar .modules').append('<li class="divider">' + _get_list_item("Setup"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
erpnext.toolbar.set_new_comments = function(new_comments) {
|
||||
var navbar_nc = $('.navbar-new-comments');
|
||||
if(cint(new_comments)) {
|
||||
|
510
public/js/transaction.js
Normal file
510
public/js/transaction.js
Normal file
@ -0,0 +1,510 @@
|
||||
// ERPNext - web based ERP (http://erpnext.com)
|
||||
// Copyright (C) 2012 Web Notes Technologies Pvt Ltd
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
wn.provide("erpnext");
|
||||
|
||||
erpnext.TransactionController = wn.ui.form.Controller.extend({
|
||||
onload: function() {
|
||||
if(this.frm.doc.__islocal) {
|
||||
var me = this,
|
||||
today = get_today(),
|
||||
currency = wn.defaults.get_default("currency");
|
||||
|
||||
$.each({
|
||||
posting_date: today,
|
||||
due_date: today,
|
||||
transaction_date: today,
|
||||
currency: currency,
|
||||
price_list_currency: currency,
|
||||
status: "Draft",
|
||||
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);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
refresh: function() {
|
||||
this.frm.clear_custom_buttons();
|
||||
erpnext.hide_naming_series();
|
||||
this.show_item_wise_taxes();
|
||||
this.frm.fields_dict.currency ? this.currency() : this.set_dynamic_labels();
|
||||
},
|
||||
|
||||
onload_post_render: function() {
|
||||
if(this.frm.doc.__islocal && this.frm.doc.company) {
|
||||
var me = this;
|
||||
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.frm.refresh();
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
validate: function() {
|
||||
this.calculate_taxes_and_totals();
|
||||
},
|
||||
|
||||
company: function() {
|
||||
if(this.frm.doc.company) {
|
||||
var me = this;
|
||||
var company_currency = this.get_company_currency();
|
||||
$.each(["currency", "price_list_currency"], function(i, fieldname) {
|
||||
if(!me.doc[fieldname]) {
|
||||
me.frm.set_value(fieldname, company_currency);
|
||||
}
|
||||
});
|
||||
this.price_list_currency();
|
||||
}
|
||||
},
|
||||
|
||||
get_company_currency: function() {
|
||||
return erpnext.get_currency(this.frm.doc.company);
|
||||
},
|
||||
|
||||
currency: function() {
|
||||
this.price_list_currency();
|
||||
},
|
||||
|
||||
price_list_name: function(use_for) {
|
||||
var me = this;
|
||||
if(this.frm.doc.price_list_name) {
|
||||
this.frm.call({
|
||||
method: "setup.utils.get_price_list_currency",
|
||||
args: {args: {
|
||||
price_list_name: this.frm.doc.price_list_name,
|
||||
use_for: use_for
|
||||
}},
|
||||
callback: function(r) {
|
||||
if(!r.exc) {
|
||||
me.price_list_currency();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
price_list_currency: function() {
|
||||
// What TODO? should we make price list system non-mandatory?
|
||||
this.frm.toggle_reqd("plc_conversion_rate",
|
||||
!!(this.frm.doc.price_list_name && this.frm.doc.price_list_currency));
|
||||
|
||||
if(this.frm.doc.price_list_currency === this.get_company_currency()) {
|
||||
this.frm.set_value("plc_conversion_rate", 1.0);
|
||||
} else if(this.frm.doc.price_list_currency === this.frm.doc.currency) {
|
||||
this.frm.set_value("plc_conversion_rate", this.frm.doc.conversion_rate);
|
||||
}
|
||||
this.set_dynamic_labels();
|
||||
},
|
||||
|
||||
plc_conversion_rate: function() {
|
||||
this.price_list_currency();
|
||||
},
|
||||
|
||||
conversion_rate: function() {
|
||||
this.price_list_currency();
|
||||
this.calculate_taxes_and_totals();
|
||||
},
|
||||
|
||||
qty: function(doc, cdt, cdn) {
|
||||
this.calculate_taxes_and_totals();
|
||||
},
|
||||
|
||||
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 {
|
||||
this.validate_on_previous_row(tax);
|
||||
this.calculate_taxes_and_totals();
|
||||
} catch(e) {
|
||||
tax.row_id = null;
|
||||
refresh_field("row_id", tax.name, tax.parentfield);
|
||||
throw e;
|
||||
}
|
||||
},
|
||||
|
||||
recalculate: function() {
|
||||
this.calculate_taxes_and_totals();
|
||||
},
|
||||
|
||||
recalculate_values: function() {
|
||||
this.calculate_taxes_and_totals();
|
||||
},
|
||||
|
||||
calculate_charges: function() {
|
||||
this.calculate_taxes_and_totals();
|
||||
},
|
||||
|
||||
included_in_print_rate: function(doc, cdt, cdn) {
|
||||
var tax = wn.model.get_doc(cdt, cdn);
|
||||
try {
|
||||
this.validate_on_previous_row(tax);
|
||||
this.validate_inclusive_tax(tax);
|
||||
this.calculate_taxes_and_totals();
|
||||
} catch(e) {
|
||||
tax.included_in_print_rate = 0;
|
||||
refresh_field("included_in_print_rate", tax.name, tax.parentfield);
|
||||
throw e;
|
||||
}
|
||||
},
|
||||
|
||||
validate_on_previous_row: function(tax) {
|
||||
// validate if a valid row id is mentioned in case of
|
||||
// On Previous Row Amount and On Previous Row Total
|
||||
if((["On Previous Row Amount", "On Previous Row Total"].indexOf(tax.charge_type) != -1) &&
|
||||
(!tax.row_id || cint(tax.row_id) >= tax.idx)) {
|
||||
var msg = repl(wn._("Row") + " # %(idx)s [%(doctype)s]: " +
|
||||
wn._("Please specify a valid") + " %(row_id_label)s", {
|
||||
idx: tax.idx,
|
||||
doctype: tax.doctype,
|
||||
row_id_label: wn.meta.get_label(tax.doctype, "row_id", tax.name)
|
||||
});
|
||||
msgprint(msg);
|
||||
throw msg;
|
||||
}
|
||||
},
|
||||
|
||||
validate_inclusive_tax: function(tax) {
|
||||
if(!this.frm.tax_doclist) this.frm.tax_doclist = this.get_tax_doclist();
|
||||
|
||||
var actual_type_error = function() {
|
||||
var msg = repl(wn._("For row") + " # %(idx)s [%(doctype)s]: " +
|
||||
"%(charge_type_label)s = \"%(charge_type)s\" " +
|
||||
wn._("cannot be included in Item's rate"), {
|
||||
idx: tax.idx,
|
||||
doctype: tax.doctype,
|
||||
charge_type_label: wn.meta.get_label(tax.doctype, "charge_type", tax.name),
|
||||
charge_type: tax.charge_type
|
||||
});
|
||||
msgprint(msg);
|
||||
throw msg;
|
||||
};
|
||||
|
||||
var on_previous_row_error = function(row_range) {
|
||||
var msg = repl(wn._("For row") + " # %(idx)s [%(doctype)s]: " +
|
||||
wn._("to be included in Item's rate, it is required that: ") +
|
||||
" [" + wn._("Row") + " # %(row_range)s] " + wn._("also be included in Item's rate"), {
|
||||
idx: tax.idx,
|
||||
doctype: tax.doctype,
|
||||
charge_type_label: wn.meta.get_label(tax.doctype, "charge_type", tax.name),
|
||||
charge_type: tax.charge_type,
|
||||
inclusive_label: wn.meta.get_label(tax.doctype, "included_in_print_rate", tax.name),
|
||||
row_range: row_range,
|
||||
});
|
||||
|
||||
msgprint(msg);
|
||||
throw msg;
|
||||
};
|
||||
|
||||
if(cint(tax.included_in_print_rate)) {
|
||||
if(tax.charge_type == "Actual") {
|
||||
// inclusive tax cannot be of type Actual
|
||||
actual_type_error();
|
||||
} else if(tax.charge_type == "On Previous Row Amount" &&
|
||||
!cint(this.frm.tax_doclist[tax.row_id - 1].included_in_print_rate)) {
|
||||
// referred row should also be an inclusive tax
|
||||
on_previous_row_error(tax.row_id);
|
||||
} else if(tax.charge_type == "On Previous Row Total") {
|
||||
var taxes_not_included = $.map(this.frm.tax_doclist.slice(0, tax.row_id),
|
||||
function(t) { return cint(t.included_in_print_rate) ? null : t; });
|
||||
if(taxes_not_included.length > 0) {
|
||||
// all rows above this tax should be inclusive
|
||||
on_previous_row_error(tax.row_id == 1 ? "1" : "1 - " + tax.row_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_load_item_tax_rate: function(item_tax_rate) {
|
||||
return item_tax_rate ? JSON.parse(item_tax_rate) : {};
|
||||
},
|
||||
|
||||
_get_tax_rate: function(tax, item_tax_map) {
|
||||
return (keys(item_tax_map).indexOf(tax.account_head) != -1) ?
|
||||
flt(item_tax_map[tax.account_head], precision("rate", tax)) :
|
||||
tax.rate;
|
||||
},
|
||||
|
||||
get_item_wise_taxes_html: function() {
|
||||
var item_tax = {};
|
||||
var tax_accounts = [];
|
||||
var company_currency = this.get_company_currency();
|
||||
|
||||
$.each(this.get_tax_doclist(), function(i, tax) {
|
||||
var tax_amount_precision = precision("tax_amount", tax);
|
||||
var tax_rate_precision = precision("rate", tax);
|
||||
$.each(JSON.parse(tax.item_wise_tax_detail || '{}'),
|
||||
function(item_code, tax_data) {
|
||||
if(!item_tax[item_code]) item_tax[item_code] = {};
|
||||
if($.isArray(tax_data)) {
|
||||
var tax_rate = tax_data[0] == null ? "" : (flt(tax_data[0], tax_rate_precision) + "%"),
|
||||
tax_amount = format_currency(flt(tax_data[1], tax_amount_precision), company_currency);
|
||||
|
||||
item_tax[item_code][tax.account_head] = [tax_rate, tax_amount];
|
||||
} else {
|
||||
item_tax[item_code][tax.account_head] = [flt(tax_data, tax_rate_precision) + "%", ""];
|
||||
}
|
||||
});
|
||||
tax_accounts.push(tax.account_head);
|
||||
});
|
||||
|
||||
var headings = $.map([wn._("Item Name")].concat(tax_accounts),
|
||||
function(head) { return '<th style="min-width: 100px;">' + (head || "") + "</th>" }).join("\n");
|
||||
|
||||
var rows = $.map(this.get_item_doclist(), function(item) {
|
||||
var item_tax_record = item_tax[item.item_code || item.item_name];
|
||||
if(!item_tax_record) { return null; }
|
||||
return repl("<tr><td>%(item_name)s</td>%(taxes)s</tr>", {
|
||||
item_name: item.item_name,
|
||||
taxes: $.map(tax_accounts, function(head) {
|
||||
return "<td>(" + item_tax_record[head][0] + ") " + item_tax_record[head][1] + "</td>"
|
||||
}).join("\n")
|
||||
});
|
||||
}).join("\n");
|
||||
|
||||
if(!rows) return "";
|
||||
return '<div style="overflow-x: scroll;"><table class="table table-bordered table-hover">\
|
||||
<thead><tr>' + headings + '</tr></thead> \
|
||||
<tbody>' + rows + '</tbody> \
|
||||
</table></div>';
|
||||
},
|
||||
|
||||
set_default_values: function() {
|
||||
$.each(wn.model.get_doclist(this.frm.doctype, this.frm.docname), function(i, doc) {
|
||||
var updated = wn.model.set_default_values(doc);
|
||||
if(doc.parentfield) {
|
||||
refresh_field(doc.parentfield);
|
||||
} else {
|
||||
refresh_field(updated);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
_validate_before_fetch: function(fieldname) {
|
||||
var me = this;
|
||||
if(!me.frm.doc[fieldname]) {
|
||||
return (wn._("Please specify") + ": " +
|
||||
wn.meta.get_label(me.frm.doc.doctype, fieldname, me.frm.doc.name) +
|
||||
". " + wn._("It is needed to fetch Item Details."));
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
validate_company_and_party: function(party_field) {
|
||||
var me = this;
|
||||
var valid = true;
|
||||
var msg = "";
|
||||
$.each(["company", party_field], function(i, fieldname) {
|
||||
var msg_for_fieldname = me._validate_before_fetch(fieldname);
|
||||
if(msg_for_fieldname) {
|
||||
msgprint(msg_for_fieldname);
|
||||
valid = false;
|
||||
}
|
||||
});
|
||||
return valid;
|
||||
},
|
||||
|
||||
get_item_doclist: function() {
|
||||
return wn.model.get_doclist(this.frm.doc.doctype, this.frm.doc.name,
|
||||
{parentfield: this.fname});
|
||||
},
|
||||
|
||||
get_tax_doclist: function() {
|
||||
return wn.model.get_doclist(this.frm.doc.doctype, this.frm.doc.name,
|
||||
{parentfield: this.other_fname});
|
||||
},
|
||||
|
||||
validate_conversion_rate: function() {
|
||||
this.frm.doc.conversion_rate = flt(this.frm.doc.conversion_rate, precision("conversion_rate"));
|
||||
var conversion_rate_label = wn.meta.get_label(this.frm.doc.doctype, "conversion_rate",
|
||||
this.frm.doc.name);
|
||||
|
||||
if(this.frm.doc.conversion_rate == 0) {
|
||||
wn.throw(wn._(conversion_rate_label) + " " + wn._("cannot be 0"));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
},
|
||||
|
||||
calculate_taxes_and_totals: function() {
|
||||
this.validate_conversion_rate();
|
||||
this.frm.item_doclist = this.get_item_doclist();
|
||||
this.frm.tax_doclist = this.get_tax_doclist();
|
||||
|
||||
this.calculate_item_values();
|
||||
this.initialize_taxes();
|
||||
this.determine_exclusive_rate && this.determine_exclusive_rate();
|
||||
this.calculate_net_total();
|
||||
this.calculate_taxes();
|
||||
this.calculate_totals();
|
||||
this._cleanup();
|
||||
|
||||
this.show_item_wise_taxes();
|
||||
},
|
||||
|
||||
initialize_taxes: function() {
|
||||
var me = this;
|
||||
$.each(this.frm.tax_doclist, function(i, tax) {
|
||||
tax.item_wise_tax_detail = {};
|
||||
$.each(["tax_amount", "total",
|
||||
"tax_amount_for_current_item", "grand_total_for_current_item",
|
||||
"tax_fraction_for_current_item", "grand_total_fraction_for_current_item"],
|
||||
function(i, fieldname) { tax[fieldname] = 0.0 });
|
||||
|
||||
me.validate_on_previous_row(tax);
|
||||
me.validate_inclusive_tax(tax);
|
||||
wn.model.round_floats_in(tax);
|
||||
});
|
||||
},
|
||||
|
||||
calculate_taxes: function() {
|
||||
var me = this;
|
||||
|
||||
$.each(this.frm.item_doclist, function(n, item) {
|
||||
var item_tax_map = me._load_item_tax_rate(item.item_tax_rate);
|
||||
|
||||
$.each(me.frm.tax_doclist, function(i, tax) {
|
||||
// tax_amount represents the amount of tax for the current step
|
||||
var current_tax_amount = me.get_current_tax_amount(item, tax, item_tax_map);
|
||||
|
||||
me.set_item_tax_amount && me.set_item_tax_amount(item, tax, current_tax_amount);
|
||||
|
||||
// case when net total is 0 but there is an actual type charge
|
||||
// in this case add the actual amount to tax.tax_amount
|
||||
// and tax.grand_total_for_current_item for the first such iteration
|
||||
if(tax.charge_type == "Actual" &&
|
||||
!(current_tax_amount || me.frm.doc.net_total || tax.tax_amount)) {
|
||||
var zero_net_total_adjustment = flt(tax.rate, precision("tax_amount", tax));
|
||||
current_tax_amount += zero_net_total_adjustment;
|
||||
}
|
||||
|
||||
// store tax_amount for current item as it will be used for
|
||||
// charge type = 'On Previous Row Amount'
|
||||
tax.tax_amount_for_current_item = current_tax_amount;
|
||||
|
||||
// accumulate tax amount into tax.tax_amount
|
||||
tax.tax_amount += current_tax_amount;
|
||||
|
||||
// for buying
|
||||
if(tax.category) {
|
||||
// if just for valuation, do not add the tax amount in total
|
||||
// hence, setting it as 0 for further steps
|
||||
current_tax_amount = (tax.category == "Valuation") ? 0.0 : current_tax_amount;
|
||||
|
||||
current_tax_amount *= (tax.add_deduct_tax == "Deduct") ? -1.0 : 1.0;
|
||||
}
|
||||
|
||||
// Calculate tax.total viz. grand total till that step
|
||||
// note: grand_total_for_current_item contains the contribution of
|
||||
// item's amount, previously applied tax and the current tax on that item
|
||||
if(i==0) {
|
||||
tax.grand_total_for_current_item = flt(item.amount + current_tax_amount,
|
||||
precision("total", tax));
|
||||
} else {
|
||||
tax.grand_total_for_current_item =
|
||||
flt(me.frm.tax_doclist[i-1].grand_total_for_current_item + current_tax_amount,
|
||||
precision("total", tax));
|
||||
}
|
||||
|
||||
// in tax.total, accumulate grand total for each item
|
||||
tax.total += tax.grand_total_for_current_item;
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
get_current_tax_amount: function(item, tax, item_tax_map) {
|
||||
var tax_rate = this._get_tax_rate(tax, item_tax_map);
|
||||
var current_tax_amount = 0.0;
|
||||
|
||||
if(tax.charge_type == "Actual") {
|
||||
// distribute the tax amount proportionally to each item row
|
||||
var actual = flt(tax.rate, precision("tax_amount", tax));
|
||||
current_tax_amount = this.frm.doc.net_total ?
|
||||
((item.amount / this.frm.doc.net_total) * actual) :
|
||||
0.0;
|
||||
|
||||
} else if(tax.charge_type == "On Net Total") {
|
||||
current_tax_amount = (tax_rate / 100.0) * item.amount;
|
||||
|
||||
} else if(tax.charge_type == "On Previous Row Amount") {
|
||||
current_tax_amount = (tax_rate / 100.0) *
|
||||
this.frm.tax_doclist[cint(tax.row_id) - 1].tax_amount_for_current_item;
|
||||
|
||||
} else if(tax.charge_type == "On Previous Row Total") {
|
||||
current_tax_amount = (tax_rate / 100.0) *
|
||||
this.frm.tax_doclist[cint(tax.row_id) - 1].grand_total_for_current_item;
|
||||
|
||||
}
|
||||
|
||||
current_tax_amount = flt(current_tax_amount, precision("tax_amount", tax));
|
||||
|
||||
// store tax breakup for each item
|
||||
tax.item_wise_tax_detail[item.item_code || item.item_name] = [tax_rate, current_tax_amount];
|
||||
|
||||
return current_tax_amount;
|
||||
},
|
||||
|
||||
_cleanup: function() {
|
||||
$.each(this.frm.tax_doclist, function(i, tax) {
|
||||
$.each(["tax_amount_for_current_item", "grand_total_for_current_item",
|
||||
"tax_fraction_for_current_item", "grand_total_fraction_for_current_item"],
|
||||
function(i, fieldname) { delete tax[fieldname]; });
|
||||
|
||||
tax.item_wise_tax_detail = JSON.stringify(tax.item_wise_tax_detail);
|
||||
});
|
||||
},
|
||||
|
||||
calculate_total_advance: function(parenttype, advance_parentfield) {
|
||||
if(this.frm.doc.doctype == parenttype && this.frm.doc.docstatus < 2) {
|
||||
var advance_doclist = wn.model.get_doclist(this.frm.doc.doctype, this.frm.doc.name,
|
||||
{parentfield: advance_parentfield});
|
||||
this.frm.doc.total_advance = flt(wn.utils.sum(
|
||||
$.map(advance_doclist, function(adv) { return adv.allocated_amount })
|
||||
), precision("total_advance"));
|
||||
|
||||
this.calculate_outstanding_amount();
|
||||
}
|
||||
},
|
||||
|
||||
_set_in_company_currency: function(item, print_field, base_field) {
|
||||
// set values in base currency
|
||||
item[base_field] = flt(item[print_field] * this.frm.doc.conversion_rate,
|
||||
precision(base_field, item));
|
||||
},
|
||||
});
|
@ -13,8 +13,7 @@
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
wn.provide('erpnext.utils');
|
||||
wn.provide("erpnext.utils");
|
||||
|
||||
erpnext.get_currency = function(company) {
|
||||
if(!company && cur_frm)
|
||||
@ -23,4 +22,4 @@ erpnext.get_currency = function(company) {
|
||||
return wn.model.get(":Company", company).default_currency || wn.boot.sysdefaults.currency;
|
||||
else
|
||||
return wn.boot.sysdefaults.currency;
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -50,7 +50,7 @@ cur_frm.cscript.onload = function(doc, cdt, cdn) {
|
||||
}
|
||||
|
||||
if(in_list(user_roles,'System Manager')) {
|
||||
cur_frm.page_layout.footer.help_area.innerHTML = '<hr>\
|
||||
cur_frm.footer.help_area.innerHTML = '<hr>\
|
||||
<p><a href="#Form/Sales Email Settings">Sales Email Settings</a><br>\
|
||||
<span class="help">Automatically extract Leads from a mail box e.g. "sales@example.com"</span></p>';
|
||||
}
|
||||
|
@ -21,109 +21,52 @@ cur_frm.cscript.other_fname = "other_charges";
|
||||
cur_frm.cscript.sales_team_fname = "sales_team";
|
||||
|
||||
// =====================================================================================
|
||||
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/selling/doctype/sales_common/sales_common.js');
|
||||
|
||||
// ONLOAD
|
||||
// ===================================================================================
|
||||
cur_frm.cscript.onload = function(doc, cdt, cdn) {
|
||||
cur_frm.cscript.manage_rounded_total();
|
||||
if(!doc.quotation_to)
|
||||
hide_field(['customer','customer_address','contact_person','customer_name','lead',
|
||||
'address_display', 'contact_display', 'contact_mobile', 'contact_email',
|
||||
'territory', 'customer_group']);
|
||||
if(!doc.price_list_name) set_multiple(cdt,cdn,{price_list_name:sys_defaults.price_list_name});
|
||||
if(!doc.status) set_multiple(cdt,cdn,{status:'Draft'});
|
||||
if(!doc.transaction_date) set_multiple(cdt,cdn,{transaction_date:get_today()});
|
||||
if(!doc.conversion_rate) set_multiple(cdt,cdn,{conversion_rate:'1.00'});
|
||||
if(!doc.currency && sys_defaults.currency) set_multiple(cdt,cdn,{currency:sys_defaults.currency});
|
||||
if(!doc.price_list_currency) set_multiple(cdt, cdn, {price_list_currency: doc.currency, plc_conversion_rate: 1});
|
||||
|
||||
if(!doc.company && sys_defaults.company) set_multiple(cdt,cdn,{company:sys_defaults.company});
|
||||
if(!doc.fiscal_year && sys_defaults.fiscal_year) set_multiple(cdt,cdn,{fiscal_year:sys_defaults.fiscal_year});
|
||||
|
||||
if(doc.quotation_to) {
|
||||
if(doc.quotation_to == 'Customer') {
|
||||
hide_field('lead');
|
||||
erpnext.selling.QuotationController = erpnext.selling.SellingController.extend({
|
||||
refresh: function(doc, dt, dn) {
|
||||
this._super();
|
||||
|
||||
if(doc.docstatus == 1 && doc.status!='Order Lost') {
|
||||
cur_frm.add_custom_button('Make Sales Order', cur_frm.cscript['Make Sales Order']);
|
||||
cur_frm.add_custom_button('Set as Lost', cur_frm.cscript['Declare Order Lost']);
|
||||
cur_frm.add_custom_button('Send SMS', cur_frm.cscript.send_sms);
|
||||
}
|
||||
else if (doc.quotation_to == 'Lead') {
|
||||
hide_field(['customer', 'customer_address', 'contact_person', 'customer_group']);
|
||||
|
||||
if (!doc.__islocal) {
|
||||
cur_frm.communication_view = new wn.views.CommunicationList({
|
||||
list: wn.model.get("Communication", {"quotation": doc.name}),
|
||||
parent: cur_frm.fields_dict.communication_html.wrapper,
|
||||
doc: doc,
|
||||
recipients: doc.contact_email
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cur_frm.cscript.onload_post_render = function(doc, dt, dn) {
|
||||
var callback = function(doc, dt, dn) {
|
||||
// defined in sales_common.js
|
||||
cur_frm.cscript.update_item_details(doc, dt, dn);
|
||||
}
|
||||
cur_frm.cscript.hide_price_list_currency(doc, dt, dn, callback);
|
||||
}
|
||||
|
||||
// hide - unhide fields based on lead or customer..
|
||||
// =======================================================================================================================
|
||||
cur_frm.cscript.lead_cust_show = function(doc,cdt,cdn){
|
||||
hide_field(['lead', 'customer','customer_address','contact_person',
|
||||
'customer_name','address_display','contact_display','contact_mobile','contact_email',
|
||||
'territory','customer_group']);
|
||||
if(doc.quotation_to == 'Lead') unhide_field(['lead']);
|
||||
else if(doc.quotation_to == 'Customer') unhide_field(['customer']);
|
||||
|
||||
this.quotation_to();
|
||||
},
|
||||
|
||||
doc.lead = doc.customer = doc.customer_name = doc.customer_address = doc.contact_person = doc.address_display = doc.contact_display = doc.contact_mobile = doc.contact_email = doc.territory = doc.customer_group = "";
|
||||
}
|
||||
|
||||
|
||||
|
||||
//================ hide - unhide fields on basis of quotation to either lead or customer ===============================
|
||||
cur_frm.cscript.quotation_to = function(doc,cdt,cdn){
|
||||
cur_frm.cscript.lead_cust_show(doc,cdt,cdn);
|
||||
}
|
||||
|
||||
|
||||
// REFRESH
|
||||
// ===================================================================================
|
||||
cur_frm.cscript.refresh = function(doc, cdt, cdn) {
|
||||
cur_frm.clear_custom_buttons();
|
||||
|
||||
if (!cur_frm.cscript.is_onload) cur_frm.cscript.hide_price_list_currency(doc, cdt, cdn);
|
||||
|
||||
|
||||
if(doc.docstatus == 1 && doc.status!='Order Lost') {
|
||||
cur_frm.add_custom_button('Make Sales Order', cur_frm.cscript['Make Sales Order']);
|
||||
cur_frm.add_custom_button('Set as Lost', cur_frm.cscript['Declare Order Lost']);
|
||||
cur_frm.add_custom_button('Send SMS', cur_frm.cscript.send_sms);
|
||||
}
|
||||
|
||||
erpnext.hide_naming_series();
|
||||
cur_frm.toggle_display("contact_section", doc.customer || doc.lead);
|
||||
quotation_to: function() {
|
||||
this.frm.toggle_reqd("lead", this.frm.doc.quotation_to == "Lead");
|
||||
this.frm.toggle_reqd("customer", this.frm.doc.quotation_to == "Customer");
|
||||
},
|
||||
|
||||
if (!doc.__islocal) {
|
||||
cur_frm.communication_view = new wn.views.CommunicationList({
|
||||
list: wn.model.get("Communication", {"quotation": doc.name}),
|
||||
parent: cur_frm.fields_dict.communication_html.wrapper,
|
||||
doc: doc,
|
||||
recipients: doc.contact_email
|
||||
});
|
||||
}
|
||||
}
|
||||
validate_company_and_party: function(party_field) {
|
||||
if(this.frm.doc.quotation_to == "Lead") {
|
||||
return true;
|
||||
} else 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 {
|
||||
return this._super(party_field);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
//customer
|
||||
cur_frm.cscript.customer = function(doc,dt,dn) {
|
||||
var pl = doc.price_list_name;
|
||||
var callback = function(r,rt) {
|
||||
var doc = locals[cur_frm.doctype][cur_frm.docname];
|
||||
cur_frm.refresh();
|
||||
if (pl != doc.price_list_name) cur_frm.cscript.price_list_name(doc, dt, dn);
|
||||
}
|
||||
|
||||
if(doc.customer) $c_obj(make_doclist(doc.doctype, doc.name),
|
||||
'get_default_customer_address', '', callback);
|
||||
if(doc.customer) unhide_field(['customer_address','contact_person','territory', 'customer_group']);
|
||||
cur_frm.toggle_display("contact_section", doc.customer || doc.lead);
|
||||
console.log(doc.customer_group);
|
||||
}
|
||||
// for backward compatibility: combine new and previous states
|
||||
$.extend(cur_frm.cscript, new erpnext.selling.QuotationController({frm: cur_frm}));
|
||||
|
||||
cur_frm.cscript.customer_address = cur_frm.cscript.contact_person = function(doc,dt,dn) {
|
||||
if(doc.customer) get_server_fields('get_customer_address', JSON.stringify({
|
||||
@ -136,8 +79,6 @@ cur_frm.cscript.customer_address = cur_frm.cscript.contact_person = function(doc
|
||||
cur_frm.fields_dict.lead.get_query = erpnext.utils.lead_query;
|
||||
|
||||
cur_frm.cscript.lead = function(doc, cdt, cdn) {
|
||||
cur_frm.toggle_display("contact_section", doc.customer || doc.lead);
|
||||
|
||||
if(doc.lead) {
|
||||
get_server_fields('get_lead_details', doc.lead,'', doc, cdt, cdn, 1);
|
||||
unhide_field('territory');
|
||||
@ -248,31 +189,6 @@ cur_frm.cscript['Declare Order Lost'] = function(){
|
||||
qtn_lost_dialog.show();
|
||||
}
|
||||
|
||||
//===================== Quotation to validation - either customer or lead mandatory ====================
|
||||
cur_frm.cscript.quot_to_validate = function(doc,cdt,cdn){
|
||||
|
||||
if(doc.quotation_to == 'Lead'){
|
||||
|
||||
if(!doc.lead){
|
||||
alert("Lead is mandatory.");
|
||||
validated = false;
|
||||
}
|
||||
}
|
||||
else if(doc.quotation_to == 'Customer'){
|
||||
if(!doc.customer){
|
||||
alert("Customer is mandatory.");
|
||||
validated = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//===================validation function =================================
|
||||
|
||||
cur_frm.cscript.validate = function(doc,cdt,cdn){
|
||||
cur_frm.cscript.recalculate_values(doc, cdt, cdn);
|
||||
cur_frm.cscript.quot_to_validate(doc,cdt,cdn);
|
||||
}
|
||||
|
||||
//================ Last Quoted Price and Last Sold Price suggestion ======================
|
||||
cur_frm.fields_dict['quotation_details'].grid.get_field('item_code').get_query= function(doc, cdt, cdn) {
|
||||
var d = locals[cdt][cdn];
|
||||
|
@ -95,17 +95,6 @@ class DocType(SellingController):
|
||||
def get_rate(self,arg):
|
||||
return get_obj('Sales Common').get_rate(arg)
|
||||
|
||||
# Load Default Charges
|
||||
# ----------------------------------------------------------
|
||||
def load_default_taxes(self):
|
||||
self.doclist = get_obj('Sales Common').load_default_taxes(self)
|
||||
|
||||
# Pull details from other charges master (Get Sales Taxes and Charges Master)
|
||||
# ----------------------------------------------------------
|
||||
def get_other_charges(self):
|
||||
self.doclist = get_obj('Sales Common').get_other_charges(self)
|
||||
|
||||
|
||||
# GET TERMS AND CONDITIONS
|
||||
# ====================================================================================
|
||||
def get_tc_details(self):
|
||||
@ -142,6 +131,8 @@ class DocType(SellingController):
|
||||
#do not allow sales item in maintenance quotation and service item in sales quotation
|
||||
#-----------------------------------------------------------------------------------------------
|
||||
def validate_order_type(self):
|
||||
super(DocType, self).validate_order_type()
|
||||
|
||||
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)
|
||||
|
@ -1,8 +1,8 @@
|
||||
[
|
||||
{
|
||||
"creation": "2013-05-22 12:10:46",
|
||||
"creation": "2013-05-24 19:29:08",
|
||||
"docstatus": 0,
|
||||
"modified": "2013-05-22 16:54:07",
|
||||
"modified": "2013-05-28 14:50:59",
|
||||
"modified_by": "Administrator",
|
||||
"owner": "Administrator"
|
||||
},
|
||||
@ -78,6 +78,7 @@
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.quotation_to == \"Customer\"",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "customer",
|
||||
"fieldtype": "Link",
|
||||
@ -92,6 +93,7 @@
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.quotation_to == \"Lead\"",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "lead",
|
||||
"fieldtype": "Link",
|
||||
@ -105,19 +107,21 @@
|
||||
"read_only": 0
|
||||
},
|
||||
{
|
||||
"depends_on": "customer",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "customer_name",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"hidden": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Customer Name",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "customer",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "address_display",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 1,
|
||||
"hidden": 0,
|
||||
"in_filter": 0,
|
||||
"label": "Address",
|
||||
"oldfieldname": "customer_address",
|
||||
@ -128,29 +132,32 @@
|
||||
"search_index": 0
|
||||
},
|
||||
{
|
||||
"depends_on": "customer",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "contact_display",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 1,
|
||||
"hidden": 0,
|
||||
"in_filter": 0,
|
||||
"label": "Contact",
|
||||
"print_hide": 0,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "customer",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "contact_mobile",
|
||||
"fieldtype": "Text",
|
||||
"hidden": 1,
|
||||
"hidden": 0,
|
||||
"label": "Mobile No",
|
||||
"print_hide": 0,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "customer",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "contact_email",
|
||||
"fieldtype": "Text",
|
||||
"hidden": 1,
|
||||
"hidden": 0,
|
||||
"label": "Contact Email",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
@ -214,6 +221,7 @@
|
||||
"oldfieldtype": "Table",
|
||||
"options": "Quotation Item",
|
||||
"read_only": 0,
|
||||
"reqd": 1,
|
||||
"width": "40px"
|
||||
},
|
||||
{
|
||||
@ -239,11 +247,19 @@
|
||||
"oldfieldname": "net_total",
|
||||
"oldfieldtype": "Currency",
|
||||
"options": "Company:company:default_currency",
|
||||
"print_hide": 0,
|
||||
"print_hide": 1,
|
||||
"read_only": 1,
|
||||
"reqd": 0,
|
||||
"width": "100px"
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "net_total_export",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Net Total (Export)",
|
||||
"options": "currency",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "recalculate_values",
|
||||
@ -423,13 +439,22 @@
|
||||
"doctype": "DocField",
|
||||
"fieldname": "other_charges_total",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Taxes and Charges Total*",
|
||||
"label": "Taxes and Charges Total",
|
||||
"oldfieldname": "other_charges_total",
|
||||
"oldfieldtype": "Currency",
|
||||
"options": "Company:company:default_currency",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "other_charges_total_export",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Taxes and Charges Total (Export)",
|
||||
"options": "currency",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "other_charges_calculation",
|
||||
@ -596,6 +621,7 @@
|
||||
"read_only": 0
|
||||
},
|
||||
{
|
||||
"depends_on": "customer",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "col_break98",
|
||||
"fieldtype": "Column Break",
|
||||
@ -649,6 +675,7 @@
|
||||
"search_index": 0
|
||||
},
|
||||
{
|
||||
"depends_on": "customer",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "customer_group",
|
||||
"fieldtype": "Link",
|
||||
@ -819,19 +846,6 @@
|
||||
"read_only": 1,
|
||||
"width": "150px"
|
||||
},
|
||||
{
|
||||
"description": "The date at which current entry is corrected in the system.",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "amendment_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Amendment Date",
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "amendment_date",
|
||||
"oldfieldtype": "Date",
|
||||
"print_hide": 1,
|
||||
"read_only": 0,
|
||||
"width": "100px"
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "communication_history",
|
||||
@ -868,23 +882,28 @@
|
||||
"cancel": 1,
|
||||
"create": 1,
|
||||
"doctype": "DocPerm",
|
||||
"role": "Sales User",
|
||||
"role": "Sales Manager",
|
||||
"submit": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"doctype": "DocPerm",
|
||||
"role": "Customer"
|
||||
},
|
||||
{
|
||||
"amend": 1,
|
||||
"cancel": 1,
|
||||
"create": 1,
|
||||
"doctype": "DocPerm",
|
||||
"role": "Sales Manager",
|
||||
"role": "Sales User",
|
||||
"submit": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 0,
|
||||
"doctype": "DocPerm",
|
||||
"role": "Customer",
|
||||
"submit": 0,
|
||||
"write": 0
|
||||
},
|
||||
{
|
||||
"amend": 1,
|
||||
"cancel": 1,
|
||||
|
@ -2,7 +2,7 @@
|
||||
{
|
||||
"creation": "2013-03-07 11:42:57",
|
||||
"docstatus": 0,
|
||||
"modified": "2013-05-22 12:08:32",
|
||||
"modified": "2013-05-22 12:10:32",
|
||||
"modified_by": "Administrator",
|
||||
"owner": "Administrator"
|
||||
},
|
||||
@ -119,7 +119,7 @@
|
||||
"options": "currency",
|
||||
"print_hide": 1,
|
||||
"print_width": "100px",
|
||||
"read_only": 0,
|
||||
"read_only": 1,
|
||||
"reqd": 0,
|
||||
"width": "100px"
|
||||
},
|
||||
@ -195,7 +195,7 @@
|
||||
"options": "Company:company:default_currency",
|
||||
"print_hide": 1,
|
||||
"print_width": "100px",
|
||||
"read_only": 0,
|
||||
"read_only": 1,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"width": "100px"
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -28,23 +28,6 @@ get_value = webnotes.conn.get_value
|
||||
|
||||
from utilities.transaction_base import TransactionBase
|
||||
|
||||
|
||||
@webnotes.whitelist()
|
||||
def get_comp_base_currency(arg=None):
|
||||
""" get default currency of company"""
|
||||
res = webnotes.conn.sql("""select default_currency from `tabCompany`
|
||||
where name = %s""", webnotes.form_dict.get('company'))
|
||||
return res and res[0][0] or None
|
||||
|
||||
@webnotes.whitelist()
|
||||
def get_price_list_currency(arg=None):
|
||||
""" Get all currency in which price list is maintained"""
|
||||
plc = webnotes.conn.sql("select distinct ref_currency from `tabItem Price` where price_list_name = %s", webnotes.form_dict['price_list'])
|
||||
plc = [d[0] for d in plc]
|
||||
base_currency = get_comp_base_currency(webnotes.form_dict['company'])
|
||||
return plc, base_currency
|
||||
|
||||
|
||||
class DocType(TransactionBase):
|
||||
def __init__(self,d,dl):
|
||||
self.doc, self.doclist = d,dl
|
||||
@ -65,26 +48,6 @@ class DocType(TransactionBase):
|
||||
|
||||
self.msg = []
|
||||
|
||||
|
||||
# Get Sales Person Details
|
||||
# ==========================
|
||||
|
||||
# TODO: To be deprecated if not in use
|
||||
def get_sales_person_details(self, obj):
|
||||
if obj.doc.doctype != 'Quotation':
|
||||
obj.doclist = obj.doc.clear_table(obj.doclist,'sales_team')
|
||||
idx = 0
|
||||
for d in webnotes.conn.sql("select sales_person, allocated_percentage, allocated_amount, incentives from `tabSales Team` where parent = '%s'" % obj.doc.customer):
|
||||
ch = addchild(obj.doc, 'sales_team', 'Sales Team', obj.doclist)
|
||||
ch.sales_person = d and cstr(d[0]) or ''
|
||||
ch.allocated_percentage = d and flt(d[1]) or 0
|
||||
ch.allocated_amount = d and flt(d[2]) or 0
|
||||
ch.incentives = d and flt(d[3]) or 0
|
||||
ch.idx = idx
|
||||
idx += 1
|
||||
return obj.doclist
|
||||
|
||||
|
||||
# Get customer's contact person details
|
||||
# ==============================================================
|
||||
def get_contact_details(self, obj = '', primary = 0):
|
||||
@ -117,182 +80,6 @@ class DocType(TransactionBase):
|
||||
if obj.doc.company:
|
||||
acc_head = webnotes.conn.sql("select name from `tabAccount` where name = '%s' and docstatus != 2" % (cstr(obj.doc.customer) + " - " + webnotes.conn.get_value('Company', obj.doc.company, 'abbr')))
|
||||
obj.doc.debit_to = acc_head and acc_head[0][0] or ''
|
||||
|
||||
|
||||
|
||||
# Get Item Details
|
||||
# ===============================================================
|
||||
def get_item_details(self, args, obj):
|
||||
import json
|
||||
if not obj.doc.price_list_name:
|
||||
msgprint("Please Select Price List before selecting Items", raise_exception=True)
|
||||
item = webnotes.conn.sql("""select description, item_name, brand, item_group, stock_uom,
|
||||
default_warehouse, default_income_account, default_sales_cost_center,
|
||||
purchase_account, description_html, barcode from `tabItem`
|
||||
where name = %s and (ifnull(end_of_life,'')='' or end_of_life > now()
|
||||
or end_of_life = '0000-00-00') and (is_sales_item = 'Yes'
|
||||
or is_service_item = 'Yes')""", args['item_code'], as_dict=1)
|
||||
|
||||
tax = webnotes.conn.sql("""select tax_type, tax_rate from `tabItem Tax`
|
||||
where parent = %s""", args['item_code'])
|
||||
t = {}
|
||||
for x in tax: t[x[0]] = flt(x[1])
|
||||
ret = {
|
||||
'description': item and item[0]['description_html'] or \
|
||||
item[0]['description'],
|
||||
'barcode': item and item[0]['barcode'] or '',
|
||||
'item_group': item and item[0]['item_group'] or '',
|
||||
'item_name': item and item[0]['item_name'] or '',
|
||||
'brand': item and item[0]['brand'] or '',
|
||||
'stock_uom': item and item[0]['stock_uom'] or '',
|
||||
'reserved_warehouse': item and item[0]['default_warehouse'] or '',
|
||||
'warehouse': item and item[0]['default_warehouse'] or \
|
||||
args.get('warehouse'),
|
||||
'income_account': item and item[0]['default_income_account'] or \
|
||||
args.get('income_account'),
|
||||
'expense_account': item and item[0]['purchase_account'] or \
|
||||
args.get('expense_account'),
|
||||
'cost_center': item and item[0]['default_sales_cost_center'] or \
|
||||
args.get('cost_center'),
|
||||
# this is done coz if item once fetched is fetched again than its qty shld be reset to 1
|
||||
'qty': 1.00,
|
||||
'adj_rate': 0,
|
||||
'amount': 0,
|
||||
'export_amount': 0,
|
||||
'item_tax_rate': json.dumps(t),
|
||||
'batch_no': ''
|
||||
}
|
||||
if(obj.doc.price_list_name and item): #this is done to fetch the changed BASIC RATE and REF RATE based on PRICE LIST
|
||||
base_ref_rate = self.get_ref_rate(args['item_code'], obj.doc.price_list_name, obj.doc.price_list_currency, obj.doc.plc_conversion_rate)
|
||||
ret['ref_rate'] = flt(base_ref_rate)/flt(obj.doc.conversion_rate)
|
||||
ret['export_rate'] = flt(base_ref_rate)/flt(obj.doc.conversion_rate)
|
||||
ret['base_ref_rate'] = flt(base_ref_rate)
|
||||
ret['basic_rate'] = flt(base_ref_rate)
|
||||
|
||||
if ret['warehouse'] or ret['reserved_warehouse']:
|
||||
|
||||
av_qty = self.get_available_qty({'item_code': args['item_code'], 'warehouse': ret['warehouse'] or ret['reserved_warehouse']})
|
||||
ret.update(av_qty)
|
||||
|
||||
# get customer code for given item from Item Customer Detail
|
||||
customer_item_code_row = webnotes.conn.sql("""\
|
||||
select ref_code from `tabItem Customer Detail`
|
||||
where parent = %s and customer_name = %s""",
|
||||
(args['item_code'], obj.doc.customer))
|
||||
if customer_item_code_row and customer_item_code_row[0][0]:
|
||||
ret['customer_item_code'] = customer_item_code_row[0][0]
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def get_item_defaults(self, args):
|
||||
item = webnotes.conn.sql("""select default_warehouse, default_income_account,
|
||||
default_sales_cost_center, purchase_account from `tabItem` where name = %s
|
||||
and (ifnull(end_of_life,'') = '' or end_of_life > now() or end_of_life = '0000-00-00')
|
||||
and (is_sales_item = 'Yes' or is_service_item = 'Yes') """,
|
||||
(args['item_code']), as_dict=1)
|
||||
ret = {
|
||||
'reserved_warehouse': item and item[0]['default_warehouse'] or '',
|
||||
'warehouse': item and item[0]['default_warehouse'] or args.get('warehouse'),
|
||||
'income_account': item and item[0]['default_income_account'] or \
|
||||
args.get('income_account'),
|
||||
'expense_account': item and item[0]['purchase_account'] or args.get('expense_account'),
|
||||
'cost_center': item and item[0]['default_sales_cost_center'] or args.get('cost_center'),
|
||||
}
|
||||
|
||||
return ret
|
||||
|
||||
def get_available_qty(self,args):
|
||||
tot_avail_qty = webnotes.conn.sql("select projected_qty, actual_qty from `tabBin` where item_code = '%s' and warehouse = '%s'" % (args['item_code'], args['warehouse']), as_dict=1)
|
||||
ret = {
|
||||
'projected_qty' : tot_avail_qty and flt(tot_avail_qty[0]['projected_qty']) or 0,
|
||||
'actual_qty' : tot_avail_qty and flt(tot_avail_qty[0]['actual_qty']) or 0
|
||||
}
|
||||
return ret
|
||||
|
||||
|
||||
# ***************** Get Ref rate as entered in Item Master ********************
|
||||
def get_ref_rate(self, item_code, price_list_name, price_list_currency, plc_conv_rate):
|
||||
ref_rate = webnotes.conn.sql("select ref_rate from `tabItem Price` where parent = %s and price_list_name = %s and ref_currency = %s and selling=1",
|
||||
(item_code, price_list_name, price_list_currency))
|
||||
base_ref_rate = ref_rate and flt(ref_rate[0][0]) * flt(plc_conv_rate) or 0
|
||||
return base_ref_rate
|
||||
|
||||
def get_barcode_details(self, barcode):
|
||||
item = webnotes.conn.sql("select name, end_of_life, is_sales_item, is_service_item \
|
||||
from `tabItem` where barcode = %s", barcode, as_dict=1)
|
||||
ret = {}
|
||||
if not item:
|
||||
msgprint("""No item found for this barcode: %s.
|
||||
May be barcode not updated in item master. Please check""" % barcode)
|
||||
elif item[0]['end_of_life'] and getdate(cstr(item[0]['end_of_life'])) < nowdate():
|
||||
msgprint("Item: %s has been expired. Please check 'End of Life' field in item master" % item[0]['name'])
|
||||
elif item[0]['is_sales_item'] == 'No' and item[0]['is_service_item'] == 'No':
|
||||
msgprint("Item: %s is not a sales or service item" % item[0]['name'])
|
||||
elif len(item) > 1:
|
||||
msgprint("There are multiple item for this barcode. \nPlease select item code manually")
|
||||
else:
|
||||
ret = {'item_code': item and item[0]['name'] or ''}
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
# ****** Re-cancellculates Basic Rate & amount based on Price List Selected ******
|
||||
def get_adj_percent(self, obj):
|
||||
for d in getlist(obj.doclist, obj.fname):
|
||||
base_ref_rate = self.get_ref_rate(d.item_code, obj.doc.price_list_name, obj.doc.price_list_currency, obj.doc.plc_conversion_rate)
|
||||
d.adj_rate = 0
|
||||
d.ref_rate = flt(base_ref_rate)/flt(obj.doc.conversion_rate)
|
||||
d.basic_rate = flt(base_ref_rate)
|
||||
d.base_ref_rate = flt(base_ref_rate)
|
||||
d.export_rate = flt(base_ref_rate)/flt(obj.doc.conversion_rate)
|
||||
d.amount = flt(d.qty)*flt(base_ref_rate)
|
||||
d.export_amount = flt(d.qty)*flt(base_ref_rate)/flt(obj.doc.conversion_rate)
|
||||
|
||||
|
||||
# Load Default Taxes
|
||||
# ====================
|
||||
def load_default_taxes(self, obj):
|
||||
if cstr(obj.doc.charge):
|
||||
return self.get_other_charges(obj)
|
||||
else:
|
||||
return self.get_other_charges(obj, 1)
|
||||
|
||||
|
||||
# Get other charges from Master
|
||||
# =================================================================================
|
||||
def get_other_charges(self,obj, default=0):
|
||||
obj.doclist = obj.doc.clear_table(obj.doclist, 'other_charges')
|
||||
if not getlist(obj.doclist, 'other_charges'):
|
||||
if default: add_cond = 'ifnull(t2.is_default,0) = 1'
|
||||
else: add_cond = 't1.parent = "'+cstr(obj.doc.charge)+'"'
|
||||
idx = 0
|
||||
other_charge = webnotes.conn.sql("""\
|
||||
select t1.*
|
||||
from
|
||||
`tabSales Taxes and Charges` t1,
|
||||
`tabSales Taxes and Charges Master` t2
|
||||
where
|
||||
t1.parent = t2.name and
|
||||
t2.company = '%s' and
|
||||
%s
|
||||
order by t1.idx""" % (obj.doc.company, add_cond), as_dict=1)
|
||||
from webnotes.model import default_fields
|
||||
for other in other_charge:
|
||||
# remove default fields like parent, parenttype etc.
|
||||
# from query results
|
||||
for field in default_fields:
|
||||
if field in other: del other[field]
|
||||
|
||||
d = addchild(obj.doc, 'other_charges', 'Sales Taxes and Charges',
|
||||
obj.doclist)
|
||||
d.fields.update(other)
|
||||
d.rate = flt(d.rate)
|
||||
d.tax_amount = flt(d.tax_rate)
|
||||
d.included_in_print_rate = cint(d.included_in_print_rate)
|
||||
d.idx = idx
|
||||
idx += 1
|
||||
return obj.doclist
|
||||
|
||||
# Get TERMS AND CONDITIONS
|
||||
# =======================================================================================
|
||||
@ -329,23 +116,6 @@ class DocType(TransactionBase):
|
||||
}
|
||||
return ret
|
||||
|
||||
# Get Commission rate
|
||||
# =======================================================================
|
||||
def get_comm_rate(self, sales_partner, obj):
|
||||
|
||||
comm_rate = webnotes.conn.sql("select commission_rate from `tabSales Partner` where name = '%s' and docstatus != 2" %(sales_partner), as_dict=1)
|
||||
if comm_rate:
|
||||
total_comm = flt(comm_rate[0]['commission_rate']) * flt(obj.doc.net_total) / 100
|
||||
ret = {
|
||||
'commission_rate' : comm_rate and flt(comm_rate[0]['commission_rate']) or 0,
|
||||
'total_commission' : flt(total_comm)
|
||||
}
|
||||
return ret
|
||||
else:
|
||||
msgprint("Business Associate : %s does not exist in the system." % (sales_partner))
|
||||
raise Exception
|
||||
|
||||
|
||||
# To verify whether rate entered in details table does not exceed max discount %
|
||||
# =======================================================================================
|
||||
def validate_max_discount(self,obj, detail_table):
|
||||
@ -354,16 +124,6 @@ class DocType(TransactionBase):
|
||||
if discount and discount[0]['max_discount'] and (flt(d.adj_rate)>flt(discount[0]['max_discount'])):
|
||||
msgprint("You cannot give more than " + cstr(discount[0]['max_discount']) + " % discount on Item Code : "+cstr(d.item_code))
|
||||
raise Exception
|
||||
|
||||
|
||||
# Get sum of allocated % of sales person (it should be 100%)
|
||||
# ========================================================================
|
||||
# it indicates % contribution of sales person in sales
|
||||
def get_allocated_sum(self,obj):
|
||||
sales_team_list = obj.doclist.get({"parentfield": "sales_team"})
|
||||
total_allocation = sum([flt(d.allocated_percentage) for d in sales_team_list])
|
||||
if sales_team_list and total_allocation != 100.0:
|
||||
msgprint("Total Allocated % of Sales Persons should be 100%", raise_exception=True)
|
||||
|
||||
# Check Conversion Rate (i.e. it will not allow conversion rate to be 1 for Currency other than default currency set in Global Defaults)
|
||||
# ===========================================================================
|
||||
|
@ -26,105 +26,54 @@ 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');
|
||||
|
||||
|
||||
cur_frm.cscript.onload = function(doc, cdt, cdn) {
|
||||
cur_frm.cscript.manage_rounded_total();
|
||||
|
||||
if(!doc.status) set_multiple(cdt,cdn,{status:'Draft'});
|
||||
if(!doc.transaction_date) set_multiple(cdt,cdn,{transaction_date:get_today()});
|
||||
if(!doc.price_list_currency) set_multiple(cdt, cdn, {price_list_currency: doc.currency, plc_conversion_rate: 1});
|
||||
// load default charges
|
||||
|
||||
if(doc.__islocal && !doc.customer){
|
||||
hide_field(['customer_address','contact_person', 'customer_name',
|
||||
'address_display', 'contact_display', 'contact_mobile',
|
||||
'contact_email', 'territory', 'customer_group']);
|
||||
}
|
||||
}
|
||||
|
||||
cur_frm.cscript.onload_post_render = function(doc, cdt, cdn) {
|
||||
var callback = function(doc, cdt, cdn) {
|
||||
if(doc.__islocal) {
|
||||
// defined in sales_common.js
|
||||
cur_frm.cscript.update_item_details(doc, cdt, cdn);
|
||||
}
|
||||
}
|
||||
|
||||
cur_frm.cscript.hide_price_list_currency(doc, cdt, cdn, callback);
|
||||
|
||||
}
|
||||
|
||||
|
||||
cur_frm.cscript.refresh = function(doc, cdt, cdn) {
|
||||
cur_frm.clear_custom_buttons();
|
||||
erpnext.hide_naming_series();
|
||||
|
||||
if (!cur_frm.cscript.is_onload) cur_frm.cscript.hide_price_list_currency(doc, cdt, cdn);
|
||||
|
||||
cur_frm.toggle_display("contact_info", doc.customer);
|
||||
|
||||
if(doc.docstatus==1) {
|
||||
if(doc.status != 'Stopped') {
|
||||
cur_frm.add_custom_button('Send SMS', cur_frm.cscript.send_sms);
|
||||
// delivery note
|
||||
if(flt(doc.per_delivered, 2) < 100 && doc.order_type=='Sales')
|
||||
cur_frm.add_custom_button('Make Delivery', cur_frm.cscript['Make Delivery Note']);
|
||||
erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend({
|
||||
refresh: function(doc, dt, dn) {
|
||||
this._super();
|
||||
|
||||
if(doc.docstatus==1) {
|
||||
if(doc.status != 'Stopped') {
|
||||
cur_frm.add_custom_button('Send SMS', cur_frm.cscript.send_sms);
|
||||
// delivery note
|
||||
if(flt(doc.per_delivered, 2) < 100 && doc.order_type=='Sales')
|
||||
cur_frm.add_custom_button('Make Delivery', cur_frm.cscript['Make Delivery Note']);
|
||||
|
||||
// maintenance
|
||||
if(flt(doc.per_delivered, 2) < 100 && (doc.order_type !='Sales')) {
|
||||
cur_frm.add_custom_button('Make Maint. Visit', cur_frm.cscript.make_maintenance_visit);
|
||||
cur_frm.add_custom_button('Make Maint. Schedule', cur_frm.cscript['Make Maintenance Schedule']);
|
||||
// maintenance
|
||||
if(flt(doc.per_delivered, 2) < 100 && (doc.order_type !='Sales')) {
|
||||
cur_frm.add_custom_button('Make Maint. Visit', cur_frm.cscript.make_maintenance_visit);
|
||||
cur_frm.add_custom_button('Make Maint. Schedule', cur_frm.cscript['Make Maintenance Schedule']);
|
||||
}
|
||||
|
||||
// indent
|
||||
if(!doc.order_type || (doc.order_type == 'Sales'))
|
||||
cur_frm.add_custom_button('Make ' + wn._('Material Request'), cur_frm.cscript['Make Material Request']);
|
||||
|
||||
// sales invoice
|
||||
if(flt(doc.per_billed, 2) < 100)
|
||||
cur_frm.add_custom_button('Make Invoice', cur_frm.cscript['Make Sales Invoice']);
|
||||
|
||||
// stop
|
||||
if(flt(doc.per_delivered, 2) < 100 || doc.per_billed < 100)
|
||||
cur_frm.add_custom_button('Stop!', cur_frm.cscript['Stop Sales Order']);
|
||||
} else {
|
||||
// un-stop
|
||||
cur_frm.add_custom_button('Unstop', cur_frm.cscript['Unstop Sales Order']);
|
||||
}
|
||||
|
||||
// indent
|
||||
if(!doc.order_type || (doc.order_type == 'Sales'))
|
||||
cur_frm.add_custom_button('Make ' + wn._('Material Request'), cur_frm.cscript['Make Material Request']);
|
||||
|
||||
// sales invoice
|
||||
if(flt(doc.per_billed, 2) < 100)
|
||||
cur_frm.add_custom_button('Make Invoice', cur_frm.cscript['Make Sales Invoice']);
|
||||
|
||||
// stop
|
||||
if(flt(doc.per_delivered, 2) < 100 || doc.per_billed < 100)
|
||||
cur_frm.add_custom_button('Stop!', cur_frm.cscript['Stop Sales Order']);
|
||||
} else {
|
||||
// un-stop
|
||||
cur_frm.add_custom_button('Unstop', cur_frm.cscript['Unstop Sales Order']);
|
||||
}
|
||||
}
|
||||
|
||||
cur_frm.cscript.order_type(doc);
|
||||
}
|
||||
|
||||
cur_frm.cscript.order_type = function(doc) {
|
||||
if(doc.order_type == "Sales") {
|
||||
cur_frm.toggle_reqd("delivery_date", 1);
|
||||
} else {
|
||||
cur_frm.toggle_reqd("delivery_date", 0);
|
||||
}
|
||||
}
|
||||
|
||||
//customer
|
||||
cur_frm.cscript.customer = function(doc,dt,dn) {
|
||||
cur_frm.toggle_display("contact_info", doc.customer);
|
||||
this.order_type(doc);
|
||||
},
|
||||
|
||||
var pl = doc.price_list_name;
|
||||
var callback = function(r,rt) {
|
||||
var callback2 = function(r, rt) {
|
||||
if(doc.customer)
|
||||
unhide_field(['customer_address', 'contact_person', 'territory','customer_group']);
|
||||
cur_frm.refresh();
|
||||
|
||||
if(!onload && (pl != doc.price_list_name)) cur_frm.cscript.price_list_name(doc, dt, dn);
|
||||
order_type: function() {
|
||||
this.frm.toggle_reqd("delivery_date", this.frm.doc.order_type == "Sales");
|
||||
},
|
||||
|
||||
reserved_warehouse: function(doc, cdt, cdn) {
|
||||
this.warehouse(doc, cdt, cdn);
|
||||
},
|
||||
});
|
||||
|
||||
}
|
||||
var doc = locals[cur_frm.doctype][cur_frm.docname];
|
||||
get_server_fields('get_shipping_address',doc.customer,'',doc, dt, dn, 0, callback2);
|
||||
|
||||
}
|
||||
if(doc.customer) $c_obj(make_doclist(doc.doctype, doc.name),
|
||||
'get_default_customer_address', '', callback);
|
||||
}
|
||||
// for backward compatibility: combine new and previous states
|
||||
$.extend(cur_frm.cscript, new erpnext.selling.SalesOrderController({frm: cur_frm}));
|
||||
|
||||
cur_frm.cscript.customer_address = cur_frm.cscript.contact_person = function(doc,dt,dn) {
|
||||
if(doc.customer) get_server_fields('get_customer_address', JSON.stringify({customer: doc.customer, address: doc.customer_address, contact: doc.contact_person}),'', doc, dt, dn, 1);
|
||||
@ -180,15 +129,6 @@ cur_frm.fields_dict['quotation_no'].get_query = function(doc) {
|
||||
ORDER BY `tabQuotation`.`name` DESC LIMIT 50', {cond:cond});
|
||||
}
|
||||
|
||||
|
||||
cur_frm.cscript.reserved_warehouse = function(doc, cdt , cdn) {
|
||||
var d = locals[cdt][cdn];
|
||||
if (d.reserved_warehouse) {
|
||||
arg = "{'item_code':'" + d.item_code + "','warehouse':'" + d.reserved_warehouse +"'}";
|
||||
get_server_fields('get_available_qty',arg,'sales_order_details',doc,cdt,cdn,1);
|
||||
}
|
||||
}
|
||||
|
||||
//----------- make maintenance schedule----------
|
||||
cur_frm.cscript['Make Maintenance Schedule'] = function() {
|
||||
var doc = cur_frm.doc;
|
||||
|
@ -58,22 +58,6 @@ class DocType(SellingController):
|
||||
def get_comm_rate(self, sales_partner):
|
||||
return get_obj('Sales Common').get_comm_rate(sales_partner, self)
|
||||
|
||||
def get_item_details(self, args=None):
|
||||
import json
|
||||
args = args and json.loads(args) or {}
|
||||
if args.get('item_code'):
|
||||
return get_obj('Sales Common').get_item_details(args, self)
|
||||
else:
|
||||
obj = get_obj('Sales Common')
|
||||
for doc in self.doclist:
|
||||
if doc.fields.get('item_code'):
|
||||
arg = {'item_code':doc.fields.get('item_code'), 'income_account':doc.fields.get('income_account'),
|
||||
'cost_center': doc.fields.get('cost_center'), 'warehouse': doc.fields.get('warehouse')};
|
||||
ret = obj.get_item_defaults(arg)
|
||||
for r in ret:
|
||||
if not doc.fields.get(r):
|
||||
doc.fields[r] = ret[r]
|
||||
|
||||
def get_adj_percent(self, arg=''):
|
||||
get_obj('Sales Common').get_adj_percent(self)
|
||||
|
||||
@ -83,12 +67,6 @@ class DocType(SellingController):
|
||||
def get_rate(self,arg):
|
||||
return get_obj('Sales Common').get_rate(arg)
|
||||
|
||||
def load_default_taxes(self):
|
||||
self.doclist = get_obj('Sales Common').load_default_taxes(self)
|
||||
|
||||
def get_other_charges(self):
|
||||
self.doclist = get_obj('Sales Common').get_other_charges(self)
|
||||
|
||||
def get_tc_details(self):
|
||||
return get_obj('Sales Common').get_tc_details(self)
|
||||
|
||||
@ -194,6 +172,8 @@ class DocType(SellingController):
|
||||
and current Sales Order""" % (self.doc.order_type, d.prevdoc_docname))
|
||||
|
||||
def validate_order_type(self):
|
||||
super(DocType, self).validate_order_type()
|
||||
|
||||
#validate delivery date
|
||||
if self.doc.order_type == 'Sales' and not self.doc.delivery_date:
|
||||
msgprint("Please enter 'Expected Delivery Date'")
|
||||
@ -226,7 +206,6 @@ class DocType(SellingController):
|
||||
sales_com_obj.check_conversion_rate(self)
|
||||
|
||||
sales_com_obj.validate_max_discount(self,'sales_order_details')
|
||||
sales_com_obj.get_allocated_sum(self)
|
||||
self.doclist = sales_com_obj.make_packing_list(self,'sales_order_details')
|
||||
|
||||
if not self.doc.status:
|
||||
|
@ -1,8 +1,8 @@
|
||||
[
|
||||
{
|
||||
"creation": "2013-03-07 14:48:34",
|
||||
"creation": "2013-05-24 19:29:08",
|
||||
"docstatus": 0,
|
||||
"modified": "2013-01-29 17:14:58",
|
||||
"modified": "2013-05-28 15:05:38",
|
||||
"modified_by": "Administrator",
|
||||
"owner": "Administrator"
|
||||
},
|
||||
@ -76,42 +76,47 @@
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "customer",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "customer_name",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"hidden": 0,
|
||||
"label": "Name",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "customer",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "address_display",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 1,
|
||||
"hidden": 0,
|
||||
"label": "Address",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "customer",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "contact_display",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 1,
|
||||
"hidden": 0,
|
||||
"label": "Contact",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "customer",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "contact_mobile",
|
||||
"fieldtype": "Text",
|
||||
"hidden": 1,
|
||||
"hidden": 0,
|
||||
"label": "Mobile No",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "customer",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "contact_email",
|
||||
"fieldtype": "Text",
|
||||
"hidden": 1,
|
||||
"hidden": 0,
|
||||
"label": "Contact Email",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
@ -230,7 +235,8 @@
|
||||
"oldfieldname": "sales_order_details",
|
||||
"oldfieldtype": "Table",
|
||||
"options": "Sales Order Item",
|
||||
"print_hide": 0
|
||||
"print_hide": 0,
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
@ -251,11 +257,19 @@
|
||||
"oldfieldname": "net_total",
|
||||
"oldfieldtype": "Currency",
|
||||
"options": "Company:company:default_currency",
|
||||
"print_hide": 0,
|
||||
"print_hide": 1,
|
||||
"read_only": 1,
|
||||
"reqd": 0,
|
||||
"width": "150px"
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "net_total_export",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Net Total (Export)",
|
||||
"options": "currency",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "recalculate_values",
|
||||
@ -426,7 +440,7 @@
|
||||
"doctype": "DocField",
|
||||
"fieldname": "other_charges_total",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Taxes and Charges Total*",
|
||||
"label": "Taxes and Charges Total",
|
||||
"oldfieldname": "other_charges_total",
|
||||
"oldfieldtype": "Currency",
|
||||
"options": "Company:company:default_currency",
|
||||
@ -434,6 +448,15 @@
|
||||
"read_only": 1,
|
||||
"width": "150px"
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "other_charges_total_export",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Taxes and Charges Total (Export)",
|
||||
"options": "currency",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "other_charges_calculation",
|
||||
@ -578,6 +601,7 @@
|
||||
"print_hide": 0
|
||||
},
|
||||
{
|
||||
"depends_on": "customer",
|
||||
"doctype": "DocField",
|
||||
"fieldname": "contact_info",
|
||||
"fieldtype": "Section Break",
|
||||
@ -955,7 +979,6 @@
|
||||
"cancel": 0,
|
||||
"create": 0,
|
||||
"doctype": "DocPerm",
|
||||
"match": "",
|
||||
"permlevel": 1,
|
||||
"report": 0,
|
||||
"role": "Sales Manager",
|
||||
@ -978,7 +1001,6 @@
|
||||
"cancel": 1,
|
||||
"create": 1,
|
||||
"doctype": "DocPerm",
|
||||
"match": "",
|
||||
"permlevel": 0,
|
||||
"report": 1,
|
||||
"role": "Sales User",
|
||||
@ -990,7 +1012,6 @@
|
||||
"cancel": 0,
|
||||
"create": 0,
|
||||
"doctype": "DocPerm",
|
||||
"match": "",
|
||||
"permlevel": 1,
|
||||
"report": 0,
|
||||
"role": "Sales User",
|
||||
@ -1013,7 +1034,6 @@
|
||||
"cancel": 0,
|
||||
"create": 0,
|
||||
"doctype": "DocPerm",
|
||||
"match": "",
|
||||
"permlevel": 1,
|
||||
"role": "Maintenance Manager",
|
||||
"submit": 0
|
||||
@ -1034,7 +1054,6 @@
|
||||
"cancel": 0,
|
||||
"create": 0,
|
||||
"doctype": "DocPerm",
|
||||
"match": "",
|
||||
"permlevel": 1,
|
||||
"role": "Maintenance User",
|
||||
"submit": 0
|
||||
|
@ -2,7 +2,7 @@
|
||||
{
|
||||
"creation": "2013-03-07 11:42:58",
|
||||
"docstatus": 0,
|
||||
"modified": "2013-05-22 12:09:03",
|
||||
"modified": "2013-05-22 12:10:03",
|
||||
"modified_by": "Administrator",
|
||||
"owner": "Administrator"
|
||||
},
|
||||
@ -113,7 +113,7 @@
|
||||
"options": "currency",
|
||||
"print_hide": 1,
|
||||
"print_width": "70px",
|
||||
"read_only": 0,
|
||||
"read_only": 1,
|
||||
"reqd": 0,
|
||||
"width": "70px"
|
||||
},
|
||||
@ -183,7 +183,7 @@
|
||||
"options": "Company:company:default_currency",
|
||||
"print_hide": 1,
|
||||
"print_width": "100px",
|
||||
"read_only": 0,
|
||||
"read_only": 1,
|
||||
"reqd": 0,
|
||||
"width": "100px"
|
||||
},
|
||||
|
@ -1,8 +1,8 @@
|
||||
[
|
||||
{
|
||||
"creation": "2013-02-22 01:27:53",
|
||||
"creation": "2013-04-19 13:30:51",
|
||||
"docstatus": 0,
|
||||
"modified": "2013-03-07 07:03:31",
|
||||
"modified": "2013-05-21 17:04:45",
|
||||
"modified_by": "Administrator",
|
||||
"owner": "Administrator"
|
||||
},
|
||||
@ -42,6 +42,7 @@
|
||||
"doctype": "DocField",
|
||||
"fieldname": "sales_designation",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"label": "Designation",
|
||||
"oldfieldname": "sales_designation",
|
||||
"oldfieldtype": "Data",
|
||||
@ -63,7 +64,7 @@
|
||||
"doctype": "DocField",
|
||||
"fieldname": "allocated_percentage",
|
||||
"fieldtype": "Float",
|
||||
"label": "Allocated (%)",
|
||||
"label": "Contribution (%)",
|
||||
"oldfieldname": "allocated_percentage",
|
||||
"oldfieldtype": "Currency",
|
||||
"print_width": "100px",
|
||||
@ -74,11 +75,12 @@
|
||||
"doctype": "DocField",
|
||||
"fieldname": "allocated_amount",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Allocated Amount",
|
||||
"label": "Contribution to Net Total",
|
||||
"oldfieldname": "allocated_amount",
|
||||
"oldfieldtype": "Currency",
|
||||
"options": "Company:company:default_currency",
|
||||
"print_width": "120px",
|
||||
"read_only": 1,
|
||||
"reqd": 0,
|
||||
"width": "120px"
|
||||
},
|
||||
|
@ -23,7 +23,7 @@ wn.pages['sales-analytics'].onload = function(wrapper) {
|
||||
new erpnext.SalesAnalytics(wrapper);
|
||||
|
||||
wrapper.appframe.add_home_breadcrumb()
|
||||
wrapper.appframe.add_module_breadcrumb("Selling")
|
||||
wrapper.appframe.add_module_icon("Selling")
|
||||
wrapper.appframe.add_breadcrumb("icon-bar-chart")
|
||||
}
|
||||
|
||||
|
@ -1,11 +0,0 @@
|
||||
<div class="layout-wrapper layout-wrapper-background">
|
||||
<div class="appframe-area"></div>
|
||||
<div class="layout-main-section">
|
||||
<div class="tree-area"></div>
|
||||
</div>
|
||||
<div class="layout-side-section">
|
||||
<div class="help">To add child nodes, explore tree and click on the node under which you want to add more nodes.
|
||||
</div>
|
||||
</div>
|
||||
<div class="clear"></div>
|
||||
</div>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user