Merge branch 'develop' of github.com:webnotes/erpnext into hotfix

This commit is contained in:
Nabin Hait
2013-12-18 10:42:04 +05:30
82 changed files with 1057 additions and 1644 deletions

View File

@@ -5,8 +5,7 @@ from __future__ import unicode_literals
import webnotes import webnotes
from webnotes.utils import flt, fmt_money, getdate from webnotes.utils import flt, fmt_money, getdate
from webnotes.model.code import get_obj from webnotes import _
from webnotes import msgprint, _
class DocType: class DocType:
def __init__(self,d,dl): def __init__(self,d,dl):
@@ -130,11 +129,12 @@ def update_outstanding_amt(account, against_voucher_type, against_voucher, on_ca
against_voucher_amount = flt(webnotes.conn.sql(""" against_voucher_amount = flt(webnotes.conn.sql("""
select sum(ifnull(debit, 0)) - sum(ifnull(credit, 0)) select sum(ifnull(debit, 0)) - sum(ifnull(credit, 0))
from `tabGL Entry` where voucher_type = 'Journal Voucher' and voucher_no = %s from `tabGL Entry` where voucher_type = 'Journal Voucher' and voucher_no = %s
and account = %s""", (against_voucher, account))[0][0]) and account = %s and ifnull(against_voucher, '') = ''""",
(against_voucher, account))[0][0])
bal = against_voucher_amount + bal bal = against_voucher_amount + bal
if against_voucher_amount < 0: if against_voucher_amount < 0:
bal = -bal bal = -bal
# Validation : Outstanding can not be negative # Validation : Outstanding can not be negative
if bal < 0 and not on_cancel: if bal < 0 and not on_cancel:
webnotes.throw(_("Outstanding for Voucher ") + against_voucher + _(" will become ") + webnotes.throw(_("Outstanding for Voucher ") + against_voucher + _(" will become ") +
@@ -158,4 +158,4 @@ def validate_frozen_account(account, adv_adj):
elif frozen_accounts_modifier not in webnotes.user.get_roles(): elif frozen_accounts_modifier not in webnotes.user.get_roles():
webnotes.throw(account + _(" is a frozen account. ") + webnotes.throw(account + _(" is a frozen account. ") +
_("To create / edit transactions against this account, you need role") + ": " + _("To create / edit transactions against this account, you need role") + ": " +
frozen_accounts_modifier) frozen_accounts_modifier)

View File

@@ -145,6 +145,8 @@ def gl_entry_details(doctype, txt, searchfield, start, page_len, filters):
and voucher_no != gle.voucher_no) and voucher_no != gle.voucher_no)
!= abs(ifnull(gle.debit, 0) - ifnull(gle.credit, 0) != abs(ifnull(gle.debit, 0) - ifnull(gle.credit, 0)
) )
and if(gle.voucher_type='Sales Invoice', (select is_pos from `tabSales Invoice`
where name=gle.voucher_no), 0)=0
%(mcond)s %(mcond)s
ORDER BY gle.posting_date desc, gle.voucher_no desc ORDER BY gle.posting_date desc, gle.voucher_no desc
limit %(start)s, %(page_len)s""" % { limit %(start)s, %(page_len)s""" % {

View File

@@ -399,9 +399,10 @@ class DocType(SellingController):
if not self.doc.cash_bank_account and flt(self.doc.paid_amount): if not self.doc.cash_bank_account and flt(self.doc.paid_amount):
msgprint("Cash/Bank Account is mandatory for POS, for making payment entry") msgprint("Cash/Bank Account is mandatory for POS, for making payment entry")
raise Exception raise Exception
if (flt(self.doc.paid_amount) + flt(self.doc.write_off_amount) - round(flt(self.doc.grand_total), 2))>0.001: if flt(self.doc.paid_amount) + flt(self.doc.write_off_amount) \
msgprint("(Paid amount + Write Off Amount) can not be greater than Grand Total") - flt(self.doc.grand_total) > 1/(10**(self.precision("grand_total") + 1)):
raise Exception webnotes.throw(_("""(Paid amount + Write Off Amount) can not be \
greater than Grand Total"""))
def validate_item_code(self): def validate_item_code(self):

View File

@@ -2,7 +2,7 @@
{ {
"creation": "2013-04-24 11:39:32", "creation": "2013-04-24 11:39:32",
"docstatus": 0, "docstatus": 0,
"modified": "2013-07-10 14:54:21", "modified": "2013-12-17 12:38:08",
"modified_by": "Administrator", "modified_by": "Administrator",
"owner": "Administrator" "owner": "Administrator"
}, },
@@ -37,11 +37,20 @@
"options": "\nActual\nOn Net Total\nOn Previous Row Amount\nOn Previous Row Total", "options": "\nActual\nOn Net Total\nOn Previous Row Amount\nOn Previous Row Total",
"reqd": 1 "reqd": 1
}, },
{
"doctype": "DocField",
"fieldname": "row_id",
"fieldtype": "Data",
"hidden": 0,
"label": "Enter Row",
"oldfieldname": "row_id",
"oldfieldtype": "Data"
},
{ {
"doctype": "DocField", "doctype": "DocField",
"fieldname": "account_head", "fieldname": "account_head",
"fieldtype": "Link", "fieldtype": "Link",
"in_list_view": 1, "in_list_view": 0,
"label": "Account Head", "label": "Account Head",
"oldfieldname": "account_head", "oldfieldname": "account_head",
"oldfieldtype": "Link", "oldfieldtype": "Link",
@@ -54,7 +63,7 @@
"doctype": "DocField", "doctype": "DocField",
"fieldname": "cost_center", "fieldname": "cost_center",
"fieldtype": "Link", "fieldtype": "Link",
"in_list_view": 1, "in_list_view": 0,
"label": "Cost Center", "label": "Cost Center",
"oldfieldname": "cost_center_other_charges", "oldfieldname": "cost_center_other_charges",
"oldfieldtype": "Link", "oldfieldtype": "Link",
@@ -72,6 +81,24 @@
"reqd": 1, "reqd": 1,
"width": "300px" "width": "300px"
}, },
{
"allow_on_submit": 0,
"description": "If checked, the tax amount will be considered as already included in the Print Rate / Print Amount",
"doctype": "DocField",
"fieldname": "included_in_print_rate",
"fieldtype": "Check",
"label": "Is this Tax included in Basic Rate?",
"no_copy": 0,
"print_hide": 1,
"print_width": "150px",
"report_hide": 1,
"width": "150px"
},
{
"doctype": "DocField",
"fieldname": "section_break_6",
"fieldtype": "Section Break"
},
{ {
"doctype": "DocField", "doctype": "DocField",
"fieldname": "rate", "fieldname": "rate",
@@ -80,7 +107,7 @@
"label": "Rate", "label": "Rate",
"oldfieldname": "rate", "oldfieldname": "rate",
"oldfieldtype": "Currency", "oldfieldtype": "Currency",
"reqd": 0 "reqd": 1
}, },
{ {
"doctype": "DocField", "doctype": "DocField",
@@ -104,15 +131,6 @@
"options": "Company:company:default_currency", "options": "Company:company:default_currency",
"read_only": 1 "read_only": 1
}, },
{
"doctype": "DocField",
"fieldname": "row_id",
"fieldtype": "Data",
"hidden": 0,
"label": "Enter Row",
"oldfieldname": "row_id",
"oldfieldtype": "Data"
},
{ {
"doctype": "DocField", "doctype": "DocField",
"fieldname": "item_wise_tax_detail", "fieldname": "item_wise_tax_detail",
@@ -134,18 +152,5 @@
"oldfieldtype": "Data", "oldfieldtype": "Data",
"print_hide": 1, "print_hide": 1,
"search_index": 1 "search_index": 1
},
{
"allow_on_submit": 0,
"description": "If checked, the tax amount will be considered as already included in the Print Rate / Print Amount",
"doctype": "DocField",
"fieldname": "included_in_print_rate",
"fieldtype": "Check",
"label": "Is this Tax included in Basic Rate?",
"no_copy": 0,
"print_hide": 1,
"print_width": "150px",
"report_hide": 1,
"width": "150px"
} }
] ]

View File

@@ -24,10 +24,6 @@ def process_gl_map(gl_map, merge_entries=True):
gl_map = merge_similar_entries(gl_map) gl_map = merge_similar_entries(gl_map)
for entry in gl_map: for entry in gl_map:
# round off upto 2 decimal
entry.debit = flt(entry.debit, 2)
entry.credit = flt(entry.credit, 2)
# toggle debit, credit if negative entry # toggle debit, credit if negative entry
if flt(entry.debit) < 0: if flt(entry.debit) < 0:
entry.credit = flt(entry.credit) - flt(entry.debit) entry.credit = flt(entry.credit) - flt(entry.debit)

View File

@@ -157,7 +157,8 @@ wn.module_page["Accounts"] = [
items: [ items: [
{ {
"label":wn._("General Ledger"), "label":wn._("General Ledger"),
page: "general-ledger" doctype: "GL Entry",
route: "query-report/General Ledger"
}, },
{ {
"label":wn._("Trial Balance"), "label":wn._("Trial Balance"),

View File

@@ -1 +0,0 @@
General Ledger report (for all transactions and accounts).

View File

@@ -1 +0,0 @@
from __future__ import unicode_literals

View File

@@ -1,396 +0,0 @@
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
wn.pages['general-ledger'].onload = function(wrapper) {
wn.ui.make_app_page({
parent: wrapper,
title: wn._('General Ledger'),
single_column: true
});
erpnext.general_ledger = new erpnext.GeneralLedger(wrapper);
wrapper.appframe.add_module_icon("Accounts")
}
erpnext.GeneralLedger = wn.views.GridReport.extend({
init: function(wrapper) {
this._super({
title: wn._("General Ledger"),
page: wrapper,
parent: $(wrapper).find('.layout-main'),
appframe: wrapper.appframe,
doctypes: ["Company", "Account", "GL Entry", "Cost Center"],
});
},
setup_columns: function() {
this.columns = [
{id: "posting_date", name: wn._("Posting Date"), field: "posting_date", width: 100,
formatter: this.date_formatter},
{id: "account", name: wn._("Account"), field: "account", width: 240,
link_formatter: {
filter_input: "account",
open_btn: true,
doctype: "'Account'"
}},
{id: "against_account", name: wn._("Against Account"), field: "against_account",
width: 240, hidden: !this.account},
{id: "debit", name: wn._("Debit"), field: "debit", width: 100,
formatter: this.currency_formatter},
{id: "credit", name: wn._("Credit"), field: "credit", width: 100,
formatter: this.currency_formatter},
{id: "voucher_type", name: wn._("Voucher Type"), field: "voucher_type", width: 120},
{id: "voucher_no", name: wn._("Voucher No"), field: "voucher_no", width: 160,
link_formatter: {
filter_input: "voucher_no",
open_btn: true,
doctype: "dataContext.voucher_type"
}},
{id: "remarks", name: wn._("Remarks"), field: "remarks", width: 200,
formatter: this.text_formatter},
];
},
filters: [
{fieldtype:"Select", label: wn._("Company"), link:"Company", default_value: wn._("Select Company..."),
filter: function(val, item, opts) {
return item.company == val || val == opts.default_value;
}},
{fieldtype:"Link", label: wn._("Account"), link:"Account",
filter: function(val, item, opts, me) {
if(!val) {
return true;
} else {
// true if GL Entry belongs to selected
// account ledger or group
return me.is_child_account(val, item.account);
}
}},
{fieldtype:"Data", label: wn._("Voucher No"),
filter: function(val, item, opts) {
if(!val) return true;
return (item.voucher_no && item.voucher_no.indexOf(val)!=-1);
}},
{fieldtype:"Date", label: wn._("From Date"), filter: function(val, item) {
return dateutil.str_to_obj(val) <= dateutil.str_to_obj(item.posting_date);
}},
{fieldtype:"Label", label: wn._("To")},
{fieldtype:"Date", label: wn._("To Date"), filter: function(val, item) {
return dateutil.str_to_obj(val) >= dateutil.str_to_obj(item.posting_date);
}},
{fieldtype: "Check", label: wn._("Group by Ledger")},
{fieldtype: "Check", label: wn._("Group by Voucher")},
{fieldtype:"Button", label: wn._("Refresh"), icon:"icon-refresh icon-white"},
{fieldtype:"Button", label: wn._("Reset Filters")}
],
setup_filters: function() {
this._super();
var me = this;
this.accounts_by_company = this.make_accounts_by_company();
// filter accounts options by company
this.filter_inputs.company.on("change", function() {
me.setup_account_filter(this);
me.refresh();
});
this.trigger_refresh_on_change(["group_by_ledger", "group_by_voucher"]);
},
setup_account_filter: function(company_filter) {
var me = this;
var $account = me.filter_inputs.account;
var company = $(company_filter).val();
var default_company = this.filter_inputs.company.get(0).opts.default_value;
var opts = $account.get(0).opts;
opts.list = $.map(wn.report_dump.data["Account"], function(ac) {
return (company===default_company ||
me.accounts_by_company[company].indexOf(ac.name)!=-1) ?
ac.name : null;
});
this.set_autocomplete($account, opts.list);
},
init_filter_values: function() {
this._super();
this.toggle_group_by_checks();
this.filter_inputs.company.trigger("change");
},
apply_filters_from_route: function() {
this._super();
this.toggle_group_by_checks();
},
make_accounts_by_company: function() {
var accounts_by_company = {};
var me = this;
$.each(wn.report_dump.data["Account"], function(i, ac) {
if(!accounts_by_company[ac.company]) accounts_by_company[ac.company] = [];
accounts_by_company[ac.company].push(ac.name);
});
return accounts_by_company;
},
is_child_account: function(account, item_account) {
account = this.account_by_name[account];
item_account = this.account_by_name[item_account];
return ((item_account.lft >= account.lft) && (item_account.rgt <= account.rgt));
},
toggle_group_by_checks: function() {
this.make_account_by_name();
// this.filter_inputs.group_by_ledger
// .parent().toggle(!!(this.account_by_name[this.account]
// && this.account_by_name[this.account].group_or_ledger==="Group"));
//
// this.filter_inputs.group_by_voucher
// .parent().toggle(!!(this.account_by_name[this.account]
// && this.account_by_name[this.account].group_or_ledger==="Ledger"));
},
prepare_data: function() {
var me = this;
var data = wn.report_dump.data["GL Entry"];
var out = [];
this.toggle_group_by_checks();
var from_date = dateutil.str_to_obj(this.from_date);
var to_date = dateutil.str_to_obj(this.to_date);
if(to_date < from_date) {
msgprint(wn._("From Date must be before To Date"));
return;
}
// add Opening, Closing, Totals rows
// if filtered by account and / or voucher
var opening = this.make_summary_row("Opening", this.account);
var totals = this.make_summary_row("Totals", this.account);
var grouped_ledgers = {};
$.each(data, function(i, item) {
if(me.apply_filter(item, "company") &&
(me.account ? me.is_child_account(me.account, item.account)
: true) && (me.voucher_no ? item.voucher_no==me.voucher_no : true)) {
var date = dateutil.str_to_obj(item.posting_date);
// create grouping by ledger
if(!grouped_ledgers[item.account]) {
grouped_ledgers[item.account] = {
entries: [],
entries_group_by_voucher: {},
opening: me.make_summary_row("Opening", item.account),
totals: me.make_summary_row("Totals", item.account),
closing: me.make_summary_row("Closing (Opening + Totals)",
item.account)
};
}
if(!grouped_ledgers[item.account].entries_group_by_voucher[item.voucher_no]) {
grouped_ledgers[item.account].entries_group_by_voucher[item.voucher_no] = {
row: {},
totals: {"debit": 0, "credit": 0}
}
}
if(!me.voucher_no && (date < from_date || item.is_opening=="Yes")) {
opening.debit += item.debit;
opening.credit += item.credit;
grouped_ledgers[item.account].opening.debit += item.debit;
grouped_ledgers[item.account].opening.credit += item.credit;
} else if(date <= to_date) {
totals.debit += item.debit;
totals.credit += item.credit;
grouped_ledgers[item.account].totals.debit += item.debit;
grouped_ledgers[item.account].totals.credit += item.credit;
grouped_ledgers[item.account].entries_group_by_voucher[item.voucher_no]
.totals.debit += item.debit;
grouped_ledgers[item.account].entries_group_by_voucher[item.voucher_no]
.totals.credit += item.credit;
}
if(item.account) {
item.against_account = me.voucher_accounts[item.voucher_type + ":"
+ item.voucher_no][(item.debit > 0 ? "credits" : "debits")].join(", ");
}
if(me.apply_filters(item) && (me.voucher_no || item.is_opening=="No")) {
out.push(item);
grouped_ledgers[item.account].entries.push(item);
if(grouped_ledgers[item.account].entries_group_by_voucher[item.voucher_no].row){
grouped_ledgers[item.account].entries_group_by_voucher[item.voucher_no]
.row = $.extend({}, item);
}
}
}
});
var closing = this.make_summary_row("Closing (Opening + Totals)", this.account);
closing.debit = opening.debit + totals.debit;
closing.credit = opening.credit + totals.credit;
if(me.account) {
me.appframe.set_title(wn._("General Ledger: ") + me.account);
// group by ledgers
if(this.account_by_name[this.account].group_or_ledger==="Group"
&& this.group_by_ledger) {
out = this.group_data_by_ledger(grouped_ledgers);
}
if(this.account_by_name[this.account].group_or_ledger==="Ledger"
&& this.group_by_voucher) {
out = this.group_data_by_voucher(grouped_ledgers);
}
opening = me.get_balance(me.account_by_name[me.account].debit_or_credit, opening)
closing = me.get_balance(me.account_by_name[me.account].debit_or_credit, closing)
out = [opening].concat(out).concat([totals, closing]);
} else {
me.appframe.set_title(wn._("General Ledger"));
out = out.concat([totals]);
}
this.data = out;
},
group_data_by_ledger: function(grouped_ledgers) {
var me = this;
var out = []
$.each(Object.keys(grouped_ledgers).sort(), function(i, account) {
if(grouped_ledgers[account].entries.length) {
grouped_ledgers[account].closing.debit =
grouped_ledgers[account].opening.debit
+ grouped_ledgers[account].totals.debit;
grouped_ledgers[account].closing.credit =
grouped_ledgers[account].opening.credit
+ grouped_ledgers[account].totals.credit;
grouped_ledgers[account].opening =
me.get_balance(me.account_by_name[me.account].debit_or_credit,
grouped_ledgers[account].opening)
grouped_ledgers[account].closing =
me.get_balance(me.account_by_name[me.account].debit_or_credit,
grouped_ledgers[account].closing)
out = out.concat([grouped_ledgers[account].opening])
.concat(grouped_ledgers[account].entries)
.concat([grouped_ledgers[account].totals,
grouped_ledgers[account].closing,
{id: "_blank" + i, debit: "", credit: ""}]);
}
});
return [{id: "_blank_first", debit: "", credit: ""}].concat(out);
},
group_data_by_voucher: function(grouped_ledgers) {
var me = this;
var out = []
$.each(Object.keys(grouped_ledgers).sort(), function(i, account) {
if(grouped_ledgers[account].entries.length) {
$.each(Object.keys(grouped_ledgers[account].entries_group_by_voucher),
function(j, voucher) {
voucher_dict = grouped_ledgers[account].entries_group_by_voucher[voucher];
if(voucher_dict &&
(voucher_dict.totals.debit || voucher_dict.totals.credit)) {
voucher_dict.row.debit = voucher_dict.totals.debit;
voucher_dict.row.credit = voucher_dict.totals.credit;
voucher_dict.row.id = "entry_grouped_by_" + voucher
out = out.concat(voucher_dict.row);
}
});
}
});
return out;
},
get_balance: function(debit_or_credit, balance) {
if(debit_or_credit == "Debit") {
balance.debit -= balance.credit; balance.credit = 0;
} else {
balance.credit -= balance.debit; balance.debit = 0;
}
return balance
},
make_summary_row: function(label, item_account) {
return {
account: label,
debit: 0.0,
credit: 0.0,
id: ["", label, item_account].join("_").replace(" ", "_").toLowerCase(),
_show: true,
_style: "font-weight: bold"
}
},
make_account_by_name: function() {
this.account_by_name = this.make_name_map(wn.report_dump.data["Account"]);
this.make_voucher_accounts_map();
},
make_voucher_accounts_map: function() {
this.voucher_accounts = {};
var data = wn.report_dump.data["GL Entry"];
for(var i=0, j=data.length; i<j; i++) {
var gl = data[i];
if(!this.voucher_accounts[gl.voucher_type + ":" + gl.voucher_no])
this.voucher_accounts[gl.voucher_type + ":" + gl.voucher_no] = {
debits: [],
credits: []
}
var va = this.voucher_accounts[gl.voucher_type + ":" + gl.voucher_no];
if(gl.debit > 0) {
va.debits.push(gl.account);
} else {
va.credits.push(gl.account);
}
}
},
get_plot_data: function() {
var data = [];
var me = this;
if(!me.account || me.voucher_no) return false;
var debit_or_credit = me.account_by_name[me.account].debit_or_credit;
var balance = debit_or_credit=="Debit" ? me.data[0].debit : me.data[0].credit;
data.push({
label: me.account,
data: [[dateutil.str_to_obj(me.from_date).getTime(), balance]]
.concat($.map(me.data, function(col, idx) {
if (col.posting_date) {
var diff = (debit_or_credit == "Debit" ? 1 : -1) * (flt(col.debit) - flt(col.credit));
balance += diff;
return [[dateutil.str_to_obj(col.posting_date).getTime(), balance - diff],
[dateutil.str_to_obj(col.posting_date).getTime(), balance]]
}
return null;
})).concat([
// closing
[dateutil.str_to_obj(me.to_date).getTime(), balance]
]),
points: {show: true},
lines: {show: true, fill: true},
});
return data;
},
get_plot_options: function() {
return {
grid: { hoverable: true, clickable: true },
xaxis: { mode: "time",
min: dateutil.str_to_obj(this.from_date).getTime(),
max: dateutil.str_to_obj(this.to_date).getTime() },
series: { downsample: { threshold: 1000 } }
}
},
});

View File

@@ -1,41 +0,0 @@
[
{
"creation": "2012-09-14 11:25:48",
"docstatus": 0,
"modified": "2013-07-11 14:42:21",
"modified_by": "Administrator",
"owner": "Administrator"
},
{
"doctype": "Page",
"icon": "icon-table",
"module": "Accounts",
"name": "__common__",
"page_name": "general-ledger",
"standard": "Yes",
"title": "General Ledger"
},
{
"doctype": "Page Role",
"name": "__common__",
"parent": "general-ledger",
"parentfield": "roles",
"parenttype": "Page"
},
{
"doctype": "Page",
"name": "general-ledger"
},
{
"doctype": "Page Role",
"role": "Analytics"
},
{
"doctype": "Page Role",
"role": "Accounts Manager"
},
{
"doctype": "Page Role",
"role": "Accounts User"
}
]

View File

@@ -85,7 +85,7 @@ class AccountsReceivableReport(object):
def get_account_map(self): def get_account_map(self):
if not hasattr(self, "account_map"): if not hasattr(self, "account_map"):
self.account_map = dict(((r.name, r) for r in webnotes.conn.sql("""select self.account_map = dict(((r.name, r) for r in webnotes.conn.sql("""select
account.name, customer.customer_name, customer.territory account.name, customer.name as customer_name, customer.territory
from `tabAccount` account, `tabCustomer` customer from `tabAccount` account, `tabCustomer` customer
where account.master_type="Customer" where account.master_type="Customer"
and customer.name=account.master_name""", as_dict=True))) and customer.name=account.master_name""", as_dict=True)))

View File

@@ -121,6 +121,6 @@ def get_costcenter_account_month_map(filters):
for ad in actual_details.get(ccd.name, {}).get(ccd.account, []): for ad in actual_details.get(ccd.name, {}).get(ccd.account, []):
if ad.month_name == month: if ad.month_name == month:
tav_dict.actual += ad.debit - ad.credit tav_dict.actual += flt(ad.debit) - flt(ad.credit)
return cam_map return cam_map

View File

@@ -0,0 +1,51 @@
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
wn.query_reports["General Ledger"] = {
"filters": [
{
"fieldname":"company",
"label": wn._("Company"),
"fieldtype": "Link",
"options": "Company",
"default": wn.defaults.get_user_default("company"),
"reqd": 1
},
{
"fieldname":"account",
"label": wn._("Account"),
"fieldtype": "Link",
"options": "Account"
},
{
"fieldname":"voucher_no",
"label": wn._("Voucher No"),
"fieldtype": "Data",
},
{
"fieldname":"group_by",
"label": wn._("Group by"),
"fieldtype": "Select",
"options": "\nGroup by Account\nGroup by Voucher"
},
{
"fieldtype": "Break",
},
{
"fieldname":"from_date",
"label": wn._("From Date"),
"fieldtype": "Date",
"default": wn.datetime.add_months(wn.datetime.get_today(), -1),
"reqd": 1,
"width": "60px"
},
{
"fieldname":"to_date",
"label": wn._("To Date"),
"fieldtype": "Date",
"default": wn.datetime.get_today(),
"reqd": 1,
"width": "60px"
}
]
}

View File

@@ -0,0 +1,79 @@
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import webnotes
from webnotes.utils import flt
from webnotes import _
def execute(filters=None):
validate_filters(filters)
columns = get_columns()
if filters.get("group_by"):
data = get_grouped_gle(filters)
else:
data = get_gl_entries(filters)
if data:
data.append(get_total_row(data))
return columns, data
def validate_filters(filters):
if filters.get("account") and filters.get("group_by") == "Group by Account":
webnotes.throw(_("Can not filter based on Account, if grouped by Account"))
if filters.get("voucher_no") and filters.get("group_by") == "Group by Voucher":
webnotes.throw(_("Can not filter based on Voucher No, if grouped by Voucher"))
def get_columns():
return ["Posting Date:Date:100", "Account:Link/Account:200", "Debit:Currency:100",
"Credit:Currency:100", "Voucher Type::120", "Voucher No::160",
"Cost Center:Link/Cost Center:100", "Remarks::200"]
def get_gl_entries(filters):
return webnotes.conn.sql("""select
posting_date, account, debit, credit, voucher_type, voucher_no, cost_center, remarks
from `tabGL Entry`
where company=%(company)s
and posting_date between %(from_date)s and %(to_date)s
{conditions}
order by posting_date, account"""\
.format(conditions=get_conditions(filters)), filters, as_list=1)
def get_conditions(filters):
conditions = []
if filters.get("account"):
conditions.append("account=%(account)s")
if filters.get("voucher_no"):
conditions.append("voucher_no=%(voucher_no)s")
return "and {}".format(" and ".join(conditions)) if conditions else ""
def get_grouped_gle(filters):
gle_map = {}
gle = get_gl_entries(filters)
for d in gle:
gle_map.setdefault(d[1 if filters["group_by"]=="Group by Account" else 5], []).append(d)
data = []
for entries in gle_map.values():
subtotal_debit = subtotal_credit = 0.0
for entry in entries:
data.append(entry)
subtotal_debit += flt(entry[2])
subtotal_credit += flt(entry[3])
data.append(["", "Total", subtotal_debit, subtotal_credit, "", "", ""])
if data:
data.append(get_total_row(gle))
return data
def get_total_row(gle):
total_debit = total_credit = 0.0
for d in gle:
total_debit += flt(d[2])
total_credit += flt(d[3])
return ["", "Total Debit/Credit", total_debit, total_credit, "", "", ""]

View File

@@ -0,0 +1,21 @@
[
{
"creation": "2013-12-06 13:22:23",
"docstatus": 0,
"modified": "2013-12-06 13:22:23",
"modified_by": "Administrator",
"owner": "Administrator"
},
{
"doctype": "Report",
"is_standard": "Yes",
"name": "__common__",
"ref_doctype": "GL Entry",
"report_name": "General Ledger",
"report_type": "Script Report"
},
{
"doctype": "Report",
"name": "General Ledger"
}
]

View File

@@ -7,11 +7,9 @@ import webnotes
from webnotes.utils import cstr, flt from webnotes.utils import cstr, flt
from webnotes.model.utils import getlist from webnotes.model.utils import getlist
from webnotes import msgprint, _ from webnotes import msgprint, _
from buying.utils import get_last_purchase_details from buying.utils import get_last_purchase_details
from controllers.buying_controller import BuyingController from controllers.buying_controller import BuyingController
class DocType(BuyingController): class DocType(BuyingController):
def __init__(self, doc, doclist=None): def __init__(self, doc, doclist=None):
self.doc = doc self.doc = doc
@@ -38,13 +36,13 @@ class DocType(BuyingController):
if flt(d.conversion_factor): if flt(d.conversion_factor):
last_purchase_rate = flt(d.purchase_rate) / flt(d.conversion_factor) last_purchase_rate = flt(d.purchase_rate) / flt(d.conversion_factor)
else: else:
msgprint(_("Row ") + cstr(d.idx) + ": " + webnotes.throw(_("Row ") + cstr(d.idx) + ": " +
_("UOM Conversion Factor is mandatory"), raise_exception=1) _("UOM Conversion Factor is mandatory"))
# update last purchsae rate # update last purchsae rate
if last_purchase_rate: if last_purchase_rate:
webnotes.conn.sql("update `tabItem` set last_purchase_rate = %s where name = %s", webnotes.conn.sql("""update `tabItem` set last_purchase_rate = %s where name = %s""",
(flt(last_purchase_rate),d.item_code)) (flt(last_purchase_rate), d.item_code))
def get_last_purchase_rate(self, obj): def get_last_purchase_rate(self, obj):
"""get last purchase rates for all items""" """get last purchase rates for all items"""
@@ -76,11 +74,11 @@ class DocType(BuyingController):
for d in getlist( obj.doclist, obj.fname): for d in getlist( obj.doclist, obj.fname):
# validation for valid qty # validation for valid qty
if flt(d.qty) < 0 or (d.parenttype != 'Purchase Receipt' and not flt(d.qty)): if flt(d.qty) < 0 or (d.parenttype != 'Purchase Receipt' and not flt(d.qty)):
msgprint("Please enter valid qty for item %s" % cstr(d.item_code)) webnotes.throw("Please enter valid qty for item %s" % cstr(d.item_code))
raise Exception
# udpate with latest quantities # udpate with latest quantities
bin = webnotes.conn.sql("select projected_qty from `tabBin` where item_code = %s and warehouse = %s", (d.item_code, d.warehouse), as_dict = 1) bin = webnotes.conn.sql("""select projected_qty from `tabBin` where
item_code = %s and warehouse = %s""", (d.item_code, d.warehouse), as_dict=1)
f_lst ={'projected_qty': bin and flt(bin[0]['projected_qty']) or 0, 'ordered_qty': 0, 'received_qty' : 0} f_lst ={'projected_qty': bin and flt(bin[0]['projected_qty']) or 0, 'ordered_qty': 0, 'received_qty' : 0}
if d.doctype == 'Purchase Receipt Item': if d.doctype == 'Purchase Receipt Item':
@@ -89,48 +87,50 @@ class DocType(BuyingController):
if d.fields.has_key(x): if d.fields.has_key(x):
d.fields[x] = f_lst[x] d.fields[x] = f_lst[x]
item = webnotes.conn.sql("select is_stock_item, is_purchase_item, is_sub_contracted_item, end_of_life from tabItem where name=%s", item = webnotes.conn.sql("""select is_stock_item, is_purchase_item,
d.item_code) is_sub_contracted_item, end_of_life from `tabItem` where name=%s""", d.item_code)
if not item: if not item:
msgprint("Item %s does not exist in Item Master." % cstr(d.item_code), raise_exception=True) webnotes.throw("Item %s does not exist in Item Master." % cstr(d.item_code))
from stock.utils import validate_end_of_life from stock.utils import validate_end_of_life
validate_end_of_life(d.item_code, item[0][3]) validate_end_of_life(d.item_code, item[0][3])
# validate stock item # validate stock item
if item[0][0]=='Yes' and d.qty and not d.warehouse: if item[0][0]=='Yes' and d.qty and not d.warehouse:
msgprint("Warehouse is mandatory for %s, since it is a stock item" % webnotes.throw("Warehouse is mandatory for %s, since it is a stock item" % d.item_code)
d.item_code, raise_exception=1)
# validate purchase item # validate purchase item
if item[0][1] != 'Yes' and item[0][2] != 'Yes': if item[0][1] != 'Yes' and item[0][2] != 'Yes':
msgprint("Item %s is not a purchase item or sub-contracted item. Please check" % (d.item_code), raise_exception=True) webnotes.throw("Item %s is not a purchase item or sub-contracted item. Please check" % (d.item_code))
# list criteria that should not repeat if item is stock item # list criteria that should not repeat if item is stock item
e = [d.schedule_date, d.item_code, d.description, d.warehouse, d.uom, d.fields.has_key('prevdoc_docname') and d.prevdoc_docname or '', d.fields.has_key('prevdoc_detail_docname') and d.prevdoc_detail_docname or '', d.fields.has_key('batch_no') and d.batch_no or ''] e = [d.schedule_date, d.item_code, d.description, d.warehouse, d.uom,
d.fields.has_key('prevdoc_docname') and d.prevdoc_docname or d.fields.has_key('sales_order_no') and d.sales_order_no or '',
d.fields.has_key('prevdoc_detail_docname') and d.prevdoc_detail_docname or '',
d.fields.has_key('batch_no') and d.batch_no or '']
# if is not stock item # if is not stock item
f = [d.schedule_date, d.item_code, d.description] f = [d.schedule_date, d.item_code, d.description]
ch = webnotes.conn.sql("select is_stock_item from `tabItem` where name = '%s'"%d.item_code) ch = webnotes.conn.sql("""select is_stock_item from `tabItem` where name = %s""", d.item_code)
if ch and ch[0][0] == 'Yes': if ch and ch[0][0] == 'Yes':
# check for same items # check for same items
if e in check_list: if e in check_list:
msgprint("""Item %s has been entered more than once with same description, schedule date, warehouse and uom.\n webnotes.throw("""Item %s has been entered more than once with same description, schedule date, warehouse and uom.\n
Please change any of the field value to enter the item twice""" % d.item_code, raise_exception = 1) Please change any of the field value to enter the item twice""" % d.item_code)
else: else:
check_list.append(e) check_list.append(e)
elif ch and ch[0][0] == 'No': elif ch and ch[0][0] == 'No':
# check for same items # check for same items
if f in chk_dupl_itm: if f in chk_dupl_itm:
msgprint("""Item %s has been entered more than once with same description, schedule date.\n webnotes.throw("""Item %s has been entered more than once with same description, schedule date.\n
Please change any of the field value to enter the item twice.""" % d.item_code, raise_exception = 1) Please change any of the field value to enter the item twice.""" % d.item_code)
else: else:
chk_dupl_itm.append(f) chk_dupl_itm.append(f)
def get_qty(self,curr_doctype,ref_tab_fname,ref_tab_dn,ref_doc_tname, transaction, curr_parent_name): def get_qty(self, curr_doctype, ref_tab_fname, ref_tab_dn, ref_doc_tname, transaction, curr_parent_name):
# Get total Quantities of current doctype (eg. PR) except for qty of this transaction # Get total Quantities of current doctype (eg. PR) except for qty of this transaction
#------------------------------ #------------------------------
# please check as UOM changes from Material Request - Purchase Order ,so doing following else uom should be same . # please check as UOM changes from Material Request - Purchase Order ,so doing following else uom should be same .
@@ -138,35 +138,37 @@ class DocType(BuyingController):
# but if in Material Request uom KG it can change in PO # but if in Material Request uom KG it can change in PO
get_qty = (transaction == 'Material Request - Purchase Order') and 'qty * conversion_factor' or 'qty' get_qty = (transaction == 'Material Request - Purchase Order') and 'qty * conversion_factor' or 'qty'
qty = webnotes.conn.sql("select sum(%s) from `tab%s` where %s = '%s' and docstatus = 1 and parent != '%s'"% ( get_qty, curr_doctype, ref_tab_fname, ref_tab_dn, curr_parent_name)) qty = webnotes.conn.sql("""select sum(%s) from `tab%s` where %s = %s and
docstatus = 1 and parent != %s""" % (get_qty, curr_doctype, ref_tab_fname, '%s', '%s'),
(ref_tab_dn, curr_parent_name))
qty = qty and flt(qty[0][0]) or 0 qty = qty and flt(qty[0][0]) or 0
# get total qty of ref doctype # get total qty of ref doctype
#-------------------- #--------------------
max_qty = webnotes.conn.sql("select qty from `tab%s` where name = '%s' and docstatus = 1"% (ref_doc_tname, ref_tab_dn)) max_qty = webnotes.conn.sql("""select qty from `tab%s` where name = %s
and docstatus = 1""" % (ref_doc_tname, '%s'), ref_tab_dn)
max_qty = max_qty and flt(max_qty[0][0]) or 0 max_qty = max_qty and flt(max_qty[0][0]) or 0
return cstr(qty)+'~~~'+cstr(max_qty) return cstr(qty)+'~~~'+cstr(max_qty)
def check_for_stopped_status(self, doctype, docname): def check_for_stopped_status(self, doctype, docname):
stopped = webnotes.conn.sql("select name from `tab%s` where name = '%s' and status = 'Stopped'" % stopped = webnotes.conn.sql("""select name from `tab%s` where name = %s and
( doctype, docname)) status = 'Stopped'""" % (doctype, '%s'), docname)
if stopped: if stopped:
msgprint("One cannot do any transaction against %s : %s, it's status is 'Stopped'" % webnotes.throw("One cannot do any transaction against %s : %s, it's status is 'Stopped'" %
( doctype, docname), raise_exception=1) (doctype, docname))
def check_docstatus(self, check, doctype, docname , detail_doctype = ''): def check_docstatus(self, check, doctype, docname, detail_doctype = ''):
if check == 'Next': if check == 'Next':
submitted = webnotes.conn.sql("""select t1.name from `tab%s` t1,`tab%s` t2 submitted = webnotes.conn.sql("""select t1.name from `tab%s` t1,`tab%s` t2
where t1.name = t2.parent and t2.prevdoc_docname = %s and t1.docstatus = 1""" where t1.name = t2.parent and t2.prevdoc_docname = %s and t1.docstatus = 1"""
% (doctype, detail_doctype, '%s'), docname) % (doctype, detail_doctype, '%s'), docname)
if submitted: if submitted:
msgprint(cstr(doctype) + ": " + cstr(submitted[0][0]) webnotes.throw(cstr(doctype) + ": " + cstr(submitted[0][0])
+ _(" has already been submitted."), raise_exception=1) + _("has already been submitted."))
if check == 'Previous': if check == 'Previous':
submitted = webnotes.conn.sql("""select name from `tab%s` submitted = webnotes.conn.sql("""select name from `tab%s`
where docstatus = 1 and name = %s"""% (doctype, '%s'), docname) where docstatus = 1 and name = %s""" % (doctype, '%s'), docname)
if not submitted: if not submitted:
msgprint(cstr(doctype) + ": " + cstr(submitted[0][0]) webnotes.throw(cstr(doctype) + ": " + cstr(submitted[0][0]) + _("not submitted"))
+ _(" not submitted"), raise_exception=1)

View File

@@ -27,6 +27,14 @@ class DocType(TransactionBase):
else: else:
self.doc.name = make_autoname(self.doc.naming_series + '.#####') self.doc.name = make_autoname(self.doc.naming_series + '.#####')
def update_address(self):
webnotes.conn.sql("""update `tabAddress` set supplier_name=%s, modified=NOW()
where supplier=%s""", (self.doc.supplier_name, self.doc.name))
def update_contact(self):
webnotes.conn.sql("""update `tabContact` set supplier_name=%s, modified=NOW()
where supplier=%s""", (self.doc.supplier_name, self.doc.name))
def update_credit_days_limit(self): def update_credit_days_limit(self):
webnotes.conn.sql("""update tabAccount set credit_days = %s where name = %s""", webnotes.conn.sql("""update tabAccount set credit_days = %s where name = %s""",
(cint(self.doc.credit_days), self.doc.name + " - " + self.get_company_abbr())) (cint(self.doc.credit_days), self.doc.name + " - " + self.get_company_abbr()))
@@ -35,6 +43,9 @@ class DocType(TransactionBase):
if not self.doc.naming_series: if not self.doc.naming_series:
self.doc.naming_series = '' self.doc.naming_series = ''
self.update_address()
self.update_contact()
# create account head # create account head
self.create_account_head() self.create_account_head()
@@ -151,10 +162,19 @@ class DocType(TransactionBase):
def before_rename(self, olddn, newdn, merge=False): def before_rename(self, olddn, newdn, merge=False):
from accounts.utils import rename_account_for from accounts.utils import rename_account_for
rename_account_for("Supplier", olddn, newdn, merge) rename_account_for("Supplier", olddn, newdn, merge)
def after_rename(self, olddn, newdn, merge=False): def after_rename(self, olddn, newdn, merge=False):
set_field = ''
if webnotes.defaults.get_global_default('supp_master_name') == 'Supplier Name': if webnotes.defaults.get_global_default('supp_master_name') == 'Supplier Name':
webnotes.conn.set(self.doc, "supplier_name", newdn) webnotes.conn.set(self.doc, "supplier_name", newdn)
self.update_contact()
set_field = ", supplier_name=%(newdn)s"
self.update_supplier_address(newdn, set_field)
def update_supplier_address(self, newdn, set_field):
webnotes.conn.sql("""update `tabAddress` set address_title=%(newdn)s
{set_field} where supplier=%(newdn)s"""\
.format(set_field=set_field), ({"newdn": newdn}))
@webnotes.whitelist() @webnotes.whitelist()
def get_dashboard_info(supplier): def get_dashboard_info(supplier):

View File

@@ -2,7 +2,7 @@
{ {
"creation": "2013-05-21 16:16:45", "creation": "2013-05-21 16:16:45",
"docstatus": 0, "docstatus": 0,
"modified": "2013-11-22 17:16:16", "modified": "2013-12-14 17:27:47",
"modified_by": "Administrator", "modified_by": "Administrator",
"owner": "Administrator" "owner": "Administrator"
}, },
@@ -632,6 +632,7 @@
"cancel": 0, "cancel": 0,
"create": 0, "create": 0,
"doctype": "DocPerm", "doctype": "DocPerm",
"match": "supplier",
"role": "Supplier", "role": "Supplier",
"submit": 0, "submit": 0,
"write": 0 "write": 0

View File

@@ -1,78 +1,78 @@
{ {
"app_name": "ERPNext", "app_name": "ERPNext",
"app_version": "3.0.0", "app_version": "3.3.0",
"requires_framework_version": "==3.0.0", "base_template": "app/portal/templates/base.html",
"base_template": "app/portal/templates/base.html", "modules": {
"modules": { "Accounts": {
"Selling": { "color": "#3498db",
"link": "selling-home", "icon": "icon-money",
"color": "#1abc9c", "link": "accounts-home",
"icon": "icon-tag", "type": "module"
"type": "module" },
}, "Activity": {
"Accounts": { "color": "#e67e22",
"link": "accounts-home", "icon": "icon-play",
"color": "#3498db", "label": "Activity",
"icon": "icon-money", "link": "activity",
"type": "module" "type": "page"
}, },
"Stock": { "Buying": {
"type": "module", "color": "#c0392b",
"link": "stock-home", "icon": "icon-shopping-cart",
"color": "#f39c12", "link": "buying-home",
"icon": "icon-truck" "type": "module"
}, },
"Buying": { "HR": {
"type": "module", "color": "#2ecc71",
"link": "buying-home", "icon": "icon-group",
"color": "#c0392b", "label": "Human Resources",
"icon": "icon-shopping-cart" "link": "hr-home",
}, "type": "module"
"Support": { },
"type": "module", "Manufacturing": {
"link": "support-home", "color": "#7f8c8d",
"color": "#2c3e50", "icon": "icon-cogs",
"icon": "icon-phone" "link": "manufacturing-home",
}, "type": "module"
"Projects": { },
"type": "module", "Notes": {
"link": "projects-home", "color": "#95a5a6",
"color": "#8e44ad", "doctype": "Note",
"icon": "icon-puzzle-piece" "icon": "icon-file-alt",
}, "label": "Notes",
"Manufacturing": { "link": "List/Note",
"type": "module", "type": "list"
"link": "manufacturing-home", },
"color": "#7f8c8d", "Projects": {
"icon": "icon-cogs" "color": "#8e44ad",
}, "icon": "icon-puzzle-piece",
"HR": { "link": "projects-home",
"type": "module", "type": "module"
"link": "hr-home", },
"color": "#2ecc71", "Selling": {
"label": "Human Resources", "color": "#1abc9c",
"icon": "icon-group" "icon": "icon-tag",
}, "link": "selling-home",
"Setup": { "type": "module"
"type": "setup", },
"link": "Setup", "Setup": {
"color": "#bdc3c7", "color": "#bdc3c7",
"icon": "icon-wrench" "icon": "icon-wrench",
}, "link": "Setup",
"Activity": { "type": "setup"
"type": "page", },
"link": "activity", "Stock": {
"color": "#e67e22", "color": "#f39c12",
"icon": "icon-play", "icon": "icon-truck",
"label": "Activity" "link": "stock-home",
}, "type": "module"
"Notes": { },
"type": "list", "Support": {
"doctype": "Note", "color": "#2c3e50",
"link": "List/Note", "icon": "icon-phone",
"color": "#95a5a6", "link": "support-home",
"label": "Notes", "type": "module"
"icon": "icon-file-alt" }
} },
} "requires_framework_version": "==3.3.0"
} }

View File

@@ -205,8 +205,8 @@ class SellingController(StockController):
self.round_floats_in(self.doc, ["grand_total", "total_advance", "write_off_amount", self.round_floats_in(self.doc, ["grand_total", "total_advance", "write_off_amount",
"paid_amount"]) "paid_amount"])
total_amount_to_pay = self.doc.grand_total - self.doc.write_off_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.doc.outstanding_amount = flt(total_amount_to_pay - self.doc.total_advance \
self.precision("outstanding_amount")) - self.doc.paid_amount, self.precision("outstanding_amount"))
def calculate_commission(self): def calculate_commission(self):
if self.meta.get_field("commission_rate"): if self.meta.get_field("commission_rate"):

View File

@@ -2,7 +2,7 @@
{ {
"creation": "2013-01-24 11:03:32", "creation": "2013-01-24 11:03:32",
"docstatus": 0, "docstatus": 0,
"modified": "2013-07-22 15:25:26", "modified": "2013-12-09 16:24:07",
"modified_by": "Administrator", "modified_by": "Administrator",
"owner": "Administrator" "owner": "Administrator"
}, },
@@ -85,7 +85,7 @@
"doctype": "DocField", "doctype": "DocField",
"fieldname": "exemption_limit", "fieldname": "exemption_limit",
"fieldtype": "Float", "fieldtype": "Float",
"hidden": 1, "hidden": 0,
"label": "Exemption Limit", "label": "Exemption Limit",
"oldfieldname": "exemption_limit", "oldfieldname": "exemption_limit",
"oldfieldtype": "Currency" "oldfieldtype": "Currency"

View File

@@ -2,7 +2,7 @@
{ {
"creation": "2013-02-20 19:10:38", "creation": "2013-02-20 19:10:38",
"docstatus": 0, "docstatus": 0,
"modified": "2013-07-05 14:44:19", "modified": "2013-12-12 17:41:52",
"modified_by": "Administrator", "modified_by": "Administrator",
"owner": "Administrator" "owner": "Administrator"
}, },
@@ -131,10 +131,12 @@
"label": "Carry Forward" "label": "Carry Forward"
}, },
{ {
"depends_on": "carry_forward",
"doctype": "DocField", "doctype": "DocField",
"fieldname": "carry_forwarded_leaves", "fieldname": "carry_forwarded_leaves",
"fieldtype": "Float", "fieldtype": "Float",
"label": "Carry Forwarded Leaves" "label": "Carry Forwarded Leaves",
"read_only": 1
}, },
{ {
"allow_on_submit": 1, "allow_on_submit": 1,

View File

@@ -12,7 +12,6 @@ class DocType:
self.doc = doc self.doc = doc
self.doclist = doclist self.doclist = doclist
def get_emp_list(self): def get_emp_list(self):
""" """
Returns list of active employees based on selected criteria Returns list of active employees based on selected criteria

View File

@@ -29,18 +29,17 @@ class DocType(TransactionBase):
if struct: if struct:
self.pull_sal_struct(struct) self.pull_sal_struct(struct)
def check_sal_struct(self): def check_sal_struct(self):
struct = webnotes.conn.sql("select name from `tabSalary Structure` where employee ='%s' and is_active = 'Yes' "%self.doc.employee) struct = webnotes.conn.sql("""select name from `tabSalary Structure`
where employee=%s and is_active = 'Yes'""", self.doc.employee)
if not struct: if not struct:
msgprint("Please create Salary Structure for employee '%s'"%self.doc.employee) msgprint("Please create Salary Structure for employee '%s'" % self.doc.employee)
self.doc.employee = '' self.doc.employee = None
return struct and struct[0][0] or '' return struct and struct[0][0] or ''
def pull_sal_struct(self, struct): def pull_sal_struct(self, struct):
from hr.doctype.salary_structure.salary_structure import make_salary_slip from hr.doctype.salary_structure.salary_structure import get_mapped_doclist
self.doclist = make_salary_slip(struct, self.doclist) self.doclist = get_mapped_doclist(struct, self.doclist)
def pull_emp_details(self): def pull_emp_details(self):
emp = webnotes.conn.get_value("Employee", self.doc.employee, emp = webnotes.conn.get_value("Employee", self.doc.employee,

View File

@@ -74,6 +74,9 @@ class DocType:
@webnotes.whitelist() @webnotes.whitelist()
def make_salary_slip(source_name, target_doclist=None): def make_salary_slip(source_name, target_doclist=None):
return [d.fields for d in get_mapped_doclist(source_name, target_doclist)]
def get_mapped_doclist(source_name, target_doclist=None):
from webnotes.model.mapper import get_mapped_doclist from webnotes.model.mapper import get_mapped_doclist
def postprocess(source, target): def postprocess(source, target):
@@ -109,4 +112,4 @@ def make_salary_slip(source_name, target_doclist=None):
} }
}, target_doclist, postprocess) }, target_doclist, postprocess)
return [d.fields for d in doclist] return doclist

View File

@@ -2,7 +2,7 @@
{ {
"creation": "2013-01-22 15:11:38", "creation": "2013-01-22 15:11:38",
"docstatus": 0, "docstatus": 0,
"modified": "2013-12-04 11:51:36", "modified": "2013-12-09 16:25:50",
"modified_by": "Administrator", "modified_by": "Administrator",
"owner": "Administrator" "owner": "Administrator"
}, },
@@ -121,12 +121,12 @@
}, },
{ {
"depends_on": "with_operations", "depends_on": "with_operations",
"description": "Specify the operations, operating cost and give a unique Operation no to your operations.",
"doctype": "DocField", "doctype": "DocField",
"fieldname": "operations", "fieldname": "operations",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Operations", "label": "Operations",
"oldfieldtype": "Section Break", "oldfieldtype": "Section Break"
"options": "Specify the operations, operating cost and give a unique Operation no to your operations."
}, },
{ {
"doctype": "DocField", "doctype": "DocField",

View File

@@ -9,7 +9,6 @@ from webnotes.model.bean import getlist
from webnotes.model.code import get_obj from webnotes.model.code import get_obj
from webnotes import msgprint, _ from webnotes import msgprint, _
class DocType: class DocType:
def __init__(self, doc, doclist=[]): def __init__(self, doc, doclist=[]):
self.doc = doc self.doc = doc
@@ -47,7 +46,7 @@ class DocType:
def validate_company(self): def validate_company(self):
if not self.doc.company: if not self.doc.company:
msgprint("Please enter Company", raise_exception=1) webnotes.throw(_("Please enter Company"))
def get_open_sales_orders(self): def get_open_sales_orders(self):
""" Pull sales orders which are pending to deliver based on criteria selected""" """ Pull sales orders which are pending to deliver based on criteria selected"""
@@ -72,9 +71,9 @@ class DocType:
and (exists (select name from `tabItem` item where item.name=so_item.item_code and (exists (select name from `tabItem` item where item.name=so_item.item_code
and (ifnull(item.is_pro_applicable, 'No') = 'Yes' and (ifnull(item.is_pro_applicable, 'No') = 'Yes'
or ifnull(item.is_sub_contracted_item, 'No') = 'Yes') %s) or ifnull(item.is_sub_contracted_item, 'No') = 'Yes') %s)
or exists (select name from `tabPacked Item` dnpi or exists (select name from `tabPacked Item` pi
where dnpi.parent = so.name and dnpi.parent_item = so_item.item_code where pi.parent = so.name and pi.parent_item = so_item.item_code
and exists (select name from `tabItem` item where item.name=dnpi.item_code and exists (select name from `tabItem` item where item.name=pi.item_code
and (ifnull(item.is_pro_applicable, 'No') = 'Yes' and (ifnull(item.is_pro_applicable, 'No') = 'Yes'
or ifnull(item.is_sub_contracted_item, 'No') = 'Yes') %s))) or ifnull(item.is_sub_contracted_item, 'No') = 'Yes') %s)))
""" % ('%s', so_filter, item_filter, item_filter), self.doc.company, as_dict=1) """ % ('%s', so_filter, item_filter, item_filter), self.doc.company, as_dict=1)
@@ -83,6 +82,8 @@ class DocType:
def add_so_in_table(self, open_so): def add_so_in_table(self, open_so):
""" Add sales orders in the table""" """ Add sales orders in the table"""
self.clear_so_table()
so_list = [d.sales_order for d in getlist(self.doclist, 'pp_so_details')] so_list = [d.sales_order for d in getlist(self.doclist, 'pp_so_details')]
for r in open_so: for r in open_so:
if cstr(r['name']) not in so_list: if cstr(r['name']) not in so_list:
@@ -104,7 +105,7 @@ class DocType:
def get_items(self): def get_items(self):
so_list = filter(None, [d.sales_order for d in getlist(self.doclist, 'pp_so_details')]) so_list = filter(None, [d.sales_order for d in getlist(self.doclist, 'pp_so_details')])
if not so_list: if not so_list:
msgprint("Please enter sales order in the above table") msgprint(_("Please enter sales order in the above table"))
return [] return []
items = webnotes.conn.sql("""select distinct parent, item_code, reserved_warehouse, items = webnotes.conn.sql("""select distinct parent, item_code, reserved_warehouse,
@@ -116,19 +117,19 @@ class DocType:
or ifnull(item.is_sub_contracted_item, 'No') = 'Yes'))""" % \ or ifnull(item.is_sub_contracted_item, 'No') = 'Yes'))""" % \
(", ".join(["%s"] * len(so_list))), tuple(so_list), as_dict=1) (", ".join(["%s"] * len(so_list))), tuple(so_list), as_dict=1)
dnpi_items = webnotes.conn.sql("""select distinct dnpi.parent, dnpi.item_code, dnpi.warehouse as reserved_warhouse, packed_items = webnotes.conn.sql("""select distinct pi.parent, pi.item_code, pi.warehouse as reserved_warhouse,
(((so_item.qty - ifnull(so_item.delivered_qty, 0)) * dnpi.qty) / so_item.qty) (((so_item.qty - ifnull(so_item.delivered_qty, 0)) * pi.qty) / so_item.qty)
as pending_qty as pending_qty
from `tabSales Order Item` so_item, `tabPacked Item` dnpi from `tabSales Order Item` so_item, `tabPacked Item` pi
where so_item.parent = dnpi.parent and so_item.docstatus = 1 where so_item.parent = pi.parent and so_item.docstatus = 1
and dnpi.parent_item = so_item.item_code and pi.parent_item = so_item.item_code
and so_item.parent in (%s) and ifnull(so_item.qty, 0) > ifnull(so_item.delivered_qty, 0) and so_item.parent in (%s) and ifnull(so_item.qty, 0) > ifnull(so_item.delivered_qty, 0)
and exists (select * from `tabItem` item where item.name=dnpi.item_code and exists (select * from `tabItem` item where item.name=pi.item_code
and (ifnull(item.is_pro_applicable, 'No') = 'Yes' and (ifnull(item.is_pro_applicable, 'No') = 'Yes'
or ifnull(item.is_sub_contracted_item, 'No') = 'Yes'))""" % \ or ifnull(item.is_sub_contracted_item, 'No') = 'Yes'))""" % \
(", ".join(["%s"] * len(so_list))), tuple(so_list), as_dict=1) (", ".join(["%s"] * len(so_list))), tuple(so_list), as_dict=1)
return items + dnpi_items return items + packed_items
def add_items(self, items): def add_items(self, items):
@@ -153,21 +154,21 @@ class DocType:
for d in getlist(self.doclist, 'pp_details'): for d in getlist(self.doclist, 'pp_details'):
self.validate_bom_no(d) self.validate_bom_no(d)
if not flt(d.planned_qty): if not flt(d.planned_qty):
msgprint("Please Enter Planned Qty for item: %s at row no: %s" % webnotes.throw("Please Enter Planned Qty for item: %s at row no: %s" %
(d.item_code, d.idx), raise_exception=1) (d.item_code, d.idx))
def validate_bom_no(self, d): def validate_bom_no(self, d):
if not d.bom_no: if not d.bom_no:
msgprint("Please enter bom no for item: %s at row no: %s" % webnotes.throw("Please enter bom no for item: %s at row no: %s" %
(d.item_code, d.idx), raise_exception=1) (d.item_code, d.idx))
else: else:
bom = webnotes.conn.sql("""select name from `tabBOM` where name = %s and item = %s bom = webnotes.conn.sql("""select name from `tabBOM` where name = %s and item = %s
and docstatus = 1 and is_active = 1""", and docstatus = 1 and is_active = 1""",
(d.bom_no, d.item_code), as_dict = 1) (d.bom_no, d.item_code), as_dict = 1)
if not bom: if not bom:
msgprint("""Incorrect BOM No: %s entered for item: %s at row no: %s webnotes.throw("""Incorrect BOM No: %s entered for item: %s at row no: %s
May be BOM is inactive or for other item or does not exists in the system""" % May be BOM is inactive or for other item or does not exists in the system""" %
(d.bom_no, d.item_doce, d.idx), raise_exception=1) (d.bom_no, d.item_doce, d.idx))
def raise_production_order(self): def raise_production_order(self):
"""It will raise production order (Draft) for all distinct FG items""" """It will raise production order (Draft) for all distinct FG items"""
@@ -181,16 +182,20 @@ class DocType:
if pro: if pro:
pro = ["""<a href="#Form/Production Order/%s" target="_blank">%s</a>""" % \ pro = ["""<a href="#Form/Production Order/%s" target="_blank">%s</a>""" % \
(p, p) for p in pro] (p, p) for p in pro]
msgprint("Production Order(s) created:\n\n" + '\n'.join(pro)) msgprint(_("Production Order(s) created:\n\n") + '\n'.join(pro))
else : else :
msgprint("No Production Order created.") msgprint(_("No Production Order created."))
def get_distinct_items_and_boms(self): def get_distinct_items_and_boms(self):
""" Club similar BOM and item for processing""" """ Club similar BOM and item for processing
bom_dict {
bom_no: ['sales_order', 'qty']
}
"""
item_dict, bom_dict = {}, {} item_dict, bom_dict = {}, {}
for d in self.doclist.get({"parentfield": "pp_details"}): for d in self.doclist.get({"parentfield": "pp_details"}):
bom_dict[d.bom_no] = bom_dict.get(d.bom_no, 0) + flt(d.planned_qty) bom_dict.setdefault(d.bom_no, []).append([d.sales_order, flt(d.planned_qty)])
item_dict[(d.item_code, d.sales_order, d.warehouse)] = { item_dict[(d.item_code, d.sales_order, d.warehouse)] = {
"production_item" : d.item_code, "production_item" : d.item_code,
"sales_order" : d.sales_order, "sales_order" : d.sales_order,
@@ -239,48 +244,60 @@ class DocType:
"item_code": [qty_required, description, stock_uom, min_order_qty] "item_code": [qty_required, description, stock_uom, min_order_qty]
} }
""" """
for bom in bom_dict: bom_wise_item_details = {}
item_list = []
for bom, so_wise_qty in bom_dict.items():
if self.doc.use_multi_level_bom: if self.doc.use_multi_level_bom:
# get all raw materials with sub assembly childs # get all raw materials with sub assembly childs
fl_bom_items = webnotes.conn.sql("""select fb.item_code, for d in webnotes.conn.sql("""select fb.item_code,
ifnull(sum(fb.qty_consumed_per_unit), 0)*%s as qty, ifnull(sum(fb.qty_consumed_per_unit), 0) as qty,
fb.description, fb.stock_uom, it.min_order_qty fb.description, fb.stock_uom, it.min_order_qty
from `tabBOM Explosion Item` fb,`tabItem` it from `tabBOM Explosion Item` fb,`tabItem` it
where it.name = fb.item_code and ifnull(it.is_pro_applicable, 'No') = 'No' where it.name = fb.item_code and ifnull(it.is_pro_applicable, 'No') = 'No'
and ifnull(it.is_sub_contracted_item, 'No') = 'No' and ifnull(it.is_sub_contracted_item, 'No') = 'No'
and fb.docstatus<2 and fb.parent=%s and fb.docstatus<2 and fb.parent=%s
group by item_code, stock_uom""", (flt(bom_dict[bom]), bom)) group by item_code, stock_uom""", bom, as_dict=1):
bom_wise_item_details.setdefault(d.item_code, d)
else: else:
# Get all raw materials considering SA items as raw materials, # Get all raw materials considering SA items as raw materials,
# so no childs of SA items # so no childs of SA items
fl_bom_items = webnotes.conn.sql("""select bom_item.item_code, for d in webnotes.conn.sql("""select bom_item.item_code,
ifnull(sum(bom_item.qty_consumed_per_unit), 0) * %s, ifnull(sum(bom_item.qty_consumed_per_unit), 0) as qty,
bom_item.description, bom_item.stock_uom, item.min_order_qty bom_item.description, bom_item.stock_uom, item.min_order_qty
from `tabBOM Item` bom_item, tabItem item from `tabBOM Item` bom_item, tabItem item
where bom_item.parent = %s and bom_item.docstatus < 2 where bom_item.parent = %s and bom_item.docstatus < 2
and bom_item.item_code = item.name and bom_item.item_code = item.name
group by item_code""", (flt(bom_dict[bom]), bom)) group by item_code""", bom, as_dict=1):
self.make_items_dict(fl_bom_items) bom_wise_item_details.setdefault(d.item_code, d)
for item, item_details in bom_wise_item_details.items():
for so_qty in so_wise_qty:
item_list.append([item, flt(item_details.qty) * so_qty[1], item_details.description,
item_details.stock_uom, item_details.min_order_qty, so_qty[0]])
self.make_items_dict(item_list)
def make_items_dict(self, item_list): def make_items_dict(self, item_list):
for i in item_list: for i in item_list:
self.item_dict[i[0]] = [(flt(self.item_dict.get(i[0], [0])[0]) + flt(i[1])), self.item_dict.setdefault(i[0], []).append([flt(i[1]), i[2], i[3], i[4], i[5]])
i[2], i[3], i[4]]
def get_csv(self): def get_csv(self):
item_list = [['Item Code', 'Description', 'Stock UOM', 'Required Qty', 'Warehouse', item_list = [['Item Code', 'Description', 'Stock UOM', 'Required Qty', 'Warehouse',
'Quantity Requested for Purchase', 'Ordered Qty', 'Actual Qty']] 'Quantity Requested for Purchase', 'Ordered Qty', 'Actual Qty']]
for d in self.item_dict: for item in self.item_dict:
item_list.append([d, self.item_dict[d][1], self.item_dict[d][2], self.item_dict[d][0]]) total_qty = sum([flt(d[0]) for d in self.item_dict[item]])
item_qty= webnotes.conn.sql("""select warehouse, indented_qty, ordered_qty, actual_qty for item_details in self.item_dict[item]:
from `tabBin` where item_code = %s""", d) item_list.append([item, item_details[1], item_details[2], item_details[0]])
i_qty, o_qty, a_qty = 0, 0, 0 item_qty = webnotes.conn.sql("""select warehouse, indented_qty, ordered_qty, actual_qty
for w in item_qty: from `tabBin` where item_code = %s""", item, as_dict=1)
i_qty, o_qty, a_qty = i_qty + flt(w[1]), o_qty + flt(w[2]), a_qty + flt(w[3]) i_qty, o_qty, a_qty = 0, 0, 0
item_list.append(['', '', '', '', w[0], flt(w[1]), flt(w[2]), flt(w[3])]) for w in item_qty:
if item_qty: i_qty, o_qty, a_qty = i_qty + flt(w.indented_qty), o_qty + flt(w.ordered_qty), a_qty + flt(w.actual_qty)
item_list.append(['', '', '', '', 'Total', i_qty, o_qty, a_qty]) item_list.append(['', '', '', '', w.warehouse, flt(w.indented_qty),
flt(w.ordered_qty), flt(w.actual_qty)])
if item_qty:
item_list.append(['', '', '', '', 'Total', i_qty, o_qty, a_qty])
return item_list return item_list
@@ -291,31 +308,49 @@ class DocType:
""" """
self.validate_data() self.validate_data()
if not self.doc.purchase_request_for_warehouse: if not self.doc.purchase_request_for_warehouse:
webnotes.msgprint("Please enter Warehouse for which Material Request will be raised", webnotes.throw(_("Please enter Warehouse for which Material Request will be raised"))
raise_exception=1)
bom_dict = self.get_distinct_items_and_boms()[0] bom_dict = self.get_distinct_items_and_boms()[0]
self.get_raw_materials(bom_dict) self.get_raw_materials(bom_dict)
if not self.item_dict: if self.item_dict:
return self.insert_purchase_request()
def get_requested_items(self):
item_projected_qty = self.get_projected_qty() item_projected_qty = self.get_projected_qty()
from accounts.utils import get_fiscal_year
fiscal_year = get_fiscal_year(nowdate())[0]
items_to_be_requested = webnotes._dict() items_to_be_requested = webnotes._dict()
for item in self.item_dict:
if flt(self.item_dict[item][0]) > item_projected_qty.get(item, 0): for item, so_item_qty in self.item_dict.items():
requested_qty = 0
total_qty = sum([flt(d[0]) for d in so_item_qty])
if total_qty > item_projected_qty.get(item, 0):
# shortage # shortage
requested_qty = flt(self.item_dict[item][0]) - item_projected_qty.get(item, 0) requested_qty = total_qty - item_projected_qty.get(item, 0)
# comsider minimum order qty # consider minimum order qty
requested_qty = requested_qty > flt(self.item_dict[item][3]) and \ requested_qty = requested_qty > flt(so_item_qty[0][3]) and \
requested_qty or flt(self.item_dict[item][3]) requested_qty or flt(so_item_qty[0][3])
items_to_be_requested[item] = requested_qty
# distribute requested qty SO wise
self.insert_purchase_request(items_to_be_requested, fiscal_year) for item_details in so_item_qty:
if requested_qty:
sales_order = item_details[4] or "No Sales Order"
if requested_qty <= item_details[0]:
adjusted_qty = requested_qty
else:
adjusted_qty = item_details[0]
items_to_be_requested.setdefault(item, {}).setdefault(sales_order, 0)
items_to_be_requested[item][sales_order] += adjusted_qty
requested_qty -= adjusted_qty
else:
break
# requested qty >= total so qty, due to minimum order qty
if requested_qty:
items_to_be_requested.setdefault(item, {}).setdefault("No Sales Order", 0)
items_to_be_requested[item]["No Sales Order"] += requested_qty
return items_to_be_requested
def get_projected_qty(self): def get_projected_qty(self):
items = self.item_dict.keys() items = self.item_dict.keys()
@@ -325,24 +360,29 @@ class DocType:
return dict(item_projected_qty) return dict(item_projected_qty)
def insert_purchase_request(self, items_to_be_requested, fiscal_year): def insert_purchase_request(self):
items_to_be_requested = self.get_requested_items()
from accounts.utils import get_fiscal_year
fiscal_year = get_fiscal_year(nowdate())[0]
purchase_request_list = [] purchase_request_list = []
if items_to_be_requested: if items_to_be_requested:
for item in items_to_be_requested: for item in items_to_be_requested:
item_wrapper = webnotes.bean("Item", item) item_wrapper = webnotes.bean("Item", item)
pr_doclist = [ pr_doclist = [{
{ "doctype": "Material Request",
"doctype": "Material Request", "__islocal": 1,
"__islocal": 1, "naming_series": "IDT",
"naming_series": "IDT", "transaction_date": nowdate(),
"transaction_date": nowdate(), "status": "Draft",
"status": "Draft", "company": self.doc.company,
"company": self.doc.company, "fiscal_year": fiscal_year,
"fiscal_year": fiscal_year, "requested_by": webnotes.session.user,
"requested_by": webnotes.session.user, "material_request_type": "Purchase"
"material_request_type": "Purchase" }]
}, for sales_order, requested_qty in items_to_be_requested[item].items():
{ pr_doclist.append({
"doctype": "Material Request Item", "doctype": "Material Request Item",
"__islocal": 1, "__islocal": 1,
"parentfield": "indent_details", "parentfield": "indent_details",
@@ -352,11 +392,12 @@ class DocType:
"uom": item_wrapper.doc.stock_uom, "uom": item_wrapper.doc.stock_uom,
"item_group": item_wrapper.doc.item_group, "item_group": item_wrapper.doc.item_group,
"brand": item_wrapper.doc.brand, "brand": item_wrapper.doc.brand,
"qty": items_to_be_requested[item], "qty": requested_qty,
"schedule_date": add_days(nowdate(), cint(item_wrapper.doc.lead_time_days)), "schedule_date": add_days(nowdate(), cint(item_wrapper.doc.lead_time_days)),
"warehouse": self.doc.purchase_request_for_warehouse "warehouse": self.doc.purchase_request_for_warehouse,
} "sales_order_no": sales_order if sales_order!="No Sales Order" else None
] })
pr_wrapper = webnotes.bean(pr_doclist) pr_wrapper = webnotes.bean(pr_doclist)
pr_wrapper.ignore_permissions = 1 pr_wrapper.ignore_permissions = 1
pr_wrapper.submit() pr_wrapper.submit()
@@ -365,7 +406,7 @@ class DocType:
if purchase_request_list: if purchase_request_list:
pur_req = ["""<a href="#Form/Material Request/%s" target="_blank">%s</a>""" % \ pur_req = ["""<a href="#Form/Material Request/%s" target="_blank">%s</a>""" % \
(p, p) for p in purchase_request_list] (p, p) for p in purchase_request_list]
webnotes.msgprint("Material Request(s) created: \n%s" % msgprint("Material Request(s) created: \n%s" %
"\n".join(pur_req)) "\n".join(pur_req))
else: else:
webnotes.msgprint("Nothing to request") msgprint(_("Nothing to request"))

View File

@@ -0,0 +1,25 @@
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import webnotes
def execute():
webnotes.reload_doc("setup", "doctype", "email_digest")
from webnotes.profile import get_system_managers
system_managers = get_system_managers(only_name=True)
if not system_managers:
return
# scheduler errors digest
edigest = webnotes.new_bean("Email Digest")
edigest.doc.fields.update({
"name": "Scheduler Errors",
"company": webnotes.conn.get_default("company"),
"frequency": "Daily",
"enabled": 1,
"recipient_list": "\n".join(system_managers),
"scheduler_errors": 1
})
edigest.insert()

View File

@@ -0,0 +1,11 @@
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import webnotes
def execute():
from webnotes.utils import extract_email_id
for name, recipients in webnotes.conn.sql("""select name, recipient_list from `tabEmail Digest`"""):
recipients = "\n".join([extract_email_id(r) for r in recipients.split("\n")])
webnotes.conn.set_value("Email Digest", name, "recipient_list", recipients)

View File

@@ -0,0 +1,17 @@
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
def execute():
import webnotes, os, shutil
from webnotes.utils import get_base_path
webnotes.delete_doc('Page', 'stock-ledger')
webnotes.delete_doc('Page', 'stock-ageing')
webnotes.delete_doc('Page', 'stock-level')
webnotes.delete_doc('Page', 'general-ledger')
for d in [["stock", "stock_ledger"], ["stock", "stock_ageing"],
["stock", "stock_level"], ["accounts", "general_ledger"]]:
path = os.path.join(get_base_path(), "app", d[0], "page", d[1])
if os.path.exists(path):
shutil.rmtree(path)

View File

@@ -37,7 +37,6 @@ def execute():
webnotes.delete_doc("DocType", "Question") webnotes.delete_doc("DocType", "Question")
webnotes.delete_doc("DocType", "Answer") webnotes.delete_doc("DocType", "Answer")
webnotes.bean("Style Settings").save()
# update comment delete # update comment delete
webnotes.conn.sql("""update tabDocPerm \ webnotes.conn.sql("""update tabDocPerm \

View File

@@ -15,4 +15,5 @@ def execute():
# reset property setters for series # reset property setters for series
for name in ("Stock Settings", "Selling Settings", "Buying Settings", "HR Settings"): for name in ("Stock Settings", "Selling Settings", "Buying Settings", "HR Settings"):
webnotes.bean(name, name).save() webnotes.reload_doc(name.split()[0], 'DocType', name)
webnotes.bean(name, name).save()

View File

@@ -256,6 +256,9 @@ patch_list = [
"patches.1311.p06_fix_report_columns", "patches.1311.p06_fix_report_columns",
"execute:webnotes.delete_doc('DocType', 'Documentation Tool')", "execute:webnotes.delete_doc('DocType', 'Documentation Tool')",
"execute:webnotes.delete_doc('Report', 'Stock Ledger') #2013-11-29", "execute:webnotes.delete_doc('Report', 'Stock Ledger') #2013-11-29",
"patches.1312.p01_delete_old_stock_reports",
"execute:webnotes.delete_doc('Report', 'Payment Collection With Ageing')", "execute:webnotes.delete_doc('Report', 'Payment Collection With Ageing')",
"execute:webnotes.delete_doc('Report', 'Payment Made With Ageing')", "execute:webnotes.delete_doc('Report', 'Payment Made With Ageing')",
"patches.1311.p07_scheduler_errors_digest",
"patches.1311.p08_email_digest_recipients",
] ]

View File

@@ -47,6 +47,14 @@ class DocType(TransactionBase):
if self.doc.lead_name: if self.doc.lead_name:
webnotes.conn.sql("update `tabLead` set status='Converted' where name = %s", self.doc.lead_name) webnotes.conn.sql("update `tabLead` set status='Converted' where name = %s", self.doc.lead_name)
def update_address(self):
webnotes.conn.sql("""update `tabAddress` set customer_name=%s, modified=NOW()
where customer=%s""", (self.doc.customer_name, self.doc.name))
def update_contact(self):
webnotes.conn.sql("""update `tabContact` set customer_name=%s, modified=NOW()
where customer=%s""", (self.doc.customer_name, self.doc.name))
def create_account_head(self): def create_account_head(self):
if self.doc.company : if self.doc.company :
abbr = self.get_company_abbr() abbr = self.get_company_abbr()
@@ -99,6 +107,9 @@ class DocType(TransactionBase):
self.validate_name_with_customer_group() self.validate_name_with_customer_group()
self.update_lead_status() self.update_lead_status()
self.update_address()
self.update_contact()
# create account head # create account head
self.create_account_head() self.create_account_head()
# update credit days and limit in account # update credit days and limit in account
@@ -146,10 +157,19 @@ class DocType(TransactionBase):
def before_rename(self, olddn, newdn, merge=False): def before_rename(self, olddn, newdn, merge=False):
from accounts.utils import rename_account_for from accounts.utils import rename_account_for
rename_account_for("Customer", olddn, newdn, merge) rename_account_for("Customer", olddn, newdn, merge)
def after_rename(self, olddn, newdn, merge=False): def after_rename(self, olddn, newdn, merge=False):
set_field = ''
if webnotes.defaults.get_global_default('cust_master_name') == 'Customer Name': if webnotes.defaults.get_global_default('cust_master_name') == 'Customer Name':
webnotes.conn.set(self.doc, "customer_name", newdn) webnotes.conn.set(self.doc, "customer_name", newdn)
self.update_contact()
set_field = ", customer_name=%(newdn)s"
self.update_customer_address(newdn, set_field)
def update_customer_address(self, newdn, set_field):
webnotes.conn.sql("""update `tabAddress` set address_title=%(newdn)s
{set_field} where customer=%(newdn)s"""\
.format(set_field=set_field), ({"newdn": newdn}))
@webnotes.whitelist() @webnotes.whitelist()
def get_dashboard_info(customer): def get_dashboard_info(customer):

View File

@@ -2,7 +2,7 @@
{ {
"creation": "2013-05-24 19:29:08", "creation": "2013-05-24 19:29:08",
"docstatus": 0, "docstatus": 0,
"modified": "2013-11-27 17:57:19", "modified": "2013-12-14 17:25:46",
"modified_by": "Administrator", "modified_by": "Administrator",
"owner": "Administrator" "owner": "Administrator"
}, },
@@ -863,6 +863,7 @@
"cancel": 0, "cancel": 0,
"create": 0, "create": 0,
"doctype": "DocPerm", "doctype": "DocPerm",
"match": "customer",
"role": "Customer", "role": "Customer",
"submit": 0, "submit": 0,
"write": 0 "write": 0

View File

@@ -110,12 +110,12 @@ class DocType(SellingController):
self.validate_uom_is_integer("stock_uom", "qty") self.validate_uom_is_integer("stock_uom", "qty")
self.validate_for_items() self.validate_for_items()
self.validate_warehouse() self.validate_warehouse()
from stock.doctype.packed_item.packed_item import make_packing_list from stock.doctype.packed_item.packed_item import make_packing_list
self.doclist = make_packing_list(self,'sales_order_details') self.doclist = make_packing_list(self,'sales_order_details')
self.validate_with_previous_doc() self.validate_with_previous_doc()
if not self.doc.status: if not self.doc.status:
self.doc.status = "Draft" self.doc.status = "Draft"
@@ -124,8 +124,7 @@ class DocType(SellingController):
"Cancelled"]) "Cancelled"])
if not self.doc.billing_status: self.doc.billing_status = 'Not Billed' if not self.doc.billing_status: self.doc.billing_status = 'Not Billed'
if not self.doc.delivery_status: self.doc.delivery_status = 'Not Delivered' if not self.doc.delivery_status: self.doc.delivery_status = 'Not Delivered'
def validate_warehouse(self): def validate_warehouse(self):
from stock.utils import validate_warehouse_user, validate_warehouse_company from stock.utils import validate_warehouse_user, validate_warehouse_company

View File

@@ -95,7 +95,7 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({
refresh: function() { refresh: function() {
this._super(); this._super();
this.frm.toggle_display("customer_name", this.frm.toggle_display("customer_name",
(this.customer_name && this.frm.doc.customer_name!==this.frm.doc.customer)); (this.frm.doc.customer_name && this.frm.doc.customer_name!==this.frm.doc.customer));
if(this.frm.fields_dict.packing_details) { if(this.frm.fields_dict.packing_details) {
var packing_list_exists = this.frm.get_doclist({parentfield: "packing_details"}).length; var packing_list_exists = this.frm.get_doclist({parentfield: "packing_details"}).length;
this.frm.toggle_display("packing_list", packing_list_exists ? true : false); this.frm.toggle_display("packing_list", packing_list_exists ? true : false);
@@ -515,7 +515,6 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({
} }
}); });
}; };
setup_field_label_map(["net_total", "other_charges_total", "grand_total", setup_field_label_map(["net_total", "other_charges_total", "grand_total",
"rounded_total", "in_words", "rounded_total", "in_words",
"outstanding_amount", "total_advance", "paid_amount", "write_off_amount"], "outstanding_amount", "total_advance", "paid_amount", "write_off_amount"],

View File

@@ -70,8 +70,10 @@ cur_frm.cscript.addremove_recipients = function(doc, dt, dn) {
check.checked = 1; check.checked = 1;
add_or_update = 'Update'; add_or_update = 'Update';
} }
var fullname = wn.user.full_name(v.name);
if(fullname !== v.name) v.name = fullname + " &lt;" + v.name + "&gt;";
if(v.enabled==0) { if(v.enabled==0) {
v.name = "<span style='color: red'>" + v.name + " (disabled user)</span>" v.name = repl("<span style='color: red'> %(name)s (disabled user)</span>", {name: v.name});
} }
var profile = $a($td(tab, i+1, 1), 'span', '', '', v.name); var profile = $a($td(tab, i+1, 1), 'span', '', '', v.name);
//profile.onclick = function() { check.checked = !check.checked; } //profile.onclick = function() { check.checked = !check.checked; }

View File

@@ -19,16 +19,16 @@ content_sequence = [
["Selling", ["new_leads", "new_enquiries", "new_quotations", "new_sales_orders"]], ["Selling", ["new_leads", "new_enquiries", "new_quotations", "new_sales_orders"]],
["Stock", ["new_delivery_notes", "new_purchase_receipts", "new_stock_entries"]], ["Stock", ["new_delivery_notes", "new_purchase_receipts", "new_stock_entries"]],
["Support", ["new_communications", "new_support_tickets", "open_tickets"]], ["Support", ["new_communications", "new_support_tickets", "open_tickets"]],
["Projects", ["new_projects"]] ["Projects", ["new_projects"]],
["System", ["scheduler_errors"]],
] ]
user_specific_content = ["calendar_events", "todo_list"] user_specific_content = ["calendar_events", "todo_list"]
digest_template = """\ digest_template = """<style>p.ed-indent { margin-right: 17px; }</style>
<style>p.ed-indent { margin-right: 17px; }</style> <h2>%(name)s</h2>
<h2>%(digest)s</h2>
<p style='color: grey'>%(date)s</p>
<h4>%(company)s</h4> <h4>%(company)s</h4>
<p style='color: grey'>%(date)s</p>
<hr> <hr>
%(with_value)s %(with_value)s
%(no_value)s %(no_value)s
@@ -53,10 +53,10 @@ class DocType(DocListController):
def get_profiles(self): def get_profiles(self):
"""get list of profiles""" """get list of profiles"""
import webnotes
profile_list = webnotes.conn.sql(""" profile_list = webnotes.conn.sql("""
select name, enabled from tabProfile select name, enabled from tabProfile
where docstatus=0 and name not in ('Administrator', 'Guest') where docstatus=0 and name not in ('Administrator', 'Guest')
and user_type = "System User"
order by enabled desc, name asc""", as_dict=1) order by enabled desc, name asc""", as_dict=1)
if self.doc.recipient_list: if self.doc.recipient_list:
@@ -81,7 +81,9 @@ class DocType(DocListController):
msg_for_this_receipient = self.get_msg_html(self.get_user_specific_content(user_id) + \ msg_for_this_receipient = self.get_msg_html(self.get_user_specific_content(user_id) + \
common_msg) common_msg)
from webnotes.utils.email_lib import sendmail from webnotes.utils.email_lib import sendmail
sendmail(recipients=user_id, subject="[ERPNext] " + (self.doc.frequency + " Digest"), sendmail(recipients=user_id,
subject="[ERPNext] [{frequency} Digest] {name}".format(
frequency=self.doc.frequency, name=self.doc.name),
msg=msg_for_this_receipient) msg=msg_for_this_receipient)
def get_digest_msg(self): def get_digest_msg(self):
@@ -123,7 +125,7 @@ class DocType(DocListController):
if with_value: if with_value:
with_value = "\n".join(with_value) with_value = "\n".join(with_value)
else: else:
with_value = "<p>There were no updates in the items selected for this digest.</p>" with_value = "<p>There were no updates in the items selected for this digest.</p><hr>"
# seperate out no value items # seperate out no value items
no_value = [o[1] for o in out if not o[0]] no_value = [o[1] for o in out if not o[0]]
@@ -138,7 +140,8 @@ class DocType(DocListController):
"date": date, "date": date,
"company": self.doc.company, "company": self.doc.company,
"with_value": with_value, "with_value": with_value,
"no_value": no_value or "" "no_value": no_value or "",
"name": self.doc.name
} }
return msg return msg
@@ -241,7 +244,7 @@ class DocType(DocListController):
return self.get_new_count("Lead", self.meta.get_label("new_leads")) return self.get_new_count("Lead", self.meta.get_label("new_leads"))
def get_new_enquiries(self): def get_new_enquiries(self):
return self.get_new_count("Opportunity", self.meta.get_label("new_enquiries")) return self.get_new_count("Opportunity", self.meta.get_label("new_enquiries"), docstatus=1)
def get_new_quotations(self): def get_new_quotations(self):
return self.get_new_sum("Quotation", self.meta.get_label("new_quotations"), "grand_total") return self.get_new_sum("Quotation", self.meta.get_label("new_quotations"), "grand_total")
@@ -253,7 +256,8 @@ class DocType(DocListController):
return self.get_new_sum("Delivery Note", self.meta.get_label("new_delivery_notes"), "grand_total") return self.get_new_sum("Delivery Note", self.meta.get_label("new_delivery_notes"), "grand_total")
def get_new_purchase_requests(self): def get_new_purchase_requests(self):
return self.get_new_count("Material Request", self.meta.get_label("new_purchase_requests")) return self.get_new_count("Material Request",
self.meta.get_label("new_purchase_requests"), docstatus=1)
def get_new_supplier_quotations(self): def get_new_supplier_quotations(self):
return self.get_new_sum("Supplier Quotation", self.meta.get_label("new_supplier_quotations"), return self.get_new_sum("Supplier Quotation", self.meta.get_label("new_supplier_quotations"),
@@ -271,13 +275,16 @@ class DocType(DocListController):
return self.get_new_sum("Stock Entry", self.meta.get_label("new_stock_entries"), "total_amount") return self.get_new_sum("Stock Entry", self.meta.get_label("new_stock_entries"), "total_amount")
def get_new_support_tickets(self): def get_new_support_tickets(self):
return self.get_new_count("Support Ticket", self.meta.get_label("new_support_tickets"), False) return self.get_new_count("Support Ticket", self.meta.get_label("new_support_tickets"),
filter_by_company=False)
def get_new_communications(self): def get_new_communications(self):
return self.get_new_count("Communication", self.meta.get_label("new_communications"), False) return self.get_new_count("Communication", self.meta.get_label("new_communications"),
filter_by_company=False)
def get_new_projects(self): def get_new_projects(self):
return self.get_new_count("Project", self.meta.get_label("new_projects"), False) return self.get_new_count("Project", self.meta.get_label("new_projects"),
filter_by_company=False)
def get_calendar_events(self, user_id): def get_calendar_events(self, user_id):
from core.doctype.event.event import get_events from core.doctype.event.event import get_events
@@ -321,22 +328,22 @@ class DocType(DocListController):
else: else:
return 0, "<p>To Do</p>" return 0, "<p>To Do</p>"
def get_new_count(self, doctype, label, filter_by_company=True): def get_new_count(self, doctype, label, docstatus=0, filter_by_company=True):
if filter_by_company: if filter_by_company:
company = """and company="%s" """ % self.doc.company company = """and company="%s" """ % self.doc.company
else: else:
company = "" company = ""
count = webnotes.conn.sql("""select count(*) from `tab%s` count = webnotes.conn.sql("""select count(*) from `tab%s`
where docstatus < 2 %s and where docstatus=%s %s and
date(creation)>=%s and date(creation)<=%s""" % (doctype, company, "%s", "%s"), date(creation)>=%s and date(creation)<=%s""" %
(self.from_date, self.to_date)) (doctype, docstatus, company, "%s", "%s"), (self.from_date, self.to_date))
count = count and count[0][0] or 0 count = count and count[0][0] or 0
return count, self.get_html(label, None, count) return count, self.get_html(label, None, count)
def get_new_sum(self, doctype, label, sum_field): def get_new_sum(self, doctype, label, sum_field):
count_sum = webnotes.conn.sql("""select count(*), sum(ifnull(`%s`, 0)) count_sum = webnotes.conn.sql("""select count(*), sum(ifnull(`%s`, 0))
from `tab%s` where docstatus < 2 and company = %s and from `tab%s` where docstatus=1 and company = %s and
date(creation)>=%s and date(creation)<=%s""" % (sum_field, doctype, "%s", date(creation)>=%s and date(creation)<=%s""" % (sum_field, doctype, "%s",
"%s", "%s"), (self.doc.company, self.from_date, self.to_date)) "%s", "%s"), (self.doc.company, self.from_date, self.to_date))
count, total = count_sum and count_sum[0] or (0, 0) count, total = count_sum and count_sum[0] or (0, 0)
@@ -448,6 +455,10 @@ class DocType(DocListController):
t for t in open_tickets]) t for t in open_tickets])
else: else:
return 0, "No Open Tickets!" return 0, "No Open Tickets!"
def get_scheduler_errors(self):
import webnotes.utils.scheduler
return webnotes.utils.scheduler.get_error_report(self.from_date, self.to_date)
def onload(self): def onload(self):
self.get_next_sending() self.get_next_sending()
@@ -466,4 +477,4 @@ def send():
where enabled=1 and docstatus<2""", as_list=1): where enabled=1 and docstatus<2""", as_list=1):
ed_obj = get_obj('Email Digest', ed[0]) ed_obj = get_obj('Email Digest', ed[0])
if (now_date == ed_obj.get_next_sending()): if (now_date == ed_obj.get_next_sending()):
ed_obj.send() ed_obj.send()

View File

@@ -2,7 +2,7 @@
{ {
"creation": "2013-02-21 14:15:31", "creation": "2013-02-21 14:15:31",
"docstatus": 0, "docstatus": 0,
"modified": "2013-07-05 14:36:13", "modified": "2013-12-16 12:37:43",
"modified_by": "Administrator", "modified_by": "Administrator",
"owner": "Administrator" "owner": "Administrator"
}, },
@@ -100,11 +100,10 @@
"label": "Add/Remove Recipients" "label": "Add/Remove Recipients"
}, },
{ {
"description": "Check all the items below that you want to send in this digest.",
"doctype": "DocField", "doctype": "DocField",
"fieldname": "select_digest_content", "fieldname": "accounts",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Select Digest Content" "label": "Accounts"
}, },
{ {
"doctype": "DocField", "doctype": "DocField",
@@ -178,7 +177,7 @@
"doctype": "DocField", "doctype": "DocField",
"fieldname": "section_break_20", "fieldname": "section_break_20",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"options": "Simple" "label": "Buying & Selling"
}, },
{ {
"doctype": "DocField", "doctype": "DocField",
@@ -234,6 +233,12 @@
"fieldtype": "Check", "fieldtype": "Check",
"label": "New Sales Orders" "label": "New Sales Orders"
}, },
{
"doctype": "DocField",
"fieldname": "section_break_34",
"fieldtype": "Section Break",
"label": "Inventory & Support"
},
{ {
"doctype": "DocField", "doctype": "DocField",
"fieldname": "stock_module", "fieldname": "stock_module",
@@ -258,12 +263,6 @@
"fieldtype": "Check", "fieldtype": "Check",
"label": "New Stock Entries" "label": "New Stock Entries"
}, },
{
"doctype": "DocField",
"fieldname": "section_break_34",
"fieldtype": "Section Break",
"options": "Simple"
},
{ {
"doctype": "DocField", "doctype": "DocField",
"fieldname": "support_module", "fieldname": "support_module",
@@ -288,6 +287,12 @@
"fieldtype": "Check", "fieldtype": "Check",
"label": "New Communications" "label": "New Communications"
}, },
{
"doctype": "DocField",
"fieldname": "section_break_40",
"fieldtype": "Section Break",
"label": "Projects & System"
},
{ {
"doctype": "DocField", "doctype": "DocField",
"fieldname": "projects_module", "fieldname": "projects_module",
@@ -302,7 +307,25 @@
}, },
{ {
"doctype": "DocField", "doctype": "DocField",
"fieldname": "utilities_module", "fieldname": "core_module",
"fieldtype": "Column Break",
"label": "System"
},
{
"doctype": "DocField",
"fieldname": "scheduler_errors",
"fieldtype": "Check",
"label": "Scheduler Failed Events"
},
{
"doctype": "DocField",
"fieldname": "user_specific",
"fieldtype": "Section Break",
"label": "User Specific"
},
{
"doctype": "DocField",
"fieldname": "general",
"fieldtype": "Column Break", "fieldtype": "Column Break",
"label": "General" "label": "General"
}, },
@@ -318,6 +341,12 @@
"fieldtype": "Check", "fieldtype": "Check",
"label": "To Do List" "label": "To Do List"
}, },
{
"doctype": "DocField",
"fieldname": "stub",
"fieldtype": "Column Break",
"label": "Stub"
},
{ {
"cancel": 1, "cancel": 1,
"create": 1, "create": 1,

View File

@@ -2,7 +2,7 @@
{ {
"creation": "2013-03-25 17:53:21", "creation": "2013-03-25 17:53:21",
"docstatus": 0, "docstatus": 0,
"modified": "2013-11-28 11:54:42", "modified": "2013-12-06 13:12:25",
"modified_by": "Administrator", "modified_by": "Administrator",
"owner": "Administrator" "owner": "Administrator"
}, },
@@ -103,6 +103,7 @@
"label": "Auto Email Id" "label": "Auto Email Id"
}, },
{ {
"default": "1",
"description": "If checked, an email with an attached HTML format will be added to part of the EMail body as well as attachment. To only send as attachment, uncheck this.", "description": "If checked, an email with an attached HTML format will be added to part of the EMail body as well as attachment. To only send as attachment, uncheck this.",
"doctype": "DocField", "doctype": "DocField",
"fieldname": "send_print_in_body_and_attachment", "fieldname": "send_print_in_body_and_attachment",

View File

@@ -22,7 +22,7 @@ class DocType:
where fieldname='naming_series'""") where fieldname='naming_series'""")
)))), )))),
"prefixes": "\n".join([''] + [i[0] for i in "prefixes": "\n".join([''] + [i[0] for i in
webnotes.conn.sql("""select name from tabSeries""")]) webnotes.conn.sql("""select name from tabSeries order by name""")])
} }
def scrub_options_list(self, ol): def scrub_options_list(self, ol):
@@ -38,7 +38,7 @@ class DocType:
self.set_series_for(self.doc.select_doc_for_series, series_list) self.set_series_for(self.doc.select_doc_for_series, series_list)
# create series # create series
map(self.insert_series, series_list) map(self.insert_series, [d.split('.')[0] for d in series_list])
msgprint('Series Updated') msgprint('Series Updated')
@@ -103,7 +103,8 @@ class DocType:
dt.validate_series(series, self.doc.select_doc_for_series) dt.validate_series(series, self.doc.select_doc_for_series)
for i in sr: for i in sr:
if i[0]: if i[0]:
if series in i[0].split("\n"): existing_series = [d.split('.')[0] for d in i[0].split("\n")]
if series.split(".")[0] in existing_series:
msgprint("Oops! Series name %s is already in use in %s. \ msgprint("Oops! Series name %s is already in use in %s. \
Please select a new one" % (series, i[1]), raise_exception=1) Please select a new one" % (series, i[1]), raise_exception=1)
@@ -120,17 +121,21 @@ class DocType:
def get_current(self, arg=None): def get_current(self, arg=None):
"""get series current""" """get series current"""
self.doc.current_value = webnotes.conn.get_value("Series", self.doc.prefix, "current") self.doc.current_value = webnotes.conn.get_value("Series",
self.doc.prefix.split('.')[0], "current")
def insert_series(self, series): def insert_series(self, series):
"""insert series if missing""" """insert series if missing"""
if not webnotes.conn.exists('Series', series): if not webnotes.conn.exists('Series', series):
webnotes.conn.sql("insert into tabSeries (name, current) values (%s,0)", (series)) webnotes.conn.sql("insert into tabSeries (name, current) values (%s, 0)",
(series))
def update_series_start(self): def update_series_start(self):
if self.doc.prefix: if self.doc.prefix:
self.insert_series(self.doc.prefix) prefix = self.doc.prefix.split('.')[0]
webnotes.conn.sql("update `tabSeries` set current = '%s' where name = '%s'" % (self.doc.current_value,self.doc.prefix)) self.insert_series(prefix)
webnotes.conn.sql("update `tabSeries` set current = %s where name = %s",
(self.doc.current_value, prefix))
msgprint("Series Updated Successfully") msgprint("Series Updated Successfully")
else: else:
msgprint("Please select prefix first") msgprint("Please select prefix first")

View File

@@ -233,8 +233,9 @@ items = [
"route": "Report/Scheduler Log", "type": "Link", "icon": "icon-exclamation-sign" }, "route": "Report/Scheduler Log", "type": "Link", "icon": "icon-exclamation-sign" },
] ]
@webnotes.whitelist(allow_roles=["System Manager"]) @webnotes.whitelist()
def get(): def get():
webnotes.only_for("System Manager")
for item in items: for item in items:
if item.get("type")=="Section": if item.get("type")=="Section":
continue continue

View File

@@ -171,7 +171,7 @@ def create_feed_and_todo():
def create_email_digest(): def create_email_digest():
from webnotes.profile import get_system_managers from webnotes.profile import get_system_managers
system_managers = get_system_managers() system_managers = get_system_managers(only_name=True)
if not system_managers: if not system_managers:
return return
@@ -186,10 +186,23 @@ def create_email_digest():
}) })
for fieldname in edigest.meta.get_fieldnames({"fieldtype": "Check"}): for fieldname in edigest.meta.get_fieldnames({"fieldtype": "Check"}):
edigest.doc.fields[fieldname] = 1 if fieldname != "scheduler_errors":
edigest.doc.fields[fieldname] = 1
edigest.insert() edigest.insert()
# scheduler errors digest
edigest = webnotes.new_bean("Email Digest")
edigest.doc.fields.update({
"name": "Scheduler Errors",
"company": webnotes.conn.get_default("company"),
"frequency": "Daily",
"recipient_list": "\n".join(system_managers),
"scheduler_errors": 1,
"enabled": 1
})
edigest.insert()
def get_fy_details(fy_start_date, fy_end_date): def get_fy_details(fy_start_date, fy_end_date):
start_year = getdate(fy_start_date).year start_year = getdate(fy_start_date).year
if start_year == getdate(fy_end_date).year: if start_year == getdate(fy_end_date).year:

View File

@@ -13,4 +13,5 @@ def on_method(bean, method):
clear_doctype_notifications(bean.controller, method) clear_doctype_notifications(bean.controller, method)
if bean.doc.doctype=="Stock Entry" and method in ("on_submit", "on_cancel"): if bean.doc.doctype=="Stock Entry" and method in ("on_submit", "on_cancel"):
update_completed_qty(bean.controller, method) update_completed_qty(bean.controller, method)

View File

@@ -34,10 +34,6 @@ def execute_daily():
from core.doctype.notification_count.notification_count import delete_notification_count_for from core.doctype.notification_count.notification_count import delete_notification_count_for
delete_notification_count_for("Event") delete_notification_count_for("Event")
# email digest
from setup.doctype.email_digest.email_digest import send
run_fn(send)
# run recurring invoices # run recurring invoices
from accounts.doctype.sales_invoice.sales_invoice import manage_recurring_invoices from accounts.doctype.sales_invoice.sales_invoice import manage_recurring_invoices
run_fn(manage_recurring_invoices) run_fn(manage_recurring_invoices)
@@ -53,10 +49,11 @@ def execute_daily():
# check reorder level # check reorder level
from stock.utils import reorder_item from stock.utils import reorder_item
run_fn(reorder_item) run_fn(reorder_item)
# email digest
from setup.doctype.email_digest.email_digest import send
run_fn(send)
# scheduler error
scheduler.report_errors()
def execute_weekly(): def execute_weekly():
from setup.doctype.backup_manager.backup_manager import take_backups_weekly from setup.doctype.backup_manager.backup_manager import take_backups_weekly
run_fn(take_backups_weekly) run_fn(take_backups_weekly)

View File

@@ -73,6 +73,9 @@ class DocType(SellingController):
self.update_current_stock() self.update_current_stock()
self.validate_with_previous_doc() self.validate_with_previous_doc()
from stock.doctype.packed_item.packed_item import make_packing_list
self.doclist = make_packing_list(self, 'delivery_note_details')
self.doc.status = 'Draft' self.doc.status = 'Draft'
if not self.doc.installation_status: self.doc.installation_status = 'Not Installed' if not self.doc.installation_status: self.doc.installation_status = 'Not Installed'
@@ -142,10 +145,6 @@ class DocType(SellingController):
bin = webnotes.conn.sql("select actual_qty, projected_qty from `tabBin` where item_code = %s and warehouse = %s", (d.item_code, d.warehouse), as_dict = 1) bin = webnotes.conn.sql("select actual_qty, projected_qty from `tabBin` where item_code = %s and warehouse = %s", (d.item_code, d.warehouse), as_dict = 1)
d.actual_qty = bin and flt(bin[0]['actual_qty']) or 0 d.actual_qty = bin and flt(bin[0]['actual_qty']) or 0
d.projected_qty = bin and flt(bin[0]['projected_qty']) or 0 d.projected_qty = bin and flt(bin[0]['projected_qty']) or 0
def on_update(self):
from stock.doctype.packed_item.packed_item import make_packing_list
self.doclist = make_packing_list(self, 'delivery_note_details')
def on_submit(self): def on_submit(self):
self.validate_packed_qty() self.validate_packed_qty()

View File

@@ -2,7 +2,7 @@
{ {
"creation": "2013-05-24 19:29:09", "creation": "2013-05-24 19:29:09",
"docstatus": 0, "docstatus": 0,
"modified": "2013-11-03 14:20:19", "modified": "2013-12-14 17:26:12",
"modified_by": "Administrator", "modified_by": "Administrator",
"owner": "Administrator" "owner": "Administrator"
}, },
@@ -229,7 +229,7 @@
"doctype": "DocField", "doctype": "DocField",
"fieldname": "po_date", "fieldname": "po_date",
"fieldtype": "Date", "fieldtype": "Date",
"hidden": 1, "hidden": 0,
"label": "Customer's Purchase Order Date", "label": "Customer's Purchase Order Date",
"no_copy": 0, "no_copy": 0,
"oldfieldname": "po_date", "oldfieldname": "po_date",
@@ -1058,7 +1058,7 @@
}, },
{ {
"doctype": "DocPerm", "doctype": "DocPerm",
"match": "customer_name", "match": "customer",
"role": "Customer" "role": "Customer"
} }
] ]

View File

@@ -4,7 +4,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import webnotes import webnotes
from webnotes.utils import cstr, flt, cint from webnotes.utils import cstr, flt
from webnotes.model.doc import addchild from webnotes.model.doc import addchild
from webnotes.model.bean import getlist from webnotes.model.bean import getlist
from webnotes import msgprint, _ from webnotes import msgprint, _
@@ -116,40 +116,29 @@ class DocType(DocListController, WebsiteGenerator):
self.doc.is_pro_applicable = "No" self.doc.is_pro_applicable = "No"
if self.doc.is_pro_applicable == 'Yes' and self.doc.is_stock_item == 'No': if self.doc.is_pro_applicable == 'Yes' and self.doc.is_stock_item == 'No':
msgprint("As Production Order can be made for this Item, then Is Stock Item Should be 'Yes' as we maintain it's stock. Refer Manufacturing and Inventory section.", raise_exception=1) webnotes.throw(_("As Production Order can be made for this item, \
it must be a stock item."))
if self.doc.has_serial_no == 'Yes' and self.doc.is_stock_item == 'No': if self.doc.has_serial_no == 'Yes' and self.doc.is_stock_item == 'No':
msgprint("'Has Serial No' can not be 'Yes' for non-stock item", raise_exception=1) msgprint("'Has Serial No' can not be 'Yes' for non-stock item", raise_exception=1)
def check_for_active_boms(self): def check_for_active_boms(self):
def _check_for_active_boms(field_label): if self.doc.is_active != "Yes" or self.doc.is_purchase_item != "Yes":
if field_label in ['Is Active', 'Is Purchase Item']: bom_mat = webnotes.conn.sql("""select distinct t1.parent
bom_mat = webnotes.conn.sql("""select distinct t1.parent from `tabBOM Item` t1, `tabBOM` t2 where t2.name = t1.parent
from `tabBOM Item` t1, `tabBOM` t2 where t2.name = t1.parent and t1.item_code =%s and ifnull(t1.bom_no, '') = '' and t2.is_active = 1
and t1.item_code =%s and ifnull(t1.bom_no, '') = '' and t2.is_active = 1 and t2.docstatus = 1 and t1.docstatus =1 """, self.doc.name)
and t2.docstatus = 1 and t1.docstatus =1 """, self.doc.name)
if bom_mat and bom_mat[0][0]: if bom_mat and bom_mat[0][0]:
msgprint(_(field_label) + _(" should be 'Yes'. As Item: ") + self.doc.name + webnotes.throw(_("Item must be active and purchase item, \
_(" is present in one or many Active BOMs"), raise_exception=1) as it is present in one or many Active BOMs"))
if ((field_label == 'Allow Production Order' if self.doc.is_manufactured_item != "Yes":
and self.doc.is_sub_contracted_item != 'Yes') bom = webnotes.conn.sql("""select name from `tabBOM` where item = %s
or (field_label == 'Is Sub Contracted Item' and is_active = 1""", (self.doc.name,))
and self.doc.is_manufactured_item != 'Yes')): if bom and bom[0][0]:
bom = webnotes.conn.sql("""select name from `tabBOM` where item = %s webnotes.throw(_("""Allow Bill of Materials should be 'Yes'. Because one or many \
and is_active = 1""", (self.doc.name,)) active BOMs present for this item"""))
if bom and bom[0][0]:
msgprint(_(field_label) + _(" should be 'Yes'. As Item: ") + self.doc.name +
_(" is present in one or many Active BOMs"), raise_exception=1)
if not cint(self.doc.fields.get("__islocal")):
fl = {'is_manufactured_item' :'Allow Bill of Materials',
'is_sub_contracted_item':'Is Sub Contracted Item',
'is_purchase_item' :'Is Purchase Item',
'is_pro_applicable' :'Allow Production Order'}
for d in fl:
if cstr(self.doc.fields.get(d)) != 'Yes':
_check_for_active_boms(fl[d])
def fill_customer_code(self): def fill_customer_code(self):
""" Append all the customer codes and insert into "customer_code" field of item table """ """ Append all the customer codes and insert into "customer_code" field of item table """

View File

@@ -37,20 +37,25 @@ class DocType(BuyingController):
for so_no in so_items.keys(): for so_no in so_items.keys():
for item in so_items[so_no].keys(): for item in so_items[so_no].keys():
already_indented = webnotes.conn.sql("select sum(qty) from `tabMaterial Request Item` where item_code = '%s' and sales_order_no = '%s' and docstatus = 1 and parent != '%s'" % (item, so_no, self.doc.name)) already_indented = webnotes.conn.sql("""select sum(qty) from `tabMaterial Request Item`
where item_code = %s and sales_order_no = %s and
docstatus = 1 and parent != %s""", (item, so_no, self.doc.name))
already_indented = already_indented and flt(already_indented[0][0]) or 0 already_indented = already_indented and flt(already_indented[0][0]) or 0
actual_so_qty = webnotes.conn.sql("select sum(qty) from `tabSales Order Item` where parent = '%s' and item_code = '%s' and docstatus = 1 group by parent" % (so_no, item)) actual_so_qty = webnotes.conn.sql("""select sum(qty) from `tabSales Order Item`
where parent = %s and item_code = %s and docstatus = 1
group by parent""", (so_no, item))
actual_so_qty = actual_so_qty and flt(actual_so_qty[0][0]) or 0 actual_so_qty = actual_so_qty and flt(actual_so_qty[0][0]) or 0
if flt(so_items[so_no][item]) + already_indented > actual_so_qty: if actual_so_qty and (flt(so_items[so_no][item]) + already_indented > actual_so_qty):
msgprint("You can raise indent of maximum qty: %s for item: %s against sales order: %s\n Anyway, you can add more qty in new row for the same item." % (actual_so_qty - already_indented, item, so_no), raise_exception=1) webnotes.throw("You can raise indent of maximum qty: %s for item: %s against sales order: %s\
\n Anyway, you can add more qty in new row for the same item."
% (actual_so_qty - already_indented, item, so_no))
def validate_schedule_date(self): def validate_schedule_date(self):
for d in getlist(self.doclist, 'indent_details'): for d in getlist(self.doclist, 'indent_details'):
if d.schedule_date < self.doc.transaction_date: if d.schedule_date < self.doc.transaction_date:
msgprint("Expected Date cannot be before Material Request Date") webnotes.throw(_("Expected Date cannot be before Material Request Date"))
raise Exception
# Validate # Validate
# --------------------- # ---------------------
@@ -80,8 +85,8 @@ class DocType(BuyingController):
for d in getlist(self.doclist, 'indent_details'): for d in getlist(self.doclist, 'indent_details'):
if webnotes.conn.get_value("Item", d.item_code, "is_stock_item") == "Yes": if webnotes.conn.get_value("Item", d.item_code, "is_stock_item") == "Yes":
if not d.warehouse: if not d.warehouse:
msgprint("Please Enter Warehouse for Item %s as it is stock item" webnotes.throw("Please Enter Warehouse for Item %s as it is stock item"
% cstr(d.item_code), raise_exception=1) % cstr(d.item_code))
qty =flt(d.qty) qty =flt(d.qty)
if is_stopped: if is_stopped:
@@ -96,16 +101,17 @@ class DocType(BuyingController):
update_bin(args) update_bin(args)
def on_submit(self): def on_submit(self):
webnotes.conn.set(self.doc,'status','Submitted') webnotes.conn.set(self.doc, 'status', 'Submitted')
self.update_bin(is_submit = 1, is_stopped = 0) self.update_bin(is_submit = 1, is_stopped = 0)
def check_modified_date(self): def check_modified_date(self):
mod_db = webnotes.conn.sql("select modified from `tabMaterial Request` where name = '%s'" % self.doc.name) mod_db = webnotes.conn.sql("""select modified from `tabMaterial Request` where name = %s""",
date_diff = webnotes.conn.sql("select TIMEDIFF('%s', '%s')" % ( mod_db[0][0],cstr(self.doc.modified))) self.doc.name)
date_diff = webnotes.conn.sql("""select TIMEDIFF('%s', '%s')"""
% (mod_db[0][0], cstr(self.doc.modified)))
if date_diff and date_diff[0][0]: if date_diff and date_diff[0][0]:
msgprint(cstr(self.doc.doctype) +" => "+ cstr(self.doc.name) +" has been modified. Please Refresh. ") webnotes.throw(cstr(self.doc.doctype) + " => " + cstr(self.doc.name) + " has been modified. Please Refresh.")
raise Exception
def update_status(self, status): def update_status(self, status):
self.check_modified_date() self.check_modified_date()
@@ -113,10 +119,10 @@ class DocType(BuyingController):
self.update_bin(is_submit = (status == 'Submitted') and 1 or 0, is_stopped = 1) self.update_bin(is_submit = (status == 'Submitted') and 1 or 0, is_stopped = 1)
# Step 2:=> Set status # Step 2:=> Set status
webnotes.conn.set(self.doc,'status',cstr(status)) webnotes.conn.set(self.doc, 'status', cstr(status))
# Step 3:=> Acknowledge User # Step 3:=> Acknowledge User
msgprint(self.doc.doctype + ": " + self.doc.name + " has been %s." % ((status == 'Submitted') and 'Unstopped' or cstr(status)) ) msgprint(self.doc.doctype + ": " + self.doc.name + " has been %s." % ((status == 'Submitted') and 'Unstopped' or cstr(status)))
def on_cancel(self): def on_cancel(self):
@@ -177,9 +183,9 @@ def update_completed_qty(controller, caller_method):
mr_doctype = webnotes.get_doctype("Material Request") mr_doctype = webnotes.get_doctype("Material Request")
if mr_obj.doc.status in ["Stopped", "Cancelled"]: if mr_obj.doc.status in ["Stopped", "Cancelled"]:
msgprint(_("Material Request") + ": %s, " % mr_obj.doc.name webnotes.throw(_("Material Request") + ": %s, " % mr_obj.doc.name
+ _(mr_doctype.get_label("status")) + " = %s. " % _(mr_obj.doc.status) + _(mr_doctype.get_label("status")) + " = %s. " % _(mr_obj.doc.status)
+ _("Cannot continue."), raise_exception=webnotes.InvalidStatusError) + _("Cannot continue."), exc=webnotes.InvalidStatusError)
_update_requested_qty(controller, mr_obj, mr_items) _update_requested_qty(controller, mr_obj, mr_items)

View File

@@ -56,9 +56,6 @@ def update_packing_list_item(obj, packing_item_code, qty, warehouse, line, packi
pi.batch_no = cstr(line.batch_no) pi.batch_no = cstr(line.batch_no)
pi.idx = packing_list_idx pi.idx = packing_list_idx
# saved, since this function is called on_update of delivery note
pi.save()
packing_list_idx += 1 packing_list_idx += 1
@@ -87,19 +84,13 @@ def cleanup_packing_list(obj, parent_items):
for d in obj.doclist.get({"parentfield": "packing_details"}): for d in obj.doclist.get({"parentfield": "packing_details"}):
if [d.parent_item, d.parent_detail_docname] not in parent_items: if [d.parent_item, d.parent_detail_docname] not in parent_items:
# mark for deletion from doclist # mark for deletion from doclist
delete_list.append(d.name) delete_list.append([d.parent_item, d.parent_detail_docname])
if not delete_list: if not delete_list:
return obj.doclist return obj.doclist
# delete from doclist # delete from doclist
obj.doclist = webnotes.doclist(filter(lambda d: d.name not in delete_list, obj.doclist)) obj.doclist = webnotes.doclist(filter(lambda d: [d.parent_item, d.parent_detail_docname]
not in delete_list, obj.doclist))
# delete from db
webnotes.conn.sql("""\
delete from `tabPacked Item`
where name in (%s)"""
% (", ".join(["%s"] * len(delete_list))),
tuple(delete_list))
return obj.doclist return obj.doclist

View File

@@ -150,7 +150,7 @@ class DocType(StockController):
where serial_no like %s and item_code=%s and ifnull(is_cancelled, 'No')='No' where serial_no like %s and item_code=%s and ifnull(is_cancelled, 'No')='No'
order by posting_date desc, posting_time desc, name desc""", order by posting_date desc, posting_time desc, name desc""",
("%%%s%%" % self.doc.name, self.doc.item_code), as_dict=1): ("%%%s%%" % self.doc.name, self.doc.item_code), as_dict=1):
if self.doc.name in get_serial_nos(sle.serial_no): if self.doc.name.upper() in get_serial_nos(sle.serial_no):
if sle.actual_qty > 0: if sle.actual_qty > 0:
sle_dict.setdefault("incoming", []).append(sle) sle_dict.setdefault("incoming", []).append(sle)
else: else:
@@ -268,7 +268,8 @@ def get_item_details(item_code):
from tabItem where name=%s""", item_code, as_dict=True)[0] from tabItem where name=%s""", item_code, as_dict=True)[0]
def get_serial_nos(serial_no): def get_serial_nos(serial_no):
return [s.strip() for s in cstr(serial_no).strip().replace(',', '\n').split('\n') if s.strip()] return [s.strip() for s in cstr(serial_no).strip().upper().replace(',', '\n').split('\n')
if s.strip()]
def make_serial_no(serial_no, sle): def make_serial_no(serial_no, sle):
sr = webnotes.new_bean("Serial No") sr = webnotes.new_bean("Serial No")

View File

@@ -106,7 +106,7 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({
}, },
callback: function(r) { callback: function(r) {
if (!r.exc) { if (!r.exc) {
for(d in getchildren('Stock Entry Detail',doc.name,'mtn_details')) { for(d in getchildren('Stock Entry Detail', me.frm.doc.name, 'mtn_details')) {
if(!d.expense_account) d.expense_account = r.message; if(!d.expense_account) d.expense_account = r.message;
} }
} }
@@ -244,7 +244,7 @@ cur_frm.script_manager.make(erpnext.stock.StockEntry);
cur_frm.cscript.toggle_related_fields = function(doc) { cur_frm.cscript.toggle_related_fields = function(doc) {
disable_from_warehouse = inList(["Material Receipt", "Sales Return"], doc.purpose); disable_from_warehouse = inList(["Material Receipt", "Sales Return"], doc.purpose);
disable_to_warehouse = inList(["Material Issue", "Purchase Return"], doc.purpose) disable_to_warehouse = inList(["Material Issue", "Purchase Return"], doc.purpose);
cur_frm.toggle_enable("from_warehouse", !disable_from_warehouse); cur_frm.toggle_enable("from_warehouse", !disable_from_warehouse);
cur_frm.toggle_enable("to_warehouse", !disable_to_warehouse); cur_frm.toggle_enable("to_warehouse", !disable_to_warehouse);
@@ -302,7 +302,7 @@ cur_frm.fields_dict['production_order'].get_query = function(doc) {
} }
cur_frm.cscript.purpose = function(doc, cdt, cdn) { cur_frm.cscript.purpose = function(doc, cdt, cdn) {
cur_frm.cscript.toggle_related_fields(doc, cdt, cdn); cur_frm.cscript.toggle_related_fields(doc);
} }
// Overloaded query for link batch_no // Overloaded query for link batch_no

View File

@@ -2,7 +2,7 @@
{ {
"creation": "2013-04-09 11:43:55", "creation": "2013-04-09 11:43:55",
"docstatus": 0, "docstatus": 0,
"modified": "2013-11-03 14:11:42", "modified": "2013-12-09 16:24:10",
"modified_by": "Administrator", "modified_by": "Administrator",
"owner": "Administrator" "owner": "Administrator"
}, },
@@ -108,7 +108,7 @@
"doctype": "DocField", "doctype": "DocField",
"fieldname": "delivery_note_no", "fieldname": "delivery_note_no",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 1, "hidden": 0,
"in_filter": 0, "in_filter": 0,
"label": "Delivery Note No", "label": "Delivery Note No",
"no_copy": 1, "no_copy": 1,
@@ -126,7 +126,7 @@
"doctype": "DocField", "doctype": "DocField",
"fieldname": "sales_invoice_no", "fieldname": "sales_invoice_no",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 1, "hidden": 0,
"label": "Sales Invoice No", "label": "Sales Invoice No",
"no_copy": 1, "no_copy": 1,
"options": "Sales Invoice", "options": "Sales Invoice",
@@ -139,7 +139,7 @@
"doctype": "DocField", "doctype": "DocField",
"fieldname": "purchase_receipt_no", "fieldname": "purchase_receipt_no",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 1, "hidden": 0,
"in_filter": 0, "in_filter": 0,
"label": "Purchase Receipt No", "label": "Purchase Receipt No",
"no_copy": 1, "no_copy": 1,
@@ -299,7 +299,7 @@
"doctype": "DocField", "doctype": "DocField",
"fieldname": "production_order", "fieldname": "production_order",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 1, "hidden": 0,
"in_filter": 1, "in_filter": 1,
"label": "Production Order", "label": "Production Order",
"no_copy": 0, "no_copy": 0,

View File

@@ -1 +0,0 @@
Average "age" of an Item in a particular Warehouse based on First-in-first-out (FIFO).

View File

@@ -1 +0,0 @@
from __future__ import unicode_literals

View File

@@ -1,183 +0,0 @@
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
wn.pages['stock-ageing'].onload = function(wrapper) {
wn.ui.make_app_page({
parent: wrapper,
title: wn._('Stock Ageing'),
single_column: true
});
new erpnext.StockAgeing(wrapper);
wrapper.appframe.add_module_icon("Stock")
}
wn.require("app/js/stock_grid_report.js");
erpnext.StockAgeing = erpnext.StockGridReport.extend({
init: function(wrapper) {
this._super({
title: wn._("Stock Ageing"),
page: wrapper,
parent: $(wrapper).find('.layout-main'),
appframe: wrapper.appframe,
doctypes: ["Item", "Warehouse", "Stock Ledger Entry", "Item Group", "Brand", "Serial No"],
})
},
setup_columns: function() {
this.columns = [
{id: "name", name: wn._("Item"), field: "name", width: 300,
link_formatter: {
open_btn: true,
doctype: '"Item"'
}},
{id: "item_name", name: wn._("Item Name"), field: "item_name",
width: 100, formatter: this.text_formatter},
{id: "description", name: wn._("Description"), field: "description",
width: 200, formatter: this.text_formatter},
{id: "brand", name: wn._("Brand"), field: "brand", width: 100},
{id: "average_age", name: wn._("Average Age"), field: "average_age",
formatter: this.currency_formatter},
{id: "earliest", name: wn._("Earliest"), field: "earliest",
formatter: this.currency_formatter},
{id: "latest", name: wn._("Latest"), field: "latest",
formatter: this.currency_formatter},
{id: "stock_uom", name: "UOM", field: "stock_uom", width: 100},
];
},
filters: [
{fieldtype:"Select", label: wn._("Warehouse"), link:"Warehouse",
default_value: "Select Warehouse..."},
{fieldtype:"Select", label: wn._("Brand"), link:"Brand",
default_value: "Select Brand...", filter: function(val, item, opts) {
return val == opts.default_value || item.brand == val;
}, link_formatter: {filter_input: "brand"}},
{fieldtype:"Select", label: wn._("Plot By"),
options: ["Average Age", "Earliest", "Latest"]},
{fieldtype:"Date", label: wn._("To Date")},
{fieldtype:"Button", label: wn._("Refresh"), icon:"icon-refresh icon-white"},
{fieldtype:"Button", label: wn._("Reset Filters")}
],
setup_filters: function() {
var me = this;
this._super();
this.trigger_refresh_on_change(["warehouse", "plot_by", "brand"]);
this.show_zero_check();
},
init_filter_values: function() {
this._super();
this.filter_inputs.to_date.val(dateutil.obj_to_user(new Date()));
},
prepare_data: function() {
var me = this;
if(!this.data) {
me._data = wn.report_dump.data["Item"];
me.item_by_name = me.make_name_map(me._data);
}
this.data = [].concat(this._data);
this.serialized_buying_rates = this.get_serialized_buying_rates();
$.each(this.data, function(i, d) {
me.reset_item_values(d);
});
this.prepare_balances();
// filter out brand
this.data = $.map(this.data, function(d) {
return me.apply_filter(d, "brand") ? d : null;
});
// filter out rows with zero values
this.data = $.map(this.data, function(d) {
return me.apply_zero_filter(null, d, null, me) ? d : null;
});
},
prepare_balances: function() {
var me = this;
var to_date = dateutil.str_to_obj(this.to_date);
var data = wn.report_dump.data["Stock Ledger Entry"];
this.item_warehouse = {};
for(var i=0, j=data.length; i<j; i++) {
var sl = data[i];
var posting_date = dateutil.str_to_obj(sl.posting_date);
if(me.is_default("warehouse") ? true : me.warehouse == sl.warehouse) {
var wh = me.get_item_warehouse(sl.warehouse, sl.item_code);
// call diff to build fifo stack in item_warehouse
var diff = me.get_value_diff(wh, sl, true);
if(posting_date > to_date)
break;
}
}
$.each(me.data, function(i, item) {
var full_fifo_stack = [];
if(me.is_default("warehouse")) {
$.each(me.item_warehouse[item.name] || {}, function(i, wh) {
full_fifo_stack = full_fifo_stack.concat(wh.fifo_stack || [])
});
} else {
full_fifo_stack = me.get_item_warehouse(me.warehouse, item.name).fifo_stack || [];
}
var age_qty = total_qty = 0.0;
var min_age = max_age = null;
$.each(full_fifo_stack, function(i, batch) {
var batch_age = dateutil.get_diff(me.to_date, batch[2]);
age_qty += batch_age * batch[0];
total_qty += batch[0];
max_age = Math.max(max_age, batch_age);
if(min_age===null) min_age=batch_age;
else min_age = Math.min(min_age, batch_age);
});
item.average_age = total_qty.toFixed(2)==0.0 ? 0
: (age_qty / total_qty).toFixed(2);
item.earliest = max_age || 0.0;
item.latest = min_age || 0.0;
});
this.data = this.data.sort(function(a, b) {
var sort_by = me.plot_by.replace(" ", "_").toLowerCase();
return b[sort_by] - a[sort_by];
});
},
get_plot_data: function() {
var data = [];
var me = this;
data.push({
label: me.plot_by,
data: $.map(me.data, function(item, idx) {
return [[idx+1, item[me.plot_by.replace(" ", "_").toLowerCase() ]]]
}),
bars: {show: true},
});
return data.length ? data : false;
},
get_plot_options: function() {
var me = this;
return {
grid: { hoverable: true, clickable: true },
xaxis: {
ticks: $.map(me.data, function(item, idx) { return [[idx+1, item.name]] }),
max: 15
},
series: { downsample: { threshold: 1000 } }
}
}
});

View File

@@ -1,37 +0,0 @@
[
{
"creation": "2012-09-21 20:15:14",
"docstatus": 0,
"modified": "2013-07-11 14:44:08",
"modified_by": "Administrator",
"owner": "Administrator"
},
{
"doctype": "Page",
"icon": "icon-table",
"module": "Stock",
"name": "__common__",
"page_name": "stock-ageing",
"standard": "Yes",
"title": "Stock Ageing"
},
{
"doctype": "Page Role",
"name": "__common__",
"parent": "stock-ageing",
"parentfield": "roles",
"parenttype": "Page"
},
{
"doctype": "Page",
"name": "stock-ageing"
},
{
"doctype": "Page Role",
"role": "Analytics"
},
{
"doctype": "Page Role",
"role": "Material Manager"
}
]

View File

@@ -138,7 +138,7 @@ wn.module_page["Stock"] = [
items: [ items: [
{ {
"label":wn._("Stock Ledger"), "label":wn._("Stock Ledger"),
doctype: "Delivery Note", doctype: "Item",
route: "query-report/Stock Ledger" route: "query-report/Stock Ledger"
}, },
{ {
@@ -146,12 +146,14 @@ wn.module_page["Stock"] = [
page: "stock-balance" page: "stock-balance"
}, },
{ {
"page":"stock-level", "label":wn._("Stock Projected Qty"),
"label": wn._("Stock Level") doctype: "Item",
route: "query-report/Stock Projected Qty"
}, },
{ {
"page":"stock-ageing", "label":wn._("Stock Ageing"),
"label": wn._("Stock Ageing") doctype: "Item",
route: "query-report/Stock Ageing"
}, },
] ]
}, },

View File

@@ -1 +0,0 @@
Stock movement report based on Stock Ledger Entry.

View File

@@ -1 +0,0 @@
from __future__ import unicode_literals

View File

@@ -1,247 +0,0 @@
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
wn.pages['stock-ledger'].onload = function(wrapper) {
wn.ui.make_app_page({
parent: wrapper,
title: wn._('Stock Ledger'),
single_column: true
});
new erpnext.StockLedger(wrapper);
wrapper.appframe.add_module_icon("Stock")
}
wn.require("app/js/stock_grid_report.js");
erpnext.StockLedger = erpnext.StockGridReport.extend({
init: function(wrapper) {
this._super({
title: wn._("Stock Ledger"),
page: wrapper,
parent: $(wrapper).find('.layout-main'),
appframe: wrapper.appframe,
doctypes: ["Item", "Item Group", "Warehouse", "Stock Ledger Entry", "Brand", "Serial No"],
})
},
setup_columns: function() {
this.hide_balance = (this.is_default("item_code") || this.voucher_no) ? true : false;
this.columns = [
{id: "posting_datetime", name: wn._("Posting Date"), field: "posting_datetime", width: 120,
formatter: this.date_formatter},
{id: "item_code", name: wn._("Item Code"), field: "item_code", width: 160,
link_formatter: {
filter_input: "item_code",
open_btn: true,
doctype: '"Item"',
}},
{id: "description", name: wn._("Description"), field: "description", width: 200,
formatter: this.text_formatter},
{id: "warehouse", name: wn._("Warehouse"), field: "warehouse", width: 100,
link_formatter: {filter_input: "warehouse"}},
{id: "brand", name: wn._("Brand"), field: "brand", width: 100},
{id: "stock_uom", name: wn._("UOM"), field: "stock_uom", width: 100},
{id: "qty", name: wn._("Qty"), field: "qty", width: 100,
formatter: this.currency_formatter},
{id: "balance", name: wn._("Balance Qty"), field: "balance", width: 100,
formatter: this.currency_formatter,
hidden: this.hide_balance},
{id: "balance_value", name: wn._("Balance Value"), field: "balance_value", width: 100,
formatter: this.currency_formatter, hidden: this.hide_balance},
{id: "voucher_type", name: wn._("Voucher Type"), field: "voucher_type", width: 120},
{id: "voucher_no", name: wn._("Voucher No"), field: "voucher_no", width: 160,
link_formatter: {
filter_input: "voucher_no",
open_btn: true,
doctype: "dataContext.voucher_type"
}},
];
},
filters: [
{fieldtype:"Select", label: wn._("Warehouse"), link:"Warehouse",
default_value: "Select Warehouse...", filter: function(val, item, opts) {
return item.warehouse == val || val == opts.default_value;
}},
{fieldtype:"Link", label: wn._("Item Code"), link:"Item", default_value: "Select Item...",
filter: function(val, item, opts) {
return item.item_code == val || !val;
}},
{fieldtype:"Select", label: "Brand", link:"Brand",
default_value: "Select Brand...", filter: function(val, item, opts) {
return val == opts.default_value || item.brand == val || item._show;
}, link_formatter: {filter_input: "brand"}},
{fieldtype:"Data", label: wn._("Voucher No"),
filter: function(val, item, opts) {
if(!val) return true;
return (item.voucher_no && item.voucher_no.indexOf(val)!=-1);
}},
{fieldtype:"Date", label: wn._("From Date"), filter: function(val, item) {
return dateutil.str_to_obj(val) <= dateutil.str_to_obj(item.posting_date);
}},
{fieldtype:"Label", label: wn._("To")},
{fieldtype:"Date", label: wn._("To Date"), filter: function(val, item) {
return dateutil.str_to_obj(val) >= dateutil.str_to_obj(item.posting_date);
}},
{fieldtype:"Button", label: wn._("Refresh"), icon:"icon-refresh icon-white"},
{fieldtype:"Button", label: wn._("Reset Filters")}
],
setup_filters: function() {
var me = this;
this._super();
this.wrapper.bind("apply_filters_from_route", function() { me.toggle_enable_brand(); });
this.filter_inputs.item_code.change(function() { me.toggle_enable_brand(); });
this.trigger_refresh_on_change(["item_code", "warehouse", "brand"]);
},
toggle_enable_brand: function() {
if(!this.filter_inputs.item_code.val()) {
this.filter_inputs.brand.prop("disabled", false);
} else {
this.filter_inputs.brand
.val(this.filter_inputs.brand.get(0).opts.default_value)
.prop("disabled", true);
}
},
init_filter_values: function() {
this._super();
this.filter_inputs.warehouse.get(0).selectedIndex = 0;
},
prepare_data: function() {
var me = this;
if(!this.item_by_name)
this.item_by_name = this.make_name_map(wn.report_dump.data["Item"]);
var data = wn.report_dump.data["Stock Ledger Entry"];
var out = [];
var opening = {
item_code: "On " + dateutil.str_to_user(this.from_date), qty: 0.0, balance: 0.0,
id:"_opening", _show: true, _style: "font-weight: bold", balance_value: 0.0
}
var total_in = {
item_code: "Total In", qty: 0.0, balance: 0.0, balance_value: 0.0,
id:"_total_in", _show: true, _style: "font-weight: bold"
}
var total_out = {
item_code: "Total Out", qty: 0.0, balance: 0.0, balance_value: 0.0,
id:"_total_out", _show: true, _style: "font-weight: bold"
}
// clear balance
$.each(wn.report_dump.data["Item"], function(i, item) {
item.balance = item.balance_value = 0.0;
});
// initialize warehouse-item map
this.item_warehouse = {};
this.serialized_buying_rates = this.get_serialized_buying_rates();
var from_datetime = dateutil.str_to_obj(me.from_date + " 00:00:00");
var to_datetime = dateutil.str_to_obj(me.to_date + " 23:59:59");
//
for(var i=0, j=data.length; i<j; i++) {
var sl = data[i];
var item = me.item_by_name[sl.item_code]
var wh = me.get_item_warehouse(sl.warehouse, sl.item_code);
sl.description = item.description;
sl.posting_datetime = sl.posting_date + " " + (sl.posting_time || "00:00:00");
sl.brand = item.brand;
var posting_datetime = dateutil.str_to_obj(sl.posting_datetime);
var is_fifo = item.valuation_method ? item.valuation_method=="FIFO"
: sys_defaults.valuation_method=="FIFO";
var value_diff = me.get_value_diff(wh, sl, is_fifo);
// opening, transactions, closing, total in, total out
var before_end = posting_datetime <= to_datetime;
if((!me.is_default("item_code") ? me.apply_filter(sl, "item_code") : true)
&& me.apply_filter(sl, "warehouse") && me.apply_filter(sl, "voucher_no")
&& me.apply_filter(sl, "brand")) {
if(posting_datetime < from_datetime) {
opening.balance += sl.qty;
opening.balance_value += value_diff;
} else if(before_end) {
if(sl.qty > 0) {
total_in.qty += sl.qty;
total_in.balance_value += value_diff;
} else {
total_out.qty += (-1 * sl.qty);
total_out.balance_value += value_diff;
}
}
}
if(!before_end) break;
// apply filters
if(me.apply_filters(sl)) {
out.push(sl);
}
// update balance
if((!me.is_default("warehouse") ? me.apply_filter(sl, "warehouse") : true)) {
sl.balance = me.item_by_name[sl.item_code].balance + sl.qty;
me.item_by_name[sl.item_code].balance = sl.balance;
sl.balance_value = me.item_by_name[sl.item_code].balance_value + value_diff;
me.item_by_name[sl.item_code].balance_value = sl.balance_value;
}
}
if(me.item_code && !me.voucher_no) {
var closing = {
item_code: "On " + dateutil.str_to_user(this.to_date),
balance: (out.length ? out[out.length-1].balance : 0), qty: 0,
balance_value: (out.length ? out[out.length-1].balance_value : 0),
id:"_closing", _show: true, _style: "font-weight: bold"
};
total_out.balance_value = -total_out.balance_value;
var out = [opening].concat(out).concat([total_in, total_out, closing]);
}
this.data = out;
},
get_plot_data: function() {
var data = [];
var me = this;
if(me.hide_balance) return false;
data.push({
label: me.item_code,
data: [[dateutil.str_to_obj(me.from_date).getTime(), me.data[0].balance]]
.concat($.map(me.data, function(col, idx) {
if (col.posting_datetime) {
return [[dateutil.str_to_obj(col.posting_datetime).getTime(), col.balance - col.qty],
[dateutil.str_to_obj(col.posting_datetime).getTime(), col.balance]]
}
return null;
})).concat([
// closing
[dateutil.str_to_obj(me.to_date).getTime(), me.data[me.data.length - 1].balance]
]),
points: {show: true},
lines: {show: true, fill: true},
});
return data;
},
get_plot_options: function() {
return {
grid: { hoverable: true, clickable: true },
xaxis: { mode: "time",
min: dateutil.str_to_obj(this.from_date).getTime(),
max: dateutil.str_to_obj(this.to_date).getTime(),
},
series: { downsample: { threshold: 1000 } }
}
},
get_tooltip_text: function(label, x, y) {
var d = new Date(x);
var date = dateutil.obj_to_user(d) + " " + d.getHours() + ":" + d.getMinutes();
var value = format_number(y);
return value.bold() + " on " + date;
}
});

View File

@@ -1,41 +0,0 @@
[
{
"creation": "2012-09-21 20:15:14",
"docstatus": 0,
"modified": "2013-07-11 14:44:19",
"modified_by": "Administrator",
"owner": "Administrator"
},
{
"doctype": "Page",
"icon": "icon-table",
"module": "Stock",
"name": "__common__",
"page_name": "stock-ledger",
"standard": "Yes",
"title": "Stock Ledger"
},
{
"doctype": "Page Role",
"name": "__common__",
"parent": "stock-ledger",
"parentfield": "roles",
"parenttype": "Page"
},
{
"doctype": "Page",
"name": "stock-ledger"
},
{
"doctype": "Page Role",
"role": "Analytics"
},
{
"doctype": "Page Role",
"role": "Material Manager"
},
{
"doctype": "Page Role",
"role": "Material User"
}
]

View File

@@ -1 +0,0 @@
Stock levels (actual, planned, reserved, ordered) for Items on a particular date.

View File

@@ -1,228 +0,0 @@
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
wn.pages['stock-level'].onload = function(wrapper) {
wn.ui.make_app_page({
parent: wrapper,
title: wn._('Stock Level'),
single_column: true
});
new erpnext.StockLevel(wrapper);
wrapper.appframe.add_module_icon("Stock")
;
}
wn.require("app/js/stock_grid_report.js");
erpnext.StockLevel = erpnext.StockGridReport.extend({
init: function(wrapper) {
var me = this;
this._super({
title: wn._("Stock Level"),
page: wrapper,
parent: $(wrapper).find('.layout-main'),
appframe: wrapper.appframe,
doctypes: ["Item", "Warehouse", "Stock Ledger Entry", "Production Order",
"Material Request Item", "Purchase Order Item", "Sales Order Item", "Brand", "Serial No"],
});
this.wrapper.bind("make", function() {
wn.utils.set_footnote(me, me.wrapper.get(0),
"<ul> \
<li style='font-weight: bold;'> \
Projected Qty = Actual Qty + Planned Qty + Requested Qty \
+ Ordered Qty - Reserved Qty </li> \
<ul> \
<li>"+wn._("Actual Qty: Quantity available in the warehouse.") +"</li>"+
"<li>"+wn._("Planned Qty: Quantity, for which, Production Order has been raised,")+
wn._("but is pending to be manufactured.")+ "</li> " +
"<li>"+wn._("Requested Qty: Quantity requested for purchase, but not ordered.") + "</li>" +
"<li>" + wn._("Ordered Qty: Quantity ordered for purchase, but not received.")+ "</li>" +
"<li>" + wn._("Reserved Qty: Quantity ordered for sale, but not delivered.") + "</li>" +
"</ul> \
</ul>");
});
},
setup_columns: function() {
this.columns = [
{id: "item_code", name: wn._("Item Code"), field: "item_code", width: 160,
link_formatter: {
filter_input: "item_code",
open_btn: true,
doctype: '"Item"',
}},
{id: "item_name", name: wn._("Item Name"), field: "item_name", width: 100,
formatter: this.text_formatter},
{id: "description", name: wn._("Description"), field: "description", width: 200,
formatter: this.text_formatter},
{id: "brand", name: wn._("Brand"), field: "brand", width: 100,
link_formatter: {filter_input: "brand"}},
{id: "warehouse", name: wn._("Warehouse"), field: "warehouse", width: 100,
link_formatter: {filter_input: "warehouse"}},
{id: "uom", name: wn._("UOM"), field: "uom", width: 60},
{id: "actual_qty", name: wn._("Actual Qty"),
field: "actual_qty", width: 80, formatter: this.currency_formatter},
{id: "planned_qty", name: wn._("Planned Qty"),
field: "planned_qty", width: 80, formatter: this.currency_formatter},
{id: "requested_qty", name: wn._("Requested Qty"),
field: "requested_qty", width: 80, formatter: this.currency_formatter},
{id: "ordered_qty", name: wn._("Ordered Qty"),
field: "ordered_qty", width: 80, formatter: this.currency_formatter},
{id: "reserved_qty", name: wn._("Reserved Qty"),
field: "reserved_qty", width: 80, formatter: this.currency_formatter},
{id: "projected_qty", name: wn._("Projected Qty"),
field: "projected_qty", width: 80, formatter: this.currency_formatter},
{id: "re_order_level", name: wn._("Re-Order Level"),
field: "re_order_level", width: 80, formatter: this.currency_formatter},
{id: "re_order_qty", name: wn._("Re-Order Qty"),
field: "re_order_qty", width: 80, formatter: this.currency_formatter},
];
},
filters: [
{fieldtype:"Link", label: wn._("Item Code"), link:"Item", default_value: "Select Item...",
filter: function(val, item, opts) {
return item.item_code == val || !val;
}},
{fieldtype:"Select", label: wn._("Warehouse"), link:"Warehouse",
default_value: "Select Warehouse...", filter: function(val, item, opts) {
return item.warehouse == val || val == opts.default_value;
}},
{fieldtype:"Select", label: wn._("Brand"), link:"Brand",
default_value: "Select Brand...", filter: function(val, item, opts) {
return val == opts.default_value || item.brand == val;
}},
{fieldtype:"Button", label: wn._("Refresh"), icon:"icon-refresh icon-white"},
{fieldtype:"Button", label: wn._("Reset Filters")}
],
setup_filters: function() {
var me = this;
this._super();
this.wrapper.bind("apply_filters_from_route", function() { me.toggle_enable_brand(); });
this.filter_inputs.item_code.change(function() { me.toggle_enable_brand(); });
this.trigger_refresh_on_change(["item_code", "warehouse", "brand"]);
},
toggle_enable_brand: function() {
if(!this.filter_inputs.item_code.val()) {
this.filter_inputs.brand.prop("disabled", false);
} else {
this.filter_inputs.brand
.val(this.filter_inputs.brand.get(0).opts.default_value)
.prop("disabled", true);
}
},
init_filter_values: function() {
this._super();
this.filter_inputs.warehouse.get(0).selectedIndex = 0;
},
prepare_data: function() {
var me = this;
if(!this._data) {
this._data = [];
this.item_warehouse_map = {};
this.item_by_name = this.make_name_map(wn.report_dump.data["Item"]);
this.calculate_quantities();
}
this.data = [].concat(this._data);
this.data = $.map(this.data, function(d) {
return me.apply_filters(d) ? d : null;
});
this.calculate_total();
},
calculate_quantities: function() {
var me = this;
$.each([
["Stock Ledger Entry", "actual_qty"],
["Production Order", "planned_qty"],
["Material Request Item", "requested_qty"],
["Purchase Order Item", "ordered_qty"],
["Sales Order Item", "reserved_qty"]],
function(i, v) {
$.each(wn.report_dump.data[v[0]], function(i, item) {
var row = me.get_row(item.item_code, item.warehouse);
row[v[1]] += flt(item.qty);
});
}
);
// sort by item, warehouse
this._data = $.map(Object.keys(this.item_warehouse_map).sort(), function(key) {
return me.item_warehouse_map[key];
});
// calculate projected qty
$.each(this._data, function(i, row) {
row.projected_qty = row.actual_qty + row.planned_qty + row.requested_qty
+ row.ordered_qty - row.reserved_qty;
});
// filter out rows with zero values
this._data = $.map(this._data, function(d) {
return me.apply_zero_filter(null, d, null, me) ? d : null;
});
},
get_row: function(item_code, warehouse) {
var key = item_code + ":" + warehouse;
if(!this.item_warehouse_map[key]) {
var item = this.item_by_name[item_code];
var row = {
item_code: item_code,
warehouse: warehouse,
description: item.description,
brand: item.brand,
item_name: item.item_name || item.name,
uom: item.stock_uom,
id: key,
}
this.reset_item_values(row);
row["re_order_level"] = item.re_order_level
row["re_order_qty"] = item.re_order_qty
this.item_warehouse_map[key] = row;
}
return this.item_warehouse_map[key];
},
calculate_total: function() {
var me = this;
// show total if a specific item is selected and warehouse is not filtered
if(this.is_default("warehouse") && !this.is_default("item_code")) {
var total = {
id: "_total",
item_code: "Total",
_style: "font-weight: bold",
_show: true
};
this.reset_item_values(total);
$.each(this.data, function(i, row) {
$.each(me.columns, function(i, col) {
if (col.formatter==me.currency_formatter) {
total[col.id] += row[col.id];
}
});
});
this.data = this.data.concat([total]);
}
}
})

View File

@@ -1,37 +0,0 @@
[
{
"creation": "2012-12-31 10:52:14",
"docstatus": 0,
"modified": "2013-07-11 14:44:21",
"modified_by": "Administrator",
"owner": "Administrator"
},
{
"doctype": "Page",
"icon": "icon-table",
"module": "Stock",
"name": "__common__",
"page_name": "stock-level",
"standard": "Yes",
"title": "Stock Level"
},
{
"doctype": "Page Role",
"name": "__common__",
"parent": "stock-level",
"parentfield": "roles",
"parenttype": "Page"
},
{
"doctype": "Page",
"name": "stock-level"
},
{
"doctype": "Page Role",
"role": "Material Manager"
},
{
"doctype": "Page Role",
"role": "Analytics"
}
]

View File

@@ -0,0 +1,40 @@
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
wn.query_reports["Stock Ageing"] = {
"filters": [
{
"fieldname":"company",
"label": wn._("Company"),
"fieldtype": "Link",
"options": "Company",
"default": wn.defaults.get_user_default("company"),
"reqd": 1
},
{
"fieldname":"to_date",
"label": wn._("To Date"),
"fieldtype": "Date",
"default": wn.datetime.get_today(),
"reqd": 1
},
{
"fieldname":"warehouse",
"label": wn._("Warehouse"),
"fieldtype": "Link",
"options": "Warehouse"
},
{
"fieldname":"item_code",
"label": wn._("Item"),
"fieldtype": "Link",
"options": "Item"
},
{
"fieldname":"brand",
"label": wn._("Brand"),
"fieldtype": "Link",
"options": "Brand"
}
]
}

View File

@@ -0,0 +1,94 @@
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import webnotes
from webnotes.utils import date_diff
def execute(filters=None):
columns = get_columns()
item_details = get_fifo_queue(filters)
to_date = filters["to_date"]
data = []
for item, item_dict in item_details.items():
fifo_queue = item_dict["fifo_queue"]
details = item_dict["details"]
if not fifo_queue: continue
average_age = get_average_age(fifo_queue, to_date)
earliest_age = date_diff(to_date, fifo_queue[0][1])
latest_age = date_diff(to_date, fifo_queue[-1][1])
data.append([item, details.item_name, details.description, details.item_group,
details.brand, average_age, earliest_age, latest_age, details.stock_uom])
return columns, data
def get_average_age(fifo_queue, to_date):
batch_age = age_qty = total_qty = 0.0
for batch in fifo_queue:
batch_age = date_diff(to_date, batch[1])
age_qty += batch_age * batch[0]
total_qty += batch[0]
return (age_qty / total_qty) if total_qty else 0.0
def get_columns():
return ["Item Code:Link/Item:100", "Item Name::100", "Description::200",
"Item Group:Link/Item Group:100", "Brand:Link/Brand:100", "Average Age:Float:100",
"Earliest:Int:80", "Latest:Int:80", "UOM:Link/UOM:100"]
def get_fifo_queue(filters):
item_details = {}
for d in get_stock_ledger_entries(filters):
item_details.setdefault(d.name, {"details": d, "fifo_queue": []})
fifo_queue = item_details[d.name]["fifo_queue"]
if d.actual_qty > 0:
fifo_queue.append([d.actual_qty, d.posting_date])
else:
qty_to_pop = abs(d.actual_qty)
while qty_to_pop:
batch = fifo_queue[0] if fifo_queue else [0, None]
if 0 < batch[0] <= qty_to_pop:
# if batch qty > 0
# not enough or exactly same qty in current batch, clear batch
qty_to_pop -= batch[0]
fifo_queue.pop(0)
else:
# all from current batch
batch[0] -= qty_to_pop
qty_to_pop = 0
return item_details
def get_stock_ledger_entries(filters):
return webnotes.conn.sql("""select
item.name, item.item_name, item_group, brand, description, item.stock_uom,
actual_qty, posting_date
from `tabStock Ledger Entry` sle,
(select name, item_name, description, stock_uom, brand
from `tabItem` {item_conditions}) item
where item_code = item.name and
company = %(company)s and
posting_date <= %(to_date)s
{sle_conditions}
order by posting_date, posting_time, sle.name"""\
.format(item_conditions=get_item_conditions(filters),
sle_conditions=get_sle_conditions(filters)), filters, as_dict=True)
def get_item_conditions(filters):
conditions = []
if filters.get("item_code"):
conditions.append("item_code=%(item_code)s")
if filters.get("brand"):
conditions.append("brand=%(brand)s")
return "where {}".format(" and ".join(conditions)) if conditions else ""
def get_sle_conditions(filters):
conditions = []
if filters.get("warehouse"):
conditions.append("warehouse=%(warehouse)s")
return "and {}".format(" and ".join(conditions)) if conditions else ""

View File

@@ -0,0 +1,21 @@
[
{
"creation": "2013-12-02 17:09:31",
"docstatus": 0,
"modified": "2013-12-02 17:09:31",
"modified_by": "Administrator",
"owner": "Administrator"
},
{
"doctype": "Report",
"is_standard": "Yes",
"name": "__common__",
"ref_doctype": "Item",
"report_name": "Stock Ageing",
"report_type": "Script Report"
},
{
"doctype": "Report",
"name": "Stock Ageing"
}
]

View File

@@ -15,14 +15,14 @@ wn.query_reports["Stock Ledger"] = {
"fieldname":"from_date", "fieldname":"from_date",
"label": wn._("From Date"), "label": wn._("From Date"),
"fieldtype": "Date", "fieldtype": "Date",
"default": wn.defaults.get_user_default("year_start_date"), "default": wn.datetime.add_months(wn.datetime.get_today(), -1),
"reqd": 1 "reqd": 1
}, },
{ {
"fieldname":"to_date", "fieldname":"to_date",
"label": wn._("To Date"), "label": wn._("To Date"),
"fieldtype": "Date", "fieldtype": "Date",
"default": wn.defaults.get_user_default("year_end_date"), "default": wn.datetime.get_today(),
"reqd": 1 "reqd": 1
}, },
{ {

View File

@@ -3,37 +3,62 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import webnotes import webnotes
from webnotes import _
def execute(filters=None): def execute(filters=None):
columns = ["Date:Datetime:95", "Item:Link/Item:100", "Item Name::100", columns = get_columns()
sl_entries = get_stock_ledger_entries(filters)
item_details = get_item_details(filters)
data = []
for sle in sl_entries:
item_detail = item_details[sle.item_code]
data.append([sle.date, sle.item_code, item_detail.item_name, item_detail.item_group,
item_detail.brand, item_detail.description, sle.warehouse, item_detail.stock_uom,
sle.actual_qty, sle.qty_after_transaction, sle.stock_value, sle.voucher_type,
sle.voucher_no, sle.batch_no, sle.serial_no, sle.company])
return columns, data
def get_columns():
return ["Date:Datetime:95", "Item:Link/Item:100", "Item Name::100",
"Item Group:Link/Item Group:100", "Brand:Link/Brand:100", "Item Group:Link/Item Group:100", "Brand:Link/Brand:100",
"Description::200", "Warehouse:Link/Warehouse:100", "Description::200", "Warehouse:Link/Warehouse:100",
"Stock UOM:Link/UOM:100", "Qty:Float:50", "Balance Qty:Float:80", "Stock UOM:Link/UOM:100", "Qty:Float:50", "Balance Qty:Float:80",
"Balance Value:Currency:100", "Voucher Type::100", "Voucher #::100", "Balance Value:Currency:100", "Voucher Type::100", "Voucher #::100",
"Batch:Link/Batch:100", "Serial #:Link/Serial No:100", "Company:Link/Company:100"] "Batch:Link/Batch:100", "Serial #:Link/Serial No:100", "Company:Link/Company:100"]
data = webnotes.conn.sql("""select concat_ws(" ", posting_date, posting_time), def get_stock_ledger_entries(filters):
item.name, item.item_name, item.item_group, brand, description, warehouse, sle.stock_uom, if not filters.get("company"):
actual_qty, qty_after_transaction, stock_value, voucher_type, voucher_no, webnotes.throw(_("Company is mandatory"))
batch_no, serial_no, company if not filters.get("from_date"):
from `tabStock Ledger Entry` sle, webnotes.throw(_("From Date is mandatory"))
(select name, item_name, description, stock_uom, brand, item_group if not filters.get("to_date"):
from `tabItem` {item_conditions}) item webnotes.throw(_("To Date is mandatory"))
where item_code = item.name and
company = %(company)s and return webnotes.conn.sql("""select concat_ws(" ", posting_date, posting_time) as date,
item_code, warehouse, actual_qty, qty_after_transaction,
stock_value, voucher_type, voucher_no, batch_no, serial_no, company
from `tabStock Ledger Entry`
where company = %(company)s and
posting_date between %(from_date)s and %(to_date)s posting_date between %(from_date)s and %(to_date)s
{sle_conditions} {sle_conditions}
order by posting_date desc, posting_time desc, sle.name desc"""\ order by posting_date desc, posting_time desc, name desc"""\
.format(item_conditions=get_item_conditions(filters), .format(sle_conditions=get_sle_conditions(filters)), filters, as_dict=1)
sle_conditions=get_sle_conditions(filters)),
filters)
return columns, data def get_item_details(filters):
item_details = {}
for item in webnotes.conn.sql("""select name, item_name, description, item_group,
brand, stock_uom from `tabItem` {item_conditions}"""\
.format(item_conditions=get_item_conditions(filters)), filters, as_dict=1):
item_details.setdefault(item.name, item)
return item_details
def get_item_conditions(filters): def get_item_conditions(filters):
conditions = [] conditions = []
if filters.get("item_code"): if filters.get("item_code"):
conditions.append("item_code=%(item_code)s") conditions.append("name=%(item_code)s")
if filters.get("brand"): if filters.get("brand"):
conditions.append("brand=%(brand)s") conditions.append("brand=%(brand)s")

View File

@@ -0,0 +1,33 @@
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
wn.query_reports["Stock Projected Qty"] = {
"filters": [
{
"fieldname":"company",
"label": wn._("Company"),
"fieldtype": "Link",
"options": "Company",
"default": wn.defaults.get_user_default("company"),
"reqd": 1
},
{
"fieldname":"warehouse",
"label": wn._("Warehouse"),
"fieldtype": "Link",
"options": "Warehouse"
},
{
"fieldname":"item_code",
"label": wn._("Item"),
"fieldtype": "Link",
"options": "Item"
},
{
"fieldname":"brand",
"label": wn._("Brand"),
"fieldtype": "Link",
"options": "Brand"
}
]
}

View File

@@ -0,0 +1,44 @@
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import webnotes
def execute(filters=None):
columns = get_columns()
data = webnotes.conn.sql("""select
item.name, item.item_name, description, item_group, brand, warehouse, item.stock_uom,
actual_qty, planned_qty, indented_qty, ordered_qty, reserved_qty,
projected_qty, item.re_order_level, item.re_order_qty
from `tabBin` bin,
(select name, company from tabWarehouse
where company=%(company)s {warehouse_conditions}) wh,
(select name, item_name, description, stock_uom, item_group,
brand, re_order_level, re_order_qty
from `tabItem` {item_conditions}) item
where item_code = item.name and warehouse = wh.name
order by item.name, wh.name"""\
.format(item_conditions=get_item_conditions(filters),
warehouse_conditions=get_warehouse_conditions(filters)), filters, debug=1)
return columns, data
def get_columns():
return ["Item Code:Link/Item:140", "Item Name::100", "Description::200",
"Brand:Link/Brand:100", "Warehouse:Link/Warehouse:120", "UOM:Link/UOM:100",
"Actual Qty:Float:100", "Planned Qty:Float:100", "Requested Qty:Float:110",
"Ordered Qty:Float:100", "Reserved Qty:Float:100", "Projected Qty:Float:100",
"Reorder Level:Float:100", "Reorder Qty:Float:100"]
def get_item_conditions(filters):
conditions = []
if filters.get("item_code"):
conditions.append("name=%(item_code)s")
if filters.get("brand"):
conditions.append("brand=%(brand)s")
return "where {}".format(" and ".join(conditions)) if conditions else ""
def get_warehouse_conditions(filters):
return " and name=%(warehouse)s" if filters.get("warehouse") else ""

View File

@@ -0,0 +1,22 @@
[
{
"creation": "2013-12-04 18:21:56",
"docstatus": 0,
"modified": "2013-12-04 18:21:56",
"modified_by": "Administrator",
"owner": "Administrator"
},
{
"add_total_row": 1,
"doctype": "Report",
"is_standard": "Yes",
"name": "__common__",
"ref_doctype": "Item",
"report_name": "Stock Projected Qty",
"report_type": "Script Report"
},
{
"doctype": "Report",
"name": "Stock Projected Qty"
}
]

View File

@@ -2,7 +2,7 @@
{ {
"creation": "2013-02-01 10:36:25", "creation": "2013-02-01 10:36:25",
"docstatus": 0, "docstatus": 0,
"modified": "2013-11-02 14:06:26", "modified": "2013-12-14 17:27:02",
"modified_by": "Administrator", "modified_by": "Administrator",
"owner": "Administrator" "owner": "Administrator"
}, },
@@ -278,6 +278,7 @@
{ {
"cancel": 0, "cancel": 0,
"doctype": "DocPerm", "doctype": "DocPerm",
"match": "customer",
"role": "Customer" "role": "Customer"
}, },
{ {

View File

@@ -1,4 +1,6 @@
{ {
"中国(简体)": "zh-cn",
"中國(繁體)": "zh-tw",
"deutsch": "de", "deutsch": "de",
"ελληνικά": "el", "ελληνικά": "el",
"english": "en", "english": "en",

View File

@@ -2,11 +2,12 @@
{ {
"creation": "2013-01-10 16:34:32", "creation": "2013-01-10 16:34:32",
"docstatus": 0, "docstatus": 0,
"modified": "2013-07-11 17:02:12", "modified": "2013-12-12 12:17:46",
"modified_by": "Administrator", "modified_by": "Administrator",
"owner": "Administrator" "owner": "Administrator"
}, },
{ {
"allow_rename": 1,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Master", "document_type": "Master",
"icon": "icon-map-marker", "icon": "icon-map-marker",

View File

@@ -2,11 +2,12 @@
{ {
"creation": "2013-01-10 16:34:32", "creation": "2013-01-10 16:34:32",
"docstatus": 0, "docstatus": 0,
"modified": "2013-10-08 16:48:50", "modified": "2013-12-12 12:18:19",
"modified_by": "Administrator", "modified_by": "Administrator",
"owner": "Administrator" "owner": "Administrator"
}, },
{ {
"allow_rename": 1,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Master", "document_type": "Master",
"icon": "icon-user", "icon": "icon-user",