Compare commits

...

41 Commits

Author SHA1 Message Date
Pratik Vyas
ad67b84d43 Merge branch 'develop' 2014-09-29 15:40:04 +05:30
Pratik Vyas
4e81e4065b bumped to version 4.5.1 2014-09-29 16:10:04 +06:00
Nabin Hait
81332789cb Merge pull request #2232 from nabinhait/hotfix
Fix gl entries for stock transactions
2014-09-29 15:25:02 +05:30
Nabin Hait
82d7c0c9eb Patch: Fix gl entries for stock transactions 2014-09-29 15:07:51 +05:30
Nabin Hait
d60235e239 minor fix in warehouse-wise stock balance report 2014-09-29 11:36:06 +05:30
Nabin Hait
9b50b0a762 Fixes for item list view 2014-09-29 11:36:06 +05:30
Pratik Vyas
21e14c4c98 Merge branch 'develop' 2014-09-26 16:49:59 +05:30
Pratik Vyas
16edacebc7 bumped to version 4.5.0 2014-09-26 17:19:59 +06:00
Nabin Hait
75027b4d54 Merge pull request #2225 from nabinhait/hotfix
GL Entries for future stock voucher and multiple minor fixes
2014-09-26 14:48:56 +05:30
Nabin Hait
b0bd99266d Fix in landed cost voucher 2014-09-26 14:30:02 +05:30
Nabin Hait
b9e04815f8 Repost gl entries for future stock vouchers 2014-09-26 14:24:42 +05:30
Nabin Hait
18ccc27b1b Minor fix in naming naming series 2014-09-26 14:23:00 +05:30
Nabin Hait
95f1fe92e2 Maintenance visit search field issue fixed 2014-09-26 14:23:00 +05:30
Nabin Hait
b783f519ee Fixes in sales/purchase invoice trends report 2014-09-26 14:23:00 +05:30
Nabin Hait
996a1010cb Moved Installation note from tools to documents section 2014-09-26 14:23:00 +05:30
Anand Doshi
32a9dfd983 Merge branch 'sbkolate-develop' into develop 2014-09-22 13:19:24 +05:30
Anand Doshi
1394509343 Fixes for recurring document 2014-09-21 19:45:49 +05:30
Pratik Vyas
0f31c36b2c Merge branch 'develop' 2014-09-19 14:46:07 +05:30
Pratik Vyas
14ac8f71b7 bumped to version 4.4.2 2014-09-19 15:16:07 +06:00
Nabin Hait
6a92d51383 Merge pull request #2212 from nabinhait/hotfix
Fetch and validate advance entries in sales/purchase invoice
2014-09-19 14:41:20 +05:30
Nabin Hait
7c831c3fe5 Get advance in sales/purchase invoice 2014-09-19 14:31:49 +05:30
Nabin Hait
778ff463af Minor fix in authorization control 2014-09-19 11:39:47 +05:30
Nabin Hait
70a31d5402 Gantt view fix for translations 2014-09-19 11:18:10 +05:30
Nabin Hait
48f5fa69f3 Fetch and validate advance entries in sales/purchase invoice 2014-09-18 15:04:11 +05:30
Pratik Vyas
83a2d12cd2 Merge branch 'develop' 2014-09-18 14:19:15 +05:30
Pratik Vyas
e539297e53 bumped to version 4.4.1 2014-09-18 14:49:14 +06:00
Nabin Hait
bbc3d015a3 Update authorization_control.py 2014-09-18 14:09:20 +05:30
Nabin Hait
e4475c635d Merge pull request #2211 from nabinhait/hotfix
Escaped single quote in authorization control queries
2014-09-18 13:01:33 +05:30
Anand Doshi
8370cb3e71 Merge branch 'develop' of https://github.com/sbkolate/erpnext into sbkolate-develop 2014-09-18 12:06:05 +05:30
Nabin Hait
4073880ecf Escaped single quote in authorization control queries 2014-09-18 11:01:26 +05:30
Nabin Hait
763c7a56dc Merge pull request #2209 from nabinhait/hotfix
Minor fix in gross profit report
2014-09-17 14:02:51 +05:30
Nabin Hait
556fbc487d Minor fix in gross profit report 2014-09-17 12:13:44 +05:30
Sambhaji Kolate
f37d4337a4 fix test for purchase order 2014-09-15 13:37:46 +05:30
Sambhaji Kolate
525ab0a925 fix build 2014-09-15 12:57:58 +05:30
Sambhaji Kolate
930e7f5578 fix conflict 2014-09-15 12:43:48 +05:30
Sambhaji Kolate
d14e15d432 fix conflict 2014-09-15 12:31:44 +05:30
Sambhaji Kolate
4d3a18890b fix conflict 2014-09-15 12:20:11 +05:30
Sambhaji Kolate
b14401c320 change convert_to_recurring() to take recurring_id dynamicaly 2014-09-11 16:09:05 +05:30
Sambhaji Kolate
b2a3f2d386 some minor changes get fixed for PO/PI 2014-09-10 17:40:48 +05:30
Sambhaji Kolate
6b679c45df Updated purchase_invoice.json and purchase_order.json with some missed out changes 2014-09-10 13:57:45 +05:30
Sambhaji Kolate
e3d2643f2b Changes for Recurring PO/PI 2014-09-10 13:07:59 +05:30
36 changed files with 2060 additions and 1848 deletions

View File

@@ -1 +1 @@
__version__ = '4.4.0'
__version__ = '4.5.1'

View File

@@ -232,7 +232,6 @@ cur_frm.fields_dict['entries'].grid.get_field('project_name').get_query = functi
}
}
cur_frm.cscript.select_print_heading = function(doc,cdt,cdn){
if(doc.select_print_heading){
// print heading

View File

@@ -142,6 +142,24 @@
"reqd": 0,
"search_index": 1
},
{
"allow_on_submit": 1,
"description": "Start date of current invoice's period",
"fieldname": "from_date",
"fieldtype": "Date",
"label": "From Date",
"no_copy": 1,
"permlevel": 0
},
{
"allow_on_submit": 1,
"description": "End date of current invoice's period",
"fieldname": "to_date",
"fieldtype": "Date",
"label": "To Date",
"no_copy": 1,
"permlevel": 0
},
{
"fieldname": "amended_from",
"fieldtype": "Link",
@@ -752,12 +770,113 @@
"print_hide": 1,
"read_only": 0,
"reqd": 0
},
{
"depends_on": "eval:doc.docstatus<2",
"fieldname": "recurring_invoice",
"fieldtype": "Section Break",
"label": "Recurring Invoice",
"options": "icon-time",
"permlevel": 0,
"print_hide": 1
},
{
"fieldname": "column_break_77",
"fieldtype": "Column Break",
"permlevel": 0,
"print_hide": 1,
"width": "50%"
},
{
"allow_on_submit": 1,
"depends_on": "eval:doc.docstatus<2",
"description": "Check if recurring invoice, uncheck to stop recurring or put proper End Date",
"fieldname": "is_recurring",
"fieldtype": "Check",
"label": "Is Recurring",
"no_copy": 1,
"permlevel": 0,
"print_hide": 1
},
{
"allow_on_submit": 1,
"depends_on": "eval:doc.is_recurring==1",
"description": "Select the period when the invoice will be generated automatically",
"fieldname": "recurring_type",
"fieldtype": "Select",
"label": "Recurring Type",
"no_copy": 1,
"options": "Monthly\nQuarterly\nHalf-yearly\nYearly",
"permlevel": 0,
"print_hide": 1
},
{
"allow_on_submit": 1,
"depends_on": "eval:doc.is_recurring==1",
"description": "The day of the month on which auto invoice will be generated e.g. 05, 28 etc",
"fieldname": "repeat_on_day_of_month",
"fieldtype": "Int",
"label": "Repeat on Day of Month",
"no_copy": 1,
"permlevel": 0,
"print_hide": 1
},
{
"depends_on": "eval:doc.is_recurring==1",
"description": "The date on which next invoice will be generated. It is generated on submit.",
"fieldname": "next_date",
"fieldtype": "Date",
"label": "Next Date",
"no_copy": 1,
"permlevel": 0,
"print_hide": 1,
"read_only": 1
},
{
"allow_on_submit": 1,
"depends_on": "eval:doc.is_recurring==1",
"description": "The date on which recurring invoice will be stop",
"fieldname": "end_date",
"fieldtype": "Date",
"label": "End Date",
"no_copy": 1,
"permlevel": 0,
"print_hide": 1
},
{
"fieldname": "column_break_82",
"fieldtype": "Column Break",
"permlevel": 0,
"print_hide": 1,
"width": "50%"
},
{
"depends_on": "eval:doc.is_recurring==1",
"description": "The unique id for tracking all recurring invoices. It is generated on submit.",
"fieldname": "recurring_id",
"fieldtype": "Data",
"label": "Recurring Id",
"no_copy": 1,
"permlevel": 0,
"print_hide": 1,
"read_only": 1
},
{
"allow_on_submit": 1,
"depends_on": "eval:doc.is_recurring==1",
"description": "Enter email id separated by commas, invoice will be mailed automatically on particular date",
"fieldname": "notification_email_address",
"fieldtype": "Small Text",
"label": "Notification Email Address",
"no_copy": 1,
"permlevel": 0,
"print_hide": 1
}
],
"icon": "icon-file-text",
"idx": 1,
"is_submittable": 1,
"modified": "2014-09-09 05:35:32.156763",
"modified": "2014-09-18 03:12:51.994059",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice",

View File

@@ -4,11 +4,10 @@
from __future__ import unicode_literals
import frappe
from frappe.utils import cint, cstr, flt, formatdate
from frappe.utils import cint, cstr, formatdate, flt
from frappe import msgprint, _, throw
from erpnext.setup.utils import get_company_currency
import frappe.defaults
from erpnext.controllers.buying_controller import BuyingController
@@ -49,6 +48,7 @@ class PurchaseInvoice(BuyingController):
self.check_conversion_rate()
self.validate_credit_acc()
self.clear_unallocated_advances("Purchase Invoice Advance", "advance_allocation_details")
self.validate_advance_jv("advance_allocation_details", "purchase_order")
self.check_for_acc_head_of_supplier()
self.check_for_stopped_status()
self.validate_with_previous_doc()
@@ -80,7 +80,7 @@ class PurchaseInvoice(BuyingController):
def get_advances(self):
super(PurchaseInvoice, self).get_advances(self.credit_to,
"Purchase Invoice Advance", "advance_allocation_details", "debit")
"Purchase Invoice Advance", "advance_allocation_details", "debit", "purchase_order")
def check_active_purchase_items(self):
for d in self.get('entries'):
@@ -249,6 +249,8 @@ class PurchaseInvoice(BuyingController):
reconcile_against_document(lst)
def on_submit(self):
super(PurchaseInvoice, self).on_submit()
self.check_prev_docstatus()
frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype,

View File

@@ -231,4 +231,8 @@ class TestPurchaseInvoice(unittest.TestCase):
self.assertTrue(not frappe.db.sql("""select name from `tabJournal Voucher Detail`
where against_voucher=%s""", pi.name))
def test_recurring_invoice(self):
from erpnext.controllers.tests.test_recurring_document import test_recurring_document
test_recurring_document(self, test_records)
test_records = frappe.get_test_records('Purchase Invoice')

View File

@@ -399,37 +399,6 @@ cur_frm.cscript.on_submit = function(doc, cdt, cdn) {
})
}
cur_frm.cscript.is_recurring = function(doc, dt, dn) {
// set default values for recurring invoices
if(doc.is_recurring) {
var owner_email = doc.owner=="Administrator"
? frappe.user_info("Administrator").email
: doc.owner;
doc.notification_email_address = $.map([cstr(owner_email),
cstr(doc.contact_email)], function(v) { return v || null; }).join(", ");
doc.repeat_on_day_of_month = frappe.datetime.str_to_obj(doc.posting_date).getDate();
}
refresh_many(["notification_email_address", "repeat_on_day_of_month"]);
}
cur_frm.cscript.from_date = function(doc, dt, dn) {
// set to_date
if(doc.from_date) {
var recurring_type_map = {'Monthly': 1, 'Quarterly': 3, 'Half-yearly': 6,
'Yearly': 12};
var months = recurring_type_map[doc.recurring_type];
if(months) {
var to_date = frappe.datetime.add_months(doc.from_date,
months);
doc.to_date = frappe.datetime.add_days(to_date, -1);
refresh_field('to_date');
}
}
}
cur_frm.cscript.send_sms = function() {
frappe.require("assets/erpnext/js/sms_manager.js");
var sms_man = new SMSManager(cur_frm.doc);

View File

@@ -174,7 +174,7 @@
"description": "Start date of current invoice's period",
"fieldname": "from_date",
"fieldtype": "Date",
"label": "From",
"label": "From Date",
"no_copy": 1,
"permlevel": 0,
"print_hide": 0,
@@ -186,7 +186,7 @@
"description": "End date of current invoice's period",
"fieldname": "to_date",
"fieldtype": "Date",
"label": "To",
"label": "To Date",
"no_copy": 1,
"permlevel": 0,
"print_hide": 0,
@@ -1192,7 +1192,7 @@
"icon": "icon-file-text",
"idx": 1,
"is_submittable": 1,
"modified": "2014-09-09 05:35:34.121045",
"modified": "2014-09-18 03:17:54.976732",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice",

View File

@@ -4,18 +4,12 @@
from __future__ import unicode_literals
import frappe
import frappe.defaults
from frappe.utils import add_days, cint, cstr, date_diff, flt, getdate, nowdate, \
get_first_day, get_last_day
from frappe.model.naming import make_autoname
from frappe.utils import cint, cstr, flt
from frappe import _, msgprint, throw
from erpnext.accounts.party import get_party_account, get_due_date
from erpnext.controllers.stock_controller import update_gl_entries_after
from frappe.model.mapper import get_mapped_doc
from erpnext.controllers.recurring_document import *
from erpnext.controllers.selling_controller import SellingController
form_grid_templates = {
@@ -56,6 +50,7 @@ class SalesInvoice(SellingController):
self.validate_debit_acc()
self.validate_fixed_asset_account()
self.clear_unallocated_advances("Sales Invoice Advance", "advance_adjustment_details")
self.validate_advance_jv("advance_adjustment_details", "sales_order")
self.add_remarks()
if cint(self.is_pos):
@@ -77,11 +72,12 @@ class SalesInvoice(SellingController):
self.set_against_income_account()
self.validate_c_form()
self.validate_time_logs_are_submitted()
validate_recurring_document(self)
self.validate_multiple_billing("Delivery Note", "dn_detail", "amount",
"delivery_note_details")
def on_submit(self):
super(SalesInvoice, self).on_submit()
if cint(self.update_stock) == 1:
self.update_stock_ledger()
else:
@@ -104,7 +100,6 @@ class SalesInvoice(SellingController):
self.update_against_document_in_jv()
self.update_time_log_batch(self.name)
convert_to_recurring(self, "RECINV.#####", self.posting_date)
def before_cancel(self):
self.update_time_log_batch(None)
@@ -145,14 +140,6 @@ class SalesInvoice(SellingController):
'overflow_type': 'delivery'
})
def on_update_after_submit(self):
validate_recurring_document(self)
convert_to_recurring(self, "RECINV.#####", self.posting_date)
def before_recurring(self):
self.aging_date = None
self.due_date = None
def get_portal_page(self):
return "invoice" if self.docstatus==1 else None
@@ -222,7 +209,7 @@ class SalesInvoice(SellingController):
def get_advances(self):
super(SalesInvoice, self).get_advances(self.debit_to,
"Sales Invoice Advance", "advance_adjustment_details", "credit")
"Sales Invoice Advance", "advance_adjustment_details", "credit", "sales_order")
def get_company_abbr(self):
return frappe.db.sql("select abbr from tabCompany where name=%s", self.company)[0][0]
@@ -486,9 +473,8 @@ class SalesInvoice(SellingController):
if repost_future_gle and cint(self.update_stock) \
and cint(frappe.defaults.get_global_default("auto_accounting_for_stock")):
items, warehouse_account = self.get_items_and_warehouse_accounts()
update_gl_entries_after(self.posting_date, self.posting_time,
warehouse_account, items)
items, warehouses = self.get_items_and_warehouses()
update_gl_entries_after(self.posting_date, self.posting_time, warehouses, items)
def get_gl_entries(self, warehouse_account=None):
from erpnext.accounts.general_ledger import merge_similar_entries

View File

@@ -9,29 +9,29 @@ from erpnext.stock.utils import get_buying_amount, get_sales_bom_buying_amount
def execute(filters=None):
if not filters: filters = {}
stock_ledger_entries = get_stock_ledger_entries(filters)
source = get_source_data(filters)
item_sales_bom = get_item_sales_bom()
columns = [__("Delivery Note/Sales Invoice") + "::120", _("Link") + "::30", _("Posting Date") + ":Date", _("Posting Time"),
columns = [_("Delivery Note/Sales Invoice") + "::120", _("Link") + "::30", _("Posting Date") + ":Date", _("Posting Time"),
_("Item Code") + ":Link/Item", _("Item Name"), _("Description"), _("Warehouse") + ":Link/Warehouse",
_("Qty") + ":Float", _("Selling Rate") + ":Currency", _("Avg. Buying Rate") + ":Currency",
_("Qty") + ":Float", _("Selling Rate") + ":Currency", _("Avg. Buying Rate") + ":Currency",
_("Selling Amount") + ":Currency", _("Buying Amount") + ":Currency",
_("Gross Profit") + ":Currency", _("Gross Profit %") + ":Percent", _("Project") + ":Link/Project"]
data = []
for row in source:
selling_amount = flt(row.base_amount)
item_sales_bom_map = item_sales_bom.get(row.parenttype, {}).get(row.name, frappe._dict())
if item_sales_bom_map.get(row.item_code):
buying_amount = get_sales_bom_buying_amount(row.item_code, row.warehouse,
buying_amount = get_sales_bom_buying_amount(row.item_code, row.warehouse,
row.parenttype, row.name, row.item_row, stock_ledger_entries, item_sales_bom_map)
else:
buying_amount = get_buying_amount(row.parenttype, row.name, row.item_row,
stock_ledger_entries.get((row.item_code, row.warehouse), []))
buying_amount = buying_amount > 0 and buying_amount or 0
gross_profit = selling_amount - buying_amount
@@ -39,41 +39,41 @@ def execute(filters=None):
gross_profit_percent = (gross_profit / selling_amount) * 100.0
else:
gross_profit_percent = 0.0
icon = """<a href="%s"><i class="icon icon-share" style="cursor: pointer;"></i></a>""" \
% ("/".join(["#Form", row.parenttype, row.name]),)
data.append([row.name, icon, row.posting_date, row.posting_time, row.item_code, row.item_name,
row.description, row.warehouse, row.qty, row.base_rate,
row.description, row.warehouse, row.qty, row.base_rate,
row.qty and (buying_amount / row.qty) or 0, row.base_amount, buying_amount,
gross_profit, gross_profit_percent, row.project])
return columns, data
def get_stock_ledger_entries(filters):
def get_stock_ledger_entries(filters):
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`"""
if filters.get("company"):
query += """ where company=%(company)s"""
query += " order by item_code desc, warehouse desc, posting_date desc, posting_time desc, name desc"
res = frappe.db.sql(query, filters, as_dict=True)
out = {}
for r in res:
if (r.item_code, r.warehouse) not in out:
out[(r.item_code, r.warehouse)] = []
out[(r.item_code, r.warehouse)].append(r)
return out
def get_item_sales_bom():
item_sales_bom = {}
for d in frappe.db.sql("""select parenttype, parent, parent_item,
item_code, warehouse, -1*qty as total_qty, parent_detail_docname
from `tabPacked Item` where docstatus=1""", as_dict=True):
@@ -81,7 +81,7 @@ def get_item_sales_bom():
frappe._dict()).setdefault(d.parent_item, []).append(d)
return item_sales_bom
def get_source_data(filters):
conditions = ""
if filters.get("company"):
@@ -90,9 +90,9 @@ def get_source_data(filters):
conditions += " and posting_date>=%(from_date)s"
if filters.get("to_date"):
conditions += " and posting_date<=%(to_date)s"
delivery_note_items = frappe.db.sql("""select item.parenttype, dn.name,
dn.posting_date, dn.posting_time, dn.project_name,
delivery_note_items = frappe.db.sql("""select item.parenttype, dn.name,
dn.posting_date, dn.posting_time, dn.project_name,
item.item_code, item.item_name, item.description, item.warehouse,
item.qty, item.base_rate, item.base_amount, item.name as "item_row",
timestamp(dn.posting_date, dn.posting_time) as posting_datetime
@@ -100,7 +100,7 @@ def get_source_data(filters):
where item.parent = dn.name and dn.docstatus = 1 %s
order by dn.posting_date desc, dn.posting_time desc""" % (conditions,), filters, as_dict=1)
sales_invoice_items = frappe.db.sql("""select item.parenttype, si.name,
sales_invoice_items = frappe.db.sql("""select item.parenttype, si.name,
si.posting_date, si.posting_time, si.project_name,
item.item_code, item.item_name, item.description, item.warehouse,
item.qty, item.base_rate, item.base_amount, item.name as "item_row",
@@ -109,9 +109,9 @@ def get_source_data(filters):
where item.parent = si.name and si.docstatus = 1 %s
and si.update_stock = 1
order by si.posting_date desc, si.posting_time desc""" % (conditions,), filters, as_dict=1)
source = delivery_note_items + sales_invoice_items
if len(source) > len(delivery_note_items):
source.sort(key=lambda d: d.posting_datetime, reverse=True)
return source
return source

View File

@@ -11,4 +11,4 @@ def execute(filters=None):
conditions = get_columns(filters, "Sales Invoice")
data = get_data(filters, conditions)
return conditions["columns"], data
return conditions["columns"], data

File diff suppressed because it is too large Load Diff

View File

@@ -162,6 +162,8 @@ class PurchaseOrder(BuyingController):
msgprint(_("Status of {0} {1} is now {2}").format(self.doctype, self.name, status))
def on_submit(self):
super(PurchaseOrder, self).on_submit()
purchase_controller = frappe.get_doc("Purchase Common")
self.update_prevdoc_status()

View File

@@ -107,6 +107,10 @@ class TestPurchaseOrder(unittest.TestCase):
po.get("po_details")[0].qty = 3.4
self.assertRaises(UOMMustBeIntegerError, po.insert)
def test_recurring_order(self):
from erpnext.controllers.tests.test_recurring_document import test_recurring_document
test_recurring_document(self, test_records)
test_dependencies = ["BOM"]

View File

@@ -26,6 +26,11 @@ def get_data():
"name": "Purchase Receipt",
"description": _("Goods received from Suppliers."),
},
{
"type": "doctype",
"name": "Installation Note",
"description": _("Installation record for a Serial No.")
},
{
"type": "doctype",
"name": "Item",
@@ -57,11 +62,6 @@ def get_data():
"name": "Stock Reconciliation",
"description": _("Upload stock balance via csv.")
},
{
"type": "doctype",
"name": "Installation Note",
"description": _("Installation record for a Serial No.")
},
{
"type": "doctype",
"name": "Packing Slip",

View File

@@ -4,12 +4,11 @@
from __future__ import unicode_literals
import frappe
from frappe import _, throw
from frappe.utils import add_days, cint, cstr, today, date_diff, flt, getdate, nowdate, \
get_first_day, get_last_day
from frappe.model.naming import make_autoname
from frappe.utils import cint, today, flt
from erpnext.setup.utils import get_company_currency, get_exchange_rate
from erpnext.accounts.utils import get_fiscal_year, validate_fiscal_year
from erpnext.utilities.transaction_base import TransactionBase
from erpnext.controllers.recurring_document import convert_to_recurring, validate_recurring_document
import json
class AccountsController(TransactionBase):
@@ -24,6 +23,24 @@ class AccountsController(TransactionBase):
self.validate_for_freezed_account()
if self.meta.get_field("is_recurring"):
validate_recurring_document(self)
def on_submit(self):
if self.meta.get_field("is_recurring"):
convert_to_recurring(self, self.get("posting_date") or self.get("transaction_date"))
def on_update_after_submit(self):
if self.meta.get_field("is_recurring"):
validate_recurring_document(self)
convert_to_recurring(self, self.get("posting_date") or self.get("transaction_date"))
def before_recurring(self):
self.fiscal_year = None
for fieldname in ("due_date", "aging_date"):
if self.meta.get_field(fieldname):
self.set(fieldname, None)
def set_missing_values(self, for_validate=False):
for fieldname in ["posting_date", "transaction_date"]:
if not self.get(fieldname) and self.meta.get_field(fieldname):
@@ -362,38 +379,67 @@ class AccountsController(TransactionBase):
frappe.db.sql("""delete from `tab%s` where parentfield=%s and parent = %s
and ifnull(allocated_amount, 0) = 0""" % (childtype, '%s', '%s'), (parentfield, self.name))
def get_advances(self, account_head, child_doctype, parentfield, dr_or_cr):
against_order_list = []
def get_advances(self, account_head, child_doctype, parentfield, dr_or_cr, against_order_field):
so_list = list(set([d.get(against_order_field) for d in self.get("entries") if d.get(against_order_field)]))
cond = ""
if so_list:
cond = "or (ifnull(t2.%s, '') in (%s))" % ("against_" + against_order_field, ', '.join(['%s']*len(so_list)))
res = frappe.db.sql("""
select
t1.name as jv_no, t1.remark, t2.%s as amount, t2.name as jv_detail_no, t2.%s as order_no
t1.name as jv_no, t1.remark, t2.%s as amount, t2.name as jv_detail_no
from
`tabJournal Voucher` t1, `tabJournal Voucher Detail` t2
where
t1.name = t2.parent and t2.account = %s and t2.is_advance = 'Yes' and t1.docstatus = 1
and ifnull(t2.against_voucher, '') = ''
and ifnull(t2.against_invoice, '') = ''
and ifnull(t2.against_jv, '') = ''
and ((
ifnull(t2.against_voucher, '') = ''
and ifnull(t2.against_invoice, '') = ''
and ifnull(t2.against_jv, '') = ''
and ifnull(t2.against_sales_order, '') = ''
and ifnull(t2.against_purchase_order, '') = ''
) %s)
order by t1.posting_date""" %
(dr_or_cr, "against_sales_order" if dr_or_cr == "credit" \
else "against_purchase_order", '%s'),
account_head, as_dict= True)
if self.get("entries"):
for i in self.get("entries"):
against_order_list.append(i.sales_order if dr_or_cr == "credit" else i.purchase_order)
(dr_or_cr, '%s', cond),
tuple([account_head] + so_list), as_dict= True)
self.set(parentfield, [])
for d in res:
if not against_order_list or d.order_no in against_order_list:
self.append(parentfield, {
"doctype": child_doctype,
"journal_voucher": d.jv_no,
"jv_detail_no": d.jv_detail_no,
"remarks": d.remark,
"advance_amount": flt(d.amount),
"allocate_amount": 0
})
self.append(parentfield, {
"doctype": child_doctype,
"journal_voucher": d.jv_no,
"jv_detail_no": d.jv_detail_no,
"remarks": d.remark,
"advance_amount": flt(d.amount),
"allocate_amount": 0
})
def validate_advance_jv(self, advance_table_fieldname, against_order_field):
order_list = list(set([d.get(against_order_field) for d in self.get("entries") if d.get(against_order_field)]))
if order_list:
account = self.get("debit_to" if self.doctype=="Sales Invoice" else "credit_to")
jv_against_order = frappe.db.sql("""select parent, %s as against_order
from `tabJournal Voucher Detail`
where docstatus=1 and account=%s and ifnull(is_advance, 'No') = 'Yes'
and ifnull(against_sales_order, '') in (%s)
group by parent, against_sales_order""" %
("against_" + against_order_field, '%s', ', '.join(['%s']*len(order_list))),
tuple([account] + order_list), as_dict=1)
if jv_against_order:
order_jv_map = {}
for d in jv_against_order:
order_jv_map.setdefault(d.against_order, []).append(d.parent)
advance_jv_against_si = [d.journal_voucher for d in self.get(advance_table_fieldname)]
for order, jv_list in order_jv_map.items():
for jv in jv_list:
if not advance_jv_against_si or jv not in advance_jv_against_si:
frappe.throw(_("Journal Voucher {0} is linked against Order {1}, hence it must be fetched as advance in Invoice as well.")
.format(jv, order))
def validate_multiple_billing(self, ref_dt, item_ref_dn, based_on, parentfield):
from erpnext.controllers.status_updater import get_tolerance_for
@@ -421,7 +467,6 @@ class AccountsController(TransactionBase):
max_allowed_amt = flt(ref_amt * (100 + tolerance) / 100)
if total_billed_amt - max_allowed_amt > 0.01:
reduce_by = total_billed_amt - max_allowed_amt
frappe.throw(_("Cannot overbill for Item {0} in row {0} more than {1}. To allow overbilling, please set in Stock Settings").format(item.item_code, item.idx, max_allowed_amt))
def get_company_default(self, fieldname):

View File

@@ -5,6 +5,7 @@ from __future__ import unicode_literals
import frappe
from frappe import _, msgprint
from frappe.utils import flt, rounded
from erpnext.setup.utils import get_company_currency
from erpnext.accounts.party import get_party_details

View File

@@ -2,15 +2,28 @@ from __future__ import unicode_literals
import frappe
import frappe.utils
import frappe.defaults
from frappe.utils import cint, cstr, getdate, nowdate, get_first_day, get_last_day
from frappe.utils import add_days, cint, cstr, date_diff, flt, getdate, nowdate, \
get_first_day, get_last_day, comma_and
from frappe.model.naming import make_autoname
from frappe import _, msgprint, throw
from erpnext.accounts.party import get_party_account, get_due_date, get_party_details
from frappe.model.mapper import get_mapped_doc
month_map = {'Monthly': 1, 'Quarterly': 3, 'Half-yearly': 6, 'Yearly': 12}
date_field_map = {
"Sales Order": "transaction_date",
"Sales Invoice": "posting_date",
"Purchase Order": "transaction_date",
"Purchase Invoice": "posting_date"
}
def create_recurring_documents():
manage_recurring_documents("Sales Order")
manage_recurring_documents("Sales Invoice")
manage_recurring_documents("Purchase Order")
manage_recurring_documents("Purchase Invoice")
def manage_recurring_documents(doctype, next_date=None, commit=True):
"""
@@ -19,10 +32,7 @@ def manage_recurring_documents(doctype, next_date=None, commit=True):
"""
next_date = next_date or nowdate()
if doctype == "Sales Order":
date_field = "transaction_date"
elif doctype == "Sales Invoice":
date_field = "posting_date"
date_field = date_field_map[doctype]
recurring_documents = frappe.db.sql("""select name, recurring_id
from `tab{}` where ifnull(is_recurring, 0)=1
@@ -51,7 +61,8 @@ def manage_recurring_documents(doctype, next_date=None, commit=True):
frappe.db.sql("update `tab%s` \
set is_recurring = 0 where name = %s" % (doctype, '%s'),
(ref_document))
notify_errors(ref_document, doctype, ref_wrapper.customer, ref_wrapper.owner)
notify_errors(ref_document, doctype, ref_wrapper.get("customer") or ref_wrapper.get("supplier"),
ref_wrapper.owner)
frappe.db.commit()
exception_list.append(frappe.get_traceback())
@@ -118,7 +129,7 @@ def send_notification(new_rv):
"fcontent": frappe.get_print_format(new_rv.doctype, new_rv.name, as_pdf=True)
}])
def notify_errors(doc, doctype, customer, owner):
def notify_errors(doc, doctype, party, owner):
from frappe.utils.user import get_system_managers
recipients = get_system_managers(only_name=True)
@@ -127,7 +138,7 @@ def notify_errors(doc, doctype, customer, owner):
message = frappe.get_template("templates/emails/recurring_document_failed.html").render({
"type": doctype,
"name": doc,
"customer": customer
"party": party
}))
assign_task_to_owner(doc, doctype, "Recurring Invoice Failed", recipients)
@@ -155,18 +166,18 @@ def validate_recurring_document(doc):
elif not (doc.from_date and doc.to_date):
throw(_("Period From and Period To dates mandatory for recurring %s") % doc.doctype)
def convert_to_recurring(doc, autoname, posting_date):
if doc.is_recurring:
if not doc.recurring_id:
frappe.db.set(doc, "recurring_id",
make_autoname(autoname))
#
def convert_to_recurring(doc, posting_date):
if doc.is_recurring:
if not doc.recurring_id:
frappe.db.set(doc, "recurring_id", doc.name)
set_next_date(doc, posting_date)
set_next_date(doc, posting_date)
elif doc.recurring_id:
frappe.db.sql("""update `tab%s`
set is_recurring = 0
where recurring_id = %s""" % (doc.doctype, '%s'), (doc.recurring_id))
elif doc.recurring_id:
frappe.db.sql("""update `tab%s` set is_recurring = 0
where recurring_id = %s""" % (doc.doctype, '%s'), (doc.recurring_id))
#
def validate_notification_email_id(doc):
if doc.notification_email_address:

View File

@@ -16,16 +16,15 @@ class StockController(AccountsController):
delete_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
if cint(frappe.defaults.get_global_default("auto_accounting_for_stock")):
warehouse_account = self.get_warehouse_account()
warehouse_account = get_warehouse_account()
if self.docstatus==1:
gl_entries = self.get_gl_entries(warehouse_account)
make_gl_entries(gl_entries)
if repost_future_gle:
items, warehouse_account = self.get_items_and_warehouse_accounts(warehouse_account)
update_gl_entries_after(self.posting_date, self.posting_time,
warehouse_account, items)
items, warehouses = self.get_items_and_warehouses()
update_gl_entries_after(self.posting_date, self.posting_time, warehouses, items, warehouse_account)
def get_gl_entries(self, warehouse_account=None, default_expense_account=None,
default_cost_center=None):
@@ -88,10 +87,8 @@ class StockController(AccountsController):
return details
def get_items_and_warehouse_accounts(self, warehouse_account=None):
def get_items_and_warehouses(self):
items, warehouses = [], []
if not warehouse_account:
warehouse_account = get_warehouse_account()
if hasattr(self, "fname"):
item_doclist = self.get(self.fname)
@@ -117,10 +114,7 @@ class StockController(AccountsController):
if d.get("t_warehouse") and d.t_warehouse not in warehouses:
warehouses.append(d.t_warehouse)
warehouse_account = {wh: warehouse_account[wh] for wh in warehouses
if warehouse_account.get(wh)}
return items, warehouse_account
return items, warehouses
def get_stock_ledger_details(self):
stock_ledger = {}
@@ -130,73 +124,6 @@ class StockController(AccountsController):
stock_ledger.setdefault(sle.voucher_detail_no, []).append(sle)
return stock_ledger
def get_warehouse_account(self):
warehouse_account = dict(frappe.db.sql("""select master_name, name from tabAccount
where account_type = 'Warehouse' and ifnull(master_name, '') != ''"""))
return warehouse_account
def update_gl_entries_after(self, warehouse_account=None):
future_stock_vouchers = self.get_future_stock_vouchers()
gle = self.get_voucherwise_gl_entries(future_stock_vouchers)
if not warehouse_account:
warehouse_account = self.get_warehouse_account()
for voucher_type, voucher_no in future_stock_vouchers:
existing_gle = gle.get((voucher_type, voucher_no), [])
voucher_obj = frappe.get_doc(voucher_type, voucher_no)
expected_gle = voucher_obj.get_gl_entries(warehouse_account)
if expected_gle:
matched = True
if existing_gle:
for entry in expected_gle:
for e in existing_gle:
if entry.account==e.account \
and entry.against_account==e.against_account\
and entry.cost_center==e.cost_center:
if entry.debit != e.debit or entry.credit != e.credit:
matched = False
break
else:
matched = False
if not matched:
self.delete_gl_entries(voucher_type, voucher_no)
voucher_obj.make_gl_entries(repost_future_gle=False)
else:
self.delete_gl_entries(voucher_type, voucher_no)
def get_future_stock_vouchers(self):
condition = ""
item_list = []
if getattr(self, "fname", None):
item_list = [d.item_code for d in self.get(self.fname)]
if item_list:
condition = "and item_code in ({})".format(", ".join(["%s"] * len(item_list)))
future_stock_vouchers = frappe.db.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) {condition}
order by timestamp(sle.posting_date, sle.posting_time) asc, name asc""".format(
condition=condition), tuple([self.posting_date, self.posting_date] + item_list),
as_list=True)
return future_stock_vouchers
def get_voucherwise_gl_entries(self, future_stock_vouchers):
gl_entries = {}
if future_stock_vouchers:
for d in frappe.db.sql("""select * from `tabGL Entry`
where posting_date >= %s and voucher_no in (%s)""" %
('%s', ', '.join(['%s']*len(future_stock_vouchers))),
tuple([self.posting_date] + [d[1] for d in future_stock_vouchers]), as_dict=1):
gl_entries.setdefault((d.voucher_type, d.voucher_no), []).append(d)
return gl_entries
def delete_gl_entries(self, voucher_type, voucher_no):
frappe.db.sql("""delete from `tabGL Entry`
where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no))
def make_adjustment_entry(self, expected_gle, voucher_obj):
from erpnext.accounts.utils import get_stock_and_account_difference
account_list = [d.account for d in expected_gle]
@@ -287,15 +214,15 @@ class StockController(AccountsController):
return serialized_items
def update_gl_entries_after(posting_date, posting_time, warehouse_account=None, for_items=None):
def update_gl_entries_after(posting_date, posting_time, for_warehouses=None, for_items=None, warehouse_account=None):
def _delete_gl_entries(voucher_type, voucher_no):
frappe.db.sql("""delete from `tabGL Entry`
where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no))
if not warehouse_account:
warehouse_account = get_warehouse_account()
future_stock_vouchers = get_future_stock_vouchers(posting_date, posting_time,
warehouse_account, for_items)
future_stock_vouchers = get_future_stock_vouchers(posting_date, posting_time, for_warehouses, for_items)
gle = get_voucherwise_gl_entries(future_stock_vouchers, posting_date)
for voucher_type, voucher_no in future_stock_vouchers:
@@ -321,7 +248,7 @@ def compare_existing_and_expected_gle(existing_gle, expected_gle):
break
return matched
def get_future_stock_vouchers(posting_date, posting_time, warehouse_account=None, for_items=None):
def get_future_stock_vouchers(posting_date, posting_time, for_warehouses=None, for_items=None):
future_stock_vouchers = []
values = []
@@ -330,9 +257,9 @@ def get_future_stock_vouchers(posting_date, posting_time, warehouse_account=None
condition += " and item_code in ({})".format(", ".join(["%s"] * len(for_items)))
values += for_items
if warehouse_account:
condition += " and warehouse in ({})".format(", ".join(["%s"] * len(warehouse_account.keys())))
values += warehouse_account.keys()
if for_warehouses:
condition += " and warehouse in ({})".format(", ".join(["%s"] * len(for_warehouses)))
values += for_warehouses
for d in frappe.db.sql("""select distinct sle.voucher_type, sle.voucher_no
from `tabStock Ledger Entry` sle

View File

@@ -2,12 +2,8 @@
# License: GNU General Public License v3. See license.txt
import frappe
import unittest, json, copy
from frappe.utils import flt
import frappe.permissions
from erpnext.accounts.utils import get_stock_and_account_difference
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory
from erpnext.projects.doctype.time_log_batch.test_time_log_batch import *
from erpnext.controllers.recurring_document import date_field_map
def test_recurring_document(obj, test_records):
from frappe.utils import get_first_day, get_last_day, add_to_date, nowdate, getdate, add_days
@@ -27,20 +23,11 @@ def test_recurring_document(obj, test_records):
"to_date": get_last_day(today)
})
if base_doc.doctype == "Sales Order":
base_doc.update({
"transaction_date": today,
"delivery_date": add_days(today, 15)
})
elif base_doc.doctype == "Sales Invoice":
base_doc.update({
"posting_date": today
})
date_field = date_field_map[base_doc.doctype]
base_doc.set(date_field, today)
if base_doc.doctype == "Sales Order":
date_field = "transaction_date"
elif base_doc.doctype == "Sales Invoice":
date_field = "posting_date"
base_doc.set("delivery_date", add_days(today, 15))
# monthly
doc1 = frappe.copy_doc(base_doc)
@@ -128,7 +115,7 @@ def _test_recurring_document(obj, base_doc, date_field, first_and_last_day):
next_date = get_next_date(base_doc.get(date_field), no_of_months,
base_doc.repeat_on_day_of_month)
manage_recurring_documents(base_doc.doctype, next_date=next_date, commit=False)
recurred_documents = frappe.db.sql("""select name from `tab%s`

View File

@@ -4,7 +4,7 @@ app_publisher = "Web Notes Technologies Pvt. Ltd. and Contributors"
app_description = "Open Source Enterprise Resource Planning for Small and Midsized Organizations"
app_icon = "icon-th"
app_color = "#e74c3c"
app_version = "4.4.0"
app_version = "4.5.1"
error_report_email = "support@erpnext.com"

View File

@@ -0,0 +1,26 @@
# 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 frappe
def execute():
warehouses_with_account = frappe.db.sql_list("""select master_name from tabAccount
where ifnull(account_type, '') = 'Warehouse'""")
stock_vouchers_without_gle = frappe.db.sql("""select distinct sle.voucher_type, sle.voucher_no
from `tabStock Ledger Entry` sle
where sle.warehouse in (%s)
and not exists(select name from `tabGL Entry`
where voucher_type=sle.voucher_type and voucher_no=sle.voucher_no)
order by sle.posting_date""" %
', '.join(['%s']*len(warehouses_with_account)), tuple(warehouses_with_account))
for voucher_type, voucher_no in stock_vouchers_without_gle:
print voucher_type, voucher_no
frappe.db.sql("""delete from `tabGL Entry`
where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no))
voucher = frappe.get_doc(voucher_type, voucher_no)
voucher.make_gl_entries()
frappe.db.commit()

View File

@@ -6,17 +6,17 @@ frappe.views.calendar["Task"] = {
"start": "exp_start_date",
"end": "exp_end_date",
"id": "name",
"title": __("subject"),
"title": "subject",
"allDay": "allDay"
},
gantt: true,
filters: [
{
"fieldtype": "Link",
"fieldname": "project",
"options": "Project",
"fieldtype": "Link",
"fieldname": "project",
"options": "Project",
"label": __("Project")
}
],
get_events_method: "erpnext.projects.doctype.task.task.get_events"
}
}

View File

@@ -24,7 +24,7 @@ var get_filters = function(){
{ "value": "Item Group", "label": __("Item Group") },
{ "value": "Supplier", "label": __("Supplier") },
{ "value": "Supplier Type", "label": __("Supplier Type") },
{ "value": "Supplier Type", "label": __("Project") }
{ "value": "Project", "label": __("Project") }
],
"default": "Item"
},

View File

@@ -25,7 +25,7 @@ var get_filters = function(){
{ "value": "Customer", "label": __("Customer") },
{ "value": "Customer Group", "label": __("Customer Group") },
{ "value": "Territory", "label": __("Territory") },
{ "value": "Supplier Type", "label": __("Project") }
{ "value": "Project", "label": __("Project") }
],
"default": "Item"
},

View File

@@ -155,7 +155,7 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({
project_name: item.project_name || me.frm.doc.project_name
}
},
callback: function(r) {
if(!r.exc) {
me.frm.script_manager.trigger("price_list_rate", cdt, cdn);
@@ -827,4 +827,35 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({
.appendTo($(this.frm.fields_dict.other_charges_calculation.wrapper).empty());
}
},
is_recurring: function() {
// set default values for recurring documents
if(this.frm.doc.is_recurring) {
var owner_email = this.frm.doc.owner=="Administrator"
? frappe.user_info("Administrator").email
: this.frm.doc.owner;
this.frm.doc.notification_email_address = $.map([cstr(owner_email),
cstr(this.frm.doc.contact_email)], function(v) { return v || null; }).join(", ");
this.frm.doc.repeat_on_day_of_month = frappe.datetime.str_to_obj(this.frm.doc.posting_date).getDate();
}
refresh_many(["notification_email_address", "repeat_on_day_of_month"]);
},
from_date: function() {
// set to_date
if(this.frm.doc.from_date) {
var recurring_type_map = {'Monthly': 1, 'Quarterly': 3, 'Half-yearly': 6,
'Yearly': 12};
var months = recurring_type_map[this.frm.doc.recurring_type];
if(months) {
var to_date = frappe.datetime.add_months(this.frm.doc.from_date,
months);
this.frm.doc.to_date = frappe.datetime.add_days(to_date, -1);
refresh_field('to_date');
}
}
}
});

View File

@@ -195,37 +195,6 @@ cur_frm.cscript.on_submit = function(doc, cdt, cdn) {
}
};
cur_frm.cscript.is_recurring = function(doc, dt, dn) {
// set default values for recurring orders
if(doc.is_recurring) {
var owner_email = doc.owner=="Administrator"
? frappe.user_info("Administrator").email
: doc.owner;
doc.notification_email_address = $.map([cstr(owner_email),
cstr(doc.contact_email)], function(v) { return v || null; }).join(", ");
doc.repeat_on_day_of_month = frappe.datetime.str_to_obj(doc.posting_date).getDate();
}
refresh_many(["notification_email_address", "repeat_on_day_of_month"]);
}
cur_frm.cscript.from_date = function(doc, dt, dn) {
// set to_date
if(doc.from_date) {
var recurring_type_map = {'Monthly': 1, 'Quarterly': 3, 'Half-yearly': 6,
'Yearly': 12};
var months = recurring_type_map[doc.recurring_type];
if(months) {
var to_date = frappe.datetime.add_months(doc.from_date,
months);
doc.to_date = frappe.datetime.add_days(to_date, -1);
refresh_field('to_date');
}
}
}
cur_frm.cscript.send_sms = function() {
frappe.require("assets/erpnext/js/sms_manager.js");
var sms_man = new SMSManager(cur_frm.doc);

File diff suppressed because it is too large Load Diff

View File

@@ -4,14 +4,10 @@
from __future__ import unicode_literals
import frappe
import frappe.utils
from frappe.utils import cstr, flt, getdate, comma_and
from frappe import _
from frappe.model.mapper import get_mapped_doc
from erpnext.controllers.recurring_document import convert_to_recurring, validate_recurring_document
from erpnext.controllers.selling_controller import SellingController
form_grid_templates = {
@@ -122,8 +118,6 @@ class SalesOrder(SellingController):
if not self.billing_status: self.billing_status = 'Not Billed'
if not self.delivery_status: self.delivery_status = 'Not Delivered'
validate_recurring_document(self)
def validate_warehouse(self):
from erpnext.stock.utils import validate_warehouse_company
@@ -157,6 +151,8 @@ class SalesOrder(SellingController):
doc.set_status(update=True)
def on_submit(self):
super(SalesOrder, self).on_submit()
self.update_stock_ledger(update_stock = 1)
self.check_credit(self.grand_total)
@@ -165,8 +161,6 @@ class SalesOrder(SellingController):
self.update_prevdoc_status('submit')
frappe.db.set(self, 'status', 'Submitted')
convert_to_recurring(self, "SO/REC/.#####", self.transaction_date)
def on_cancel(self):
# Cannot cancel stopped SO
@@ -255,11 +249,6 @@ class SalesOrder(SellingController):
def get_portal_page(self):
return "order" if self.docstatus==1 else None
def on_update_after_submit(self):
validate_recurring_document(self)
convert_to_recurring(self, "SO/REC/.#####", self.transaction_date)
@frappe.whitelist()
def make_material_request(source_name, target_doc=None):
def postprocess(source, doc):

View File

@@ -3,19 +3,11 @@
from __future__ import unicode_literals
import frappe
from frappe.utils import cstr, flt, has_common, make_esc, comma_or
from frappe.utils import cstr, flt, has_common, comma_or
from frappe import session, _
from erpnext.utilities.transaction_base import TransactionBase
class AuthorizationControl(TransactionBase):
# Get Names of all Approving Users and Roles
# -------------------------------------------
def get_appr_user_role(self, det, doctype_name, total, based_on, condition, item, company):
amt_list, appr_users, appr_roles = [], [], []
users, roles = '',''
@@ -24,10 +16,18 @@ class AuthorizationControl(TransactionBase):
amt_list.append(flt(x[0]))
max_amount = max(amt_list)
app_dtl = frappe.db.sql("select approving_user, approving_role from `tabAuthorization Rule` where transaction = %s and (value = %s or value > %s) and docstatus != 2 and based_on = %s and company = %s %s" % ('%s', '%s', '%s', '%s', '%s', condition), (doctype_name, flt(max_amount), total, based_on, company))
app_dtl = frappe.db.sql("""select approving_user, approving_role from `tabAuthorization Rule`
where transaction = %s and (value = %s or value > %s)
and docstatus != 2 and based_on = %s and company = %s %s""" %
('%s', '%s', '%s', '%s', '%s', condition),
(doctype_name, flt(max_amount), total, based_on, company))
if not app_dtl:
app_dtl = frappe.db.sql("select approving_user, approving_role from `tabAuthorization Rule` where transaction = %s and (value = %s or value > %s) and docstatus != 2 and based_on = %s and ifnull(company,'') = '' %s" % ('%s', '%s', '%s', '%s', condition), (doctype_name, flt(max_amount), total, based_on))
app_dtl = frappe.db.sql("""select approving_user, approving_role from `tabAuthorization Rule`
where transaction = %s and (value = %s or value > %s) and docstatus != 2
and based_on = %s and ifnull(company,'') = '' %s""" %
('%s', '%s', '%s', '%s', condition), (doctype_name, flt(max_amount), total, based_on))
for d in app_dtl:
if(d[0]): appr_users.append(d[0])
if(d[1]): appr_roles.append(d[1])
@@ -36,43 +36,56 @@ class AuthorizationControl(TransactionBase):
frappe.msgprint(_("Not authroized since {0} exceeds limits").format(_(based_on)))
frappe.throw(_("Can be approved by {0}").format(comma_or(appr_roles + appr_users)))
# Check if authorization rule is set specific to user
# ----------------------------------------------------
def validate_auth_rule(self, doctype_name, total, based_on, cond, company, item = ''):
chk = 1
add_cond1,add_cond2 = '',''
if based_on == 'Itemwise Discount':
add_cond1 += " and master_name = '"+cstr(item)+"'"
itemwise_exists = frappe.db.sql("select value from `tabAuthorization Rule` where transaction = %s and value <= %s and based_on = %s and company = %s and docstatus != 2 %s %s" % ('%s', '%s', '%s', '%s', cond, add_cond1), (doctype_name, total, based_on, company))
add_cond1 += " and master_name = '"+cstr(item).replace("'", "\\'")+"'"
itemwise_exists = frappe.db.sql("""select value from `tabAuthorization Rule`
where transaction = %s and value <= %s
and based_on = %s and company = %s and docstatus != 2 %s %s""" %
('%s', '%s', '%s', '%s', cond, add_cond1), (doctype_name, total, based_on, company))
if not itemwise_exists:
itemwise_exists = frappe.db.sql("select value from `tabAuthorization Rule` where transaction = %s and value <= %s and based_on = %s and ifnull(company,'') = '' and docstatus != 2 %s %s" % ('%s', '%s', '%s', cond, add_cond1), (doctype_name, total, based_on))
itemwise_exists = frappe.db.sql("""select value from `tabAuthorization Rule`
where transaction = %s and value <= %s and based_on = %s
and ifnull(company,'') = '' and docstatus != 2 %s %s""" %
('%s', '%s', '%s', cond, add_cond1), (doctype_name, total, based_on))
if itemwise_exists:
self.get_appr_user_role(itemwise_exists, doctype_name, total, based_on, cond+add_cond1, item,company)
chk = 0
if chk == 1:
if based_on == 'Itemwise Discount': add_cond2 += " and ifnull(master_name,'') = ''"
appr = frappe.db.sql("select value from `tabAuthorization Rule` where transaction = %s and value <= %s and based_on = %s and company = %s and docstatus != 2 %s %s" % ('%s', '%s', '%s', '%s', cond, add_cond2), (doctype_name, total, based_on, company))
if based_on == 'Itemwise Discount':
add_cond2 += " and ifnull(master_name,'') = ''"
appr = frappe.db.sql("""select value from `tabAuthorization Rule`
where transaction = %s and value <= %s and based_on = %s
and company = %s and docstatus != 2 %s %s""" %
('%s', '%s', '%s', '%s', cond, add_cond2), (doctype_name, total, based_on, company))
if not appr:
appr = frappe.db.sql("select value from `tabAuthorization Rule` where transaction = %s and value <= %s and based_on = %s and ifnull(company,'') = '' and docstatus != 2 %s %s"% ('%s', '%s', '%s', cond, add_cond2), (doctype_name, total, based_on))
appr = frappe.db.sql("""select value from `tabAuthorization Rule`
where transaction = %s and value <= %s and based_on = %s
and ifnull(company,'') = '' and docstatus != 2 %s %s""" %
('%s', '%s', '%s', cond, add_cond2), (doctype_name, total, based_on))
self.get_appr_user_role(appr, doctype_name, total, based_on, cond+add_cond2, item, company)
# Bifurcate Authorization based on type
# --------------------------------------
def bifurcate_based_on_type(self, doctype_name, total, av_dis, based_on, doc_obj, val, company):
add_cond = ''
auth_value = av_dis
if val == 1: add_cond += " and system_user = '"+session['user']+"'"
if val == 1: add_cond += " and system_user = '"+session['user'].replace("'", "\\'")+"'"
elif val == 2: add_cond += " and system_role IN %s" % ("('"+"','".join(frappe.user.get_roles())+"')")
else: add_cond += " and ifnull(system_user,'') = '' and ifnull(system_role,'') = ''"
if based_on == 'Grand Total': auth_value = total
elif based_on == 'Customerwise Discount':
if doc_obj:
if doc_obj.doctype == 'Sales Invoice': customer = doc_obj.customer
else: customer = doc_obj.customer_name
add_cond = " and master_name = '"+make_esc("'")(cstr(customer))+"'"
add_cond = " and master_name = '"+cstr(customer).replace("'", "\\'")+"'"
if based_on == 'Itemwise Discount':
if doc_obj:
for t in doc_obj.get(doc_obj.fname):
@@ -80,9 +93,6 @@ class AuthorizationControl(TransactionBase):
else:
self.validate_auth_rule(doctype_name, auth_value, based_on, add_cond, company)
# Check Approving Authority for transactions other than expense voucher and Appraisal
# -------------------------
def validate_approving_authority(self, doctype_name,company, total, doc_obj = ''):
av_dis = 0
if doc_obj:
@@ -94,11 +104,12 @@ class AuthorizationControl(TransactionBase):
if price_list_rate: av_dis = 100 - flt(base_rate * 100 / price_list_rate)
final_based_on = ['Grand Total','Average Discount','Customerwise Discount','Itemwise Discount']
# Individual User
# ================
# Check for authorization set for individual user
based_on = [x[0] for x in frappe.db.sql("select distinct based_on from `tabAuthorization Rule` where transaction = %s and system_user = %s and (company = %s or ifnull(company,'')='') and docstatus != 2", (doctype_name, session['user'], company))]
# Check for authorization set for individual user
based_on = [x[0] for x in frappe.db.sql("""select distinct based_on from `tabAuthorization Rule`
where transaction = %s and system_user = %s
and (company = %s or ifnull(company,'')='') and docstatus != 2""",
(doctype_name, session['user'], company))]
for d in based_on:
self.bifurcate_based_on_type(doctype_name, total, av_dis, d, doc_obj, 1, company)
@@ -107,8 +118,6 @@ class AuthorizationControl(TransactionBase):
for r in based_on:
if r in final_based_on and r != 'Itemwise Discount': final_based_on.remove(r)
# Specific Role
# ===============
# Check for authorization set on particular roles
based_on = [x[0] for x in frappe.db.sql("""select based_on
from `tabAuthorization Rule`
@@ -124,19 +133,24 @@ class AuthorizationControl(TransactionBase):
for r in based_on:
if r in final_based_on and r != 'Itemwise Discount': final_based_on.remove(r)
# Global Rule
# =============
# Check for global authorization
for g in final_based_on:
self.bifurcate_based_on_type(doctype_name, total, av_dis, g, doc_obj, 0, company)
#========================================================================================================================
# payroll related check
def get_value_based_rule(self,doctype_name,employee,total_claimed_amount,company):
val_lst =[]
val = frappe.db.sql("select value from `tabAuthorization Rule` where transaction=%s and (to_emp=%s or to_designation IN (select designation from `tabEmployee` where name=%s)) and ifnull(value,0)< %s and company = %s and docstatus!=2",(doctype_name,employee,employee,total_claimed_amount,company))
val = frappe.db.sql("""select value from `tabAuthorization Rule`
where transaction=%s and (to_emp=%s or
to_designation IN (select designation from `tabEmployee` where name=%s))
and ifnull(value,0)< %s and company = %s and docstatus!=2""",
(doctype_name,employee,employee,total_claimed_amount,company))
if not val:
val = frappe.db.sql("select value from `tabAuthorization Rule` where transaction=%s and (to_emp=%s or to_designation IN (select designation from `tabEmployee` where name=%s)) and ifnull(value,0)< %s and ifnull(company,'') = '' and docstatus!=2",(doctype_name, employee, employee, total_claimed_amount))
val = frappe.db.sql("""select value from `tabAuthorization Rule`
where transaction=%s and (to_emp=%s or
to_designation IN (select designation from `tabEmployee` where name=%s))
and ifnull(value,0)< %s and ifnull(company,'') = '' and docstatus!=2""",
(doctype_name, employee, employee, total_claimed_amount))
if val:
val_lst = [y[0] for y in val]
@@ -144,13 +158,23 @@ class AuthorizationControl(TransactionBase):
val_lst.append(0)
max_val = max(val_lst)
rule = frappe.db.sql("select name, to_emp, to_designation, approving_role, approving_user from `tabAuthorization Rule` where transaction=%s and company = %s and (to_emp=%s or to_designation IN (select designation from `tabEmployee` where name=%s)) and ifnull(value,0)= %s and docstatus!=2",(doctype_name,company,employee,employee,flt(max_val)), as_dict=1)
rule = frappe.db.sql("""select name, to_emp, to_designation, approving_role, approving_user
from `tabAuthorization Rule`
where transaction=%s and company = %s
and (to_emp=%s or to_designation IN (select designation from `tabEmployee` where name=%s))
and ifnull(value,0)= %s and docstatus!=2""",
(doctype_name,company,employee,employee,flt(max_val)), as_dict=1)
if not rule:
rule = frappe.db.sql("select name, to_emp, to_designation, approving_role, approving_user from `tabAuthorization Rule` where transaction=%s and ifnull(company,'') = '' and (to_emp=%s or to_designation IN (select designation from `tabEmployee` where name=%s)) and ifnull(value,0)= %s and docstatus!=2",(doctype_name,employee,employee,flt(max_val)), as_dict=1)
rule = frappe.db.sql("""select name, to_emp, to_designation, approving_role, approving_user
from `tabAuthorization Rule`
where transaction=%s and ifnull(company,'') = ''
and (to_emp=%s or to_designation IN (select designation from `tabEmployee` where name=%s))
and ifnull(value,0)= %s and docstatus!=2""",
(doctype_name,employee,employee,flt(max_val)), as_dict=1)
return rule
#---------------------------------------------------------------------------------------------------------------------
# related to payroll module only
def get_approver_name(self, doctype_name, total, doc_obj=''):
app_user=[]
@@ -159,11 +183,22 @@ class AuthorizationControl(TransactionBase):
if doc_obj:
if doctype_name == 'Expense Claim':
rule = self.get_value_based_rule(doctype_name,doc_obj.employee,doc_obj.total_claimed_amount, doc_obj.company)
rule = self.get_value_based_rule(doctype_name, doc_obj.employee,
doc_obj.total_claimed_amount, doc_obj.company)
elif doctype_name == 'Appraisal':
rule = frappe.db.sql("select name, to_emp, to_designation, approving_role, approving_user from `tabAuthorization Rule` where transaction=%s and (to_emp=%s or to_designation IN (select designation from `tabEmployee` where name=%s)) and company = %s and docstatus!=2",(doctype_name,doc_obj.employee, doc_obj.employee, doc_obj.company),as_dict=1)
rule = frappe.db.sql("""select name, to_emp, to_designation, approving_role, approving_user
from `tabAuthorization Rule` where transaction=%s
and (to_emp=%s or to_designation IN (select designation from `tabEmployee` where name=%s))
and company = %s and docstatus!=2""",
(doctype_name,doc_obj.employee, doc_obj.employee, doc_obj.company),as_dict=1)
if not rule:
rule = frappe.db.sql("select name, to_emp, to_designation, approving_role, approving_user from `tabAuthorization Rule` where transaction=%s and (to_emp=%s or to_designation IN (select designation from `tabEmployee` where name=%s)) and ifnull(company,'') = '' and docstatus!=2",(doctype_name,doc_obj.employee, doc_obj.employee),as_dict=1)
rule = frappe.db.sql("""select name, to_emp, to_designation, approving_role, approving_user
from `tabAuthorization Rule`
where transaction=%s and (to_emp=%s or
to_designation IN (select designation from `tabEmployee` where name=%s))
and ifnull(company,'') = '' and docstatus!=2""",
(doctype_name,doc_obj.employee, doc_obj.employee), as_dict=1)
if rule:
for m in rule:
@@ -171,7 +206,11 @@ class AuthorizationControl(TransactionBase):
if m['approving_user']:
app_specific_user.append(m['approving_user'])
elif m['approving_role']:
user_lst = [z[0] for z in frappe.db.sql("select distinct t1.name from `tabUser` t1, `tabUserRole` t2 where t2.role=%s and t2.parent=t1.name and t1.name !='Administrator' and t1.name != 'Guest' and t1.docstatus !=2",m['approving_role'])]
user_lst = [z[0] for z in frappe.db.sql("""select distinct t1.name
from `tabUser` t1, `tabUserRole` t2 where t2.role=%s
and t2.parent=t1.name and t1.name !='Administrator'
and t1.name != 'Guest' and t1.docstatus !=2""", m['approving_role'])]
for x in user_lst:
if not x in app_user:
app_user.append(x)

View File

@@ -48,11 +48,10 @@ class NamingSeries(Document):
# validate names
for i in options: self.validate_series_name(i)
if self.user_must_always_select:
if options and self.user_must_always_select:
options = [''] + options
default = ''
else:
default = options[0]
default = options[0] if options else ''
# update in property setter
prop_dict = {'options': "\n".join(options), 'default': default}

View File

@@ -1,5 +1,5 @@
frappe.listview_settings['Item'] = {
add_fields: ["item_name", "stock_uom", "item_group", "image",
"is_stock_item", "is_sales_item", "is_purchase_item",
"is_manufactured_item", "show_in_website"]
add_fields: ["`tabItem`.`item_name`", "`tabItem`.`stock_uom`", "`tabItem`.`item_group`", "`tabItem`.`image`",
"`tabItem`.`is_stock_item`", "`tabItem`.`is_sales_item`", "`tabItem`.`is_purchase_item`",
"`tabItem`.`is_manufactured_item`", "`tabItem`.`show_in_website`"]
};

View File

@@ -15,7 +15,7 @@ class LandedCostVoucher(Document):
self.set("landed_cost_items", [])
for pr in self.get("landed_cost_purchase_receipts"):
pr_items = frappe.db.sql("""select pr_item.item_code, pr_item.description,
pr_item.qty, pr_item.rate, pr_item.amount, pr_item.name
pr_item.qty, pr_item.base_rate, pr_item.base_amount, pr_item.name
from `tabPurchase Receipt Item` pr_item where parent = %s
and exists(select name from tabItem where name = pr_item.item_code and is_stock_item = 'Yes')""",
pr.purchase_receipt, as_dict=True)
@@ -25,8 +25,8 @@ class LandedCostVoucher(Document):
item.item_code = d.item_code
item.description = d.description
item.qty = d.qty
item.rate = d.rate
item.amount = d.amount
item.rate = d.base_rate
item.amount = d.base_amount
item.purchase_receipt = pr.purchase_receipt
item.purchase_receipt_item = d.name

View File

@@ -71,10 +71,10 @@ def get_item_warehouse_map(filters):
for d in sle:
iwb_map.setdefault(d.company, {}).setdefault(d.item_code, {}).\
setdefault(d.warehouse, frappe._dict({\
"opening_qty": 0.0, "opening_val": 0.0,
"in_qty": 0.0, "in_val": 0.0,
"out_qty": 0.0, "out_val": 0.0,
"bal_qty": 0.0, "bal_val": 0.0,
"opening_qty": 0.0, "opening_val": 0.0,
"in_qty": 0.0, "in_val": 0.0,
"out_qty": 0.0, "out_val": 0.0,
"bal_qty": 0.0, "bal_val": 0.0,
"val_rate": 0.0, "uom": None
}))
qty_dict = iwb_map[d.company][d.item_code][d.warehouse]
@@ -82,19 +82,19 @@ def get_item_warehouse_map(filters):
if d.posting_date < filters["from_date"]:
qty_dict.opening_qty += flt(d.actual_qty)
qty_dict.opening_val += flt(d.actual_qty * d.valuation_rate)
qty_dict.opening_val += flt(d.actual_qty) * flt(d.valuation_rate)
elif d.posting_date >= filters["from_date"] and d.posting_date <= filters["to_date"]:
qty_dict.val_rate = d.valuation_rate
if flt(d.actual_qty) > 0:
qty_dict.in_qty += flt(d.actual_qty)
qty_dict.in_val += flt(d.actual_qty * d.valuation_rate)
qty_dict.in_val += flt(d.actual_qty) * flt(d.valuation_rate)
else:
qty_dict.out_qty += abs(flt(d.actual_qty))
qty_dict.out_val += flt(abs(flt(d.actual_qty)) * d.valuation_rate)
qty_dict.out_val += flt(abs(flt(d.actual_qty) * flt(d.valuation_rate)))
qty_dict.bal_qty += flt(d.actual_qty)
qty_dict.bal_val += flt(d.actual_qty * d.valuation_rate)
qty_dict.bal_val += flt(d.actual_qty) * flt(d.valuation_rate)
return iwb_map

View File

@@ -279,7 +279,7 @@
"icon": "icon-file-text",
"idx": 1,
"is_submittable": 1,
"modified": "2014-06-23 07:55:49.200714",
"modified": "2014-09-26 11:37:41.026433",
"modified_by": "Administrator",
"module": "Support",
"name": "Maintenance Visit",
@@ -301,7 +301,7 @@
"write": 1
}
],
"search_fields": "status,maintenance_type,customer,customer_name, address,mntc_date,company,fiscal_year",
"search_fields": "status,maintenance_type,customer,customer_name,mntc_date,company,fiscal_year",
"sort_field": "modified",
"sort_order": "DESC"
}

View File

@@ -1,6 +1,6 @@
<h2>Recurring {{ type }} Failed</h2>
<p>An error occured while creating recurring {{ type }} <b>{{ name }}</b> for <b>{{ customer }}</b>.</p>
<p>An error occured while creating recurring {{ type }} <b>{{ name }}</b> for <b>{{ party }}</b>.</p>
<p>This could be because of some invalid email ids in the {{ type }}.</p>
<p>To stop sending repetitive error notifications from the system, we have unchecked
"Convert into Recurring" field in the {{ type }} {{ name }}.</p>

View File

@@ -1,7 +1,7 @@
from setuptools import setup, find_packages
import os
version = "4.4.0"
version = "4.5.1"
with open("requirements.txt", "r") as f:
install_requires = f.readlines()