[minor] repost future gl entries only for current voucher items

This commit is contained in:
Nabin Hait 2013-09-04 17:55:45 +05:30
commit bf4d27ecf1
32 changed files with 293 additions and 78 deletions

View File

@ -70,5 +70,5 @@ cur_frm.fields_dict["expense_account"].get_query = function(doc) {
}
cur_frm.fields_dict.user.get_query = function(doc,cdt,cdn) {
return{ query:"controllers.queries.profile_query"}
return{ query:"core.doctype.profile.profile.profile_query"}
}

View File

@ -80,7 +80,6 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
source_doctype: "Delivery Note",
get_query: function() {
var filters = {
docstatus: 1,
company: cur_frm.doc.company
};
if(cur_frm.doc.customer) filters["customer"] = cur_frm.doc.customer;

View File

@ -3,6 +3,7 @@
from __future__ import unicode_literals
import webnotes
from webnotes.widgets.reportview import get_match_cond
def get_filters_cond(doctype, filters, conditions):
if filters:
@ -22,34 +23,6 @@ def get_filters_cond(doctype, filters, conditions):
cond = ''
return cond
def get_match_cond(doctype, searchfield = 'name'):
from webnotes.widgets.reportview import build_match_conditions
cond = build_match_conditions(doctype)
if cond:
cond = ' and ' + cond
else:
cond = ''
return cond
# searches for enabled profiles
def profile_query(doctype, txt, searchfield, start, page_len, filters):
return webnotes.conn.sql("""select name, concat_ws(' ', first_name, middle_name, last_name)
from `tabProfile`
where ifnull(enabled, 0)=1
and docstatus < 2
and name not in ('Administrator', 'Guest')
and (%(key)s like "%(txt)s"
or concat_ws(' ', first_name, middle_name, last_name) like "%(txt)s")
%(mcond)s
order by
case when name like "%(txt)s" then 0 else 1 end,
case when concat_ws(' ', first_name, middle_name, last_name) like "%(txt)s"
then 0 else 1 end,
name asc
limit %(start)s, %(page_len)s""" % {'key': searchfield, 'txt': "%%%s%%" % txt,
'mcond':get_match_cond(doctype, searchfield), 'start': start, 'page_len': page_len})
# searches for active employees
def employee_query(doctype, txt, searchfield, start, page_len, filters):
return webnotes.conn.sql("""select name, employee_name from `tabEmployee`
@ -196,10 +169,12 @@ def get_price_list_currency(doctype, txt, searchfield, start, page_len, filters)
def get_delivery_notes_to_be_billed(doctype, txt, searchfield, start, page_len, filters):
return webnotes.conn.sql("""select `tabDelivery Note`.name, `tabDelivery Note`.customer_name
from `tabDelivery Note`
where `tabDelivery Note`.`%(key)s` like %(txt)s %(fcond)s and
where `tabDelivery Note`.`%(key)s` like %(txt)s and
`tabDelivery Note`.docstatus = 1 %(fcond)s and
(ifnull((select sum(qty) from `tabDelivery Note Item` where
`tabDelivery Note Item`.parent=`tabDelivery Note`.name), 0) >
ifnull((select sum(qty) from `tabSales Invoice Item` where
`tabSales Invoice Item`.docstatus = 1 and
`tabSales Invoice Item`.delivery_note=`tabDelivery Note`.name), 0))
%(mcond)s order by `tabDelivery Note`.`%(key)s` asc
limit %(start)s, %(page_len)s""" % {

View File

@ -14,11 +14,11 @@ class StockController(AccountsController):
def make_gl_entries(self):
if not cint(webnotes.defaults.get_global_default("auto_accounting_for_stock")):
return
if self.doc.docstatus==1:
gl_entries = self.get_gl_entries_for_stock()
gl_entries = self.get_gl_entries_for_stock()
make_gl_entries(gl_entries)
else:
else:
delete_gl_entries(voucher_type=self.doc.doctype, voucher_no=self.doc.name)
self.update_gl_entries_after()
@ -117,15 +117,19 @@ class StockController(AccountsController):
def get_future_stock_vouchers(self):
future_stock_vouchers = []
item_codes = webnotes.conn.sql_list("""select distinct item_code
from `tabStock Ledger Entry`
where voucher_type=%s and voucher_no=%s""", (self.doc.doctype, self.doc.name))
if hasattr(self, "fname"):
item_list = [d.item_code for d in self.doclist.get({"parentfield": self.fname})]
condition = ''.join(['and item_code in (\'', '\', \''.join(item_list) ,'\')'])
else:
condition = ""
for d in webnotes.conn.sql("""select distinct sle.voucher_type, sle.voucher_no
from `tabStock Ledger Entry` sle
where timestamp(sle.posting_date, sle.posting_time) >= timestamp(%s, %s)
order by timestamp(sle.posting_date, sle.posting_time) asc, name asc""",
(self.doc.posting_date, self.doc.posting_time), as_dict=True):
where timestamp(sle.posting_date, sle.posting_time) >= timestamp(%s, %s) %s
order by timestamp(sle.posting_date, sle.posting_time) asc, name asc""" %
('%s', '%s', condition), (self.doc.posting_date, self.doc.posting_time),
as_dict=True):
future_stock_vouchers.append([d.voucher_type, d.voucher_no])
return future_stock_vouchers

View File

@ -0,0 +1,24 @@
---
{
"_label": "Calculate Incentive for Sales Team"
}
---
Can be used in any Sales Transaction with **Sales Team** Table:
cur_frm.cscript.custom_validate = function(doc) {
// calculate incentives for each person on the deal
total_incentive = 0
$.each(wn.model.get("Sales Team", {parent:doc.name}), function(i, d) {
// calculate incentive
var incentive_percent = 2;
if(doc.grand_total > 400) incentive_percent = 4;
// actual incentive
d.incentives = flt(doc.grand_total) * incentive_percent / 100;
total_incentive += flt(d.incentives)
});
doc.total_incentive = total_incentive;
}

View File

@ -0,0 +1,20 @@
---
{
"_label": "Custom Script: Fetch Values from Master"
}
---
To pull a value of a link on selection, use the `add_fetch` method.
add_fetch(link_fieldname, source_fieldname, target_fieldname)
### Example
You create Custom Field **VAT ID** (`vat_id`) in **Customer** and **Sales Invoice** and want to make sure this value gets updated every time you select a Customer in a Sales Invoice.
Then in the Sales Invoice Custom Script, add this line:
cur_frm.add_fetch('customer','vat_id','vat_id')
---
See: [How to create a custom script](!docs.dev.custom_script.html)

View File

@ -0,0 +1,35 @@
---
{
"_label": "Generate Item Code based on Custom Logic"
}
---
Add this in the Custom Script of **Item**, so that the new Item Code is generated just before the a new Item is saved.
cur_frm.cscript.custom_validate = function(doc) {
// clear item_code (name is from item_code)
doc.item_code = "";
// first 2 characters based on item_group
switch(doc.item_group) {
case "Test A":
doc.item_code = "TA";
break;
case "Test B":
doc.item_code = "TB";
break;
default:
doc.item_code = "XX";
}
// add next 2 characters based on brand
switch(doc.brand) {
case "Brand A":
doc.item_code += "BA";
break;
case "Brand B":
doc.item_code += "BB";
break;
default:
doc.item_code += "BX";
}
}

View File

@ -0,0 +1,13 @@
---
{
"_label": "Make an Item read-only after Saving"
}
---
Use the method `cur_frm.set_df_property` to update the field's display.
In this script we also use the `__islocal` property of the doc to check if the document has been saved atleast once or is never saved. If `__islocal` is `1`, then the document has never been saved.
cur_frm.cscript.custom_refresh = function(doc) {
// use the __islocal value of doc, to check if the doc is saved or not
cur_frm.set_df_property("myfield", "read_only", doc.__islocal ? 0 : 1);
}

View File

@ -0,0 +1,12 @@
---
{
"_label": "Date Validation: Do not allow past dates in a date field"
}
---
cur_frm.cscript.custom_validate = function(doc) {
if (doc.from_date < get_today()) {
msgprint("You can not select past date in From Date");
validated = false;
}
}

View File

@ -0,0 +1,12 @@
---
{
"_label": "Restrict Purpose of Stock Entry"
}
---
cur_frm.cscript.custom_validate = function(doc) {
if(user=="user1@example.com" && doc.purpose!="Material Receipt") {
msgprint("You are only allowed Material Receipt");
validated = false;
}
}

View File

@ -0,0 +1,22 @@
---
{
"_label": "Restrict User Based on Child Record (Warehouse)"
}
---
// restrict certain warehouse to Material Manager
cur_frm.cscript.custom_validate = function(doc) {
if(user_roles.indexOf("Material Manager")==-1) {
var restricted_in_source = wn.model.get("Stock Entry Detail",
{parent:cur_frm.doc.name, s_warehouse:"Restricted"});
var restricted_in_target = wn.model.get("Stock Entry Detail",
{parent:cur_frm.doc.name, t_warehouse:"Restricted"})
if(restricted_in_source.length || restricted_in_target.length) {
msgprint("Only Material Manager can make entry in Restricted Warehouse");
validated = false;
}
}
}

View File

@ -0,0 +1,17 @@
---
{
"_label": "Restrict Cancel Rights based on Certain Order Value"
}
---
Add a handler to `custom_before_cancel` event:
cur_frm.cscript.custom_before_cancel = function(doc) {
if (user_roles.indexOf("Accounts User")!=-1 && user_roles.indexOf("Accounts Manager")==-1
&& user_roles.indexOf("System Manager")==-1) {
if (flt(doc.grand_total) > 10000) {
msgprint("You can not cancel this transaction, because grand total \
is greater than 10000");
validated = false;
}
}
}

View File

@ -1,10 +0,0 @@
---
{
"_label": "Client Scripts: Custoimzing ERPNext"
}
---

View File

@ -0,0 +1,31 @@
---
{
"_label": "Custom Script Examples",
"_toc": [
"docs.dev.custom_script.fetch",
"docs.dev.custom_script.validate",
"docs.dev.custom_script.validate1",
"docs.dev.custom_script.validate2",
"docs.dev.custom_script.validate3",
"docs.dev.custom_script.read_only",
"docs.dev.custom_script.calculate",
"docs.dev.custom_script.item_code"
]
}
---
### How to Create a Custom Script
Create a Custom Script (you must have System Manager role for this):
1. Got to: Setup > Custom Script > New Custom Script
1. Select the DocType in which you want to add the Custom Script
---
### Notes
1. Server Custom Scripts are only available for the Administrator.
1. Client Custom Scripts are in Javascript and Server Custom Scripts are in Python.
1. For testing, make sure to go to Tools > Clear Cache and refresh after updating a Custom Script.

View File

@ -6,7 +6,7 @@
"docs.dev.quickstart",
"docs.dev.framework",
"docs.dev.modules",
"docs.dev.client_script",
"docs.dev.custom_script",
"docs.dev.api",
"docs.dev.translate",
"docs.dev.docs"

View File

@ -6,7 +6,7 @@ erpnext.hr.EmployeeController = wn.ui.form.Controller.extend({
setup: function() {
this.setup_leave_approver_select();
this.frm.fields_dict.user_id.get_query = function(doc,cdt,cdn) {
return { query:"controllers.queries.profile_query"} }
return { query:"core.doctype.profile.profile.profile_query"} }
this.frm.fields_dict.reports_to.get_query = function(doc,cdt,cdn) {
return{ query:"controllers.queries.employee_query"} }
},

View File

@ -27,8 +27,8 @@ erpnext.hr.ExpenseClaimController = wn.ui.form.Controller.extend({
var d1 = wn.model.add_child(jv, 'Journal Voucher Detail', 'entries');
d1.credit = cur_frm.doc.total_sanctioned_amount;
if(r.message) {
d1.account = r.message[0].account;
d1.balance = r.message[0].balance;
d1.account = r.message.account;
d1.balance = r.message.balance;
}
loaddoc('Journal Voucher', jv.name);

View File

@ -261,6 +261,7 @@ patch_list = [
"execute:webnotes.bean('Style Settings').save() #2013-08-20",
"patches.september_2013.p01_fix_buying_amount_gl_entries",
"patches.september_2013.p01_update_communication",
"patches.september_2013.p02_fix_serial_no_status",
"patches.august_2013.p06_deprecate_is_cancelled",
"execute:webnotes.delete_doc('DocType', 'Budget Control')",
]

View File

@ -0,0 +1,30 @@
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd.
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import webnotes
def execute():
stock_entries = webnotes.conn.sql("""select ste_item.serial_no, ste.name
from `tabStock Entry Detail` ste_item, `tabStock Entry` ste
where ste.name = ste_item.parent
and ifnull(ste_item.serial_no, '') != ''
and ste.purpose='Material Transfer'
and ste.modified>='2013-08-14'
order by ste.posting_date desc, ste.posting_time desc, ste.name desc""", as_dict=1)
for d in stock_entries:
serial_nos = d.serial_no.split("\n")
for sr in serial_nos:
serial_no = sr.strip()
if serial_no:
serial_bean = webnotes.bean("Serial No", serial_no)
if serial_bean.doc.status == "Not Available":
latest_sle = webnotes.conn.sql("""select voucher_no from `tabStock Ledger Entry`
where item_code=%s and warehouse=%s and serial_no like %s
order by name desc limit 1""", (serial_bean.doc.item_code,
serial_bean.doc.warehouse, "%%%s%%" % serial_no))
if latest_sle and latest_sle[0][0] == d.name:
serial_bean.doc.status = "Available"
serial_bean.save()

View File

@ -5,7 +5,7 @@
wn.provide("erpnext.queries");
$.extend(erpnext.queries, {
profile: function() {
return { query: "controllers.queries.profile_query" };
return { query: "core.doctype.profile.profile.profile_query" };
},
lead: function() {

View File

@ -40,8 +40,7 @@ class DocType(TransactionBase):
def validate_values(self):
if webnotes.defaults.get_global_default('cust_master_name') == 'Naming Series' and not self.doc.naming_series:
msgprint("Series is Mandatory.")
raise Exception
webnotes.throw("Series is Mandatory.", webnotes.MandatoryError)
def validate(self):
self.validate_values()

View File

@ -14,12 +14,12 @@ erpnext.LeadController = wn.ui.form.Controller.extend({
onload: function() {
if(cur_frm.fields_dict.lead_owner.df.options.match(/^Profile/)) {
cur_frm.fields_dict.lead_owner.get_query = function(doc,cdt,cdn) {
return { query:"controllers.queries.profile_query" } }
return { query:"core.doctype.profile.profile.profile_query" } }
}
if(cur_frm.fields_dict.contact_by.df.options.match(/^Profile/)) {
cur_frm.fields_dict.contact_by.get_query = function(doc,cdt,cdn) {
return { query:"controllers.queries.profile_query" } }
return { query:"core.doctype.profile.profile.profile_query" } }
}
if(in_list(user_roles,'System Manager')) {

View File

@ -7,7 +7,7 @@ import webnotes
from webnotes.utils import cstr, getdate
from webnotes.model.bean import getlist
from webnotes.model.code import get_obj
from webnotes import msgprint
from webnotes import _, msgprint
sql = webnotes.conn.sql
@ -278,3 +278,7 @@ def _make_customer(source_name, ignore_permissions=False):
return customer
else:
raise e
except webnotes.MandatoryError:
from webnotes.utils import get_url_to_form
webnotes.throw(_("Before proceeding, please create Customer from Lead") + \
(" - %s" % get_url_to_form("Lead", lead_name)))

View File

@ -161,7 +161,7 @@ def _get_item_discount(item_group, customer):
FROM `tabItem Group` AS node, `tabItem Group` AS parent
WHERE parent.lft <= node.lft and parent.rgt >= node.rgt and node.name = %s
GROUP BY parent.name
ORDER BY parent.lft desc""", item_group)]
ORDER BY parent.lft desc""", (item_group,))]
discount = 0
for d in parent_item_groups:

View File

@ -69,10 +69,10 @@ cur_frm.cscript.transaction = function(doc,cdt,cdn){
cur_frm.fields_dict.system_user.get_query = function(doc,cdt,cdn) {
return{ query:"controllers.queries.profile_query" } }
return{ query:"core.doctype.profile.profile.profile_query" } }
cur_frm.fields_dict.approving_user.get_query = function(doc,cdt,cdn) {
return{ query:"controllers.queries.profile_query" } }
return{ query:"core.doctype.profile.profile.profile_query" } }
cur_frm.fields_dict['approving_role'].get_query = cur_frm.fields_dict['system_role'].get_query;

View File

@ -16,7 +16,9 @@ def boot_session(bootinfo):
if webnotes.session['user']!='Guest':
bootinfo['letter_heads'] = get_letter_heads()
load_country_and_currency(bootinfo)
import webnotes.model.doctype
bootinfo['notification_settings'] = webnotes.doc("Notification Control",
"Notification Control").get_values()
@ -36,7 +38,15 @@ def boot_session(bootinfo):
bootinfo['docs'] += webnotes.conn.sql("""select name, default_currency, cost_center
from `tabCompany`""", as_dict=1, update={"doctype":":Company"})
def load_country_and_currency(bootinfo):
if bootinfo.control_panel.country and \
webnotes.conn.exists("Country", bootinfo.control_panel.country):
bootinfo["docs"] += [webnotes.doc("Country", bootinfo.control_panel.country)]
bootinfo["docs"] += webnotes.conn.sql("""select * from tabCurrency
where ifnull(enabled,0)=1""", as_dict=1, update={"doctype":":Currency"})
def get_letter_heads():
"""load letter heads with startup"""
import webnotes

View File

@ -64,6 +64,12 @@ def check_if_expired():
webnotes.response['message'] = 'Account Expired'
raise webnotes.AuthenticationError
def on_build():
from website.helpers.make_web_include_files import make
make()
from home.page.latest_updates import latest_updates
latest_updates.make()
def comment_added(doc):
"""add comment to feed"""

View File

@ -117,9 +117,6 @@ class TestPurchaseReceipt(unittest.TestCase):
self.assertFalse(webnotes.conn.get_value("Serial No", pr.doclist[1].serial_no,
"warehouse"))
self.assertEqual(webnotes.conn.get_value("Serial No", pr.doclist[1].serial_no,
"status"), "Not Available")
def get_gl_entries(voucher_type, voucher_no):
return webnotes.conn.sql("""select account, debit, credit

View File

@ -52,9 +52,7 @@ class DocType(StockController):
webnotes.throw(_("Item Code cannot be changed for Serial No."), SerialNoCannotCannotChangeError)
if not self.via_stock_ledger and warehouse != self.doc.warehouse:
webnotes.throw(_("Warehouse cannot be changed for Serial No."), SerialNoCannotCannotChangeError)
if not self.doc.warehouse and self.doc.status=="Available":
self.doc.status = "Not Available"
def validate_item(self):
"""
@ -94,4 +92,4 @@ class DocType(StockController):
serial_nos = map(lambda i: i==old and new or i, item[1].split('\n'))
webnotes.conn.sql("""update `tab%s` set serial_no = %s
where name=%s""" % (dt[0], '%s', '%s'),
('\n'.join(serial_nos), item[0]))
('\n'.join(serial_nos), item[0]))

View File

@ -316,7 +316,7 @@ class DocType(StockController):
def update_stock_ledger(self):
sl_entries = []
for d in getlist(self.doclist, 'mtn_details'):
if cstr(d.s_warehouse):
if cstr(d.s_warehouse) and self.doc.docstatus == 1:
sl_entries.append(self.get_sl_entries(d, {
"warehouse": cstr(d.s_warehouse),
"actual_qty": -flt(d.transfer_qty),
@ -329,6 +329,16 @@ class DocType(StockController):
"actual_qty": flt(d.transfer_qty),
"incoming_rate": flt(d.incoming_rate)
}))
# On cancellation, make stock ledger entry for
# target warehouse first, to update serial no values properly
if cstr(d.s_warehouse) and self.doc.docstatus == 2:
sl_entries.append(self.get_sl_entries(d, {
"warehouse": cstr(d.s_warehouse),
"actual_qty": -flt(d.transfer_qty),
"incoming_rate": 0
}))
self.make_sl_entries(sl_entries, self.doc.amended_from and 'Yes' or 'No')

View File

@ -700,6 +700,9 @@ class TestStockEntry(unittest.TestCase):
self.assertTrue(webnotes.conn.exists("Serial No", "ABCD"))
self.assertTrue(webnotes.conn.exists("Serial No", "EFGH"))
se.cancel()
self.assertFalse(webnotes.conn.get_value("Serial No", "ABCD", "warehouse"))
def test_serial_no_not_exists(self):
self._clear_stock_account_balance()
se = webnotes.bean(copy=test_records[0])
@ -755,6 +758,9 @@ class TestStockEntry(unittest.TestCase):
se.insert()
se.submit()
self.assertTrue(webnotes.conn.get_value("Serial No", serial_no, "warehouse"), "_Test Warehouse 1 - _TC")
se.cancel()
self.assertTrue(webnotes.conn.get_value("Serial No", serial_no, "warehouse"), "_Test Warehouse - _TC")
def test_serial_warehouse_error(self):
self._clear_stock_account_balance()
@ -778,7 +784,6 @@ class TestStockEntry(unittest.TestCase):
serial_no = get_serial_nos(se.doclist[1].serial_no)[0]
self.assertFalse(webnotes.conn.get_value("Serial No", serial_no, "warehouse"))
self.assertTrue(webnotes.conn.get_value("Serial No", serial_no, "status"), "Not Available")
def make_serialized_item():
se = webnotes.bean(copy=test_records[0])

View File

@ -124,13 +124,14 @@ class DocType(DocListController):
if self.doc.actual_qty < 0:
if sr.doc.warehouse!=self.doc.warehouse:
webnotes.throw(_("Warehouse does not belong to Item") + \
(": %s (%s)" % (self.doc.item_code, serial_no)), SerialNoWarehouseError)
webnotes.throw(_("Serial No") + ": " + serial_no +
_(" does not belong to Warehouse") + ": " + self.doc.warehouse,
SerialNoWarehouseError)
if self.doc.voucher_type in ("Delivery Note", "Sales Invoice") \
and sr.doc.status != "Available":
webnotes.throw(_("Serial No status must be 'Available' to Deliver") + \
": " + serial_no, SerialNoStatusError)
webnotes.throw(_("Serial No status must be 'Available' to Deliver")
+ ": " + serial_no, SerialNoStatusError)
sr.doc.warehouse = None