Merge branch 'master' into edge
This commit is contained in:
commit
98f16f0788
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
@ -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"
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
|
0
accounts/report/gross_profit/__init__.py
Normal file
0
accounts/report/gross_profit/__init__.py
Normal file
11
accounts/report/gross_profit/gross_profit.js
Normal file
11
accounts/report/gross_profit/gross_profit.js
Normal 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")
|
||||
}
|
||||
]
|
||||
}
|
87
accounts/report/gross_profit/gross_profit.py
Normal file
87
accounts/report/gross_profit/gross_profit.py
Normal 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)
|
21
accounts/report/gross_profit/gross_profit.txt
Normal file
21
accounts/report/gross_profit/gross_profit.txt
Normal 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"
|
||||
}
|
||||
]
|
@ -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"
|
||||
|
@ -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',
|
||||
]
|
@ -1 +0,0 @@
|
||||
from __future__ import unicode_literals
|
@ -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());
|
||||
|
||||
}
|
@ -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)
|
@ -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
|
@ -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"
|
||||
}
|
||||
]
|
@ -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'):
|
||||
|
Loading…
x
Reference in New Issue
Block a user