[merge] 4.x to 5.0

This commit is contained in:
Nabin Hait 2014-10-06 12:56:51 +05:30
commit c68646c694
65 changed files with 3986 additions and 3726 deletions

View File

@ -1,5 +1,9 @@
# Contributing to Frappe / ERPNext
### Update 16-Sep-14
Please send pull requests to branch v5.0
## Reporting issues
We only accept issues that are bug reports or feature requests. Bugs must be isolated and reproducible problems. Please read the following guidelines before opening any issue.

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

@ -163,7 +163,7 @@ def create_party_account(party, party_type, company):
company_details = frappe.db.get_value("Company", company,
["abbr", "receivables_group", "payables_group"], as_dict=True)
if not frappe.db.exists("Account", (party + " - " + company_details.abbr)):
if not frappe.db.exists("Account", (party.strip() + " - " + company_details.abbr)):
parent_account = company_details.receivables_group \
if party_type=="Customer" else company_details.payables_group
if not parent_account:

View File

@ -21,7 +21,7 @@ class AccountsReceivableReport(object):
def get_columns(self, customer_naming_by):
columns = [
_("Posting Date") + ":Date:80", _("Account") + ":Link/Account:150",
_("Voucher Type") + "::110", _("Voucher No") + "::120", "::30",
_("Voucher Type") + "::110", _("Voucher No") + ":Dynamic Link/Voucher Type:120",
_("Due Date") + ":Date:80",
_("Invoiced Amount") + ":Currency:100", _("Payment Received") + ":Currency:100",
_("Outstanding Amount") + ":Currency:100", _("Age") + ":Int:50", "0-30:Currency:100",
@ -63,11 +63,6 @@ class AccountsReceivableReport(object):
row += [self.get_territory(gle.account), gle.remarks]
data.append(row)
for i in range(0, len(data)):
data[i].insert(4, """<a href="%s"><i class="icon icon-share" style="cursor: pointer;"></i></a>""" \
% ("/".join(["#Form", data[i][2], data[i][3]]),))
return data
def get_entries_after(self, report_date):

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.item_code, row.qty, 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",
@ -108,9 +108,9 @@ def get_source_data(filters):
from `tabSales Invoice` si, `tabSales Invoice Item` item
where item.parent = si.name and si.docstatus = 1 %s
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

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

@ -135,12 +135,6 @@ def get_data():
"name": "Item-wise Purchase History",
"doctype": "Item"
},
{
"type": "report",
"is_query_report": True,
"name": "Item-wise Last Purchase Rate",
"doctype": "Item"
},
{
"type": "report",
"is_query_report": True,

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",
@ -171,6 +171,12 @@ def get_data():
"label": _("Stock Analytics"),
"icon": "icon-bar-chart"
},
{
"type": "report",
"is_query_report": True,
"name": "Warehouse-Wise Stock Balance",
"doctype": "Warehouse"
},
]
},
{
@ -222,12 +228,6 @@ def get_data():
"name": "Batch-Wise Balance History",
"doctype": "Batch"
},
{
"type": "report",
"is_query_report": True,
"name": "Warehouse-Wise Stock Balance",
"doctype": "Warehouse"
},
{
"type": "report",
"is_query_report": True,

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):
@ -361,38 +378,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
@ -420,7 +466,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`

File diff suppressed because it is too large Load Diff

View File

@ -96,7 +96,7 @@ cur_frm.cscript['Transfer Raw Materials'] = function() {
}
cur_frm.cscript['Update Finished Goods'] = function() {
cur_frm.cscript.make_se('Manufacture/Repack');
cur_frm.cscript.make_se('Manufacture');
}
cur_frm.fields_dict['production_item'].get_query = function(doc) {

View File

@ -109,15 +109,15 @@
"permlevel": 0
},
{
"depends_on": "eval:doc.docstatus==1",
"description": "Automatically updated via Stock Entry of type Manufacture/Repack",
"fieldname": "produced_qty",
"fieldtype": "Float",
"label": "Manufactured Qty",
"no_copy": 1,
"oldfieldname": "produced_qty",
"oldfieldtype": "Currency",
"permlevel": 0,
"depends_on": "eval:doc.docstatus==1",
"description": "Automatically updated via Stock Entry of type Manufacture or Repack",
"fieldname": "produced_qty",
"fieldtype": "Float",
"label": "Manufactured Qty",
"no_copy": 1,
"oldfieldname": "produced_qty",
"oldfieldtype": "Currency",
"permlevel": 0,
"read_only": 1
},
{

View File

@ -103,7 +103,7 @@ class ProductionOrder(Document):
status = "Submitted"
if stock_entries:
status = "In Process"
produced_qty = stock_entries.get("Manufacture/Repack")
produced_qty = stock_entries.get("Manufacture")
if flt(produced_qty) == flt(self.qty):
status = "Completed"
@ -113,7 +113,7 @@ class ProductionOrder(Document):
def update_produced_qty(self):
produced_qty = frappe.db.sql("""select sum(fg_completed_qty)
from `tabStock Entry` where production_order=%s and docstatus=1
and purpose='Manufacture/Repack'""", self.name)
and purpose='Manufacture'""", self.name)
produced_qty = flt(produced_qty[0][0]) if produced_qty else 0
if produced_qty > self.qty:

View File

@ -31,7 +31,7 @@ class TestProductionOrder(unittest.TestCase):
s.submit()
# from wip to fg
s = frappe.get_doc(make_stock_entry(pro_doc.name, "Manufacture/Repack", 4))
s = frappe.get_doc(make_stock_entry(pro_doc.name, "Manufacture", 4))
s.insert()
s.submit()
@ -49,7 +49,7 @@ class TestProductionOrder(unittest.TestCase):
test_stock_entry.make_stock_entry("_Test Item", None, "_Test Warehouse - _TC", 100, 100)
test_stock_entry.make_stock_entry("_Test Item Home Desktop 100", None, "_Test Warehouse - _TC", 100, 100)
s = frappe.get_doc(make_stock_entry(pro_doc.name, "Manufacture/Repack", 7))
s = frappe.get_doc(make_stock_entry(pro_doc.name, "Manufacture", 7))
s.insert()
self.assertRaises(StockOverProductionError, s.submit)

View File

@ -6,12 +6,12 @@
"doctype": "Report",
"idx": 1,
"is_standard": "Yes",
"modified": "2014-06-03 07:18:17.082436",
"modified": "2014-09-17 12:41:55.740299",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Issued Items Against Production Order",
"owner": "Administrator",
"query": "select\n ste.production_order as \"Production Order:Link/Production Order:120\",\n ste.posting_date as \"Issue Date:Date:140\",\n ste_item.item_code as \"Item Code:Link/Item:120\",\n\tste_item.description as \"Description::150\",\n\tste_item.transfer_qty as \"Qty:Float:100\",\n\tste_item.stock_uom as \"UOM:Link/UOM:80\",\n\tste_item.amount as \"Amount:Currency:120\",\n\tste_item.serial_no as \"Serial No:Link/Serial No:80\",\n\tste_item.s_warehouse as \"Source Warehouse:Link/Warehouse:120\",\n\tste_item.t_warehouse as \"Target Warehouse:Link/Warehouse:120\",\n\tpro.production_item as \"Finished Goods:Link/Item:120\", \n\tste.name as \"Stock Entry:Link/Stock Entry:120\"\nfrom\n\t`tabStock Entry` ste, `tabStock Entry Detail` ste_item, `tabProduction Order` pro\nwhere\n\tifnull(ste.production_order, '') != '' and ste.name = ste_item.parent \n\tand ste.production_order = pro.name and ste.docstatus = 1 \n\tand ste.purpose = 'Manufacture/Repack'\norder by ste.posting_date, ste.production_order, ste_item.item_code",
"query": "select\n ste.production_order as \"Production Order:Link/Production Order:120\",\n ste.posting_date as \"Issue Date:Date:140\",\n ste_item.item_code as \"Item Code:Link/Item:120\",\n\tste_item.description as \"Description::150\",\n\tste_item.transfer_qty as \"Qty:Float:100\",\n\tste_item.stock_uom as \"UOM:Link/UOM:80\",\n\tste_item.amount as \"Amount:Currency:120\",\n\tste_item.serial_no as \"Serial No:Link/Serial No:80\",\n\tste_item.s_warehouse as \"Source Warehouse:Link/Warehouse:120\",\n\tste_item.t_warehouse as \"Target Warehouse:Link/Warehouse:120\",\n\tpro.production_item as \"Finished Goods:Link/Item:120\", \n\tste.name as \"Stock Entry:Link/Stock Entry:120\"\nfrom\n\t`tabStock Entry` ste, `tabStock Entry Detail` ste_item, `tabProduction Order` pro\nwhere\n\tifnull(ste.production_order, '') != '' and ste.name = ste_item.parent \n\tand ste.production_order = pro.name and ste.docstatus = 1 \n\tand ste.purpose = 'Manufacture' or 'Repack'\norder by ste.posting_date, ste.production_order, ste_item.item_code",
"ref_doctype": "Production Order",
"report_name": "Issued Items Against Production Order",
"report_type": "Query Report"

View File

@ -81,4 +81,5 @@ erpnext.patches.v4_2.default_website_style
erpnext.patches.v4_2.set_company_country
erpnext.patches.v4_2.update_sales_order_invoice_field_name
erpnext.patches.v4_2.cost_of_production_cycle
erpnext.patches.v5_0.update_frozen_accounts_permission_role
erpnext.patches.v4_2.seprate_manufacture_and_repack
erpnext.patches.v5_0.update_frozen_accounts_permission_role

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

@ -0,0 +1,9 @@
# 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():
frappe.db.sql("""update `tabStock Entry` set purpose='Manufacture' where purpose='Manufacture/Repack' and ifnull(production_order,"")!="" """)
frappe.db.sql("""update `tabStock Entry` set purpose='Repack' where purpose='Manufacture/Repack' and ifnull(production_order,"")="" """)

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

@ -26,7 +26,7 @@ def get_columns():
_("Cost of Issued Items") + ":Currency:160", _("Cost of Delivered Items") + ":Currency:160",
_("Project Name") + "::120", _("Project Status") + "::120", _("Company") + ":Link/Company:100",
_("Customer") + ":Link/Customer:140", _("Project Value") + ":Currency:120",
_("Project Start Date") + ":Date:120", _("Completion Date") + ":Date:120"]+
_("Project Start Date") + ":Date:120", _("Completion Date") + ":Date:120"]
def get_project_details():
return frappe.db.sql(""" select name, project_name, status, company, customer, project_value,

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

@ -7,34 +7,43 @@ from frappe import msgprint, _
def execute(filters=None):
if not filters: filters = {}
columns = get_columns(filters)
data = get_entries(filters)
entries = get_entries(filters)
item_details = get_item_details()
data = []
for d in entries:
data.append([
d.name, d.customer, d.territory, d.posting_date, d.item_code,
item_details.get(d.item_code, {}).get("item_group"), item_details.get(d.item_code, {}).get("brand"),
d.qty, d.base_amount, d.sales_person, d.allocated_percentage, d.contribution_amt
])
return columns, data
def get_columns(filters):
if not filters.get("doc_type"):
msgprint(_("Please select the document type first"), raise_exception=1)
return [filters["doc_type"] + ":Link/" + filters["doc_type"] + ":140",
_("Customer") + ":Link/Customer:140", _("Territory") + ":Link/Territory:100", _("Posting Date") + ":Date:100",
_("Item Code") + ":Link/Item:120", _("Qty") + ":Float:100", _("Amount") + ":Currency:120",
_("Sales Person") + ":Link/Sales Person:140", _("Contribution %") + ":Float:110",
return [filters["doc_type"] + ":Link/" + filters["doc_type"] + ":140",
_("Customer") + ":Link/Customer:140", _("Territory") + ":Link/Territory:100", _("Posting Date") + ":Date:100",
_("Item Code") + ":Link/Item:120", _("Item Group") + ":Link/Item Group:120",
_("Brand") + ":Link/Brand:120", _("Qty") + ":Float:100", _("Amount") + ":Currency:120",
_("Sales Person") + ":Link/Sales Person:140", _("Contribution %") + ":Float:110",
_("Contribution Amount") + ":Currency:140"]
def get_entries(filters):
date_field = filters["doc_type"] == "Sales Order" and "transaction_date" or "posting_date"
conditions, items = get_conditions(filters, date_field)
entries = frappe.db.sql("""select dt.name, dt.customer, dt.territory, dt.%s,
dt_item.item_code, dt_item.qty, dt_item.base_amount, st.sales_person,
st.allocated_percentage, dt_item.base_amount*st.allocated_percentage/100
from `tab%s` dt, `tab%s Item` dt_item, `tabSales Team` st
where st.parent = dt.name and dt.name = dt_item.parent and st.parenttype = %s
and dt.docstatus = 1 %s order by st.sales_person, dt.name desc""" %
(date_field, filters["doc_type"], filters["doc_type"], '%s', conditions),
tuple([filters["doc_type"]] + items), as_list=1)
entries = frappe.db.sql("""select dt.name, dt.customer, dt.territory, dt.%s as posting_date,
dt_item.item_code, dt_item.qty, dt_item.base_amount, st.sales_person,
st.allocated_percentage, dt_item.base_amount*st.allocated_percentage/100 as contribution_amt
from `tab%s` dt, `tab%s Item` dt_item, `tabSales Team` st
where st.parent = dt.name and dt.name = dt_item.parent and st.parenttype = %s
and dt.docstatus = 1 %s order by st.sales_person, dt.name desc""" %
(date_field, filters["doc_type"], filters["doc_type"], '%s', conditions),
tuple([filters["doc_type"]] + items), as_dict=1)
return entries
def get_conditions(filters, date_field):
@ -45,18 +54,18 @@ def get_conditions(filters, date_field):
filters["customer"].replace("'", "\'")
if filters.get("territory"): conditions += " and dt.territory = '%s'" % \
filters["territory"].replace("'", "\'")
if filters.get("from_date"): conditions += " and dt.%s >= '%s'" % \
(date_field, filters["from_date"])
if filters.get("to_date"): conditions += " and dt.%s <= '%s'" % (date_field, filters["to_date"])
if filters.get("sales_person"): conditions += " and st.sales_person = '%s'" % \
filters["sales_person"].replace("'", "\'")
items = get_items(filters)
if items:
conditions += " and dt_item.item_code in (%s)" % ', '.join(['%s']*len(items))
return conditions, items
def get_items(filters):
@ -66,7 +75,14 @@ def get_items(filters):
items = []
if key:
items = frappe.db.sql_list("""select name from tabItem where %s = %s""" %
items = frappe.db.sql_list("""select name from tabItem where %s = %s""" %
(key, '%s'), (filters[key]))
return items
return items
def get_item_details():
item_details = {}
for d in frappe.db.sql("""select name, item_group, brand from `tabItem`""", as_dict=1):
item_details.setdefault(d.name, d)
return item_details

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

@ -1,19 +1,6 @@
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
cur_frm.cscript.get_states=function(doc, dt, dn) {
return $c('runserverobj', args={'method': 'check_state', 'docs':doc},
function(r, rt) {
if(r.message)
set_field_options('state', r.message);
}
);
}
cur_frm.cscript.country = function(doc, dt, dn) {
cur_frm.cscript.get_states(doc, dt, dn);
}
if(cur_frm.fields_dict['territory']) {
cur_frm.fields_dict['territory'].get_query = function(doc, dt, dn) {
return {

View File

@ -73,7 +73,7 @@ cur_frm.cscript.addremove_recipients = function(doc, dt, dn) {
var fullname = frappe.user.full_name(v.name);
if(fullname !== v.name) v.name = fullname + " &lt;" + v.name + "&gt;";
if(v.enabled==0) {
v.name = repl("<span style='color: red'> %(name)s (disabled user)</span>", {name: v.name});
v.name = repl("<span style='color: red'> %(name)s (" + __("disabled user") + ")</span>", {name: v.name});
}
var user = $a($td(tab, i+1, 1), 'span', '', '', v.name);
//user.onclick = function() { check.checked = !check.checked; }
@ -81,11 +81,11 @@ cur_frm.cscript.addremove_recipients = function(doc, dt, dn) {
// Display add recipients button
if(r.user_list.length>15) {
$btn($td(tab, 0, 1), add_or_update + ' Recipients', function() {
$btn($td(tab, 0, 1), __('{0} Recipients',[__(add_or_update)]), function() {
cur_frm.cscript.add_to_rec_list(doc, tab, r.user_list.length);
});
}
$btn($td(tab, r.user_list.length+1, 1), add_or_update + ' Recipients', function() {
$btn($td(tab, r.user_list.length+1, 1),__('{0} Recipients',[__(add_or_update)]), function() {
cur_frm.cscript.add_to_rec_list(doc, tab, r.user_list.length);
});

View File

@ -128,7 +128,7 @@ class EmailDigest(Document):
with_value = "\n".join(with_value)
else:
has_updates = False
with_value = "<p>There were no updates in the items selected for this digest.</p><hr>"
with_value = "<p>" + _("There were no updates in the items selected for this digest.") + "</p><hr>"
if not has_updates and send_only_if_updates:
return
@ -136,7 +136,7 @@ class EmailDigest(Document):
# seperate out no value items
no_value = [o[1] for o in out if not o[0]]
if no_value:
no_value = """<h4>No Updates For:</h4>""" + "\n".join(no_value)
no_value = """<h4>""" + _("No Updates For") + """:</h4>""" + "\n".join(no_value)
date = self.frequency == "Daily" and formatdate(self.from_date) or \
"%s to %s" % (formatdate(self.from_date), formatdate(self.to_date))
@ -310,9 +310,9 @@ class EmailDigest(Document):
(e.subject, datetime_in_user_format(e.starts_on), datetime_in_user_format(e.ends_on))
if html:
return 1, "<h4>Upcoming Calendar Events (max 10):</h4><ul>" + html + "</ul><hr>"
return 1, "<h4>" + _("Upcoming Calendar Events (max 10)") + ":</h4><ul>" + html + "</ul><hr>"
else:
return 0, "<p>Calendar Events</p>"
return 0, "<p>" + _("Calendar Events") + "</p>"
def get_todo_list(self, user_id):
todo_list = frappe.db.sql("""select *

View File

@ -60,11 +60,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

@ -3,8 +3,15 @@
from __future__ import unicode_literals
import frappe
from frappe import _
from frappe.model.document import Document
class Batch(Document):
pass
def validate(self):
self.item_has_batch_enabled()
def item_has_batch_enabled(self):
has_batch_no = frappe.db.get_value("Item",self.item,"has_batch_no")
if has_batch_no =='No':
frappe.throw(_("The selected item cannot have Batch"))

View File

@ -0,0 +1,14 @@
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
import frappe
from frappe.exceptions import ValidationError
import unittest
class TestBatch(unittest.TestCase):
def test_item_has_batch_enabled(self):
self.assertRaises(ValidationError, frappe.get_doc({
"doctype": "Batch",
"name": "_test Batch",
"item": "_Test Item"
}).save)

View File

@ -19,7 +19,7 @@ cur_frm.cscript.refresh = function(doc) {
cur_frm.cscript.edit_prices_button();
if (!doc.__islocal && doc.is_stock_item == 'Yes') {
cur_frm.toggle_enable(['has_serial_no', 'is_stock_item', 'valuation_method'],
cur_frm.toggle_enable(['has_serial_no', 'is_stock_item', 'valuation_method', 'has_batch_no'],
(doc.__onload && doc.__onload.sle_exists=="exists") ? false : true);
}
@ -185,4 +185,4 @@ cur_frm.cscript.image = function() {
else {
msgprint(__("You may need to update: {0}", [frappe.meta.get_docfield(cur_frm.doc.doctype, "description_html").label]));
}
}
}

View File

@ -187,13 +187,14 @@ class Item(WebsiteGenerator):
def cant_change(self):
if not self.get("__islocal"):
vals = frappe.db.get_value("Item", self.name,
["has_serial_no", "is_stock_item", "valuation_method"], as_dict=True)
["has_serial_no", "is_stock_item", "valuation_method", "has_batch_no"], as_dict=True)
if vals and ((self.is_stock_item == "No" and vals.is_stock_item == "Yes") or
vals.has_serial_no != self.has_serial_no or
vals.has_batch_no != self.has_batch_no or
cstr(vals.valuation_method) != cstr(self.valuation_method)):
if self.check_if_sle_exists() == "exists":
frappe.throw(_("As there are existing stock transactions for this item, you can not change the values of 'Has Serial No', 'Is Stock Item' and 'Valuation Method'"))
frappe.throw(_("As there are existing stock transactions for this item, you can not change the values of 'Has Serial No', 'Has Batch No', 'Is Stock Item' and 'Valuation Method'"))
def validate_item_type_for_reorder(self):
if self.re_order_level or len(self.get("item_reorder", {"material_request_type": "Purchase"})):

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

@ -9,6 +9,6 @@ class TestItem(unittest.TestCase):
def test_duplicate_item(self):
from erpnext.stock.doctype.item_price.item_price import ItemPriceDuplicateItem
doc = frappe.copy_doc(test_records[0])
self.assertRaises(ItemPriceDuplicateItem, doc.insert)
self.assertRaises(ItemPriceDuplicateItem, doc.save)
test_records = frappe.get_test_records('Item Price')

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

@ -120,7 +120,7 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({
clean_up: function() {
// Clear Production Order record from locals, because it is updated via Stock Entry
if(this.frm.doc.production_order &&
this.frm.doc.purpose == "Manufacture/Repack") {
this.frm.doc.purpose == "Manufacture") {
frappe.model.remove_from_locals("Production Order",
this.frm.doc.production_order);
}
@ -162,7 +162,7 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({
},
toggle_enable_bom: function() {
this.frm.toggle_enable("bom_no", !this.frm.doc.production_order);
this.frm.toggle_enable("bom_no", this.frm.doc.purpose!="Manufacture");
},
get_doctype_docname: function() {
@ -339,6 +339,8 @@ cur_frm.cscript.toggle_related_fields = function(doc) {
cur_frm.fields_dict["mtn_details"].grid.set_column_disp("s_warehouse", !disable_from_warehouse);
cur_frm.fields_dict["mtn_details"].grid.set_column_disp("t_warehouse", !disable_to_warehouse);
cur_frm.cscript.toggle_enable_bom();
if(doc.purpose == 'Purchase Return') {
doc.customer = doc.customer_name = doc.customer_address =
doc.delivery_note_no = doc.sales_invoice_no = null;
@ -351,6 +353,8 @@ cur_frm.cscript.toggle_related_fields = function(doc) {
doc.delivery_note_no = doc.sales_invoice_no = doc.supplier =
doc.supplier_name = doc.supplier_address = doc.purchase_receipt_no = null;
}
}
cur_frm.fields_dict['production_order'].get_query = function(doc) {
@ -457,4 +461,5 @@ cur_frm.fields_dict.customer.get_query = function(doc, cdt, cdn) {
cur_frm.fields_dict.supplier.get_query = function(doc, cdt, cdn) {
return { query: "erpnext.controllers.queries.supplier_query" }
}
cur_frm.add_fetch('production_order', 'total_fixed_cost', 'total_fixed_cost');
cur_frm.add_fetch('production_order', 'total_fixed_cost', 'total_fixed_cost');
cur_frm.add_fetch('bom_no', 'total_fixed_cost', 'total_fixed_cost');

File diff suppressed because it is too large Load Diff

View File

@ -47,7 +47,7 @@ class StockEntry(StockController):
self.validate_uom_is_integer("uom", "qty")
self.validate_uom_is_integer("stock_uom", "transfer_qty")
self.validate_warehouse(pro_obj)
self.validate_production_order(pro_obj)
self.validate_production_order()
self.get_stock_and_rate()
self.validate_incoming_rate()
self.validate_bom()
@ -58,6 +58,7 @@ class StockEntry(StockController):
self.validate_valuation_rate()
self.set_total_amount()
def on_submit(self):
self.update_stock_ledger()
@ -78,7 +79,7 @@ class StockEntry(StockController):
def validate_purpose(self):
valid_purposes = ["Material Issue", "Material Receipt", "Material Transfer",
"Manufacture/Repack", "Subcontract", "Sales Return", "Purchase Return"]
"Manufacture", "Repack", "Subcontract", "Sales Return", "Purchase Return"]
if self.purpose not in valid_purposes:
frappe.throw(_("Purpose must be one of {0}").format(comma_or(valid_purposes)))
@ -141,7 +142,7 @@ class StockEntry(StockController):
if self.purpose in target_mandatory and not d.t_warehouse:
frappe.throw(_("Target warehouse is mandatory for row {0}").format(d.idx))
if self.purpose == "Manufacture/Repack":
if self.purpose in ["Manufacture", "Repack"]:
if validate_for_manufacture_repack:
if d.bom_no:
d.s_warehouse = None
@ -160,14 +161,11 @@ class StockEntry(StockController):
if cstr(d.s_warehouse) == cstr(d.t_warehouse):
frappe.throw(_("Source and target warehouse cannot be same for row {0}").format(d.idx))
def validate_production_order(self, pro_obj=None):
if not pro_obj:
if self.production_order:
pro_obj = frappe.get_doc('Production Order', self.production_order)
else:
return
if self.purpose == "Manufacture/Repack":
def validate_production_order(self):
if self.purpose == "Manufacture":
# check if production order is entered
if not self.production_order:
frappe.throw(_("Production order number is mandatory for stock entry purpose manufacture"))
# check for double entry
self.check_duplicate_entry_for_production_order()
elif self.purpose != "Material Transfer":
@ -196,7 +194,7 @@ class StockEntry(StockController):
+ self.production_order + ":" + ", ".join(other_ste), DuplicateEntryForProductionOrderError)
def validate_valuation_rate(self):
if self.purpose == "Manufacture/Repack":
if self.purpose in ["Manufacture", "Repack"]:
valuation_at_source, valuation_at_target = 0, 0
for d in self.get("mtn_details"):
if d.s_warehouse and not d.t_warehouse:
@ -252,7 +250,7 @@ class StockEntry(StockController):
raw_material_cost += flt(d.amount)
# set incoming rate for fg item
if self.purpose == "Manufacture/Repack":
if self.purpose in ["Manufacture", "Repack"]:
number_of_fg_items = len([t.t_warehouse for t in self.get("mtn_details") if t.t_warehouse])
for d in self.get("mtn_details"):
if d.bom_no or (d.t_warehouse and number_of_fg_items == 1):
@ -395,7 +393,7 @@ class StockEntry(StockController):
pro_doc = frappe.get_doc("Production Order", self.production_order)
_validate_production_order(pro_doc)
pro_doc.run_method("update_status")
if self.purpose == "Manufacture/Repack":
if self.purpose == "Manufacture":
pro_doc.run_method("update_produced_qty")
self.update_planned_qty(pro_doc)
@ -467,20 +465,20 @@ class StockEntry(StockController):
def get_items(self):
self.set('mtn_details', [])
self.validate_production_order()
pro_obj = None
if self.production_order:
# common validations
pro_obj = frappe.get_doc('Production Order', self.production_order)
if pro_obj:
self.validate_production_order(pro_obj)
self.bom_no = pro_obj.bom_no
else:
# invalid production order
self.production_order = None
if self.bom_no:
if self.purpose in ["Material Issue", "Material Transfer", "Manufacture/Repack",
if self.purpose in ["Material Issue", "Material Transfer", "Manufacture", "Repack",
"Subcontract"]:
if self.production_order and self.purpose == "Material Transfer":
item_dict = self.get_pending_raw_materials(pro_obj)
@ -497,7 +495,7 @@ class StockEntry(StockController):
self.add_to_stock_entry_detail(item_dict)
# add finished good item to Stock Entry Detail table -- along with bom_no
if self.production_order and self.purpose == "Manufacture/Repack":
if self.production_order and self.purpose == "Manufacture":
item = frappe.db.get_value("Item", pro_obj.production_item, ["item_name",
"description", "stock_uom", "expense_account", "buying_cost_center"], as_dict=1)
self.add_to_stock_entry_detail({
@ -513,7 +511,7 @@ class StockEntry(StockController):
}
}, bom_no=pro_obj.bom_no)
elif self.purpose in ["Material Receipt", "Manufacture/Repack"]:
elif self.purpose in ["Material Receipt", "Repack"]:
if self.purpose=="Material Receipt":
self.from_warehouse = ""

View File

@ -6,7 +6,8 @@
"Material Issue": "icon-arrow-right",
"Material Receipt": "icon-arrow-left",
"Material Transfer": "icon-resize-horizontal",
"Manufacture/Repack": "icon-wrench",
"Manufacture": "icon-wrench",
"Repack": "icon-wrench",
"Sales Return": "icon-warning-sign",
"Purchase Return": "icon-warning-sign",
"Subcontract": "icon-truck"

View File

@ -108,6 +108,6 @@
],
"posting_date": "2013-01-25",
"posting_time": "17:14:24",
"purpose": "Manufacture/Repack"
"purpose": "Repack"
}
]

View File

@ -843,7 +843,7 @@ class TestStockEntry(unittest.TestCase):
stock_entry = frappe.new_doc("Stock Entry")
stock_entry.update({
"purpose": "Manufacture/Repack",
"purpose": "Manufacture",
"production_order": production_order.name,
"bom_no": bom_no,
"fg_completed_qty": "1",

View File

@ -1,224 +1,223 @@
{
"allow_import": 1,
"allow_rename": 1,
"creation": "2013-03-07 18:50:32",
"description": "A logical Warehouse against which stock entries are made.",
"docstatus": 0,
"doctype": "DocType",
"document_type": "Master",
"allow_import": 1,
"allow_rename": 1,
"creation": "2013-03-07 18:50:32",
"description": "A logical Warehouse against which stock entries are made.",
"docstatus": 0,
"doctype": "DocType",
"document_type": "Master",
"fields": [
{
"fieldname": "warehouse_detail",
"fieldtype": "Section Break",
"label": "Warehouse Detail",
"oldfieldtype": "Section Break",
"permlevel": 0,
"fieldname": "warehouse_detail",
"fieldtype": "Section Break",
"label": "Warehouse Detail",
"oldfieldtype": "Section Break",
"permlevel": 0,
"read_only": 0
},
},
{
"fieldname": "warehouse_name",
"fieldtype": "Data",
"label": "Warehouse Name",
"oldfieldname": "warehouse_name",
"oldfieldtype": "Data",
"permlevel": 0,
"read_only": 0,
"fieldname": "warehouse_name",
"fieldtype": "Data",
"label": "Warehouse Name",
"oldfieldname": "warehouse_name",
"oldfieldtype": "Data",
"permlevel": 0,
"read_only": 0,
"reqd": 1
},
},
{
"fieldname": "company",
"fieldtype": "Link",
"in_filter": 1,
"label": "Company",
"oldfieldname": "company",
"oldfieldtype": "Link",
"options": "Company",
"permlevel": 0,
"read_only": 0,
"reqd": 1,
"fieldname": "company",
"fieldtype": "Link",
"in_filter": 1,
"label": "Company",
"oldfieldname": "company",
"oldfieldtype": "Link",
"options": "Company",
"permlevel": 0,
"read_only": 0,
"reqd": 1,
"search_index": 1
},
},
{
"depends_on": "eval:sys_defaults.auto_accounting_for_stock",
"description": "Account for the warehouse (Perpetual Inventory) will be created under this Account.",
"fieldname": "create_account_under",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Parent Account",
"options": "Account",
"depends_on": "eval:sys_defaults.auto_accounting_for_stock",
"description": "Account for the warehouse (Perpetual Inventory) will be created under this Account.",
"fieldname": "create_account_under",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Parent Account",
"options": "Account",
"permlevel": 0
},
},
{
"fieldname": "disabled",
"fieldtype": "Check",
"label": "Disabled",
"fieldname": "disabled",
"fieldtype": "Check",
"label": "Disabled",
"permlevel": 0
},
},
{
"description": "For Reference Only.",
"fieldname": "warehouse_contact_info",
"fieldtype": "Section Break",
"label": "Warehouse Contact Info",
"permlevel": 0,
"description": "For Reference Only.",
"fieldname": "warehouse_contact_info",
"fieldtype": "Section Break",
"label": "Warehouse Contact Info",
"permlevel": 0,
"read_only": 0
},
},
{
"fieldname": "email_id",
"fieldtype": "Data",
"hidden": 1,
"label": "Email Id",
"oldfieldname": "email_id",
"oldfieldtype": "Data",
"permlevel": 0,
"print_hide": 0,
"fieldname": "email_id",
"fieldtype": "Data",
"hidden": 1,
"label": "Email Id",
"oldfieldname": "email_id",
"oldfieldtype": "Data",
"permlevel": 0,
"print_hide": 0,
"read_only": 0
},
},
{
"fieldname": "phone_no",
"fieldtype": "Data",
"label": "Phone No",
"oldfieldname": "phone_no",
"oldfieldtype": "Int",
"options": "Phone",
"permlevel": 0,
"fieldname": "phone_no",
"fieldtype": "Data",
"label": "Phone No",
"oldfieldname": "phone_no",
"oldfieldtype": "Int",
"options": "Phone",
"permlevel": 0,
"read_only": 0
},
},
{
"fieldname": "mobile_no",
"fieldtype": "Data",
"label": "Mobile No",
"oldfieldname": "mobile_no",
"oldfieldtype": "Int",
"options": "Phone",
"permlevel": 0,
"fieldname": "mobile_no",
"fieldtype": "Data",
"label": "Mobile No",
"oldfieldname": "mobile_no",
"oldfieldtype": "Int",
"options": "Phone",
"permlevel": 0,
"read_only": 0
},
},
{
"fieldname": "column_break0",
"fieldtype": "Column Break",
"oldfieldtype": "Column Break",
"permlevel": 0,
"fieldname": "column_break0",
"fieldtype": "Column Break",
"oldfieldtype": "Column Break",
"permlevel": 0,
"read_only": 0
},
},
{
"fieldname": "address_line_1",
"fieldtype": "Data",
"label": "Address Line 1",
"oldfieldname": "address_line_1",
"oldfieldtype": "Data",
"permlevel": 0,
"fieldname": "address_line_1",
"fieldtype": "Data",
"label": "Address Line 1",
"oldfieldname": "address_line_1",
"oldfieldtype": "Data",
"permlevel": 0,
"read_only": 0
},
},
{
"fieldname": "address_line_2",
"fieldtype": "Data",
"label": "Address Line 2",
"oldfieldname": "address_line_2",
"oldfieldtype": "Data",
"permlevel": 0,
"fieldname": "address_line_2",
"fieldtype": "Data",
"label": "Address Line 2",
"oldfieldname": "address_line_2",
"oldfieldtype": "Data",
"permlevel": 0,
"read_only": 0
},
},
{
"fieldname": "city",
"fieldtype": "Data",
"in_list_view": 1,
"label": "City",
"oldfieldname": "city",
"oldfieldtype": "Data",
"permlevel": 0,
"read_only": 0,
"fieldname": "city",
"fieldtype": "Data",
"in_list_view": 1,
"label": "City",
"oldfieldname": "city",
"oldfieldtype": "Data",
"permlevel": 0,
"read_only": 0,
"reqd": 0
},
},
{
"fieldname": "state",
"fieldtype": "Data",
"label": "State",
"oldfieldname": "state",
"oldfieldtype": "Select",
"options": "Suggest",
"permlevel": 0,
"fieldname": "state",
"fieldtype": "Data",
"label": "State",
"oldfieldname": "state",
"oldfieldtype": "Select",
"permlevel": 0,
"read_only": 0
},
},
{
"fieldname": "pin",
"fieldtype": "Int",
"label": "PIN",
"oldfieldname": "pin",
"oldfieldtype": "Int",
"permlevel": 0,
"fieldname": "pin",
"fieldtype": "Int",
"label": "PIN",
"oldfieldname": "pin",
"oldfieldtype": "Int",
"permlevel": 0,
"read_only": 0
}
],
"icon": "icon-building",
"idx": 1,
"modified": "2014-08-04 02:55:16.750848",
"modified_by": "Administrator",
"module": "Stock",
"name": "Warehouse",
"owner": "Administrator",
],
"icon": "icon-building",
"idx": 1,
"modified": "2014-09-15 02:55:16.750848",
"modified_by": "Administrator",
"module": "Stock",
"name": "Warehouse",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"create": 1,
"delete": 1,
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Material Master Manager",
"submit": 0,
"amend": 0,
"create": 1,
"delete": 1,
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Material Master Manager",
"submit": 0,
"write": 1
},
},
{
"amend": 0,
"apply_user_permissions": 1,
"create": 0,
"delete": 0,
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Material User",
"submit": 0,
"amend": 0,
"apply_user_permissions": 1,
"create": 0,
"delete": 0,
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Material User",
"submit": 0,
"write": 0
},
},
{
"apply_user_permissions": 1,
"delete": 0,
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"apply_user_permissions": 1,
"delete": 0,
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Sales User"
},
},
{
"apply_user_permissions": 1,
"delete": 0,
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"apply_user_permissions": 1,
"delete": 0,
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Purchase User"
},
},
{
"apply_user_permissions": 1,
"delete": 0,
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"apply_user_permissions": 1,
"delete": 0,
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts User"
},
},
{
"apply_user_permissions": 1,
"permlevel": 0,
"read": 1,
"apply_user_permissions": 1,
"permlevel": 0,
"read": 1,
"role": "Manufacturing User"
}
]
}
}

View File

@ -19,9 +19,15 @@ def execute(filters=None):
for wh in sorted(iwb_map[company][item]):
qty_dict = iwb_map[company][item][wh]
data.append([item, item_map[item]["item_name"],
item_map[item]["item_group"],
item_map[item]["brand"],
item_map[item]["description"], wh,
qty_dict.opening_qty, qty_dict.in_qty,
qty_dict.out_qty, qty_dict.bal_qty, company
qty_dict.uom, qty_dict.opening_qty,
qty_dict.opening_val, qty_dict.in_qty,
qty_dict.in_val, qty_dict.out_qty,
qty_dict.out_val, qty_dict.bal_qty,
qty_dict.bal_val, qty_dict.val_rate,
company
])
return columns, data
@ -29,9 +35,11 @@ def execute(filters=None):
def get_columns(filters):
"""return columns based on filters"""
columns = ["Item:Link/Item:100", "Item Name::150", "Description::150", \
"Warehouse:Link/Warehouse:100", "Opening Qty:Float:90", \
"In Qty:Float:80", "Out Qty:Float:80", "Balance Qty:Float:90", "Company:Link/Company:100"]
columns = ["Item:Link/Item:100", "Item Name::150", "Item Group::100", "Brand::90", \
"Description::140", "Warehouse:Link/Warehouse:100", "Stock UOM::90", "Opening Qty:Float:100", \
"Opening Value:Float:110", "In Qty:Float:80", "In Value:Float:80", "Out Qty:Float:80", \
"Out Value:Float:80", "Balance Qty:Float:100", "Balance Value:Float:100", \
"Valuation Rate:Float:90", "Company:Link/Company:100"]
return columns
@ -50,8 +58,8 @@ def get_conditions(filters):
#get all details
def get_stock_ledger_entries(filters):
conditions = get_conditions(filters)
return frappe.db.sql("""select item_code, warehouse,
posting_date, actual_qty, company
return frappe.db.sql("""select item_code, warehouse, posting_date,
actual_qty, valuation_rate, stock_uom, company
from `tabStock Ledger Entry`
where docstatus < 2 %s order by item_code, warehouse""" %
conditions, as_dict=1)
@ -63,24 +71,37 @@ 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, "in_qty": 0.0, "out_qty": 0.0, "bal_qty": 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]
qty_dict.uom = d.stock_uom
if d.posting_date < filters["from_date"]:
qty_dict.opening_qty += flt(d.actual_qty)
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) * flt(d.valuation_rate)
else:
qty_dict.out_qty += abs(flt(d.actual_qty))
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) * flt(d.valuation_rate)
return iwb_map
def get_item_details(filters):
item_map = {}
for d in frappe.db.sql("select name, item_name, description from tabItem", as_dict=1):
for d in frappe.db.sql("select name, item_name, item_group, brand, \
description from tabItem", as_dict=1):
item_map.setdefault(d.name, d)
return item_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

@ -14,9 +14,9 @@ $.extend(cur_frm.cscript, {
erpnext.toggle_naming_series();
if(!doc.__islocal) {
if(cur_frm.fields_dict.status.get_status()=="Write") {
if(doc.status!='Closed') cur_frm.add_custom_button('Close',
if(doc.status!='Closed') cur_frm.add_custom_button(__('Close'),
cur_frm.cscript['Close Ticket'], "icon-ok", "btn-success");
if(doc.status=='Closed') cur_frm.add_custom_button('Re-Open Ticket',
if(doc.status=='Closed') cur_frm.add_custom_button(__('Re-Open Ticket'),
cur_frm.cscript['Re-Open Ticket'], null, "btn-default");
}

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,264 +1,263 @@
{
"allow_import": 1,
"allow_rename": 1,
"creation": "2013-01-10 16:34:32",
"docstatus": 0,
"doctype": "DocType",
"document_type": "Master",
"allow_import": 1,
"allow_rename": 1,
"creation": "2013-01-10 16:34:32",
"docstatus": 0,
"doctype": "DocType",
"document_type": "Master",
"fields": [
{
"fieldname": "address_details",
"fieldtype": "Section Break",
"label": "Address Details",
"options": "icon-map-marker",
"fieldname": "address_details",
"fieldtype": "Section Break",
"label": "Address Details",
"options": "icon-map-marker",
"permlevel": 0
},
},
{
"description": "Name of person or organization that this address belongs to.",
"fieldname": "address_title",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Address Title",
"permlevel": 0,
"description": "Name of person or organization that this address belongs to.",
"fieldname": "address_title",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Address Title",
"permlevel": 0,
"reqd": 0
},
},
{
"fieldname": "address_type",
"fieldtype": "Select",
"label": "Address Type",
"options": "Billing\nShipping\nOffice\nPersonal\nPlant\nPostal\nShop\nSubsidiary\nWarehouse\nOther",
"permlevel": 0,
"fieldname": "address_type",
"fieldtype": "Select",
"label": "Address Type",
"options": "Billing\nShipping\nOffice\nPersonal\nPlant\nPostal\nShop\nSubsidiary\nWarehouse\nOther",
"permlevel": 0,
"reqd": 1
},
},
{
"fieldname": "address_line1",
"fieldtype": "Data",
"label": "Address Line 1",
"permlevel": 0,
"fieldname": "address_line1",
"fieldtype": "Data",
"label": "Address Line 1",
"permlevel": 0,
"reqd": 1
},
},
{
"fieldname": "address_line2",
"fieldtype": "Data",
"label": "Address Line 2",
"fieldname": "address_line2",
"fieldtype": "Data",
"label": "Address Line 2",
"permlevel": 0
},
},
{
"fieldname": "city",
"fieldtype": "Data",
"in_filter": 1,
"in_list_view": 1,
"label": "City/Town",
"permlevel": 0,
"reqd": 1,
"fieldname": "city",
"fieldtype": "Data",
"in_filter": 1,
"in_list_view": 1,
"label": "City/Town",
"permlevel": 0,
"reqd": 1,
"search_index": 1
},
},
{
"fieldname": "state",
"fieldtype": "Data",
"in_filter": 1,
"in_list_view": 1,
"label": "State",
"options": "Suggest",
"permlevel": 0,
"fieldname": "state",
"fieldtype": "Data",
"in_filter": 1,
"in_list_view": 1,
"label": "State",
"permlevel": 0,
"search_index": 0
},
},
{
"fieldname": "pincode",
"fieldtype": "Data",
"in_filter": 1,
"in_list_view": 1,
"label": "Pincode",
"permlevel": 0,
"fieldname": "pincode",
"fieldtype": "Data",
"in_filter": 1,
"in_list_view": 1,
"label": "Pincode",
"permlevel": 0,
"search_index": 1
},
},
{
"fieldname": "country",
"fieldtype": "Link",
"in_filter": 1,
"in_list_view": 1,
"label": "Country",
"options": "Country",
"permlevel": 0,
"reqd": 1,
"fieldname": "country",
"fieldtype": "Link",
"in_filter": 1,
"in_list_view": 1,
"label": "Country",
"options": "Country",
"permlevel": 0,
"reqd": 1,
"search_index": 1
},
},
{
"fieldname": "column_break0",
"fieldtype": "Column Break",
"permlevel": 0,
"print_hide": 0,
"fieldname": "column_break0",
"fieldtype": "Column Break",
"permlevel": 0,
"print_hide": 0,
"width": "50%"
},
},
{
"fieldname": "email_id",
"fieldtype": "Data",
"label": "Email Id",
"fieldname": "email_id",
"fieldtype": "Data",
"label": "Email Id",
"permlevel": 0
},
},
{
"fieldname": "phone",
"fieldtype": "Data",
"label": "Phone",
"permlevel": 0,
"fieldname": "phone",
"fieldtype": "Data",
"label": "Phone",
"permlevel": 0,
"reqd": 1
},
},
{
"fieldname": "fax",
"fieldtype": "Data",
"in_filter": 1,
"label": "Fax",
"fieldname": "fax",
"fieldtype": "Data",
"in_filter": 1,
"label": "Fax",
"permlevel": 0
},
},
{
"default": "0",
"description": "Check to make primary address",
"fieldname": "is_primary_address",
"fieldtype": "Check",
"label": "Preferred Billing Address",
"default": "0",
"description": "Check to make primary address",
"fieldname": "is_primary_address",
"fieldtype": "Check",
"label": "Preferred Billing Address",
"permlevel": 0
},
},
{
"default": "0",
"description": "Check to make Shipping Address",
"fieldname": "is_shipping_address",
"fieldtype": "Check",
"in_list_view": 1,
"label": "Preferred Shipping Address",
"default": "0",
"description": "Check to make Shipping Address",
"fieldname": "is_shipping_address",
"fieldtype": "Check",
"in_list_view": 1,
"label": "Preferred Shipping Address",
"permlevel": 0
},
},
{
"fieldname": "linked_with",
"fieldtype": "Section Break",
"label": "Reference",
"options": "icon-pushpin",
"fieldname": "linked_with",
"fieldtype": "Section Break",
"label": "Reference",
"options": "icon-pushpin",
"permlevel": 0
},
},
{
"fieldname": "customer",
"fieldtype": "Link",
"label": "Customer",
"options": "Customer",
"fieldname": "customer",
"fieldtype": "Link",
"label": "Customer",
"options": "Customer",
"permlevel": 0
},
},
{
"fieldname": "customer_name",
"fieldtype": "Data",
"in_filter": 1,
"in_list_view": 0,
"label": "Customer Name",
"permlevel": 0,
"fieldname": "customer_name",
"fieldtype": "Data",
"in_filter": 1,
"in_list_view": 0,
"label": "Customer Name",
"permlevel": 0,
"read_only": 1
},
},
{
"fieldname": "supplier",
"fieldtype": "Link",
"label": "Supplier",
"options": "Supplier",
"fieldname": "supplier",
"fieldtype": "Link",
"label": "Supplier",
"options": "Supplier",
"permlevel": 0
},
},
{
"fieldname": "supplier_name",
"fieldtype": "Data",
"in_filter": 1,
"in_list_view": 0,
"label": "Supplier Name",
"permlevel": 0,
"read_only": 1,
"fieldname": "supplier_name",
"fieldtype": "Data",
"in_filter": 1,
"in_list_view": 0,
"label": "Supplier Name",
"permlevel": 0,
"read_only": 1,
"search_index": 0
},
},
{
"fieldname": "sales_partner",
"fieldtype": "Link",
"label": "Sales Partner",
"options": "Sales Partner",
"fieldname": "sales_partner",
"fieldtype": "Link",
"label": "Sales Partner",
"options": "Sales Partner",
"permlevel": 0
},
},
{
"fieldname": "column_break_22",
"fieldtype": "Column Break",
"fieldname": "column_break_22",
"fieldtype": "Column Break",
"permlevel": 0
},
},
{
"depends_on": "eval:!doc.supplier && !doc.sales_partner",
"fieldname": "lead",
"fieldtype": "Link",
"label": "Lead",
"options": "Lead",
"depends_on": "eval:!doc.supplier && !doc.sales_partner",
"fieldname": "lead",
"fieldtype": "Link",
"label": "Lead",
"options": "Lead",
"permlevel": 0
},
},
{
"depends_on": "eval:!doc.supplier && !doc.sales_partner",
"fieldname": "lead_name",
"fieldtype": "Data",
"label": "Lead Name",
"permlevel": 0,
"depends_on": "eval:!doc.supplier && !doc.sales_partner",
"fieldname": "lead_name",
"fieldtype": "Data",
"label": "Lead Name",
"permlevel": 0,
"read_only": 1
}
],
"icon": "icon-map-marker",
"idx": 1,
"in_dialog": 0,
"modified": "2014-05-27 03:49:07.273657",
"modified_by": "Administrator",
"module": "Utilities",
"name": "Address",
"owner": "Administrator",
],
"icon": "icon-map-marker",
"idx": 1,
"in_dialog": 0,
"modified": "2014-09-15 03:49:07.273657",
"modified_by": "Administrator",
"module": "Utilities",
"name": "Address",
"owner": "Administrator",
"permissions": [
{
"apply_user_permissions": 1,
"create": 1,
"delete": 0,
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Sales User",
"submit": 0,
"apply_user_permissions": 1,
"create": 1,
"delete": 0,
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Sales User",
"submit": 0,
"write": 1
},
},
{
"apply_user_permissions": 1,
"create": 1,
"delete": 0,
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Purchase User",
"submit": 0,
"apply_user_permissions": 1,
"create": 1,
"delete": 0,
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Purchase User",
"submit": 0,
"write": 1
},
},
{
"apply_user_permissions": 1,
"create": 1,
"delete": 0,
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Maintenance User",
"submit": 0,
"apply_user_permissions": 1,
"create": 1,
"delete": 0,
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Maintenance User",
"submit": 0,
"write": 1
},
},
{
"apply_user_permissions": 1,
"create": 1,
"delete": 0,
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts User",
"submit": 0,
"apply_user_permissions": 1,
"create": 1,
"delete": 0,
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts User",
"submit": 0,
"write": 1
}
],
"search_fields": "customer, supplier, sales_partner, country, state",
"sort_field": "modified",
],
"search_fields": "customer, supplier, sales_partner, country, state",
"sort_field": "modified",
"sort_order": "DESC"
}
}

View File

@ -1,344 +1,342 @@
{
"allow_import": 1,
"allow_rename": 1,
"creation": "2013-01-10 16:34:32",
"docstatus": 0,
"doctype": "DocType",
"document_type": "Master",
"allow_import": 1,
"allow_rename": 1,
"creation": "2013-01-10 16:34:32",
"docstatus": 0,
"doctype": "DocType",
"document_type": "Master",
"fields": [
{
"fieldname": "contact_section",
"fieldtype": "Section Break",
"label": "Contact Details",
"options": "icon-user",
"fieldname": "contact_section",
"fieldtype": "Section Break",
"label": "Contact Details",
"options": "icon-user",
"permlevel": 0
},
},
{
"fieldname": "first_name",
"fieldtype": "Data",
"in_list_view": 0,
"label": "First Name",
"oldfieldname": "first_name",
"oldfieldtype": "Data",
"permlevel": 0,
"fieldname": "first_name",
"fieldtype": "Data",
"in_list_view": 0,
"label": "First Name",
"oldfieldname": "first_name",
"oldfieldtype": "Data",
"permlevel": 0,
"reqd": 1
},
},
{
"fieldname": "last_name",
"fieldtype": "Data",
"in_list_view": 0,
"label": "Last Name",
"oldfieldname": "last_name",
"oldfieldtype": "Data",
"fieldname": "last_name",
"fieldtype": "Data",
"in_list_view": 0,
"label": "Last Name",
"oldfieldname": "last_name",
"oldfieldtype": "Data",
"permlevel": 0
},
},
{
"fieldname": "cb00",
"fieldtype": "Column Break",
"fieldname": "cb00",
"fieldtype": "Column Break",
"permlevel": 0
},
},
{
"default": "Passive",
"fieldname": "status",
"fieldtype": "Select",
"in_list_view": 1,
"label": "Status",
"options": "Passive\nOpen\nReplied",
"default": "Passive",
"fieldname": "status",
"fieldtype": "Select",
"in_list_view": 1,
"label": "Status",
"options": "Passive\nOpen\nReplied",
"permlevel": 0
},
},
{
"fieldname": "email_id",
"fieldtype": "Data",
"in_list_view": 0,
"label": "Email Id",
"oldfieldname": "email_id",
"oldfieldtype": "Data",
"permlevel": 0,
"reqd": 0,
"fieldname": "email_id",
"fieldtype": "Data",
"in_list_view": 0,
"label": "Email Id",
"oldfieldname": "email_id",
"oldfieldtype": "Data",
"permlevel": 0,
"reqd": 0,
"search_index": 1
},
},
{
"fieldname": "phone",
"fieldtype": "Data",
"label": "Phone",
"oldfieldname": "contact_no",
"oldfieldtype": "Data",
"permlevel": 0,
"fieldname": "phone",
"fieldtype": "Data",
"label": "Phone",
"oldfieldname": "contact_no",
"oldfieldtype": "Data",
"permlevel": 0,
"reqd": 0
},
},
{
"fieldname": "contact_details",
"fieldtype": "Section Break",
"label": "Reference",
"options": "icon-pushpin",
"fieldname": "contact_details",
"fieldtype": "Section Break",
"label": "Reference",
"options": "icon-pushpin",
"permlevel": 0
},
},
{
"depends_on": "eval:!doc.supplier && !doc.sales_partner",
"fieldname": "customer",
"fieldtype": "Link",
"label": "Customer",
"oldfieldname": "customer",
"oldfieldtype": "Link",
"options": "Customer",
"permlevel": 0,
"depends_on": "eval:!doc.supplier && !doc.sales_partner",
"fieldname": "customer",
"fieldtype": "Link",
"label": "Customer",
"oldfieldname": "customer",
"oldfieldtype": "Link",
"options": "Customer",
"permlevel": 0,
"print_hide": 0
},
},
{
"depends_on": "eval:!doc.supplier && !doc.sales_partner",
"fieldname": "customer_name",
"fieldtype": "Data",
"in_list_view": 0,
"label": "Customer Name",
"permlevel": 0,
"depends_on": "eval:!doc.supplier && !doc.sales_partner",
"fieldname": "customer_name",
"fieldtype": "Data",
"in_list_view": 0,
"label": "Customer Name",
"permlevel": 0,
"read_only": 1
},
},
{
"fieldname": "column_break1",
"fieldtype": "Column Break",
"oldfieldtype": "Column Break",
"permlevel": 0,
"fieldname": "column_break1",
"fieldtype": "Column Break",
"oldfieldtype": "Column Break",
"permlevel": 0,
"width": "50%"
},
},
{
"depends_on": "eval:!doc.customer && !doc.sales_partner",
"fieldname": "supplier",
"fieldtype": "Link",
"label": "Supplier",
"options": "Supplier",
"depends_on": "eval:!doc.customer && !doc.sales_partner",
"fieldname": "supplier",
"fieldtype": "Link",
"label": "Supplier",
"options": "Supplier",
"permlevel": 0
},
},
{
"allow_on_submit": 0,
"depends_on": "eval:!doc.customer && !doc.sales_partner",
"fieldname": "supplier_name",
"fieldtype": "Data",
"in_list_view": 0,
"label": "Supplier Name",
"permlevel": 0,
"allow_on_submit": 0,
"depends_on": "eval:!doc.customer && !doc.sales_partner",
"fieldname": "supplier_name",
"fieldtype": "Data",
"in_list_view": 0,
"label": "Supplier Name",
"permlevel": 0,
"read_only": 1
},
},
{
"depends_on": "eval:!doc.customer && !doc.supplier",
"fieldname": "sales_partner",
"fieldtype": "Link",
"label": "Sales Partner",
"options": "Sales Partner",
"depends_on": "eval:!doc.customer && !doc.supplier",
"fieldname": "sales_partner",
"fieldtype": "Link",
"label": "Sales Partner",
"options": "Sales Partner",
"permlevel": 0
},
},
{
"default": "0",
"depends_on": "eval:(doc.customer || doc.supplier || doc.sales_partner)",
"fieldname": "is_primary_contact",
"fieldtype": "Check",
"label": "Is Primary Contact",
"oldfieldname": "is_primary_contact",
"oldfieldtype": "Select",
"default": "0",
"depends_on": "eval:(doc.customer || doc.supplier || doc.sales_partner)",
"fieldname": "is_primary_contact",
"fieldtype": "Check",
"label": "Is Primary Contact",
"oldfieldname": "is_primary_contact",
"oldfieldtype": "Select",
"permlevel": 0
},
},
{
"fieldname": "more_info",
"fieldtype": "Section Break",
"label": "More Info",
"options": "icon-file-text",
"fieldname": "more_info",
"fieldtype": "Section Break",
"label": "More Info",
"options": "icon-file-text",
"permlevel": 0
},
},
{
"fieldname": "mobile_no",
"fieldtype": "Data",
"label": "Mobile No",
"oldfieldname": "mobile_no",
"oldfieldtype": "Data",
"fieldname": "mobile_no",
"fieldtype": "Data",
"label": "Mobile No",
"oldfieldname": "mobile_no",
"oldfieldtype": "Data",
"permlevel": 0
},
},
{
"description": "Enter department to which this Contact belongs",
"fieldname": "department",
"fieldtype": "Data",
"label": "Department",
"options": "Suggest",
"description": "Enter department to which this Contact belongs",
"fieldname": "department",
"fieldtype": "Data",
"label": "Department",
"permlevel": 0
},
},
{
"description": "Enter designation of this Contact",
"fieldname": "designation",
"fieldtype": "Data",
"label": "Designation",
"options": "Suggest",
"description": "Enter designation of this Contact",
"fieldname": "designation",
"fieldtype": "Data",
"label": "Designation",
"permlevel": 0
},
},
{
"fieldname": "unsubscribed",
"fieldtype": "Check",
"label": "Unsubscribed",
"fieldname": "unsubscribed",
"fieldtype": "Check",
"label": "Unsubscribed",
"permlevel": 0
}
],
"icon": "icon-user",
"idx": 1,
"in_create": 0,
"in_dialog": 0,
"modified": "2014-09-11 18:53:17.311624",
"modified_by": "Administrator",
"module": "Utilities",
"name": "Contact",
"owner": "Administrator",
],
"icon": "icon-user",
"idx": 1,
"in_create": 0,
"in_dialog": 0,
"modified": "2014-09-11 18:53:17.311624",
"modified_by": "Administrator",
"module": "Utilities",
"name": "Contact",
"owner": "Administrator",
"permissions": [
{
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"submit": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"submit": 0,
"write": 1
},
},
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Sales Master Manager",
"submit": 0,
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Sales Master Manager",
"submit": 0,
"write": 1
},
},
{
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Purchase Master Manager",
"submit": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Purchase Master Manager",
"submit": 0,
"write": 1
},
},
{
"cancel": 0,
"create": 1,
"delete": 0,
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Sales Manager",
"submit": 0,
"cancel": 0,
"create": 1,
"delete": 0,
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Sales Manager",
"submit": 0,
"write": 1
},
},
{
"cancel": 0,
"create": 1,
"delete": 0,
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Purchase Manager",
"submit": 0,
"cancel": 0,
"create": 1,
"delete": 0,
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Purchase Manager",
"submit": 0,
"write": 1
},
},
{
"cancel": 0,
"create": 1,
"delete": 0,
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Maintenance Manager",
"submit": 0,
"cancel": 0,
"create": 1,
"delete": 0,
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Maintenance Manager",
"submit": 0,
"write": 1
},
},
{
"cancel": 0,
"create": 1,
"delete": 0,
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts Manager",
"submit": 0,
"cancel": 0,
"create": 1,
"delete": 0,
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts Manager",
"submit": 0,
"write": 1
},
},
{
"cancel": 0,
"create": 1,
"delete": 0,
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Sales User",
"submit": 0,
"cancel": 0,
"create": 1,
"delete": 0,
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Sales User",
"submit": 0,
"write": 1
},
},
{
"cancel": 0,
"create": 1,
"delete": 0,
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Purchase User",
"submit": 0,
"cancel": 0,
"create": 1,
"delete": 0,
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Purchase User",
"submit": 0,
"write": 1
},
},
{
"cancel": 0,
"create": 1,
"delete": 0,
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Maintenance User",
"submit": 0,
"cancel": 0,
"create": 1,
"delete": 0,
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Maintenance User",
"submit": 0,
"write": 1
},
},
{
"cancel": 0,
"create": 1,
"delete": 0,
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts User",
"submit": 0,
"cancel": 0,
"create": 1,
"delete": 0,
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts User",
"submit": 0,
"write": 1
},
},
{
"amend": 0,
"cancel": 0,
"create": 0,
"delete": 0,
"match": "",
"permlevel": 1,
"read": 1,
"report": 1,
"role": "All",
"amend": 0,
"cancel": 0,
"create": 0,
"delete": 0,
"match": "",
"permlevel": 1,
"read": 1,
"report": 1,
"role": "All",
"submit": 0
}
]
}
}