Merge branch 'master' into edge

This commit is contained in:
Anand Doshi 2013-02-26 11:34:22 +05:30
commit 98f16f0788
16 changed files with 152 additions and 232 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -151,6 +151,11 @@ wn.module_page["Accounts"] = [
route: "Report/Profile/Trend Analyzer",
doctype: "Sales Invoice"
},
{
"label":wn._("Gross Profit"),
route: "query-report/Gross Profit",
doctype: "Sales Invoice"
},
]
},
{

View File

View File

@ -0,0 +1,11 @@
wn.query_reports["Gross Profit"] = {
"filters": [
{
"fieldname":"company",
"label": "Company",
"fieldtype": "Link",
"options": "Company",
"default": wn.defaults.get_user_default("company")
}
]
}

View File

@ -0,0 +1,87 @@
from __future__ import unicode_literals
import webnotes
from webnotes.utils import flt
stock_ledger_entries = None
item_sales_bom = None
def execute(filters=None):
if not filters: filters = {}
get_stock_ledger_entries(filters)
get_sales_bom()
delivery_note_items = webnotes.conn.sql("""select dn.name, dn.posting_date, dn.posting_time,
dn.project_name, item.item_code, item.item_name, item.description, item.warehouse,
item.qty, item.basic_rate, item.amount, item.name as "item_row"
from `tabDelivery Note` dn, `tabDelivery Note Item` item
where item.parent = dn.name and dn.docstatus = 1
order by dn.posting_date desc, dn.posting_time desc""", as_dict=1)
columns = ["Delivery Note:Link/Delivery Note", "Posting Date:Date", "Posting Time",
"Item Code:Link/Item", "Item Name", "Description", "Warehouse:Link/Warehouse",
"Qty:Float", "Selling Rate:Currency", "Selling Amount:Currency", "Buying Amount:Currency",
"Gross Profit:Currency", "Gross Profit %:Percent", "Project:Link/Project"]
data = []
for row in delivery_note_items:
selling_amount = flt(row.amount)
buying_amount = get_buying_amount(row)
if selling_amount:
gross_profit = selling_amount - buying_amount
gross_profit_percent = (gross_profit / selling_amount) * 100.0
else:
gross_profit = gross_profit_percent = 0.0
data.append([row.name, row.posting_date, row.posting_time, row.item_code, row.item_name,
row.description, row.warehouse, row.qty, row.basic_rate, row.amount, buying_amount,
gross_profit, gross_profit_percent, row.project])
return columns, data
def get_buying_amount(row):
if item_sales_bom.get(row.item_code):
# sales bom item
buying_amount = 0.0
for bom_item in item_sales_bom[row.item_code]:
buying_amount += _get_buying_amount(row.name, "[** No Item Row **]",
bom_item.item_code, row.warehouse, bom_item.qty * row.qty)
return buying_amount
else:
# doesn't have sales bom
return _get_buying_amount(row.name, row.item_row, row.item_code, row.warehouse, row.qty)
def _get_buying_amount(voucher_no, item_row, item_code, warehouse, qty):
for i, sle in enumerate(stock_ledger_entries):
if sle.voucher_type == "Delivery Note" and sle.voucher_no == voucher_no:
if (sle.voucher_detail_no == item_row) or \
(sle.item_code == item_code and sle.warehouse == warehouse and \
abs(flt(sle.qty)) == qty):
buying_amount = flt(stock_ledger_entries[i+1].stock_value) - flt(sle.stock_value)
return buying_amount
return 0.0
def get_sales_bom():
global item_sales_bom
item_sales_bom = {}
for r in webnotes.conn.sql("""select parent, item_code, qty from `tabSales BOM Item`""", as_dict=1):
item_sales_bom.setdefault(r.parent, []).append(r)
def get_stock_ledger_entries(filters):
global stock_ledger_entries
query = """select item_code, voucher_type, voucher_no,
voucher_detail_no, posting_date, posting_time, stock_value,
warehouse, actual_qty as qty
from `tabStock Ledger Entry` where ifnull(`is_cancelled`, "No") = "No" """
if filters.get("company"):
query += """ and company=%(company)s"""
query += " order by item_code desc, warehouse desc, posting_date desc, posting_time desc, name desc"
stock_ledger_entries = webnotes.conn.sql(query, filters, as_dict=True)

View File

@ -0,0 +1,21 @@
[
{
"creation": "2013-02-25 17:03:34",
"docstatus": 0,
"modified": "2013-02-25 17:03:34",
"modified_by": "Administrator",
"owner": "Administrator"
},
{
"doctype": "Report",
"is_standard": "Yes",
"name": "__common__",
"ref_doctype": "Sales Invoice",
"report_name": "Gross Profit",
"report_type": "Script Report"
},
{
"doctype": "Report",
"name": "Gross Profit"
}
]

View File

@ -1,4 +1,8 @@
erpnext.updates = [
["26th February", [
"Gross Profit: The report has been rewritten and now it is under Accounts module"
]
],
["21st February, 2013", [
"Item: Warehouse-wise Re-order Level and Quantity",
"Buying: Purchase Request renamed to Material Request"

View File

@ -195,4 +195,8 @@ patch_list = [
"execute:webnotes.conn.sql(\"update `tabReport` set report_type=if(ifnull(query, '')='', 'Report Builder', 'Query Report') where is_standard='No'\")",
"execute:webnotes.conn.sql(\"update `tabReport` set report_name=name where ifnull(report_name,'')='' and is_standard='No'\")",
"patches.february_2013.p08_todo_query_report",
"execute:webnotes.delete_doc('Search Criteria', 'gross_profit') # 2013-02-26",
'execute:webnotes.reload_doc("accounts", "Print Format", "Sales Invoice Classic") # 2013-02-26',
'execute:webnotes.reload_doc("accounts", "Print Format", "Sales Invoice Modern") # 2013-02-26',
'execute:webnotes.reload_doc("accounts", "Print Format", "Sales Invoice Spartan") # 2013-02-26',
]

View File

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

View File

@ -1,38 +0,0 @@
// ERPNext - web based ERP (http://erpnext.com)
// Copyright (C) 2012 Web Notes Technologies Pvt Ltd
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
report.customize_filters = function() {
this.mytabs.items['Select Columns'].hide();
this.mytabs.tabs['More Filters'].hide();
this.hide_all_filters();
this.filter_fields_dict['Delivery Note'+FILTER_SEP +'ID'].df.filter_hide = 0;
this.filter_fields_dict['Delivery Note'+FILTER_SEP +'From Posting Date'].df.filter_hide = 0;
this.filter_fields_dict['Delivery Note'+FILTER_SEP +'To Posting Date'].df.filter_hide = 0;
this.filter_fields_dict['Delivery Note Item'+FILTER_SEP +'Item Code'].df.filter_hide = 0;
this.filter_fields_dict['Delivery Note'+FILTER_SEP +'Project Name'].df.filter_hide = 0;
this.filter_fields_dict['Delivery Note'+FILTER_SEP +'ID'].df.in_first_page = 1;
this.filter_fields_dict['Delivery Note'+FILTER_SEP +'From Posting Date'].df.in_first_page = 1;
this.filter_fields_dict['Delivery Note'+FILTER_SEP +'To Posting Date'].df.in_first_page = 1;
this.filter_fields_dict['Delivery Note Item'+FILTER_SEP +'Item Code'].df.in_first_page = 1;
this.filter_fields_dict['Delivery Note'+FILTER_SEP +'Project Name'].df.in_first_page = 1;
this.filter_fields_dict['Delivery Note' + FILTER_SEP +
'From Posting Date'].df['report_default'] = sys_defaults["year_start_date"]
this.filter_fields_dict['Delivery Note' + FILTER_SEP +
'To Posting Date'].df['report_default'] = dateutil.obj_to_str(new Date());
}

View File

@ -1,104 +0,0 @@
# ERPNext - web based ERP (http://erpnext.com)
# Copyright (C) 2012 Web Notes Technologies Pvt Ltd
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import unicode_literals
from webnotes.utils import flt
if not (filter_values.get('posting_date') or filter_values.get('posting_date1')):
msgprint("Please select From and To Posting Date", raise_exception=1)
columns = [
['Delivery Note', 'Link', '120px', 'Delivery Note'],
['Posting Date', 'Date', '120px', ''],
['Posting Time', 'Data', '120px', ''],
['Item Code', 'Link', '120px', 'Item'],
['Item Name', 'Data', '120px', ''],
['Description', 'Data', '120px', ''],
['Warehouse', 'Link', '120px', 'Warehouse'],
['Project Name', 'Link', '120px', 'Project'],
['Quantity', 'Currency', '120px', ''],
['Rate', 'Currency', '120px', ''],
['Amount', 'Currency', '120px', ''],
#['DN Item Row Id', 'Data', '120px', ''],
['Purchase Cost', 'Currency', '150px', ''],
['Gross Profit', 'Currency', '150px', ''],
['Gross Profit (%)', 'Currrency', '150px', '']
]
for c in columns:
colnames.append(c[0])
coltypes.append(c[1])
colwidths.append(c[2])
coloptions.append(c[3])
col_idx[c[0]] = len(colnames)-1
sle = sql("""
select
actual_qty, incoming_rate, voucher_no, item_code, warehouse, voucher_detail_no
from
`tabStock Ledger Entry`
where
voucher_type = 'Delivery Note'
and ifnull(is_cancelled, 'No') = 'No'
order by posting_date desc, posting_time desc, name desc
""", as_dict=1)
def get_purchase_cost(dn, item, wh, qty, dn_item_row_id):
from webnotes.utils import flt
global sle
purchase_cost = 0
packing_items = sql("select item_code, qty from `tabSales BOM Item` where parent = %s", item)
if packing_items:
packing_items = [[t[0], flt(t[1])*qty] for t in packing_items]
else:
packing_items = [[item, qty]]
for d in sle:
if packing_items:
if d['voucher_no'] == dn \
and [d['item_code'], flt(abs(d['actual_qty']))] in packing_items \
and (not d['voucher_detail_no'] or d['voucher_detail_no'] == dn_item_row_id):
purchase_cost += flt(d['incoming_rate'])*flt(abs(d['actual_qty']))
packing_items.remove([d['item_code'], flt(abs(d['actual_qty']))])
else:
break
return purchase_cost
out, tot_amount, tot_pur_cost = [], 0, 0
for r in res:
purchase_cost = get_purchase_cost(r[col_idx['Delivery Note']], r[col_idx['Item Code']], \
r[col_idx['Warehouse']], r[col_idx['Quantity']], r[-1])
r.pop(-1)
r.append(purchase_cost)
gp = flt(r[col_idx['Amount']]) - flt(purchase_cost)
gp_percent = r[col_idx['Amount']] and purchase_cost and \
round((gp*100/flt(r[col_idx['Amount']])), 2) or 0
r.append(fmt_money(gp))
r.append(fmt_money(gp_percent))
out.append(r)
tot_amount += flt(r[col_idx['Amount']])
tot_pur_cost += flt(purchase_cost)
# Add Total Row
l_row = ['' for i in range(len(colnames))]
l_row[col_idx['Project Name']] = '<b>TOTALS</b>'
l_row[col_idx['Amount']] = fmt_money(tot_amount)
l_row[col_idx['Purchase Cost']] = fmt_money(tot_pur_cost)
l_row[col_idx['Gross Profit']] = fmt_money(flt(tot_amount) - flt(tot_pur_cost))
l_row[col_idx['Gross Profit (%)']] = tot_amount and \
round((flt(tot_amount) - flt(tot_pur_cost))*100 / flt(tot_amount), 2)
out.append(l_row)

View File

@ -1,15 +0,0 @@
SELECT
dn.name, dn.posting_date, dn.posting_time, dn_item.item_code,
dn_item.item_name, dn_item.description, dn_item.warehouse,
dn.project_name, dn_item.qty, dn_item.basic_rate, dn_item.amount, dn_item.name
FROM
`tabDelivery Note Item` dn_item, `tabDelivery Note` dn
WHERE
dn_item.parent = dn.name
AND dn.docstatus = 1
AND dn.name like '%(name)s%%'
AND ifnull(dn_item.item_code, '') like '%(item_code)s%%'
AND ifnull(dn.project_name, '') like '%(project_name)s%%'
AND dn.posting_date >= '%(posting_date)s'
AND dn.posting_date <= '%(posting_date1)s'
ORDER BY dn.name DESC

View File

@ -1,28 +0,0 @@
[
{
"owner": "Administrator",
"docstatus": 0,
"creation": "2012-05-14 18:22:18",
"modified_by": "Administrator",
"modified": "2012-09-24 14:11:39"
},
{
"description": "Invoice wise",
"parent_doc_type": "Delivery Note",
"module": "Selling",
"standard": "Yes",
"sort_order": "DESC",
"filters": "{\"Delivery Note\\u0001Submitted\":1,\"Delivery Note\\u0001Status\":[],\"Delivery Note\\u0001Fiscal Year\":[]}",
"doc_type": "Delivery Note Item",
"name": "__common__",
"doctype": "Search Criteria",
"sort_by": "`tabDelivery Note`.`name`",
"page_len": 50,
"criteria_name": "Gross Profit",
"columns": "Delivery Note\u0001ID,Delivery Note\u0001Posting Date,Delivery Note\u0001Posting Time,Delivery Note Item\u0001Item Code,Delivery Note Item\u0001Item Name,Delivery Note Item\u0001Description,Delivery Note Item\u0001Warehouse,Delivery Note\u0001Project Name,Delivery Note Item\u0001Quantity,Delivery Note Item\u0001Rate*,Delivery Note Item\u0001Amount*"
},
{
"name": "gross_profit",
"doctype": "Search Criteria"
}
]

View File

@ -30,8 +30,6 @@ class DocType:
self.doc = doc
self.doclist = doclist
# validate receiver numbers
# =========================================================
def validate_receiver_nos(self,receiver_list):
validated_receiver_list = []
for d in receiver_list:
@ -40,37 +38,20 @@ class DocType:
for x in invalid_char_list:
d = d.replace(x, '')
# mobile no validation for erpnext gateway
if webnotes.conn.get_value('SMS Settings', None, 'sms_gateway_url'):
mob_no = d
else:
if not d.startswith("0") and len(d) == 10:
mob_no = "91" + d
elif d.startswith("0") and len(d) == 11:
mob_no = "91" + d[1:]
elif len(d) == 12:
mob_no = d
else:
msgprint("Invalid mobile no : " + cstr(d))
raise Exception
if not mob_no.isdigit():
msgprint("Invalid mobile no : " + cstr(mob_no))
raise Exception
validated_receiver_list.append(mob_no)
validated_receiver_list.append(d)
if not validated_receiver_list:
msgprint("Please enter valid mobile nos")
raise Exception
msgprint("Please enter valid mobile nos", raise_exception=1)
return validated_receiver_list
def get_sender_name(self):
"returns name as SMS sender"
sender_name = webnotes.conn.get_value('Global Defaults', None, 'sms_sender_name') or 'ERPNXT'
if len(sender_name) > 6:
sender_name = webnotes.conn.get_value('Global Defaults', None, 'sms_sender_name') or \
'ERPNXT'
if len(sender_name) > 6 and \
webnotes.conn.get_value("Control Panel", None, "country") == "India":
msgprint("""
As per TRAI rule, sender name must be exactly 6 characters.
Kindly change sender name in Setup --> Global Defaults.
@ -82,8 +63,8 @@ class DocType:
def get_contact_number(self, arg):
"returns mobile number of the contact"
args = load_json(arg)
number = sql('select mobile_no, phone from tabContact where name=%s and %s=%s' % ('%s', args['key'], '%s'),\
(args['contact_name'], args['value']))
number = sql("""select mobile_no, phone from tabContact where name=%s and %s=%s""" %
('%s', args['key'], '%s'), (args['contact_name'], args['value']))
return number and (number[0][0] or number[0][1]) or ''
def send_form_sms(self, arg):
@ -91,9 +72,6 @@ class DocType:
args = load_json(arg)
self.send_sms([str(args['number'])], str(args['message']))
# Send SMS
# =========================================================
def send_sms(self, receiver_list, msg, sender_name = ''):
receiver_list = self.validate_receiver_nos(receiver_list)
@ -103,15 +81,11 @@ class DocType:
'sender_name' : sender_name or self.get_sender_name()
}
# personalized or erpnext gateway
if webnotes.conn.get_value('SMS Settings', None, 'sms_gateway_url'):
ret = self.send_via_personalized_gateway(arg)
ret = self.send_via_gateway(arg)
msgprint(ret)
# Send sms via personalized gateway
# ==========================================================
def send_via_personalized_gateway(self, arg):
def send_via_gateway(self, arg):
ss = get_obj('SMS Settings', 'SMS Settings', with_children=1)
args = {ss.doc.message_parameter : arg.get('message')}
for d in getlist(ss.doclist, 'static_parameter_details'):