Merge branch 'develop' into blanket_order_in_selling_module_page
This commit is contained in:
commit
dbf9251324
@ -42,15 +42,14 @@ frappe.ui.form.on('Account', {
|
|||||||
// show / hide convert buttons
|
// show / hide convert buttons
|
||||||
frm.trigger('add_toolbar_buttons');
|
frm.trigger('add_toolbar_buttons');
|
||||||
}
|
}
|
||||||
frm.add_custom_button(__('Update Account Name / Number'), function () {
|
if (frm.has_perm('write')) {
|
||||||
frm.trigger("update_account_number");
|
frm.add_custom_button(__('Update Account Name / Number'), function () {
|
||||||
});
|
frm.trigger("update_account_number");
|
||||||
}
|
});
|
||||||
|
frm.add_custom_button(__('Merge Account'), function () {
|
||||||
if(!frm.doc.__islocal) {
|
frm.trigger("merge_account");
|
||||||
frm.add_custom_button(__('Merge Account'), function () {
|
});
|
||||||
frm.trigger("merge_account");
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
account_type: function (frm) {
|
account_type: function (frm) {
|
||||||
|
@ -268,7 +268,7 @@ def update_account_number(name, account_name, account_number=None):
|
|||||||
|
|
||||||
new_name = get_account_autoname(account_number, account_name, account.company)
|
new_name = get_account_autoname(account_number, account_name, account.company)
|
||||||
if name != new_name:
|
if name != new_name:
|
||||||
frappe.rename_doc("Account", name, new_name, ignore_permissions=1)
|
frappe.rename_doc("Account", name, new_name, force=1)
|
||||||
return new_name
|
return new_name
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
@ -287,7 +287,7 @@ def merge_account(old, new, is_group, root_type, company):
|
|||||||
frappe.db.set_value("Account", new, "parent_account",
|
frappe.db.set_value("Account", new, "parent_account",
|
||||||
frappe.db.get_value("Account", old, "parent_account"))
|
frappe.db.get_value("Account", old, "parent_account"))
|
||||||
|
|
||||||
frappe.rename_doc("Account", old, new, merge=1, ignore_permissions=1)
|
frappe.rename_doc("Account", old, new, merge=1, force=1)
|
||||||
|
|
||||||
return new
|
return new
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -23,6 +23,12 @@ frappe.query_reports["Gross Profit"] = {
|
|||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"default": frappe.defaults.get_user_default("year_end_date")
|
"default": frappe.defaults.get_user_default("year_end_date")
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"sales_invoice",
|
||||||
|
"label": __("Sales Invoice"),
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Sales Invoice"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"fieldname":"group_by",
|
"fieldname":"group_by",
|
||||||
"label": __("Group By"),
|
"label": __("Group By"),
|
||||||
|
@ -302,6 +302,12 @@ class GrossProfitGenerator(object):
|
|||||||
sales_person_cols = ""
|
sales_person_cols = ""
|
||||||
sales_team_table = ""
|
sales_team_table = ""
|
||||||
|
|
||||||
|
if self.filters.get("sales_invoice"):
|
||||||
|
conditions += " and `tabSales Invoice`.name = %(sales_invoice)s"
|
||||||
|
|
||||||
|
if self.filters.get("item_code"):
|
||||||
|
conditions += " and `tabSales Invoice Item`.item_code = %(item_code)s"
|
||||||
|
|
||||||
self.si_list = frappe.db.sql("""
|
self.si_list = frappe.db.sql("""
|
||||||
select
|
select
|
||||||
`tabSales Invoice Item`.parenttype, `tabSales Invoice Item`.parent,
|
`tabSales Invoice Item`.parenttype, `tabSales Invoice Item`.parent,
|
||||||
|
@ -4,6 +4,12 @@
|
|||||||
|
|
||||||
frappe.query_reports["Inactive Sales Items"] = {
|
frappe.query_reports["Inactive Sales Items"] = {
|
||||||
"filters": [
|
"filters": [
|
||||||
|
{
|
||||||
|
fieldname: "territory",
|
||||||
|
label: __("Territory"),
|
||||||
|
fieldtype: "Link",
|
||||||
|
options: "Territory"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
fieldname: "item",
|
fieldname: "item",
|
||||||
label: __("Item"),
|
label: __("Item"),
|
||||||
|
@ -7,13 +7,11 @@ from frappe.utils import getdate, add_days, today, cint
|
|||||||
from frappe import _
|
from frappe import _
|
||||||
|
|
||||||
def execute(filters=None):
|
def execute(filters=None):
|
||||||
|
|
||||||
columns = get_columns()
|
columns = get_columns()
|
||||||
data = get_data(filters)
|
data = get_data(filters)
|
||||||
return columns, data
|
return columns, data
|
||||||
|
|
||||||
def get_columns():
|
def get_columns():
|
||||||
|
|
||||||
columns = [
|
columns = [
|
||||||
{
|
{
|
||||||
"fieldname": "territory",
|
"fieldname": "territory",
|
||||||
@ -74,36 +72,37 @@ def get_columns():
|
|||||||
|
|
||||||
|
|
||||||
def get_data(filters):
|
def get_data(filters):
|
||||||
|
|
||||||
data = []
|
data = []
|
||||||
items = get_items(filters)
|
items = get_items(filters)
|
||||||
|
territories = get_territories(filters)
|
||||||
sales_invoice_data = get_sales_details(filters)
|
sales_invoice_data = get_sales_details(filters)
|
||||||
|
|
||||||
for item in items:
|
for territory in territories:
|
||||||
row = {
|
for item in items:
|
||||||
|
row = {
|
||||||
|
"territory": territory.name,
|
||||||
"item_group": item.item_group,
|
"item_group": item.item_group,
|
||||||
"item": item.name,
|
"item": item.name,
|
||||||
"item_name": item.item_name
|
"item_name": item.item_name
|
||||||
}
|
}
|
||||||
|
|
||||||
if sales_invoice_data.get(item.name):
|
if sales_invoice_data.get((territory.name,item.name)):
|
||||||
item_obj = sales_invoice_data[item.name]
|
item_obj = sales_invoice_data[(territory.name,item.name)]
|
||||||
if item_obj.days_since_last_order > cint(filters['days']):
|
if item_obj.days_since_last_order > cint(filters['days']):
|
||||||
row.update({
|
row.update({
|
||||||
"territory": item_obj.territory,
|
"territory": item_obj.territory,
|
||||||
"customer": item_obj.customer,
|
"customer": item_obj.customer,
|
||||||
"last_order_date": item_obj.last_order_date,
|
"last_order_date": item_obj.last_order_date,
|
||||||
"qty": item_obj.qty,
|
"qty": item_obj.qty,
|
||||||
"days_since_last_order": item_obj.days_since_last_order
|
"days_since_last_order": item_obj.days_since_last_order
|
||||||
})
|
})
|
||||||
|
|
||||||
data.append(row)
|
data.append(row)
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
def get_sales_details(filters):
|
def get_sales_details(filters):
|
||||||
|
|
||||||
data = []
|
data = []
|
||||||
item_details_map = {}
|
item_details_map = {}
|
||||||
|
|
||||||
@ -118,12 +117,21 @@ def get_sales_details(filters):
|
|||||||
.format(date_field = date_field, doctype = filters['based_on']), as_dict=1)
|
.format(date_field = date_field, doctype = filters['based_on']), as_dict=1)
|
||||||
|
|
||||||
for d in sales_data:
|
for d in sales_data:
|
||||||
item_details_map.setdefault(d.item_name, d)
|
item_details_map.setdefault((d.territory,d.item_name), d)
|
||||||
|
|
||||||
return item_details_map
|
return item_details_map
|
||||||
|
|
||||||
def get_items(filters):
|
def get_territories(filters):
|
||||||
|
|
||||||
|
filter_dict = {}
|
||||||
|
if filters.get("territory"):
|
||||||
|
filter_dict.update({'name': filters['territory']})
|
||||||
|
|
||||||
|
territories = frappe.get_all("Territory", fields=["name"], filters=filter_dict)
|
||||||
|
|
||||||
|
return territories
|
||||||
|
|
||||||
|
def get_items(filters):
|
||||||
filters_dict = {
|
filters_dict = {
|
||||||
"disabled": 0,
|
"disabled": 0,
|
||||||
"is_stock_item": 1
|
"is_stock_item": 1
|
||||||
|
@ -135,3 +135,25 @@ def get_appropriate_company(filters):
|
|||||||
company = get_default_company()
|
company = get_default_company()
|
||||||
|
|
||||||
return company
|
return company
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def get_invoiced_item_gross_margin(sales_invoice=None, item_code=None, company=None, with_item_data=False):
|
||||||
|
from erpnext.accounts.report.gross_profit.gross_profit import GrossProfitGenerator
|
||||||
|
|
||||||
|
sales_invoice = sales_invoice or frappe.form_dict.get('sales_invoice')
|
||||||
|
item_code = item_code or frappe.form_dict.get('item_code')
|
||||||
|
company = company or frappe.get_cached_value("Sales Invoice", sales_invoice, 'company')
|
||||||
|
|
||||||
|
filters = {
|
||||||
|
'sales_invoice': sales_invoice,
|
||||||
|
'item_code': item_code,
|
||||||
|
'company': company,
|
||||||
|
'group_by': 'Invoice'
|
||||||
|
}
|
||||||
|
|
||||||
|
gross_profit_data = GrossProfitGenerator(filters)
|
||||||
|
result = gross_profit_data.grouped_data
|
||||||
|
if not with_item_data:
|
||||||
|
result = sum([d.gross_profit for d in result])
|
||||||
|
|
||||||
|
return result
|
||||||
|
@ -369,7 +369,9 @@ def make_purchase_receipt(source_name, target_doc=None):
|
|||||||
"field_map": {
|
"field_map": {
|
||||||
"name": "purchase_order_item",
|
"name": "purchase_order_item",
|
||||||
"parent": "purchase_order",
|
"parent": "purchase_order",
|
||||||
"bom": "bom"
|
"bom": "bom",
|
||||||
|
"material_request": "material_request",
|
||||||
|
"material_request_item": "material_request_item"
|
||||||
},
|
},
|
||||||
"postprocess": update_item,
|
"postprocess": update_item,
|
||||||
"condition": lambda doc: abs(doc.received_qty) < abs(doc.qty) and doc.delivered_by_supplier!=1
|
"condition": lambda doc: abs(doc.received_qty) < abs(doc.qty) and doc.delivered_by_supplier!=1
|
||||||
@ -403,7 +405,7 @@ def make_purchase_invoice(source_name, target_doc=None):
|
|||||||
or item.get("buying_cost_center")
|
or item.get("buying_cost_center")
|
||||||
or item_group.get("buying_cost_center"))
|
or item_group.get("buying_cost_center"))
|
||||||
|
|
||||||
doc = get_mapped_doc("Purchase Order", source_name, {
|
fields = {
|
||||||
"Purchase Order": {
|
"Purchase Order": {
|
||||||
"doctype": "Purchase Invoice",
|
"doctype": "Purchase Invoice",
|
||||||
"field_map": {
|
"field_map": {
|
||||||
@ -426,8 +428,16 @@ def make_purchase_invoice(source_name, target_doc=None):
|
|||||||
"Purchase Taxes and Charges": {
|
"Purchase Taxes and Charges": {
|
||||||
"doctype": "Purchase Taxes and Charges",
|
"doctype": "Purchase Taxes and Charges",
|
||||||
"add_if_empty": True
|
"add_if_empty": True
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if frappe.get_single("Accounts Settings").automatically_fetch_payment_terms == 1:
|
||||||
|
fields["Payment Schedule"] = {
|
||||||
|
"doctype": "Payment Schedule",
|
||||||
|
"add_if_empty": True
|
||||||
}
|
}
|
||||||
}, target_doc, postprocess)
|
|
||||||
|
doc = get_mapped_doc("Purchase Order", source_name, fields, target_doc, postprocess)
|
||||||
|
|
||||||
return doc
|
return doc
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ import unittest
|
|||||||
import frappe
|
import frappe
|
||||||
import frappe.defaults
|
import frappe.defaults
|
||||||
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
|
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
|
||||||
from frappe.utils import flt, add_days, nowdate
|
from frappe.utils import flt, add_days, nowdate, getdate
|
||||||
from erpnext.stock.doctype.item.test_item import make_item
|
from erpnext.stock.doctype.item.test_item import make_item
|
||||||
from erpnext.buying.doctype.purchase_order.purchase_order import (make_purchase_receipt, make_purchase_invoice, make_rm_stock_entry as make_subcontract_transfer_entry)
|
from erpnext.buying.doctype.purchase_order.purchase_order import (make_purchase_receipt, make_purchase_invoice, make_rm_stock_entry as make_subcontract_transfer_entry)
|
||||||
from erpnext.stock.doctype.material_request.test_material_request import make_material_request
|
from erpnext.stock.doctype.material_request.test_material_request import make_material_request
|
||||||
@ -142,9 +142,9 @@ class TestPurchaseOrder(unittest.TestCase):
|
|||||||
po.submit()
|
po.submit()
|
||||||
|
|
||||||
self.assertEqual(po.payment_schedule[0].payment_amount, 2500.0)
|
self.assertEqual(po.payment_schedule[0].payment_amount, 2500.0)
|
||||||
self.assertEqual(po.payment_schedule[0].due_date, po.transaction_date)
|
self.assertEqual(getdate(po.payment_schedule[0].due_date), getdate(po.transaction_date))
|
||||||
self.assertEqual(po.payment_schedule[1].payment_amount, 2500.0)
|
self.assertEqual(po.payment_schedule[1].payment_amount, 2500.0)
|
||||||
self.assertEqual(po.payment_schedule[1].due_date, add_days(po.transaction_date, 30))
|
self.assertEqual(getdate(po.payment_schedule[1].due_date), add_days(getdate(po.transaction_date), 30))
|
||||||
pi = make_purchase_invoice(po.name)
|
pi = make_purchase_invoice(po.name)
|
||||||
pi.save()
|
pi.save()
|
||||||
|
|
||||||
@ -152,9 +152,9 @@ class TestPurchaseOrder(unittest.TestCase):
|
|||||||
self.assertEqual(len(pi.get("items", [])), 1)
|
self.assertEqual(len(pi.get("items", [])), 1)
|
||||||
|
|
||||||
self.assertEqual(pi.payment_schedule[0].payment_amount, 2500.0)
|
self.assertEqual(pi.payment_schedule[0].payment_amount, 2500.0)
|
||||||
self.assertEqual(pi.payment_schedule[0].due_date, po.transaction_date)
|
self.assertEqual(getdate(pi.payment_schedule[0].due_date), getdate(po.transaction_date))
|
||||||
self.assertEqual(pi.payment_schedule[1].payment_amount, 2500.0)
|
self.assertEqual(pi.payment_schedule[1].payment_amount, 2500.0)
|
||||||
self.assertEqual(pi.payment_schedule[1].due_date, add_days(po.transaction_date, 30))
|
self.assertEqual(getdate(pi.payment_schedule[1].due_date), add_days(getdate(po.transaction_date), 30))
|
||||||
|
|
||||||
def test_subcontracting(self):
|
def test_subcontracting(self):
|
||||||
po = create_purchase_order(item_code="_Test FG Item", is_subcontracted="Yes")
|
po = create_purchase_order(item_code="_Test FG Item", is_subcontracted="Yes")
|
||||||
@ -303,6 +303,10 @@ class TestPurchaseOrder(unittest.TestCase):
|
|||||||
make_stock_entry(target="_Test Warehouse - _TC", qty=10, basic_rate=100)
|
make_stock_entry(target="_Test Warehouse - _TC", qty=10, basic_rate=100)
|
||||||
make_stock_entry(target="_Test Warehouse - _TC", item_code="_Test Item Home Desktop 100",
|
make_stock_entry(target="_Test Warehouse - _TC", item_code="_Test Item Home Desktop 100",
|
||||||
qty=20, basic_rate=100)
|
qty=20, basic_rate=100)
|
||||||
|
make_stock_entry(target="_Test Warehouse 1 - _TC", item_code="_Test Item",
|
||||||
|
qty=30, basic_rate=100)
|
||||||
|
make_stock_entry(target="_Test Warehouse 1 - _TC", item_code="_Test Item Home Desktop 100",
|
||||||
|
qty=30, basic_rate=100)
|
||||||
|
|
||||||
bin1 = frappe.db.get_value("Bin",
|
bin1 = frappe.db.get_value("Bin",
|
||||||
filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
|
filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
|
||||||
@ -349,6 +353,11 @@ class TestPurchaseOrder(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertEquals(bin5.reserved_qty_for_sub_contract, bin2.reserved_qty_for_sub_contract - 6)
|
self.assertEquals(bin5.reserved_qty_for_sub_contract, bin2.reserved_qty_for_sub_contract - 6)
|
||||||
|
|
||||||
|
make_stock_entry(target="_Test Warehouse 1 - _TC", item_code="_Test Item",
|
||||||
|
qty=40, basic_rate=100)
|
||||||
|
make_stock_entry(target="_Test Warehouse 1 - _TC", item_code="_Test Item Home Desktop 100",
|
||||||
|
qty=40, basic_rate=100)
|
||||||
|
|
||||||
# make Purchase Receipt against PO
|
# make Purchase Receipt against PO
|
||||||
pr = make_purchase_receipt(po.name)
|
pr = make_purchase_receipt(po.name)
|
||||||
pr.supplier_warehouse = "_Test Warehouse 1 - _TC"
|
pr.supplier_warehouse = "_Test Warehouse 1 - _TC"
|
||||||
|
@ -0,0 +1,39 @@
|
|||||||
|
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
// For license information, please see license.txt
|
||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
|
frappe.query_reports["Procurement Tracker"] = {
|
||||||
|
"filters": [
|
||||||
|
{
|
||||||
|
fieldname: "company",
|
||||||
|
label: __("Company"),
|
||||||
|
fieldtype: "Link",
|
||||||
|
options: "Company",
|
||||||
|
default: frappe.defaults.get_user_default("Company"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldname: "cost_center",
|
||||||
|
label: __("Cost Center"),
|
||||||
|
fieldtype: "Link",
|
||||||
|
options: "Cost Center",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldname: "project",
|
||||||
|
label: __("Project"),
|
||||||
|
fieldtype: "Link",
|
||||||
|
options: "Project",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldname: "from_date",
|
||||||
|
label: __("From Date"),
|
||||||
|
fieldtype: "Date",
|
||||||
|
default: frappe.defaults.get_user_default("year_start_date"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldname:"to_date",
|
||||||
|
label: __("To Date"),
|
||||||
|
fieldtype: "Date",
|
||||||
|
default: frappe.defaults.get_user_default("year_end_date"),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"add_total_row": 1,
|
||||||
|
"creation": "2019-03-29 17:05:45.196949",
|
||||||
|
"disable_prepared_report": 0,
|
||||||
|
"disabled": 0,
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Report",
|
||||||
|
"idx": 0,
|
||||||
|
"is_standard": "Yes",
|
||||||
|
"modified": "2019-03-29 17:18:06.678728",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Buying",
|
||||||
|
"name": "Procurement Tracker",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"prepared_report": 0,
|
||||||
|
"ref_doctype": "Purchase Order",
|
||||||
|
"report_name": "Procurement Tracker",
|
||||||
|
"report_type": "Script Report",
|
||||||
|
"roles": []
|
||||||
|
}
|
282
erpnext/buying/report/procurement_tracker/procurement_tracker.py
Normal file
282
erpnext/buying/report/procurement_tracker/procurement_tracker.py
Normal file
@ -0,0 +1,282 @@
|
|||||||
|
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
import frappe
|
||||||
|
from frappe import _
|
||||||
|
|
||||||
|
def execute(filters=None):
|
||||||
|
columns = get_columns(filters)
|
||||||
|
data = get_data(filters)
|
||||||
|
return columns, data
|
||||||
|
|
||||||
|
def get_columns(filters):
|
||||||
|
columns = [
|
||||||
|
{
|
||||||
|
"label": _("Material Request Date"),
|
||||||
|
"fieldname": "material_request_date",
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"width": 140
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Material Request No"),
|
||||||
|
"options": "Material Request",
|
||||||
|
"fieldname": "material_request_no",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"width": 140
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Cost Center"),
|
||||||
|
"options": "Cost Center",
|
||||||
|
"fieldname": "cost_center",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"width": 140
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Project"),
|
||||||
|
"options": "Project",
|
||||||
|
"fieldname": "project",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"width": 140
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Requesting Site"),
|
||||||
|
"options": "Warehouse",
|
||||||
|
"fieldname": "requesting_site",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"width": 140
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Requestor"),
|
||||||
|
"options": "Employee",
|
||||||
|
"fieldname": "requestor",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"width": 140
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Description"),
|
||||||
|
"fieldname": "description",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"width": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Quantity"),
|
||||||
|
"fieldname": "quantity",
|
||||||
|
"fieldtype": "Int",
|
||||||
|
"width": 140
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Unit of Measure"),
|
||||||
|
"options": "UOM",
|
||||||
|
"fieldname": "unit_of_measurement",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"width": 140
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Status"),
|
||||||
|
"fieldname": "status",
|
||||||
|
"fieldtype": "data",
|
||||||
|
"width": 140
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Purchase Order Date"),
|
||||||
|
"fieldname": "purchase_order_date",
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"width": 140
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Purchase Order"),
|
||||||
|
"options": "Purchase Order",
|
||||||
|
"fieldname": "purchase_order",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"width": 140
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Supplier"),
|
||||||
|
"options": "Supplier",
|
||||||
|
"fieldname": "supplier",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"width": 140
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Estimated Cost"),
|
||||||
|
"fieldname": "estimated_cost",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"width": 140
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Actual Cost"),
|
||||||
|
"fieldname": "actual_cost",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"width": 140
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Purchase Order Amount"),
|
||||||
|
"fieldname": "purchase_order_amt",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"width": 140
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Purchase Order Amount(Company Currency)"),
|
||||||
|
"fieldname": "purchase_order_amt_usd",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"width": 140
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Expected Delivery Date"),
|
||||||
|
"fieldname": "expected_delivery_date",
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"width": 140
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Actual Delivery Date"),
|
||||||
|
"fieldname": "actual_delivery_date",
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"width": 140
|
||||||
|
},
|
||||||
|
]
|
||||||
|
return columns
|
||||||
|
|
||||||
|
def get_conditions(filters):
|
||||||
|
conditions = ""
|
||||||
|
|
||||||
|
if filters.get("company"):
|
||||||
|
conditions += " AND company='%s'"% filters.get('company')
|
||||||
|
|
||||||
|
if filters.get("cost_center") or filters.get("project"):
|
||||||
|
conditions += """
|
||||||
|
AND (cost_center='%s'
|
||||||
|
OR project='%s')
|
||||||
|
"""% (filters.get('cost_center'), filters.get('project'))
|
||||||
|
|
||||||
|
if filters.get("from_date"):
|
||||||
|
conditions += "AND transaction_date>=%s"% filters.get('from_date')
|
||||||
|
|
||||||
|
if filters.get("to_date"):
|
||||||
|
conditions += "AND transaction_date<=%s"% filters.get('to_date')
|
||||||
|
return conditions
|
||||||
|
|
||||||
|
def get_data(filters):
|
||||||
|
conditions = get_conditions(filters)
|
||||||
|
purchase_order_entry = get_po_entries(conditions)
|
||||||
|
mr_records, procurement_record_against_mr = get_mapped_mr_details(conditions)
|
||||||
|
pr_records = get_mapped_pr_records()
|
||||||
|
pi_records = get_mapped_pi_records()
|
||||||
|
print(pi_records)
|
||||||
|
|
||||||
|
procurement_record=[]
|
||||||
|
if procurement_record_against_mr:
|
||||||
|
procurement_record += procurement_record_against_mr
|
||||||
|
for po in purchase_order_entry:
|
||||||
|
# fetch material records linked to the purchase order item
|
||||||
|
mr_record = mr_records.get(po.material_request_item, [{}])[0]
|
||||||
|
procurement_detail = {
|
||||||
|
"material_request_date": mr_record.get('transaction_date'),
|
||||||
|
"cost_center": po.cost_center,
|
||||||
|
"project": po.project,
|
||||||
|
"requesting_site": po.warehouse,
|
||||||
|
"requestor": po.owner,
|
||||||
|
"material_request_no": po.material_request,
|
||||||
|
"description": po.description,
|
||||||
|
"quantity": po.qty,
|
||||||
|
"unit_of_measurement": po.stock_uom,
|
||||||
|
"status": po.status,
|
||||||
|
"purchase_order_date": po.transaction_date,
|
||||||
|
"purchase_order": po.parent,
|
||||||
|
"supplier": po.supplier,
|
||||||
|
"estimated_cost": mr_record.get('amount'),
|
||||||
|
"actual_cost": pi_records.get(po.name),
|
||||||
|
"purchase_order_amt": po.amount,
|
||||||
|
"purchase_order_amt_in_company_currency": po.base_amount,
|
||||||
|
"expected_delivery_date": po.schedule_date,
|
||||||
|
"actual_delivery_date": pr_records.get(po.name)
|
||||||
|
}
|
||||||
|
procurement_record.append(procurement_detail)
|
||||||
|
return procurement_record
|
||||||
|
|
||||||
|
def get_mapped_mr_details(conditions):
|
||||||
|
mr_records = {}
|
||||||
|
mr_details = frappe.db.sql("""
|
||||||
|
SELECT
|
||||||
|
mr.transaction_date,
|
||||||
|
mr.per_ordered,
|
||||||
|
mr_item.name,
|
||||||
|
mr_item.parent,
|
||||||
|
mr_item.amount
|
||||||
|
FROM `tabMaterial Request` mr, `tabMaterial Request Item` mr_item
|
||||||
|
WHERE
|
||||||
|
mr.per_ordered>=0
|
||||||
|
AND mr.name=mr_item.parent
|
||||||
|
AND mr.docstatus=1
|
||||||
|
{conditions}
|
||||||
|
""".format(conditions=conditions), as_dict=1) #nosec
|
||||||
|
|
||||||
|
procurement_record_against_mr = []
|
||||||
|
for record in mr_details:
|
||||||
|
if record.per_ordered:
|
||||||
|
mr_records.setdefault(record.name, []).append(frappe._dict(record))
|
||||||
|
else:
|
||||||
|
procurement_record_details = dict(
|
||||||
|
material_request_date=record.transaction_date,
|
||||||
|
material_request_no=record.parent,
|
||||||
|
estimated_cost=record.amount
|
||||||
|
)
|
||||||
|
procurement_record_against_mr.append(procurement_record_details)
|
||||||
|
return mr_records, procurement_record_against_mr
|
||||||
|
|
||||||
|
def get_mapped_pi_records():
|
||||||
|
return frappe._dict(frappe.db.sql("""
|
||||||
|
SELECT
|
||||||
|
pi_item.po_detail,
|
||||||
|
pi_item.base_amount
|
||||||
|
FROM `tabPurchase Invoice Item` as pi_item
|
||||||
|
INNER JOIN `tabPurchase Order` as po
|
||||||
|
ON pi_item.`purchase_order` = po.`name`
|
||||||
|
WHERE
|
||||||
|
pi_item.docstatus = 1
|
||||||
|
AND po.status not in ("Closed","Completed","Cancelled")
|
||||||
|
AND pi_item.po_detail IS NOT NULL
|
||||||
|
"""))
|
||||||
|
|
||||||
|
def get_mapped_pr_records():
|
||||||
|
return frappe._dict(frappe.db.sql("""
|
||||||
|
SELECT
|
||||||
|
pr_item.purchase_order_item,
|
||||||
|
pr.posting_date
|
||||||
|
FROM `tabPurchase Receipt` pr, `tabPurchase Receipt Item` pr_item
|
||||||
|
WHERE
|
||||||
|
pr.docstatus=1
|
||||||
|
AND pr.name=pr_item.parent
|
||||||
|
AND pr_item.purchase_order_item IS NOT NULL
|
||||||
|
AND pr.status not in ("Closed","Completed","Cancelled")
|
||||||
|
"""))
|
||||||
|
|
||||||
|
def get_po_entries(conditions):
|
||||||
|
return frappe.db.sql("""
|
||||||
|
SELECT
|
||||||
|
po_item.name,
|
||||||
|
po_item.parent,
|
||||||
|
po_item.cost_center,
|
||||||
|
po_item.project,
|
||||||
|
po_item.warehouse,
|
||||||
|
po_item.material_request,
|
||||||
|
po_item.material_request_item,
|
||||||
|
po_item.description,
|
||||||
|
po_item.stock_uom,
|
||||||
|
po_item.qty,
|
||||||
|
po_item.amount,
|
||||||
|
po_item.base_amount,
|
||||||
|
po_item.schedule_date,
|
||||||
|
po.transaction_date,
|
||||||
|
po.supplier,
|
||||||
|
po.status,
|
||||||
|
po.owner
|
||||||
|
FROM `tabPurchase Order` po, `tabPurchase Order Item` po_item
|
||||||
|
WHERE
|
||||||
|
po.docstatus = 1
|
||||||
|
AND po.name = po_item.parent
|
||||||
|
AND po.status not in ("Closed","Completed","Cancelled")
|
||||||
|
{conditions}
|
||||||
|
GROUP BY
|
||||||
|
po.name,po_item.item_code
|
||||||
|
""".format(conditions=conditions), as_dict=1) #nosec
|
@ -0,0 +1,67 @@
|
|||||||
|
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
import unittest
|
||||||
|
from datetime import datetime
|
||||||
|
import frappe
|
||||||
|
from erpnext.buying.report.procurement_tracker.procurement_tracker import execute
|
||||||
|
from erpnext.stock.doctype.material_request.test_material_request import make_material_request
|
||||||
|
from erpnext.stock.doctype.material_request.material_request import make_purchase_order
|
||||||
|
from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_receipt
|
||||||
|
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
|
||||||
|
|
||||||
|
class TestProcurementTracker(unittest.TestCase):
|
||||||
|
def test_result_for_procurement_tracker(self):
|
||||||
|
filters = {
|
||||||
|
'company': '_Test Procurement Company',
|
||||||
|
'cost_center': '_Test Cost Center - _TC'
|
||||||
|
}
|
||||||
|
expected_data = self.generate_expected_data()
|
||||||
|
report = execute(filters)
|
||||||
|
|
||||||
|
length = len(report[1])
|
||||||
|
self.assertEqual(expected_data, report[1][length-1])
|
||||||
|
|
||||||
|
def generate_expected_data(self):
|
||||||
|
if not frappe.db.exists("Company", "_Test Procurement Company"):
|
||||||
|
frappe.get_doc(dict(
|
||||||
|
doctype="Company",
|
||||||
|
company_name="_Test Procurement Company",
|
||||||
|
abbr="_TPC",
|
||||||
|
default_currency="INR",
|
||||||
|
country="India"
|
||||||
|
)).insert()
|
||||||
|
warehouse = create_warehouse("_Test Procurement Warehouse", company="_Test Procurement Company")
|
||||||
|
mr = make_material_request(company="_Test Procurement Company", warehouse=warehouse)
|
||||||
|
po = make_purchase_order(mr.name)
|
||||||
|
po.supplier = "_Test Supplier"
|
||||||
|
po.get("items")[0].cost_center = "_Test Cost Center - _TC"
|
||||||
|
po.submit()
|
||||||
|
pr = make_purchase_receipt(po.name)
|
||||||
|
pr.submit()
|
||||||
|
frappe.db.commit()
|
||||||
|
date_obj = datetime.date(datetime.now())
|
||||||
|
|
||||||
|
expected_data = {
|
||||||
|
"material_request_date": date_obj,
|
||||||
|
"cost_center": "_Test Cost Center - _TC",
|
||||||
|
"project": None,
|
||||||
|
"requesting_site": "_Test Procurement Warehouse - _TPC",
|
||||||
|
"requestor": "Administrator",
|
||||||
|
"material_request_no": mr.name,
|
||||||
|
"description": '_Test Item 1',
|
||||||
|
"quantity": 10.0,
|
||||||
|
"unit_of_measurement": "_Test UOM",
|
||||||
|
"status": "To Bill",
|
||||||
|
"purchase_order_date": date_obj,
|
||||||
|
"purchase_order": po.name,
|
||||||
|
"supplier": "_Test Supplier",
|
||||||
|
"estimated_cost": 0.0,
|
||||||
|
"actual_cost": None,
|
||||||
|
"purchase_order_amt": 5000.0,
|
||||||
|
"purchase_order_amt_in_company_currency": 300000.0,
|
||||||
|
"expected_delivery_date": date_obj,
|
||||||
|
"actual_delivery_date": date_obj
|
||||||
|
}
|
||||||
|
return expected_data
|
@ -124,16 +124,8 @@ def find_variant(template, args, variant_item_code=None):
|
|||||||
|
|
||||||
conditions = " or ".join(conditions)
|
conditions = " or ".join(conditions)
|
||||||
|
|
||||||
# use approximate match and shortlist possible variant matches
|
from erpnext.portal.product_configurator.utils import get_item_codes_by_attributes
|
||||||
# it is approximate because we are matching using OR condition
|
possible_variants = [i for i in get_item_codes_by_attributes(args, template) if i != variant_item_code]
|
||||||
# and it need not be exact match at this stage
|
|
||||||
# this uses a simpler query instead of using multiple exists conditions
|
|
||||||
possible_variants = frappe.db.sql_list("""select name from `tabItem` item
|
|
||||||
where variant_of=%s and exists (
|
|
||||||
select name from `tabItem Variant Attribute` iv_attribute
|
|
||||||
where iv_attribute.parent=item.name
|
|
||||||
and ({conditions}) and parent != %s
|
|
||||||
)""".format(conditions=conditions), (template, cstr(variant_item_code)))
|
|
||||||
|
|
||||||
for variant in possible_variants:
|
for variant in possible_variants:
|
||||||
variant = frappe.get_doc("Item", variant)
|
variant = frappe.get_doc("Item", variant)
|
||||||
@ -317,7 +309,7 @@ def make_variant_item_code(template_item_code, template_item_name, variant):
|
|||||||
}, as_dict=True)
|
}, as_dict=True)
|
||||||
|
|
||||||
if not item_attribute:
|
if not item_attribute:
|
||||||
return
|
continue
|
||||||
# frappe.throw(_('Invalid attribute {0} {1}').format(frappe.bold(attr.attribute),
|
# frappe.throw(_('Invalid attribute {0} {1}').format(frappe.bold(attr.attribute),
|
||||||
# frappe.bold(attr.attribute_value)), title=_('Invalid Attribute'),
|
# frappe.bold(attr.attribute_value)), title=_('Invalid Attribute'),
|
||||||
# exc=InvalidItemAttributeValueError)
|
# exc=InvalidItemAttributeValueError)
|
||||||
|
@ -96,7 +96,9 @@ status_map = {
|
|||||||
["Partially Ordered", "eval:self.status != 'Stopped' and self.per_ordered < 100 and self.per_ordered > 0 and self.docstatus == 1"],
|
["Partially Ordered", "eval:self.status != 'Stopped' and self.per_ordered < 100 and self.per_ordered > 0 and self.docstatus == 1"],
|
||||||
["Ordered", "eval:self.status != 'Stopped' and self.per_ordered == 100 and self.docstatus == 1 and self.material_request_type == 'Purchase'"],
|
["Ordered", "eval:self.status != 'Stopped' and self.per_ordered == 100 and self.docstatus == 1 and self.material_request_type == 'Purchase'"],
|
||||||
["Transferred", "eval:self.status != 'Stopped' and self.per_ordered == 100 and self.docstatus == 1 and self.material_request_type == 'Material Transfer'"],
|
["Transferred", "eval:self.status != 'Stopped' and self.per_ordered == 100 and self.docstatus == 1 and self.material_request_type == 'Material Transfer'"],
|
||||||
["Issued", "eval:self.status != 'Stopped' and self.per_ordered == 100 and self.docstatus == 1 and self.material_request_type == 'Material Issue'"]
|
["Issued", "eval:self.status != 'Stopped' and self.per_ordered == 100 and self.docstatus == 1 and self.material_request_type == 'Material Issue'"],
|
||||||
|
["Received", "eval:self.status != 'Stopped' and self.per_received == 100 and self.docstatus == 1 and self.material_request_type == 'Purchase'"],
|
||||||
|
["Partially Received", "eval:self.status != 'Stopped' and self.per_received > 0 and self.per_received < 100 and self.docstatus == 1 and self.material_request_type == 'Purchase'"]
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,29 +5,32 @@ frappe.provide("erpnext");
|
|||||||
cur_frm.email_field = "email_id";
|
cur_frm.email_field = "email_id";
|
||||||
|
|
||||||
erpnext.LeadController = frappe.ui.form.Controller.extend({
|
erpnext.LeadController = frappe.ui.form.Controller.extend({
|
||||||
setup: function() {
|
setup: function () {
|
||||||
this.frm.fields_dict.customer.get_query = function(doc, cdt, cdn) {
|
this.frm.fields_dict.customer.get_query = function (doc, cdt, cdn) {
|
||||||
return { query: "erpnext.controllers.queries.customer_query" } }
|
return { query: "erpnext.controllers.queries.customer_query" }
|
||||||
|
}
|
||||||
|
|
||||||
|
this.frm.toggle_reqd("lead_name", !this.frm.doc.organization_lead);
|
||||||
},
|
},
|
||||||
|
|
||||||
onload: function() {
|
onload: function () {
|
||||||
|
if (cur_frm.fields_dict.lead_owner.df.options.match(/^User/)) {
|
||||||
if(cur_frm.fields_dict.lead_owner.df.options.match(/^User/)) {
|
cur_frm.fields_dict.lead_owner.get_query = function (doc, cdt, cdn) {
|
||||||
cur_frm.fields_dict.lead_owner.get_query = function(doc, cdt, cdn) {
|
|
||||||
return { query: "frappe.core.doctype.user.user.user_query" }
|
return { query: "frappe.core.doctype.user.user.user_query" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(cur_frm.fields_dict.contact_by.df.options.match(/^User/)) {
|
if (cur_frm.fields_dict.contact_by.df.options.match(/^User/)) {
|
||||||
cur_frm.fields_dict.contact_by.get_query = function(doc, cdt, cdn) {
|
cur_frm.fields_dict.contact_by.get_query = function (doc, cdt, cdn) {
|
||||||
return { query: "frappe.core.doctype.user.user.user_query" } }
|
return { query: "frappe.core.doctype.user.user.user_query" }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
refresh: function() {
|
refresh: function () {
|
||||||
var doc = this.frm.doc;
|
var doc = this.frm.doc;
|
||||||
erpnext.toggle_naming_series();
|
erpnext.toggle_naming_series();
|
||||||
frappe.dynamic_link = {doc: doc, fieldname: 'name', doctype: 'Lead'}
|
frappe.dynamic_link = { doc: doc, fieldname: 'name', doctype: 'Lead' }
|
||||||
|
|
||||||
if(!doc.__islocal && doc.__onload && !doc.__onload.is_customer) {
|
if(!doc.__islocal && doc.__onload && !doc.__onload.is_customer) {
|
||||||
this.frm.add_custom_button(__("Customer"), this.create_customer, __('Create'));
|
this.frm.add_custom_button(__("Customer"), this.create_customer, __('Create'));
|
||||||
@ -35,49 +38,46 @@ erpnext.LeadController = frappe.ui.form.Controller.extend({
|
|||||||
this.frm.add_custom_button(__("Quotation"), this.make_quotation, __('Create'));
|
this.frm.add_custom_button(__("Quotation"), this.make_quotation, __('Create'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!this.frm.doc.__islocal) {
|
if (!this.frm.doc.__islocal) {
|
||||||
frappe.contacts.render_address_and_contact(cur_frm);
|
frappe.contacts.render_address_and_contact(cur_frm);
|
||||||
} else {
|
} else {
|
||||||
frappe.contacts.clear_address_and_contact(cur_frm);
|
frappe.contacts.clear_address_and_contact(cur_frm);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
create_customer: function() {
|
create_customer: function () {
|
||||||
frappe.model.open_mapped_doc({
|
frappe.model.open_mapped_doc({
|
||||||
method: "erpnext.crm.doctype.lead.lead.make_customer",
|
method: "erpnext.crm.doctype.lead.lead.make_customer",
|
||||||
frm: cur_frm
|
frm: cur_frm
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
create_opportunity: function() {
|
create_opportunity: function () {
|
||||||
frappe.model.open_mapped_doc({
|
frappe.model.open_mapped_doc({
|
||||||
method: "erpnext.crm.doctype.lead.lead.make_opportunity",
|
method: "erpnext.crm.doctype.lead.lead.make_opportunity",
|
||||||
frm: cur_frm
|
frm: cur_frm
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
make_quotation: function() {
|
make_quotation: function () {
|
||||||
frappe.model.open_mapped_doc({
|
frappe.model.open_mapped_doc({
|
||||||
method: "erpnext.crm.doctype.lead.lead.make_quotation",
|
method: "erpnext.crm.doctype.lead.lead.make_quotation",
|
||||||
frm: cur_frm
|
frm: cur_frm
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
organization_lead: function() {
|
organization_lead: function () {
|
||||||
if (this.frm.doc.organization_lead == 1) {
|
this.frm.toggle_reqd("lead_name", !this.frm.doc.organization_lead);
|
||||||
this.frm.set_df_property('company_name', 'reqd', 1);
|
this.frm.toggle_reqd("company_name", this.frm.doc.organization_lead);
|
||||||
} else {
|
|
||||||
this.frm.set_df_property('company_name', 'reqd', 0);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
company_name: function() {
|
company_name: function () {
|
||||||
if (this.frm.doc.organization_lead == 1) {
|
if (this.frm.doc.organization_lead == 1) {
|
||||||
this.frm.set_value("lead_name", this.frm.doc.company_name);
|
this.frm.set_value("lead_name", this.frm.doc.company_name);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
contact_date: function() {
|
contact_date: function () {
|
||||||
if (this.frm.doc.contact_date) {
|
if (this.frm.doc.contact_date) {
|
||||||
let d = moment(this.frm.doc.contact_date);
|
let d = moment(this.frm.doc.contact_date);
|
||||||
d.add(1, "hours");
|
d.add(1, "hours");
|
||||||
@ -86,4 +86,4 @@ erpnext.LeadController = frappe.ui.form.Controller.extend({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$.extend(cur_frm.cscript, new erpnext.LeadController({frm: cur_frm}));
|
$.extend(cur_frm.cscript, new erpnext.LeadController({ frm: cur_frm }));
|
||||||
|
@ -145,7 +145,7 @@
|
|||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"remember_last_selected_value": 0,
|
"remember_last_selected_value": 0,
|
||||||
"report_hide": 0,
|
"report_hide": 0,
|
||||||
"reqd": 1,
|
"reqd": 0,
|
||||||
"search_index": 1,
|
"search_index": 1,
|
||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"translatable": 0,
|
"translatable": 0,
|
||||||
@ -268,7 +268,7 @@
|
|||||||
"ignore_xss_filter": 0,
|
"ignore_xss_filter": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
"in_global_search": 0,
|
"in_global_search": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
"in_standard_filter": 0,
|
||||||
"label": "Lead Owner",
|
"label": "Lead Owner",
|
||||||
"length": 0,
|
"length": 0,
|
||||||
@ -1419,17 +1419,18 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_web_view": 0,
|
"has_web_view": 0,
|
||||||
|
"hide_heading": 0,
|
||||||
"hide_toolbar": 0,
|
"hide_toolbar": 0,
|
||||||
"icon": "fa fa-user",
|
"icon": "fa fa-user",
|
||||||
"idx": 5,
|
"idx": 5,
|
||||||
"image_field": "image",
|
"image_field": "image",
|
||||||
|
"image_view": 0,
|
||||||
"in_create": 0,
|
"in_create": 0,
|
||||||
"is_submittable": 0,
|
"is_submittable": 0,
|
||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"menu_index": 0,
|
"modified": "2019-05-10 03:22:57.283628",
|
||||||
"modified": "2019-04-11 22:12:50.029368",
|
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "CRM",
|
"module": "CRM",
|
||||||
"name": "Lead",
|
"name": "Lead",
|
||||||
|
@ -109,7 +109,11 @@ class Lead(SellingController):
|
|||||||
|
|
||||||
def set_lead_name(self):
|
def set_lead_name(self):
|
||||||
if not self.lead_name:
|
if not self.lead_name:
|
||||||
frappe.db.set_value("Lead", self.name, "lead_name", self.company_name)
|
# Check for leads being created through data import
|
||||||
|
if not self.company_name:
|
||||||
|
frappe.throw(_("A Lead requires either a person's name or an organization's name"))
|
||||||
|
|
||||||
|
self.lead_name = self.company_name
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def make_customer(source_name, target_doc=None):
|
def make_customer(source_name, target_doc=None):
|
||||||
@ -225,4 +229,4 @@ def make_lead_from_communication(communication, ignore_communication_links=False
|
|||||||
lead_name = lead.name
|
lead_name = lead.name
|
||||||
|
|
||||||
link_communication_to_document(doc, "Lead", lead_name, ignore_communication_links)
|
link_communication_to_document(doc, "Lead", lead_name, ignore_communication_links)
|
||||||
return lead_name
|
return lead_name
|
||||||
|
@ -4,5 +4,18 @@
|
|||||||
frappe.ui.form.on('HR Settings', {
|
frappe.ui.form.on('HR Settings', {
|
||||||
refresh: function(frm) {
|
refresh: function(frm) {
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
encrypt_salary_slips_in_emails: function(frm) {
|
||||||
|
let encrypt_state = frm.doc.encrypt_salary_slips_in_emails;
|
||||||
|
frm.set_df_property('password_policy', 'reqd', encrypt_state);
|
||||||
|
},
|
||||||
|
|
||||||
|
validate: function(frm) {
|
||||||
|
let policy = frm.doc.password_policy;
|
||||||
|
if (policy.includes(' ') || policy.includes('--')) {
|
||||||
|
frappe.msgprint("Password policy cannot contain spaces or simultaneous hyphens. The format will be restructured automatically");
|
||||||
|
}
|
||||||
|
frm.set_value('password_policy', policy.split(new RegExp(" |-", 'g')).filter((token) => token).join('-'));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"allow_copy": 0,
|
||||||
|
"allow_events_in_timeline": 0,
|
||||||
"allow_guest_to_view": 0,
|
"allow_guest_to_view": 0,
|
||||||
"allow_import": 0,
|
"allow_import": 0,
|
||||||
"allow_rename": 0,
|
"allow_rename": 0,
|
||||||
@ -13,10 +14,12 @@
|
|||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_in_quick_entry": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "employee_settings",
|
"fieldname": "employee_settings",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -43,12 +46,14 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_in_quick_entry": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
"default": "",
|
"default": "",
|
||||||
"description": "Enter retirement age in years",
|
"description": "Enter retirement age in years",
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "retirement_age",
|
"fieldname": "retirement_age",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -76,12 +81,14 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_in_quick_entry": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
"default": "Naming Series",
|
"default": "Naming Series",
|
||||||
"description": "Employee record is created using selected field. ",
|
"description": "Employee record is created using selected field. ",
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "emp_created_by",
|
"fieldname": "emp_created_by",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -109,11 +116,13 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_in_quick_entry": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
"default": "",
|
"default": "",
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "leave_approval_notification_template",
|
"fieldname": "leave_approval_notification_template",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -142,10 +151,12 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_in_quick_entry": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "leave_status_notification_template",
|
"fieldname": "leave_status_notification_template",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -174,10 +185,12 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_in_quick_entry": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "column_break_4",
|
"fieldname": "column_break_4",
|
||||||
"fieldtype": "Column Break",
|
"fieldtype": "Column Break",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -204,11 +217,13 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_in_quick_entry": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
"description": "Don't send Employee Birthday Reminders",
|
"description": "Don't send Employee Birthday Reminders",
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "stop_birthday_reminders",
|
"fieldname": "stop_birthday_reminders",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -235,10 +250,12 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_in_quick_entry": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "maintain_bill_work_hours_same",
|
"fieldname": "maintain_bill_work_hours_same",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -266,11 +283,13 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_in_quick_entry": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
"default": "1",
|
"default": "1",
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "leave_approver_mandatory_in_leave_application",
|
"fieldname": "leave_approver_mandatory_in_leave_application",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -298,11 +317,13 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_in_quick_entry": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
"default": "1",
|
"default": "1",
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "expense_approver_mandatory_in_expense_claim",
|
"fieldname": "expense_approver_mandatory_in_expense_claim",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -330,10 +351,13 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_in_quick_entry": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
|
"depends_on": "",
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "payroll_settings",
|
"fieldname": "payroll_settings",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -360,11 +384,13 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_in_quick_entry": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
"description": "If checked, Total no. of Working Days will include holidays, and this will reduce the value of Salary Per Day",
|
"description": "If checked, Total no. of Working Days will include holidays, and this will reduce the value of Salary Per Day",
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "include_holidays_in_total_working_days",
|
"fieldname": "include_holidays_in_total_working_days",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -391,12 +417,14 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_in_quick_entry": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
"default": "1",
|
"default": "1",
|
||||||
"description": "Emails salary slip to employee based on preferred email selected in Employee",
|
"description": "Emails salary slip to employee based on preferred email selected in Employee",
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "email_salary_slip_to_employee",
|
"fieldname": "email_salary_slip_to_employee",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -424,10 +452,82 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_in_quick_entry": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
|
"depends_on": "eval: doc.email_salary_slip_to_employee == 1;",
|
||||||
|
"description": "The salary slip emailed to the employee will be password protected, the password will be generated based on the password policy.",
|
||||||
|
"fetch_if_empty": 0,
|
||||||
|
"fieldname": "encrypt_salary_slips_in_emails",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_global_search": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"in_standard_filter": 0,
|
||||||
|
"label": "Encrypt Salary Slips in Emails",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"translatable": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_in_quick_entry": 0,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"depends_on": "eval: doc.encrypt_salary_slips_in_emails == 1",
|
||||||
|
"description": "<b>Example:</b> SAL-{first_name}-{date_of_birth.year} <br>This will generate a password like SAL-Jane-1972",
|
||||||
|
"fetch_if_empty": 0,
|
||||||
|
"fieldname": "password_policy",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_global_search": 0,
|
||||||
|
"in_list_view": 1,
|
||||||
|
"in_standard_filter": 0,
|
||||||
|
"label": "Password Policy",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"translatable": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_in_quick_entry": 0,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "max_working_hours_against_timesheet",
|
"fieldname": "max_working_hours_against_timesheet",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -455,10 +555,12 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_in_quick_entry": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "leave_settings",
|
"fieldname": "leave_settings",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -481,14 +583,17 @@
|
|||||||
"reqd": 0,
|
"reqd": 0,
|
||||||
"search_index": 0,
|
"search_index": 0,
|
||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
|
"translatable": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_in_quick_entry": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
|
"fetch_if_empty": 0,
|
||||||
"fieldname": "show_leaves_of_all_department_members_in_calendar",
|
"fieldname": "show_leaves_of_all_department_members_in_calendar",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -511,22 +616,21 @@
|
|||||||
"reqd": 0,
|
"reqd": 0,
|
||||||
"search_index": 0,
|
"search_index": 0,
|
||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
|
"translatable": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_web_view": 0,
|
"has_web_view": 0,
|
||||||
"hide_heading": 0,
|
|
||||||
"hide_toolbar": 0,
|
"hide_toolbar": 0,
|
||||||
"icon": "fa fa-cog",
|
"icon": "fa fa-cog",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"image_view": 0,
|
|
||||||
"in_create": 0,
|
"in_create": 0,
|
||||||
"is_submittable": 0,
|
"is_submittable": 0,
|
||||||
"issingle": 1,
|
"issingle": 1,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2018-05-03 15:36:13.015466",
|
"modified": "2019-04-25 15:08:12.983571",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "shivam@example.com",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "HR Settings",
|
"name": "HR Settings",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
@ -553,9 +657,9 @@
|
|||||||
],
|
],
|
||||||
"quick_entry": 0,
|
"quick_entry": 0,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"read_only_onload": 0,
|
|
||||||
"show_name_in_global_search": 0,
|
"show_name_in_global_search": 0,
|
||||||
"sort_order": "ASC",
|
"sort_order": "ASC",
|
||||||
"track_changes": 0,
|
"track_changes": 0,
|
||||||
"track_seen": 0
|
"track_seen": 0,
|
||||||
|
"track_views": 0
|
||||||
}
|
}
|
@ -5,11 +5,21 @@
|
|||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
|
from frappe import _
|
||||||
|
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
class HRSettings(Document):
|
class HRSettings(Document):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
|
self.set_naming_series()
|
||||||
|
self.validate_password_policy()
|
||||||
|
|
||||||
|
def set_naming_series(self):
|
||||||
from erpnext.setup.doctype.naming_series.naming_series import set_by_naming_series
|
from erpnext.setup.doctype.naming_series.naming_series import set_by_naming_series
|
||||||
set_by_naming_series("Employee", "employee_number",
|
set_by_naming_series("Employee", "employee_number",
|
||||||
self.get("emp_created_by")=="Naming Series", hide_name_field=True)
|
self.get("emp_created_by")=="Naming Series", hide_name_field=True)
|
||||||
|
|
||||||
|
def validate_password_policy(self):
|
||||||
|
if self.email_salary_slip_to_employee and self.encrypt_salary_slips_in_emails:
|
||||||
|
if not self.password_policy:
|
||||||
|
frappe.throw(_("Password policy for Salary Slips is not set"))
|
@ -520,13 +520,20 @@ class SalarySlip(TransactionBase):
|
|||||||
|
|
||||||
def email_salary_slip(self):
|
def email_salary_slip(self):
|
||||||
receiver = frappe.db.get_value("Employee", self.employee, "prefered_email")
|
receiver = frappe.db.get_value("Employee", self.employee, "prefered_email")
|
||||||
|
hr_settings = frappe.get_single("HR Settings")
|
||||||
|
message = "Please see attachment"
|
||||||
|
password = None
|
||||||
|
if hr_settings.encrypt_salary_slips_in_emails:
|
||||||
|
password = generate_password_for_pdf(hr_settings.password_policy, self.employee)
|
||||||
|
message += """<br>Note: Your salary slip is password protected,
|
||||||
|
the password to unlock the PDF is of the format {0}. """.format(hr_settings.password_policy)
|
||||||
|
|
||||||
if receiver:
|
if receiver:
|
||||||
email_args = {
|
email_args = {
|
||||||
"recipients": [receiver],
|
"recipients": [receiver],
|
||||||
"message": _("Please see attachment"),
|
"message": _(message),
|
||||||
"subject": 'Salary Slip - from {0} to {1}'.format(self.start_date, self.end_date),
|
"subject": 'Salary Slip - from {0} to {1}'.format(self.start_date, self.end_date),
|
||||||
"attachments": [frappe.attach_print(self.doctype, self.name, file_name=self.name)],
|
"attachments": [frappe.attach_print(self.doctype, self.name, file_name=self.name, password=password)],
|
||||||
"reference_doctype": self.doctype,
|
"reference_doctype": self.doctype,
|
||||||
"reference_name": self.name
|
"reference_name": self.name
|
||||||
}
|
}
|
||||||
@ -843,3 +850,7 @@ def unlink_ref_doc_from_salary_slip(ref_no):
|
|||||||
for ss in linked_ss:
|
for ss in linked_ss:
|
||||||
ss_doc = frappe.get_doc("Salary Slip", ss)
|
ss_doc = frappe.get_doc("Salary Slip", ss)
|
||||||
frappe.db.set_value("Salary Slip", ss_doc.name, "journal_entry", "")
|
frappe.db.set_value("Salary Slip", ss_doc.name, "journal_entry", "")
|
||||||
|
|
||||||
|
def generate_password_for_pdf(policy_template, employee):
|
||||||
|
employee = frappe.get_doc("Employee", employee)
|
||||||
|
return policy_template.format(**employee.as_dict())
|
||||||
|
@ -5,9 +5,19 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
from frappe import _
|
||||||
|
from frappe.utils import time_diff_in_seconds
|
||||||
from erpnext.hr.doctype.employee.employee import get_employee_emails
|
from erpnext.hr.doctype.employee.employee import get_employee_emails
|
||||||
|
|
||||||
class TrainingEvent(Document):
|
class TrainingEvent(Document):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
|
self.set_employee_emails()
|
||||||
|
self.validate_period()
|
||||||
|
|
||||||
|
def set_employee_emails(self):
|
||||||
self.employee_emails = ', '.join(get_employee_emails([d.employee
|
self.employee_emails = ', '.join(get_employee_emails([d.employee
|
||||||
for d in self.employees]))
|
for d in self.employees]))
|
||||||
|
|
||||||
|
def validate_period(self):
|
||||||
|
if time_diff_in_seconds(self.end_time, self.start_time) <= 0:
|
||||||
|
frappe.throw(_('End time cannot be before start time'))
|
0
erpnext/manufacturing/notification/__init__.py
Normal file
0
erpnext/manufacturing/notification/__init__.py
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"attach_print": 0,
|
||||||
|
"channel": "Email",
|
||||||
|
"condition": "doc.status == \"Received\" or doc.status == \"Partially Received\"",
|
||||||
|
"creation": "2019-04-29 11:53:23.981418",
|
||||||
|
"days_in_advance": 0,
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Notification",
|
||||||
|
"document_type": "Material Request",
|
||||||
|
"enabled": 1,
|
||||||
|
"event": "Value Change",
|
||||||
|
"idx": 0,
|
||||||
|
"is_standard": 1,
|
||||||
|
"message": "<b>Material Request Type</b>: {{ doc.material_request_type }}<br>\n<b>Company</b>: {{ doc.company }}\n\n<h3>Order Summary</h3>\n\n<table border=2 >\n <tr align=\"center\">\n <th>Item Name</th>\n <th>Received Quantity</th>\n </tr>\n {% for item in doc.items %}\n {% if frappe.utils.flt(item.received_qty, 2) > 0.0 %}\n <tr align=\"center\">\n <td>{{ item.item_code }}</td>\n <td>{{ frappe.utils.flt(item.received_qty, 2) }}</td>\n </tr>\n {% endif %}\n {% endfor %}\n</table>",
|
||||||
|
"method": "",
|
||||||
|
"modified": "2019-05-01 18:02:51.090037",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Manufacturing",
|
||||||
|
"name": "Material Request Receipt Notification",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"recipients": [
|
||||||
|
{
|
||||||
|
"email_by_document_field": "requested_by"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"sender_email": "",
|
||||||
|
"subject": "{{ doc.name }} has been received",
|
||||||
|
"value_changed": "status"
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
<b>Material Request Type</b>: {{ doc.material_request_type }}<br>
|
||||||
|
<b>Company</b>: {{ doc.company }}
|
||||||
|
|
||||||
|
<h3>Order Summary</h3>
|
||||||
|
|
||||||
|
<table border=2 >
|
||||||
|
<tr align="center">
|
||||||
|
<th>Item Name</th>
|
||||||
|
<th>Received Quantity</th>
|
||||||
|
</tr>
|
||||||
|
{% for item in doc.items %}
|
||||||
|
{% if frappe.utils.flt(item.received_qty, 2) > 0.0 %}
|
||||||
|
<tr align="center">
|
||||||
|
<td>{{ item.item_code }}</td>
|
||||||
|
<td>{{ frappe.utils.flt(item.received_qty, 2) }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
@ -0,0 +1,7 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import frappe
|
||||||
|
|
||||||
|
def get_context(context):
|
||||||
|
# do your magic here
|
||||||
|
pass
|
@ -512,6 +512,7 @@ erpnext.patches.v11_0.rename_employee_loan_to_loan
|
|||||||
erpnext.patches.v11_0.move_leave_approvers_from_employee #13-06-2018
|
erpnext.patches.v11_0.move_leave_approvers_from_employee #13-06-2018
|
||||||
erpnext.patches.v11_0.update_department_lft_rgt
|
erpnext.patches.v11_0.update_department_lft_rgt
|
||||||
erpnext.patches.v11_0.add_default_email_template_for_leave
|
erpnext.patches.v11_0.add_default_email_template_for_leave
|
||||||
|
execute:frappe.reload_doc("HR", "doctype", "HR Settings")
|
||||||
erpnext.patches.v11_0.set_default_email_template_in_hr #08-06-2018
|
erpnext.patches.v11_0.set_default_email_template_in_hr #08-06-2018
|
||||||
erpnext.patches.v11_0.uom_conversion_data #30-06-2018
|
erpnext.patches.v11_0.uom_conversion_data #30-06-2018
|
||||||
erpnext.patches.v10_0.taxes_issue_with_pos
|
erpnext.patches.v10_0.taxes_issue_with_pos
|
||||||
@ -600,3 +601,4 @@ erpnext.patches.v11_1.woocommerce_set_creation_user
|
|||||||
erpnext.patches.v11_1.set_salary_details_submittable
|
erpnext.patches.v11_1.set_salary_details_submittable
|
||||||
erpnext.patches.v11_1.rename_depends_on_lwp
|
erpnext.patches.v11_1.rename_depends_on_lwp
|
||||||
execute:frappe.delete_doc("Report", "Inactive Items")
|
execute:frappe.delete_doc("Report", "Inactive Items")
|
||||||
|
erpnext.patches.v11_1.delete_scheduling_tool
|
||||||
|
@ -2,7 +2,6 @@ from __future__ import unicode_literals
|
|||||||
import frappe
|
import frappe
|
||||||
|
|
||||||
def execute():
|
def execute():
|
||||||
|
|
||||||
hr_settings = frappe.get_single("HR Settings")
|
hr_settings = frappe.get_single("HR Settings")
|
||||||
hr_settings.leave_approval_notification_template = "Leave Approval Notification"
|
hr_settings.leave_approval_notification_template = "Leave Approval Notification"
|
||||||
hr_settings.leave_status_notification_template = "Leave Status Notification"
|
hr_settings.leave_status_notification_template = "Leave Status Notification"
|
||||||
|
9
erpnext/patches/v11_1/delete_scheduling_tool.py
Normal file
9
erpnext/patches/v11_1/delete_scheduling_tool.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
|
# License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
import frappe
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
if frappe.db.exists("DocType", "Scheduling Tool"):
|
||||||
|
frappe.delete_doc("DocType", "Scheduling Tool", ignore_permissions=True)
|
@ -66,15 +66,14 @@ class ItemVariantsCacheManager:
|
|||||||
as_list=1
|
as_list=1
|
||||||
)
|
)
|
||||||
|
|
||||||
disabled_items = [i.name for i in frappe.db.get_all('Item', {'disabled': 1})]
|
disabled_items = set([i.name for i in frappe.db.get_all('Item', {'disabled': 1})])
|
||||||
|
|
||||||
attribute_value_item_map = frappe._dict({})
|
attribute_value_item_map = frappe._dict({})
|
||||||
item_attribute_value_map = frappe._dict({})
|
item_attribute_value_map = frappe._dict({})
|
||||||
|
|
||||||
|
item_variants_data = [r for r in item_variants_data if r[0] not in disabled_items]
|
||||||
for row in item_variants_data:
|
for row in item_variants_data:
|
||||||
item_code, attribute, attribute_value = row
|
item_code, attribute, attribute_value = row
|
||||||
if item_code in disabled_items:
|
|
||||||
continue
|
|
||||||
# (attr, value) => [item1, item2]
|
# (attr, value) => [item1, item2]
|
||||||
attribute_value_item_map.setdefault((attribute, attribute_value), []).append(item_code)
|
attribute_value_item_map.setdefault((attribute, attribute_value), []).append(item_code)
|
||||||
# item => {attr1: value1, attr2: value2}
|
# item => {attr1: value1, attr2: value2}
|
||||||
@ -97,6 +96,10 @@ class ItemVariantsCacheManager:
|
|||||||
for key in keys:
|
for key in keys:
|
||||||
frappe.cache().hdel(key, self.item_code)
|
frappe.cache().hdel(key, self.item_code)
|
||||||
|
|
||||||
|
def rebuild_cache(self):
|
||||||
|
self.clear_cache()
|
||||||
|
enqueue_build_cache(self.item_code)
|
||||||
|
|
||||||
|
|
||||||
def build_cache(item_code):
|
def build_cache(item_code):
|
||||||
frappe.cache().hset('item_cache_build_in_progress', item_code, 1)
|
frappe.cache().hset('item_cache_build_in_progress', item_code, 1)
|
||||||
|
@ -102,6 +102,9 @@ def get_item_codes_by_attributes(attribute_filters, template_item_code=None):
|
|||||||
for attribute, values in attribute_filters.items():
|
for attribute, values in attribute_filters.items():
|
||||||
attribute_values = values
|
attribute_values = values
|
||||||
|
|
||||||
|
if not isinstance(attribute_values, list):
|
||||||
|
attribute_values = [attribute_values]
|
||||||
|
|
||||||
if not attribute_values: continue
|
if not attribute_values: continue
|
||||||
|
|
||||||
wheres = []
|
wheres = []
|
||||||
@ -257,7 +260,8 @@ def get_items_with_selected_attributes(item_code, selected_attributes):
|
|||||||
|
|
||||||
items = []
|
items = []
|
||||||
for attribute, value in selected_attributes.items():
|
for attribute, value in selected_attributes.items():
|
||||||
items.append(set(attribute_value_item_map[(attribute, value)]))
|
filtered_items = attribute_value_item_map.get((attribute, value), [])
|
||||||
|
items.append(set(filtered_items))
|
||||||
|
|
||||||
return set.intersection(*items)
|
return set.intersection(*items)
|
||||||
|
|
||||||
|
@ -109,6 +109,18 @@ frappe.ui.form.on("Project", {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
status: function(frm) {
|
||||||
|
if (frm.doc.status === 'Cancelled') {
|
||||||
|
frappe.confirm(__('Set tasks in this project as cancelled?'), () => {
|
||||||
|
frm.doc.tasks = frm.doc.tasks.map(task => {
|
||||||
|
task.status = 'Cancelled';
|
||||||
|
return task;
|
||||||
|
});
|
||||||
|
frm.refresh_field('tasks');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
frappe.ui.form.on("Project Task", {
|
frappe.ui.form.on("Project Task", {
|
||||||
|
@ -35,6 +35,9 @@ class Project(Document):
|
|||||||
|
|
||||||
def load_tasks(self):
|
def load_tasks(self):
|
||||||
"""Load `tasks` from the database"""
|
"""Load `tasks` from the database"""
|
||||||
|
if frappe.flags.in_import:
|
||||||
|
return
|
||||||
|
|
||||||
self.tasks = []
|
self.tasks = []
|
||||||
for task in self.get_tasks():
|
for task in self.get_tasks():
|
||||||
task_map = {
|
task_map = {
|
||||||
@ -358,7 +361,8 @@ class Project(Document):
|
|||||||
if not self.get('deleted_task_list'): return
|
if not self.get('deleted_task_list'): return
|
||||||
|
|
||||||
for d in self.get('deleted_task_list'):
|
for d in self.get('deleted_task_list'):
|
||||||
frappe.delete_doc("Task", d)
|
# unlink project
|
||||||
|
frappe.db.set_value('Task', d, 'project', '')
|
||||||
|
|
||||||
self.deleted_task_list = []
|
self.deleted_task_list = []
|
||||||
|
|
||||||
@ -481,16 +485,17 @@ def daily_reminder():
|
|||||||
projects = get_projects_for_collect_progress("Daily", fields)
|
projects = get_projects_for_collect_progress("Daily", fields)
|
||||||
|
|
||||||
for project in projects:
|
for project in projects:
|
||||||
if not check_project_update_exists(project.name, project.get("daily_time_to_send")):
|
if allow_to_make_project_update(project.name, project.get("daily_time_to_send"), "Daily"):
|
||||||
send_project_update_email_to_users(project.name)
|
send_project_update_email_to_users(project.name)
|
||||||
|
|
||||||
def twice_daily_reminder():
|
def twice_daily_reminder():
|
||||||
fields = ["first_email", "second_email"]
|
fields = ["first_email", "second_email"]
|
||||||
projects = get_projects_for_collect_progress("Twice Daily", fields)
|
projects = get_projects_for_collect_progress("Twice Daily", fields)
|
||||||
|
fields.remove("name")
|
||||||
|
|
||||||
for project in projects:
|
for project in projects:
|
||||||
for d in fields:
|
for d in fields:
|
||||||
if not check_project_update_exists(project.name, project.get(d)):
|
if allow_to_make_project_update(project.name, project.get(d), "Twicely"):
|
||||||
send_project_update_email_to_users(project.name)
|
send_project_update_email_to_users(project.name)
|
||||||
|
|
||||||
def weekly_reminder():
|
def weekly_reminder():
|
||||||
@ -502,14 +507,19 @@ def weekly_reminder():
|
|||||||
if current_day != project.day_to_send:
|
if current_day != project.day_to_send:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if not check_project_update_exists(project.name, project.get("weekly_time_to_send")):
|
if allow_to_make_project_update(project.name, project.get("weekly_time_to_send"), "Weekly"):
|
||||||
send_project_update_email_to_users(project.name)
|
send_project_update_email_to_users(project.name)
|
||||||
|
|
||||||
def check_project_update_exists(project, time):
|
def allow_to_make_project_update(project, time, frequency):
|
||||||
data = frappe.db.sql(""" SELECT name from `tabProject Update`
|
data = frappe.db.sql(""" SELECT name from `tabProject Update`
|
||||||
WHERE project = %s and date = %s and time >= %s """, (project, today(), time))
|
WHERE project = %s and date = %s """, (project, today()))
|
||||||
|
|
||||||
return True if data and data[0][0] else False
|
# len(data) > 1 condition is checked for twicely frequency
|
||||||
|
if data and (frequency in ['Daily', 'Weekly'] or len(data) > 1):
|
||||||
|
return False
|
||||||
|
|
||||||
|
if get_time(nowtime()) >= get_time(time):
|
||||||
|
return True
|
||||||
|
|
||||||
def get_projects_for_collect_progress(frequency, fields):
|
def get_projects_for_collect_progress(frequency, fields):
|
||||||
fields.extend(["name"])
|
fields.extend(["name"])
|
||||||
|
@ -105,7 +105,7 @@ class Task(NestedSet):
|
|||||||
|
|
||||||
def update_project(self):
|
def update_project(self):
|
||||||
if self.project and not self.flags.from_project:
|
if self.project and not self.flags.from_project:
|
||||||
frappe.get_doc("Project", self.project).update_project()
|
frappe.get_cached_doc("Project", self.project).update_project()
|
||||||
|
|
||||||
def check_recursion(self):
|
def check_recursion(self):
|
||||||
if self.flags.ignore_recursion_check: return
|
if self.flags.ignore_recursion_check: return
|
||||||
@ -150,7 +150,7 @@ class Task(NestedSet):
|
|||||||
|
|
||||||
def populate_depends_on(self):
|
def populate_depends_on(self):
|
||||||
if self.parent_task:
|
if self.parent_task:
|
||||||
parent = frappe.get_doc('Task', self.parent_task)
|
parent = frappe.get_cached_doc('Task', self.parent_task)
|
||||||
if not self.name in [row.task for row in parent.depends_on]:
|
if not self.name in [row.task for row in parent.depends_on]:
|
||||||
parent.append("depends_on", {
|
parent.append("depends_on", {
|
||||||
"doctype": "Task Depends On",
|
"doctype": "Task Depends On",
|
||||||
|
@ -390,15 +390,6 @@ frappe.ui.form.ItemQuickEntryForm = frappe.ui.form.QuickEntryForm.extend({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
if (mandatory.length) {
|
|
||||||
frappe.msgprint({
|
|
||||||
title: __('Missing Values Required'),
|
|
||||||
message: __('Following fields have missing values:') + '<br><br><ul><li>' + mandatory.join('<li>') + '</ul>',
|
|
||||||
indicator: 'orange'
|
|
||||||
});
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.is_manufacturer) {
|
if (this.is_manufacturer) {
|
||||||
$.each(this.manufacturer_fields, function(index, field) {
|
$.each(this.manufacturer_fields, function(index, field) {
|
||||||
attribute[field.fieldname] = me.dialog.fields_dict[field.fieldname].input.value;
|
attribute[field.fieldname] = me.dialog.fields_dict[field.fieldname].input.value;
|
||||||
|
@ -227,18 +227,18 @@ class GSTR3BReport(Document):
|
|||||||
|
|
||||||
for d in inter_state_supply.get("Unregistered", []):
|
for d in inter_state_supply.get("Unregistered", []):
|
||||||
self.report_dict["inter_sup"]["unreg_details"].append(d)
|
self.report_dict["inter_sup"]["unreg_details"].append(d)
|
||||||
self.report_dict["sup_details"]["osup_det"]["txval"] += d["txval"]
|
self.report_dict["sup_details"]["osup_det"]["txval"] += flt(d["txval"], 2)
|
||||||
self.report_dict["sup_details"]["osup_det"]["iamt"] += d["iamt"]
|
self.report_dict["sup_details"]["osup_det"]["iamt"] += flt(d["iamt"], 2)
|
||||||
|
|
||||||
for d in inter_state_supply.get("Registered Composition", []):
|
for d in inter_state_supply.get("Registered Composition", []):
|
||||||
self.report_dict["inter_sup"]["comp_details"].append(d)
|
self.report_dict["inter_sup"]["comp_details"].append(d)
|
||||||
self.report_dict["sup_details"]["osup_det"]["txval"] += d["txval"]
|
self.report_dict["sup_details"]["osup_det"]["txval"] += flt(d["txval"], 2)
|
||||||
self.report_dict["sup_details"]["osup_det"]["iamt"] += d["iamt"]
|
self.report_dict["sup_details"]["osup_det"]["iamt"] += flt(d["iamt"], 2)
|
||||||
|
|
||||||
for d in inter_state_supply.get("UIN Holders", []):
|
for d in inter_state_supply.get("UIN Holders", []):
|
||||||
self.report_dict["inter_sup"]["uin_details"].append(d)
|
self.report_dict["inter_sup"]["uin_details"].append(d)
|
||||||
self.report_dict["sup_details"]["osup_det"]["txval"] += d["txval"]
|
self.report_dict["sup_details"]["osup_det"]["txval"] += flt(d["txval"], 2)
|
||||||
self.report_dict["sup_details"]["osup_det"]["iamt"] += d["iamt"]
|
self.report_dict["sup_details"]["osup_det"]["iamt"] += flt(d["iamt"], 2)
|
||||||
|
|
||||||
def get_total_taxable_value(self, doctype, reverse_charge):
|
def get_total_taxable_value(self, doctype, reverse_charge):
|
||||||
|
|
||||||
|
@ -29,10 +29,10 @@ class Gstr1Report(object):
|
|||||||
place_of_supply,
|
place_of_supply,
|
||||||
ecommerce_gstin,
|
ecommerce_gstin,
|
||||||
reverse_charge,
|
reverse_charge,
|
||||||
gst_category,
|
invoice_type,
|
||||||
return_against,
|
return_against,
|
||||||
is_return,
|
is_return,
|
||||||
gst_category,
|
invoice_type,
|
||||||
export_type,
|
export_type,
|
||||||
port_code,
|
port_code,
|
||||||
shipping_bill_number,
|
shipping_bill_number,
|
||||||
@ -324,8 +324,8 @@ class Gstr1Report(object):
|
|||||||
"fieldtype": "Data"
|
"fieldtype": "Data"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "gst_category",
|
"fieldname": "invoice_type",
|
||||||
"label": "GST Category",
|
"label": "Invoice Type",
|
||||||
"fieldtype": "Data"
|
"fieldtype": "Data"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -577,7 +577,7 @@ def get_json():
|
|||||||
download_json_file(report_name, filters["type_of_business"], gst_json)
|
download_json_file(report_name, filters["type_of_business"], gst_json)
|
||||||
|
|
||||||
def get_b2b_json(res, gstin):
|
def get_b2b_json(res, gstin):
|
||||||
inv_type, out = {"Registered Regular": "R", "Deemed Export": "DE", "URD": "URD", "SEZ": "SEZ"}, []
|
inv_type, out = {"Regular": "R", "Deemed Export": "DE", "URD": "URD", "SEZ": "SEZ"}, []
|
||||||
for gst_in in res:
|
for gst_in in res:
|
||||||
b2b_item, inv = {"ctin": gst_in, "inv": []}, []
|
b2b_item, inv = {"ctin": gst_in, "inv": []}, []
|
||||||
if not gst_in: continue
|
if not gst_in: continue
|
||||||
@ -586,7 +586,7 @@ def get_b2b_json(res, gstin):
|
|||||||
inv_item = get_basic_invoice_detail(invoice[0])
|
inv_item = get_basic_invoice_detail(invoice[0])
|
||||||
inv_item["pos"] = "%02d" % int(invoice[0]["place_of_supply"].split('-')[0])
|
inv_item["pos"] = "%02d" % int(invoice[0]["place_of_supply"].split('-')[0])
|
||||||
inv_item["rchrg"] = invoice[0]["reverse_charge"]
|
inv_item["rchrg"] = invoice[0]["reverse_charge"]
|
||||||
inv_item["inv_typ"] = inv_type.get(invoice[0].get("gst_category", ""),"")
|
inv_item["inv_typ"] = inv_type.get(invoice[0].get("invoice_type", ""),"")
|
||||||
|
|
||||||
if inv_item["pos"]=="00": continue
|
if inv_item["pos"]=="00": continue
|
||||||
inv_item["itms"] = []
|
inv_item["itms"] = []
|
||||||
|
@ -123,5 +123,6 @@ frappe.ui.form.on("Customer", {
|
|||||||
},
|
},
|
||||||
validate: function(frm) {
|
validate: function(frm) {
|
||||||
if(frm.doc.lead_name) frappe.model.clear_doc("Lead", frm.doc.lead_name);
|
if(frm.doc.lead_name) frappe.model.clear_doc("Lead", frm.doc.lead_name);
|
||||||
|
|
||||||
},
|
},
|
||||||
});
|
});
|
@ -60,6 +60,10 @@ class Customer(TransactionBase):
|
|||||||
if self.loyalty_program == customer.loyalty_program and not self.loyalty_program_tier:
|
if self.loyalty_program == customer.loyalty_program and not self.loyalty_program_tier:
|
||||||
self.loyalty_program_tier = customer.loyalty_program_tier
|
self.loyalty_program_tier = customer.loyalty_program_tier
|
||||||
|
|
||||||
|
if self.sales_team:
|
||||||
|
if sum([member.allocated_percentage for member in self.sales_team]) != 100:
|
||||||
|
frappe.throw(_("Total contribution percentage should be equal to 100"))
|
||||||
|
|
||||||
def check_customer_group_change(self):
|
def check_customer_group_change(self):
|
||||||
frappe.flags.customer_group_changed = False
|
frappe.flags.customer_group_changed = False
|
||||||
|
|
||||||
|
@ -585,7 +585,7 @@ $.extend(erpnext.item, {
|
|||||||
"label": row.attribute,
|
"label": row.attribute,
|
||||||
"fieldname": row.attribute,
|
"fieldname": row.attribute,
|
||||||
"fieldtype": fieldtype,
|
"fieldtype": fieldtype,
|
||||||
"reqd": 1,
|
"reqd": 0,
|
||||||
"description": desc
|
"description": desc
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -600,6 +600,7 @@ $.extend(erpnext.item, {
|
|||||||
if(!args) return;
|
if(!args) return;
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method:"erpnext.controllers.item_variant.get_variant",
|
method:"erpnext.controllers.item_variant.get_variant",
|
||||||
|
btn: d.get_primary_btn(),
|
||||||
args: {
|
args: {
|
||||||
"template": frm.doc.name,
|
"template": frm.doc.name,
|
||||||
"args": d.get_values()
|
"args": d.get_values()
|
||||||
|
@ -3552,7 +3552,7 @@
|
|||||||
"remember_last_selected_value": 0,
|
"remember_last_selected_value": 0,
|
||||||
"report_hide": 0,
|
"report_hide": 0,
|
||||||
"reqd": 0,
|
"reqd": 0,
|
||||||
"search_index": 0,
|
"search_index": 1,
|
||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"translatable": 0,
|
"translatable": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
@ -3585,7 +3585,7 @@
|
|||||||
"remember_last_selected_value": 0,
|
"remember_last_selected_value": 0,
|
||||||
"report_hide": 0,
|
"report_hide": 0,
|
||||||
"reqd": 0,
|
"reqd": 0,
|
||||||
"search_index": 0,
|
"search_index": 1,
|
||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"translatable": 0,
|
"translatable": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
@ -4272,7 +4272,7 @@
|
|||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"max_attachments": 1,
|
"max_attachments": 1,
|
||||||
"modified": "2019-04-08 11:47:59.269724",
|
"modified": "2019-05-09 18:47:30.475833",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Item",
|
"name": "Item",
|
||||||
|
@ -800,10 +800,12 @@ class Item(WebsiteGenerator):
|
|||||||
|
|
||||||
def validate_variant_attributes(self):
|
def validate_variant_attributes(self):
|
||||||
if self.is_new() and self.variant_of and self.variant_based_on == 'Item Attribute':
|
if self.is_new() and self.variant_of and self.variant_based_on == 'Item Attribute':
|
||||||
|
# remove attributes with no attribute_value set
|
||||||
|
self.attributes = [d for d in self.attributes if cstr(d.attribute_value).strip()]
|
||||||
|
|
||||||
args = {}
|
args = {}
|
||||||
for d in self.attributes:
|
for i, d in enumerate(self.attributes):
|
||||||
if cstr(d.attribute_value).strip() == '':
|
d.idx = i + 1
|
||||||
frappe.throw(_("Please specify Attribute Value for attribute {0}").format(d.attribute))
|
|
||||||
args[d.attribute] = d.attribute_value
|
args[d.attribute] = d.attribute_value
|
||||||
|
|
||||||
variant = get_variant(self.variant_of, args, self.name)
|
variant = get_variant(self.variant_of, args, self.name)
|
||||||
@ -964,7 +966,7 @@ def invalidate_item_variants_cache_for_website(doc):
|
|||||||
|
|
||||||
if item_code:
|
if item_code:
|
||||||
item_cache = ItemVariantsCacheManager(item_code)
|
item_cache = ItemVariantsCacheManager(item_code)
|
||||||
item_cache.clear_cache()
|
item_cache.rebuild_cache()
|
||||||
|
|
||||||
|
|
||||||
def check_stock_uom_with_bin(item, stock_uom):
|
def check_stock_uom_with_bin(item, stock_uom):
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,5 @@
|
|||||||
frappe.listview_settings['Material Request'] = {
|
frappe.listview_settings['Material Request'] = {
|
||||||
add_fields: ["material_request_type", "status", "per_ordered"],
|
add_fields: ["material_request_type", "status", "per_ordered", "per_received"],
|
||||||
get_indicator: function(doc) {
|
get_indicator: function(doc) {
|
||||||
if(doc.status=="Stopped") {
|
if(doc.status=="Stopped") {
|
||||||
return [__("Stopped"), "red", "status,=,Stopped"];
|
return [__("Stopped"), "red", "status,=,Stopped"];
|
||||||
@ -8,7 +8,11 @@ frappe.listview_settings['Material Request'] = {
|
|||||||
} else if(doc.docstatus==1 && flt(doc.per_ordered, 2) < 100) {
|
} else if(doc.docstatus==1 && flt(doc.per_ordered, 2) < 100) {
|
||||||
return [__("Partially ordered"), "yellow", "per_ordered,<,100"];
|
return [__("Partially ordered"), "yellow", "per_ordered,<,100"];
|
||||||
} else if(doc.docstatus==1 && flt(doc.per_ordered, 2) == 100) {
|
} else if(doc.docstatus==1 && flt(doc.per_ordered, 2) == 100) {
|
||||||
if (doc.material_request_type == "Purchase") {
|
if (doc.material_request_type == "Purchase" && flt(doc.per_received, 2) < 100 && flt(doc.per_received, 2) > 0) {
|
||||||
|
return [__("Partially Received"), "yellow", "per_received,<,100"];
|
||||||
|
} else if (doc.material_request_type == "Purchase" && flt(doc.per_received, 2) == 100) {
|
||||||
|
return [__("Received"), "green", "per_received,=,100"];
|
||||||
|
} else if (doc.material_request_type == "Purchase") {
|
||||||
return [__("Ordered"), "green", "per_ordered,=,100"];
|
return [__("Ordered"), "green", "per_ordered,=,100"];
|
||||||
} else if (doc.material_request_type == "Material Transfer") {
|
} else if (doc.material_request_type == "Material Transfer") {
|
||||||
return [__("Transfered"), "green", "per_ordered,=,100"];
|
return [__("Transfered"), "green", "per_ordered,=,100"];
|
||||||
|
@ -629,7 +629,8 @@ def make_material_request(**args):
|
|||||||
"item_code": args.item_code or "_Test Item",
|
"item_code": args.item_code or "_Test Item",
|
||||||
"qty": args.qty or 10,
|
"qty": args.qty or 10,
|
||||||
"schedule_date": args.schedule_date or today(),
|
"schedule_date": args.schedule_date or today(),
|
||||||
"warehouse": args.warehouse or "_Test Warehouse - _TC"
|
"warehouse": args.warehouse or "_Test Warehouse - _TC",
|
||||||
|
"cost_center": args.cost_center or "_Test Cost Center - _TC"
|
||||||
})
|
})
|
||||||
mr.insert()
|
mr.insert()
|
||||||
if not args.do_not_submit:
|
if not args.do_not_submit:
|
||||||
|
@ -38,6 +38,7 @@
|
|||||||
"projected_qty",
|
"projected_qty",
|
||||||
"actual_qty",
|
"actual_qty",
|
||||||
"ordered_qty",
|
"ordered_qty",
|
||||||
|
"received_qty",
|
||||||
"accounting_details",
|
"accounting_details",
|
||||||
"expense_account",
|
"expense_account",
|
||||||
"column_break_35",
|
"column_break_35",
|
||||||
@ -364,11 +365,16 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "section_break_37",
|
"fieldname": "section_break_37",
|
||||||
"fieldtype": "Section Break"
|
"fieldtype": "Section Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "received_qty",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"label": "Received Quantity"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"modified": "2019-05-01 17:48:12.361976",
|
"modified": "2019-05-08 10:27:25.008801",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Material Request Item",
|
"name": "Material Request Item",
|
||||||
|
@ -35,6 +35,17 @@ class PurchaseReceipt(BuyingController):
|
|||||||
'percent_join_field': 'purchase_order',
|
'percent_join_field': 'purchase_order',
|
||||||
'overflow_type': 'receipt'
|
'overflow_type': 'receipt'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
'source_dt': 'Purchase Receipt Item',
|
||||||
|
'target_dt': 'Material Request Item',
|
||||||
|
'join_field': 'material_request_item',
|
||||||
|
'target_field': 'received_qty',
|
||||||
|
'target_parent_dt': 'Material Request',
|
||||||
|
'target_parent_field': 'per_received',
|
||||||
|
'target_ref_field': 'qty',
|
||||||
|
'source_field': 'qty',
|
||||||
|
'percent_join_field': 'material_request'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
'source_dt': 'Purchase Receipt Item',
|
'source_dt': 'Purchase Receipt Item',
|
||||||
'target_dt': 'Purchase Order Item',
|
'target_dt': 'Purchase Order Item',
|
||||||
|
@ -64,7 +64,9 @@
|
|||||||
"rejected_warehouse",
|
"rejected_warehouse",
|
||||||
"quality_inspection",
|
"quality_inspection",
|
||||||
"purchase_order",
|
"purchase_order",
|
||||||
|
"material_request",
|
||||||
"purchase_order_item",
|
"purchase_order_item",
|
||||||
|
"material_request_item",
|
||||||
"column_break_40",
|
"column_break_40",
|
||||||
"is_fixed_asset",
|
"is_fixed_asset",
|
||||||
"asset",
|
"asset",
|
||||||
@ -753,11 +755,22 @@
|
|||||||
"fieldname": "image_section",
|
"fieldname": "image_section",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"label": "Image"
|
"label": "Image"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "material_request",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Material Request",
|
||||||
|
"options": "Material Request"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "material_request_item",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"label": "Material Request Item"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"modified": "2019-05-01 17:45:17.447900",
|
"modified": "2019-05-08 10:25:27.157675",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Purchase Receipt Item",
|
"name": "Purchase Receipt Item",
|
||||||
|
@ -129,6 +129,10 @@ class StockEntry(StockController):
|
|||||||
if self.purpose not in valid_purposes:
|
if self.purpose not in valid_purposes:
|
||||||
frappe.throw(_("Purpose must be one of {0}").format(comma_or(valid_purposes)))
|
frappe.throw(_("Purpose must be one of {0}").format(comma_or(valid_purposes)))
|
||||||
|
|
||||||
|
if self.job_card and self.purpose != 'Material Transfer for Manufacture':
|
||||||
|
frappe.throw(_("For job card {0}, you can only make the 'Material Transfer for Manufacture' type stock entry")
|
||||||
|
.format(self.job_card))
|
||||||
|
|
||||||
def set_transfer_qty(self):
|
def set_transfer_qty(self):
|
||||||
for item in self.get("items"):
|
for item in self.get("items"):
|
||||||
if not flt(item.qty):
|
if not flt(item.qty):
|
||||||
@ -1116,7 +1120,7 @@ class StockEntry(StockController):
|
|||||||
frappe.MappingMismatchError)
|
frappe.MappingMismatchError)
|
||||||
|
|
||||||
def validate_batch(self):
|
def validate_batch(self):
|
||||||
if self.purpose in ["Material Transfer for Manufacture", "Manufacture", "Repack", "Send to Subcontractor", "Material Issue"]:
|
if self.purpose in ["Material Transfer for Manufacture", "Manufacture", "Repack", "Send to Subcontractor"]:
|
||||||
for item in self.get("items"):
|
for item in self.get("items"):
|
||||||
if item.batch_no:
|
if item.batch_no:
|
||||||
disabled = frappe.db.get_value("Batch", item.batch_no, "disabled")
|
disabled = frappe.db.get_value("Batch", item.batch_no, "disabled")
|
||||||
|
@ -40,6 +40,13 @@ $(document).ready(function() {
|
|||||||
right: 0%;
|
right: 0%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
{% include "templates/styles/card_style.css" %}
|
||||||
|
header, footer {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
html, body {
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
@ -5,50 +5,35 @@ from __future__ import unicode_literals
|
|||||||
import frappe, erpnext
|
import frappe, erpnext
|
||||||
|
|
||||||
from frappe import _
|
from frappe import _
|
||||||
|
from six import iteritems
|
||||||
|
|
||||||
def get_level():
|
def get_level():
|
||||||
activation_level = 0
|
activation_level = 0
|
||||||
|
sales_data = []
|
||||||
|
min_count = 0
|
||||||
|
doctypes = {"Item": 5, "Customer": 5, "Sales Order": 2, "Sales Invoice": 2, "Purchase Order": 2, "Employee": 3, "Lead": 3, "Quotation": 3,
|
||||||
|
"Payment Entry": 2, "User": 5, "Student": 5, "Instructor": 5, "BOM": 3, "Journal Entry": 3, "Stock Entry": 3}
|
||||||
|
for doctype, min_count in iteritems(doctypes):
|
||||||
|
count = frappe.db.count(doctype)
|
||||||
|
if count > min_count:
|
||||||
|
activation_level += 1
|
||||||
|
sales_data.append({doctype: count})
|
||||||
|
|
||||||
if frappe.db.get_single_value('System Settings', 'setup_complete'):
|
if frappe.db.get_single_value('System Settings', 'setup_complete'):
|
||||||
activation_level = 1
|
|
||||||
|
|
||||||
if frappe.db.count('Item') > 5:
|
|
||||||
activation_level += 1
|
activation_level += 1
|
||||||
|
|
||||||
if frappe.db.count('Customer') > 5:
|
communication_number = frappe.db.count('Communication', dict(communication_medium='Email'))
|
||||||
activation_level += 1
|
if communication_number > 10:
|
||||||
|
|
||||||
if frappe.db.count('Sales Order') > 2:
|
|
||||||
activation_level += 1
|
|
||||||
|
|
||||||
if frappe.db.count('Purchase Order') > 2:
|
|
||||||
activation_level += 1
|
|
||||||
|
|
||||||
if frappe.db.count('Employee') > 3:
|
|
||||||
activation_level += 1
|
|
||||||
|
|
||||||
if frappe.db.count('Lead') > 3:
|
|
||||||
activation_level += 1
|
|
||||||
|
|
||||||
if frappe.db.count('Payment Entry') > 2:
|
|
||||||
activation_level += 1
|
|
||||||
|
|
||||||
if frappe.db.count('Communication', dict(communication_medium='Email')) > 10:
|
|
||||||
activation_level += 1
|
|
||||||
|
|
||||||
if frappe.db.count('User') > 5:
|
|
||||||
activation_level += 1
|
|
||||||
|
|
||||||
if frappe.db.count('Student') > 5:
|
|
||||||
activation_level += 1
|
|
||||||
|
|
||||||
if frappe.db.count('Instructor') > 5:
|
|
||||||
activation_level += 1
|
activation_level += 1
|
||||||
|
sales_data.append({"Communication": communication_number})
|
||||||
|
|
||||||
# recent login
|
# recent login
|
||||||
if frappe.db.sql('select name from tabUser where last_login > date_sub(now(), interval 2 day) limit 1'):
|
if frappe.db.sql('select name from tabUser where last_login > date_sub(now(), interval 2 day) limit 1'):
|
||||||
activation_level += 1
|
activation_level += 1
|
||||||
|
|
||||||
return activation_level
|
level = {"activation_level": activation_level, "sales_data": sales_data}
|
||||||
|
|
||||||
|
return level
|
||||||
|
|
||||||
def get_help_messages():
|
def get_help_messages():
|
||||||
'''Returns help messages to be shown on Desktop'''
|
'''Returns help messages to be shown on Desktop'''
|
||||||
|
Loading…
x
Reference in New Issue
Block a user