Merge branch 'develop' into v12-lead-address-contact
21
README.md
@@ -13,9 +13,26 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
Includes: Accounting, Inventory, Manufacturing, CRM, Sales, Purchase, Project Management, HRMS. Requires MariaDB.
|
ERPNext as a monolith includes the following areas for managing businesses:
|
||||||
|
|
||||||
ERPNext is built on the [Frappe](https://github.com/frappe/frappe) Framework, a full-stack web app framework in Python & JavaScript.
|
1. [Accounting](https://erpnext.com/docs/user/manual/en/accounts)
|
||||||
|
1. [Inventory](https://erpnext.com/docs/user/manual/en/stock)
|
||||||
|
1. [CRM](https://erpnext.com/docs/user/manual/en/CRM)
|
||||||
|
1. [Sales](https://erpnext.com/docs/user/manual/en/selling)
|
||||||
|
1. [Purchase](https://erpnext.com/docs/user/manual/en/buying)
|
||||||
|
1. [HRMS](https://erpnext.com/docs/user/manual/en/human-resources)
|
||||||
|
1. [Project Management](https://erpnext.com/docs/user/manual/en/projects)
|
||||||
|
1. [Support](https://erpnext.com/docs/user/manual/en/support)
|
||||||
|
1. [Asset Management](https://erpnext.com/docs/user/manual/en/asset)
|
||||||
|
1. [Quality Management](https://erpnext.com/docs/user/manual/en/quality-management)
|
||||||
|
1. [Manufacturing](https://erpnext.com/docs/user/manual/en/manufacturing)
|
||||||
|
1. [Website Management](https://erpnext.com/docs/user/manual/en/website)
|
||||||
|
1. [Customize ERPNext](https://erpnext.com/docs/user/manual/en/customize-erpnext)
|
||||||
|
1. [And More](https://erpnext.com/docs/user/manual/en/)
|
||||||
|
|
||||||
|
ERPNext requires MariaDB.
|
||||||
|
|
||||||
|
ERPNext is built on the [Frappe Framework](https://github.com/frappe/frappe), a full-stack web app framework built with Python & JavaScript.
|
||||||
|
|
||||||
- [User Guide](https://erpnext.com/docs/user)
|
- [User Guide](https://erpnext.com/docs/user)
|
||||||
- [Discussion Forum](https://discuss.erpnext.com/)
|
- [Discussion Forum](https://discuss.erpnext.com/)
|
||||||
|
|||||||
@@ -109,12 +109,13 @@ class Account(NestedSet):
|
|||||||
if not descendants: return
|
if not descendants: return
|
||||||
|
|
||||||
parent_acc_name_map = {}
|
parent_acc_name_map = {}
|
||||||
parent_acc_name = frappe.db.get_value('Account', self.parent_account, "account_name")
|
parent_acc_name, parent_acc_number = frappe.db.get_value('Account', self.parent_account, \
|
||||||
|
["account_name", "account_number"])
|
||||||
for d in frappe.db.get_values('Account',
|
for d in frappe.db.get_values('Account',
|
||||||
{"company": ["in", descendants], "account_name": parent_acc_name},
|
{ "company": ["in", descendants], "account_name": parent_acc_name,
|
||||||
|
"account_number": parent_acc_number },
|
||||||
["company", "name"], as_dict=True):
|
["company", "name"], as_dict=True):
|
||||||
parent_acc_name_map[d["company"]] = d["name"]
|
parent_acc_name_map[d["company"]] = d["name"]
|
||||||
|
|
||||||
if not parent_acc_name_map: return
|
if not parent_acc_name_map: return
|
||||||
|
|
||||||
self.create_account_for_child_company(parent_acc_name_map, descendants, parent_acc_name)
|
self.create_account_for_child_company(parent_acc_name_map, descendants, parent_acc_name)
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ def upload_bank_statement():
|
|||||||
with open(frappe.uploaded_file, "rb") as upfile:
|
with open(frappe.uploaded_file, "rb") as upfile:
|
||||||
fcontent = upfile.read()
|
fcontent = upfile.read()
|
||||||
else:
|
else:
|
||||||
from frappe.utils.file_manager import get_uploaded_content
|
fcontent = frappe.local.uploaded_file
|
||||||
fname, fcontent = get_uploaded_content()
|
fname = frappe.local.uploaded_filename
|
||||||
|
|
||||||
if frappe.safe_encode(fname).lower().endswith("csv".encode('utf-8')):
|
if frappe.safe_encode(fname).lower().endswith("csv".encode('utf-8')):
|
||||||
from frappe.utils.csvutils import read_csv_content
|
from frappe.utils.csvutils import read_csv_content
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ class GLEntry(Document):
|
|||||||
self.validate_and_set_fiscal_year()
|
self.validate_and_set_fiscal_year()
|
||||||
self.pl_must_have_cost_center()
|
self.pl_must_have_cost_center()
|
||||||
self.validate_cost_center()
|
self.validate_cost_center()
|
||||||
self.validate_dimensions_for_pl_and_bs()
|
|
||||||
|
|
||||||
if not self.flags.from_repost:
|
if not self.flags.from_repost:
|
||||||
self.check_pl_account()
|
self.check_pl_account()
|
||||||
@@ -39,6 +38,7 @@ class GLEntry(Document):
|
|||||||
def on_update_with_args(self, adv_adj, update_outstanding = 'Yes', from_repost=False):
|
def on_update_with_args(self, adv_adj, update_outstanding = 'Yes', from_repost=False):
|
||||||
if not from_repost:
|
if not from_repost:
|
||||||
self.validate_account_details(adv_adj)
|
self.validate_account_details(adv_adj)
|
||||||
|
self.validate_dimensions_for_pl_and_bs()
|
||||||
check_freezing_date(self.posting_date, adv_adj)
|
check_freezing_date(self.posting_date, adv_adj)
|
||||||
|
|
||||||
validate_frozen_account(self.account, adv_adj)
|
validate_frozen_account(self.account, adv_adj)
|
||||||
|
|||||||
@@ -968,7 +968,7 @@ def get_exchange_rate(posting_date, account=None, account_currency=None, company
|
|||||||
|
|
||||||
# The date used to retreive the exchange rate here is the date passed
|
# The date used to retreive the exchange rate here is the date passed
|
||||||
# in as an argument to this function.
|
# in as an argument to this function.
|
||||||
elif (not exchange_rate or exchange_rate==1) and account_currency and posting_date:
|
elif (not exchange_rate or flt(exchange_rate)==1) and account_currency and posting_date:
|
||||||
exchange_rate = get_exchange_rate(account_currency, company_currency, posting_date)
|
exchange_rate = get_exchange_rate(account_currency, company_currency, posting_date)
|
||||||
else:
|
else:
|
||||||
exchange_rate = 1
|
exchange_rate = 1
|
||||||
|
|||||||
@@ -652,14 +652,16 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
(frm.doc.payment_type=="Receive" && frm.doc.party_type=="Student")
|
(frm.doc.payment_type=="Receive" && frm.doc.party_type=="Student")
|
||||||
) {
|
) {
|
||||||
if(total_positive_outstanding > total_negative_outstanding)
|
if(total_positive_outstanding > total_negative_outstanding)
|
||||||
frm.set_value("paid_amount",
|
if (!frm.doc.paid_amount)
|
||||||
total_positive_outstanding - total_negative_outstanding);
|
frm.set_value("paid_amount",
|
||||||
|
total_positive_outstanding - total_negative_outstanding);
|
||||||
} else if (
|
} else if (
|
||||||
total_negative_outstanding &&
|
total_negative_outstanding &&
|
||||||
total_positive_outstanding < total_negative_outstanding
|
total_positive_outstanding < total_negative_outstanding
|
||||||
) {
|
) {
|
||||||
frm.set_value("received_amount",
|
if (!frm.doc.received_amount)
|
||||||
total_negative_outstanding - total_positive_outstanding);
|
frm.set_value("received_amount",
|
||||||
|
total_negative_outstanding - total_positive_outstanding);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -332,6 +332,7 @@
|
|||||||
"label": "Reference"
|
"label": "Reference"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"depends_on": "eval:doc.docstatus==0",
|
||||||
"fieldname": "get_outstanding_invoice",
|
"fieldname": "get_outstanding_invoice",
|
||||||
"fieldtype": "Button",
|
"fieldtype": "Button",
|
||||||
"label": "Get Outstanding Invoice"
|
"label": "Get Outstanding Invoice"
|
||||||
@@ -575,7 +576,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"modified": "2019-11-06 12:59:43.151721",
|
"modified": "2019-12-08 13:02:30.016610",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Payment Entry",
|
"name": "Payment Entry",
|
||||||
|
|||||||
@@ -389,8 +389,7 @@
|
|||||||
"fieldname": "rate_or_discount",
|
"fieldname": "rate_or_discount",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"label": "Rate or Discount",
|
"label": "Rate or Discount",
|
||||||
"options": "\nRate\nDiscount Percentage\nDiscount Amount",
|
"options": "\nRate\nDiscount Percentage\nDiscount Amount"
|
||||||
"reqd": 1
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "Grand Total",
|
"default": "Grand Total",
|
||||||
@@ -439,19 +438,20 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
"depends_on": "eval:!doc.mixed_conditions",
|
"depends_on": "eval:!doc.mixed_conditions && doc.apply_on != 'Transaction'",
|
||||||
"fieldname": "same_item",
|
"fieldname": "same_item",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Same Item"
|
"label": "Same Item"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "eval:!doc.same_item || doc.mixed_conditions",
|
"depends_on": "eval:(!doc.same_item || doc.apply_on == 'Transaction') || doc.mixed_conditions",
|
||||||
"fieldname": "free_item",
|
"fieldname": "free_item",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Free Item",
|
"label": "Free Item",
|
||||||
"options": "Item"
|
"options": "Item"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"default": "0",
|
||||||
"fieldname": "free_qty",
|
"fieldname": "free_qty",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"label": "Qty"
|
"label": "Qty"
|
||||||
@@ -554,7 +554,7 @@
|
|||||||
],
|
],
|
||||||
"icon": "fa fa-gift",
|
"icon": "fa fa-gift",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"modified": "2019-10-15 12:39:40.399792",
|
"modified": "2019-12-18 17:29:22.957077",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Pricing Rule",
|
"name": "Pricing Rule",
|
||||||
|
|||||||
@@ -47,6 +47,9 @@ class PricingRule(Document):
|
|||||||
if tocheck and not self.get(tocheck):
|
if tocheck and not self.get(tocheck):
|
||||||
throw(_("{0} is required").format(self.meta.get_label(tocheck)), frappe.MandatoryError)
|
throw(_("{0} is required").format(self.meta.get_label(tocheck)), frappe.MandatoryError)
|
||||||
|
|
||||||
|
if self.price_or_product_discount == 'Price' and not self.rate_or_discount:
|
||||||
|
throw(_("Rate or Discount is required for the price discount."), frappe.MandatoryError)
|
||||||
|
|
||||||
def validate_applicable_for_selling_or_buying(self):
|
def validate_applicable_for_selling_or_buying(self):
|
||||||
if not self.selling and not self.buying:
|
if not self.selling and not self.buying:
|
||||||
throw(_("Atleast one of the Selling or Buying must be selected"))
|
throw(_("Atleast one of the Selling or Buying must be selected"))
|
||||||
@@ -182,7 +185,7 @@ def get_serial_no_for_item(args):
|
|||||||
|
|
||||||
def get_pricing_rule_for_item(args, price_list_rate=0, doc=None, for_validate=False):
|
def get_pricing_rule_for_item(args, price_list_rate=0, doc=None, for_validate=False):
|
||||||
from erpnext.accounts.doctype.pricing_rule.utils import (get_pricing_rules,
|
from erpnext.accounts.doctype.pricing_rule.utils import (get_pricing_rules,
|
||||||
get_applied_pricing_rules, get_pricing_rule_items)
|
get_applied_pricing_rules, get_pricing_rule_items, get_product_discount_rule)
|
||||||
|
|
||||||
if isinstance(doc, string_types):
|
if isinstance(doc, string_types):
|
||||||
doc = json.loads(doc)
|
doc = json.loads(doc)
|
||||||
@@ -241,9 +244,11 @@ def get_pricing_rule_for_item(args, price_list_rate=0, doc=None, for_validate=Fa
|
|||||||
if pricing_rule.coupon_code_based==1 and args.coupon_code==None:
|
if pricing_rule.coupon_code_based==1 and args.coupon_code==None:
|
||||||
return item_details
|
return item_details
|
||||||
|
|
||||||
if (not pricing_rule.validate_applied_rule and
|
if not pricing_rule.validate_applied_rule:
|
||||||
pricing_rule.price_or_product_discount == "Price"):
|
if pricing_rule.price_or_product_discount == "Price":
|
||||||
apply_price_discount_pricing_rule(pricing_rule, item_details, args)
|
apply_price_discount_rule(pricing_rule, item_details, args)
|
||||||
|
else:
|
||||||
|
get_product_discount_rule(pricing_rule, item_details, doc)
|
||||||
|
|
||||||
item_details.has_pricing_rule = 1
|
item_details.has_pricing_rule = 1
|
||||||
|
|
||||||
@@ -293,7 +298,7 @@ def get_pricing_rule_details(args, pricing_rule):
|
|||||||
'child_docname': args.get('child_docname')
|
'child_docname': args.get('child_docname')
|
||||||
})
|
})
|
||||||
|
|
||||||
def apply_price_discount_pricing_rule(pricing_rule, item_details, args):
|
def apply_price_discount_rule(pricing_rule, item_details, args):
|
||||||
item_details.pricing_rule_for = pricing_rule.rate_or_discount
|
item_details.pricing_rule_for = pricing_rule.rate_or_discount
|
||||||
|
|
||||||
if ((pricing_rule.margin_type == 'Amount' and pricing_rule.currency == args.currency)
|
if ((pricing_rule.margin_type == 'Amount' and pricing_rule.currency == args.currency)
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ from __future__ import unicode_literals
|
|||||||
import frappe, copy, json
|
import frappe, copy, json
|
||||||
from frappe import throw, _
|
from frappe import throw, _
|
||||||
from six import string_types
|
from six import string_types
|
||||||
from frappe.utils import flt, cint, get_datetime
|
from frappe.utils import flt, cint, get_datetime, get_link_to_form, today
|
||||||
from erpnext.setup.doctype.item_group.item_group import get_child_item_groups
|
from erpnext.setup.doctype.item_group.item_group import get_child_item_groups
|
||||||
from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses
|
from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses
|
||||||
from erpnext.stock.get_item_details import get_conversion_factor
|
from erpnext.stock.get_item_details import get_conversion_factor
|
||||||
@@ -284,7 +284,7 @@ def filter_pricing_rules_for_qty_amount(qty, rate, pricing_rules, args=None):
|
|||||||
status = True
|
status = True
|
||||||
|
|
||||||
# if user has created item price against the transaction UOM
|
# if user has created item price against the transaction UOM
|
||||||
if rule.get("uom") == args.get("uom"):
|
if args and rule.get("uom") == args.get("uom"):
|
||||||
conversion_factor = 1.0
|
conversion_factor = 1.0
|
||||||
|
|
||||||
if status and (flt(rate) >= (flt(rule.min_amt) * conversion_factor)
|
if status and (flt(rate) >= (flt(rule.min_amt) * conversion_factor)
|
||||||
@@ -408,7 +408,8 @@ def apply_pricing_rule_on_transaction(doc):
|
|||||||
conditions = get_other_conditions(conditions, values, doc)
|
conditions = get_other_conditions(conditions, values, doc)
|
||||||
|
|
||||||
pricing_rules = frappe.db.sql(""" Select `tabPricing Rule`.* from `tabPricing Rule`
|
pricing_rules = frappe.db.sql(""" Select `tabPricing Rule`.* from `tabPricing Rule`
|
||||||
where {conditions} """.format(conditions = conditions), values, as_dict=1)
|
where {conditions} and `tabPricing Rule`.disable = 0
|
||||||
|
""".format(conditions = conditions), values, as_dict=1)
|
||||||
|
|
||||||
if pricing_rules:
|
if pricing_rules:
|
||||||
pricing_rules = filter_pricing_rules_for_qty_amount(doc.total_qty,
|
pricing_rules = filter_pricing_rules_for_qty_amount(doc.total_qty,
|
||||||
@@ -420,39 +421,65 @@ def apply_pricing_rule_on_transaction(doc):
|
|||||||
doc.set('apply_discount_on', d.apply_discount_on)
|
doc.set('apply_discount_on', d.apply_discount_on)
|
||||||
|
|
||||||
for field in ['additional_discount_percentage', 'discount_amount']:
|
for field in ['additional_discount_percentage', 'discount_amount']:
|
||||||
if not d.get(field): continue
|
|
||||||
|
|
||||||
pr_field = ('discount_percentage'
|
pr_field = ('discount_percentage'
|
||||||
if field == 'additional_discount_percentage' else field)
|
if field == 'additional_discount_percentage' else field)
|
||||||
|
|
||||||
|
if not d.get(pr_field): continue
|
||||||
|
|
||||||
if d.validate_applied_rule and doc.get(field) < d.get(pr_field):
|
if d.validate_applied_rule and doc.get(field) < d.get(pr_field):
|
||||||
frappe.msgprint(_("User has not applied rule on the invoice {0}")
|
frappe.msgprint(_("User has not applied rule on the invoice {0}")
|
||||||
.format(doc.name))
|
.format(doc.name))
|
||||||
else:
|
else:
|
||||||
doc.set(field, d.get(pr_field))
|
doc.set(field, d.get(pr_field))
|
||||||
|
|
||||||
|
doc.calculate_taxes_and_totals()
|
||||||
elif d.price_or_product_discount == 'Product':
|
elif d.price_or_product_discount == 'Product':
|
||||||
apply_pricing_rule_for_free_items(doc, d)
|
item_details = frappe._dict({'parenttype': doc.doctype})
|
||||||
|
get_product_discount_rule(d, item_details, doc)
|
||||||
|
apply_pricing_rule_for_free_items(doc, item_details.free_item_data)
|
||||||
|
doc.set_missing_values()
|
||||||
|
|
||||||
def get_applied_pricing_rules(item_row):
|
def get_applied_pricing_rules(item_row):
|
||||||
return (item_row.get("pricing_rules").split(',')
|
return (item_row.get("pricing_rules").split(',')
|
||||||
if item_row.get("pricing_rules") else [])
|
if item_row.get("pricing_rules") else [])
|
||||||
|
|
||||||
def apply_pricing_rule_for_free_items(doc, pricing_rule):
|
def get_product_discount_rule(pricing_rule, item_details, doc=None):
|
||||||
if pricing_rule.get('free_item'):
|
free_item = (pricing_rule.free_item
|
||||||
|
if not pricing_rule.same_item or pricing_rule.apply_on == 'Transaction' else item_details.item_code)
|
||||||
|
|
||||||
|
if not free_item:
|
||||||
|
frappe.throw(_("Free item not set in the pricing rule {0}")
|
||||||
|
.format(get_link_to_form("Pricing Rule", pricing_rule.name)))
|
||||||
|
|
||||||
|
item_details.free_item_data = {
|
||||||
|
'item_code': free_item,
|
||||||
|
'qty': pricing_rule.free_qty or 1,
|
||||||
|
'rate': pricing_rule.free_item_rate or 0,
|
||||||
|
'price_list_rate': pricing_rule.free_item_rate or 0,
|
||||||
|
'is_free_item': 1
|
||||||
|
}
|
||||||
|
|
||||||
|
item_data = frappe.get_cached_value('Item', free_item, ['item_name',
|
||||||
|
'description', 'stock_uom'], as_dict=1)
|
||||||
|
|
||||||
|
item_details.free_item_data.update(item_data)
|
||||||
|
item_details.free_item_data['uom'] = pricing_rule.free_item_uom or item_data.stock_uom
|
||||||
|
item_details.free_item_data['conversion_factor'] = get_conversion_factor(free_item,
|
||||||
|
item_details.free_item_data['uom']).get("conversion_factor", 1)
|
||||||
|
|
||||||
|
if item_details.get("parenttype") == 'Purchase Order':
|
||||||
|
item_details.free_item_data['schedule_date'] = doc.schedule_date if doc else today()
|
||||||
|
|
||||||
|
if item_details.get("parenttype") == 'Sales Order':
|
||||||
|
item_details.free_item_data['delivery_date'] = doc.delivery_date if doc else today()
|
||||||
|
|
||||||
|
def apply_pricing_rule_for_free_items(doc, pricing_rule_args, set_missing_values=False):
|
||||||
|
if pricing_rule_args.get('item_code'):
|
||||||
items = [d.item_code for d in doc.items
|
items = [d.item_code for d in doc.items
|
||||||
if d.item_code == (d.item_code
|
if d.item_code == (pricing_rule_args.get("item_code")) and d.is_free_item]
|
||||||
if pricing_rule.get('same_item') else pricing_rule.get('free_item')) and d.is_free_item]
|
|
||||||
|
|
||||||
if not items:
|
if not items:
|
||||||
doc.append('items', {
|
doc.append('items', pricing_rule_args)
|
||||||
'item_code': pricing_rule.get('free_item'),
|
|
||||||
'qty': pricing_rule.get('free_qty'),
|
|
||||||
'uom': pricing_rule.get('free_item_uom'),
|
|
||||||
'rate': pricing_rule.get('free_item_rate') or 0,
|
|
||||||
'is_free_item': 1
|
|
||||||
})
|
|
||||||
|
|
||||||
doc.set_missing_values()
|
|
||||||
|
|
||||||
def get_pricing_rule_items(pr_doc):
|
def get_pricing_rule_items(pr_doc):
|
||||||
apply_on_data = []
|
apply_on_data = []
|
||||||
|
|||||||
@@ -382,21 +382,11 @@ cur_frm.fields_dict['items'].grid.get_field("item_code").get_query = function(do
|
|||||||
|
|
||||||
cur_frm.fields_dict['credit_to'].get_query = function(doc) {
|
cur_frm.fields_dict['credit_to'].get_query = function(doc) {
|
||||||
// filter on Account
|
// filter on Account
|
||||||
if (doc.supplier) {
|
return {
|
||||||
return {
|
filters: {
|
||||||
filters: {
|
'account_type': 'Payable',
|
||||||
'account_type': 'Payable',
|
'is_group': 0,
|
||||||
'is_group': 0,
|
'company': doc.company
|
||||||
'company': doc.company
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
filters: {
|
|
||||||
'report_type': 'Balance Sheet',
|
|
||||||
'is_group': 0,
|
|
||||||
'company': doc.company
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -248,7 +248,7 @@ class PurchaseInvoice(BuyingController):
|
|||||||
def set_against_expense_account(self):
|
def set_against_expense_account(self):
|
||||||
against_accounts = []
|
against_accounts = []
|
||||||
for item in self.get("items"):
|
for item in self.get("items"):
|
||||||
if item.expense_account not in against_accounts:
|
if item.expense_account and (item.expense_account not in against_accounts):
|
||||||
against_accounts.append(item.expense_account)
|
against_accounts.append(item.expense_account)
|
||||||
|
|
||||||
self.against_expense_account = ",".join(against_accounts)
|
self.against_expense_account = ",".join(against_accounts)
|
||||||
@@ -830,7 +830,11 @@ class PurchaseInvoice(BuyingController):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def make_gle_for_rounding_adjustment(self, gl_entries):
|
def make_gle_for_rounding_adjustment(self, gl_entries):
|
||||||
if self.rounding_adjustment:
|
# if rounding adjustment in small and conversion rate is also small then
|
||||||
|
# base_rounding_adjustment may become zero due to small precision
|
||||||
|
# eg: rounding_adjustment = 0.01 and exchange rate = 0.05 and precision of base_rounding_adjustment is 2
|
||||||
|
# then base_rounding_adjustment becomes zero and error is thrown in GL Entry
|
||||||
|
if self.rounding_adjustment and self.base_rounding_adjustment:
|
||||||
round_off_account, round_off_cost_center = \
|
round_off_account, round_off_cost_center = \
|
||||||
get_round_off_account_and_cost_center(self.company)
|
get_round_off_account_and_cost_center(self.company)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{% include "erpnext/regional/india/taxes.js" %}
|
||||||
|
|
||||||
|
erpnext.setup_auto_gst_taxation('Purchase Invoice');
|
||||||
@@ -1,300 +1,108 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"allow_import": 1,
|
||||||
"allow_import": 1,
|
"allow_rename": 1,
|
||||||
"allow_rename": 1,
|
"creation": "2013-01-10 16:34:08",
|
||||||
"autoname": "field:title",
|
"description": "Standard tax template that can be applied to all Purchase Transactions. This template can contain list of tax heads and also other expense heads like \"Shipping\", \"Insurance\", \"Handling\" etc.\n\n#### Note\n\nThe tax rate you define here will be the standard tax rate for all **Items**. If there are **Items** that have different rates, they must be added in the **Item Tax** table in the **Item** master.\n\n#### Description of Columns\n\n1. Calculation Type: \n - This can be on **Net Total** (that is the sum of basic amount).\n - **On Previous Row Total / Amount** (for cumulative taxes or charges). If you select this option, the tax will be applied as a percentage of the previous row (in the tax table) amount or total.\n - **Actual** (as mentioned).\n2. Account Head: The Account ledger under which this tax will be booked\n3. Cost Center: If the tax / charge is an income (like shipping) or expense it needs to be booked against a Cost Center.\n4. Description: Description of the tax (that will be printed in invoices / quotes).\n5. Rate: Tax rate.\n6. Amount: Tax amount.\n7. Total: Cumulative total to this point.\n8. Enter Row: If based on \"Previous Row Total\" you can select the row number which will be taken as a base for this calculation (default is the previous row).\n9. Consider Tax or Charge for: In this section you can specify if the tax / charge is only for valuation (not a part of total) or only for total (does not add value to the item) or for both.\n10. Add or Deduct: Whether you want to add or deduct the tax.",
|
||||||
"beta": 0,
|
"doctype": "DocType",
|
||||||
"creation": "2013-01-10 16:34:08",
|
"document_type": "Setup",
|
||||||
"custom": 0,
|
"field_order": [
|
||||||
"description": "Standard tax template that can be applied to all Purchase Transactions. This template can contain list of tax heads and also other expense heads like \"Shipping\", \"Insurance\", \"Handling\" etc.\n\n#### Note\n\nThe tax rate you define here will be the standard tax rate for all **Items**. If there are **Items** that have different rates, they must be added in the **Item Tax** table in the **Item** master.\n\n#### Description of Columns\n\n1. Calculation Type: \n - This can be on **Net Total** (that is the sum of basic amount).\n - **On Previous Row Total / Amount** (for cumulative taxes or charges). If you select this option, the tax will be applied as a percentage of the previous row (in the tax table) amount or total.\n - **Actual** (as mentioned).\n2. Account Head: The Account ledger under which this tax will be booked\n3. Cost Center: If the tax / charge is an income (like shipping) or expense it needs to be booked against a Cost Center.\n4. Description: Description of the tax (that will be printed in invoices / quotes).\n5. Rate: Tax rate.\n6. Amount: Tax amount.\n7. Total: Cumulative total to this point.\n8. Enter Row: If based on \"Previous Row Total\" you can select the row number which will be taken as a base for this calculation (default is the previous row).\n9. Consider Tax or Charge for: In this section you can specify if the tax / charge is only for valuation (not a part of total) or only for total (does not add value to the item) or for both.\n10. Add or Deduct: Whether you want to add or deduct the tax.",
|
"title",
|
||||||
"docstatus": 0,
|
"is_default",
|
||||||
"doctype": "DocType",
|
"disabled",
|
||||||
"document_type": "Setup",
|
"column_break4",
|
||||||
"editable_grid": 0,
|
"company",
|
||||||
|
"tax_category",
|
||||||
|
"section_break6",
|
||||||
|
"taxes"
|
||||||
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"fieldname": "title",
|
||||||
"bold": 0,
|
"fieldtype": "Data",
|
||||||
"collapsible": 0,
|
"label": "Title",
|
||||||
"columns": 0,
|
"no_copy": 1,
|
||||||
"fieldname": "title",
|
"oldfieldname": "title",
|
||||||
"fieldtype": "Data",
|
"oldfieldtype": "Data",
|
||||||
"hidden": 0,
|
"reqd": 1
|
||||||
"ignore_user_permissions": 0,
|
},
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 1,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Title",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 1,
|
|
||||||
"oldfieldname": "title",
|
|
||||||
"oldfieldtype": "Data",
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"default": "0",
|
||||||
"bold": 0,
|
"fieldname": "is_default",
|
||||||
"collapsible": 0,
|
"fieldtype": "Check",
|
||||||
"columns": 0,
|
"in_list_view": 1,
|
||||||
"fieldname": "is_default",
|
"label": "Default"
|
||||||
"fieldtype": "Check",
|
},
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_list_view": 1,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Default",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"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,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"default": "0",
|
||||||
"bold": 0,
|
"fieldname": "disabled",
|
||||||
"collapsible": 0,
|
"fieldtype": "Check",
|
||||||
"columns": 0,
|
"in_list_view": 1,
|
||||||
"fieldname": "disabled",
|
"label": "Disabled"
|
||||||
"fieldtype": "Check",
|
},
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_list_view": 1,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Disabled",
|
|
||||||
"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,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"fieldname": "column_break4",
|
||||||
"bold": 0,
|
"fieldtype": "Column Break"
|
||||||
"collapsible": 0,
|
},
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "column_break4",
|
|
||||||
"fieldtype": "Column Break",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "",
|
|
||||||
"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,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"fieldname": "company",
|
||||||
"bold": 0,
|
"fieldtype": "Link",
|
||||||
"collapsible": 0,
|
"in_list_view": 1,
|
||||||
"columns": 0,
|
"in_standard_filter": 1,
|
||||||
"fieldname": "company",
|
"label": "Company",
|
||||||
"fieldtype": "Link",
|
"options": "Company",
|
||||||
"hidden": 0,
|
"remember_last_selected_value": 1,
|
||||||
"ignore_user_permissions": 0,
|
"reqd": 1
|
||||||
"ignore_xss_filter": 0,
|
},
|
||||||
"in_filter": 1,
|
|
||||||
"in_list_view": 1,
|
|
||||||
"in_standard_filter": 1,
|
|
||||||
"label": "Company",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Company",
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 1,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"fieldname": "section_break6",
|
||||||
"bold": 0,
|
"fieldtype": "Section Break"
|
||||||
"collapsible": 0,
|
},
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "section_break6",
|
|
||||||
"fieldtype": "Section Break",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "",
|
|
||||||
"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,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"fieldname": "taxes",
|
||||||
"bold": 0,
|
"fieldtype": "Table",
|
||||||
"collapsible": 0,
|
"label": "Purchase Taxes and Charges",
|
||||||
"columns": 0,
|
"oldfieldname": "purchase_tax_details",
|
||||||
"fieldname": "taxes",
|
"oldfieldtype": "Table",
|
||||||
"fieldtype": "Table",
|
"options": "Purchase Taxes and Charges"
|
||||||
"hidden": 0,
|
},
|
||||||
"ignore_user_permissions": 0,
|
{
|
||||||
"ignore_xss_filter": 0,
|
"fieldname": "tax_category",
|
||||||
"in_filter": 0,
|
"fieldtype": "Link",
|
||||||
"in_list_view": 0,
|
"label": "Tax Category",
|
||||||
"in_standard_filter": 0,
|
"options": "Tax Category"
|
||||||
"label": "Purchase Taxes and Charges",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "purchase_tax_details",
|
|
||||||
"oldfieldtype": "Table",
|
|
||||||
"options": "Purchase Taxes and Charges",
|
|
||||||
"permlevel": 0,
|
|
||||||
"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,
|
|
||||||
"unique": 0
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"hide_heading": 0,
|
"icon": "fa fa-money",
|
||||||
"hide_toolbar": 0,
|
"idx": 1,
|
||||||
"icon": "fa fa-money",
|
"modified": "2019-11-25 13:05:26.220275",
|
||||||
"idx": 1,
|
"modified_by": "Administrator",
|
||||||
"image_view": 0,
|
"module": "Accounts",
|
||||||
"in_create": 0,
|
"name": "Purchase Taxes and Charges Template",
|
||||||
|
"owner": "wasim@webnotestech.com",
|
||||||
"is_submittable": 0,
|
|
||||||
"issingle": 0,
|
|
||||||
"istable": 0,
|
|
||||||
"max_attachments": 0,
|
|
||||||
"modified": "2016-11-07 05:18:44.095798",
|
|
||||||
"modified_by": "Administrator",
|
|
||||||
"module": "Accounts",
|
|
||||||
"name": "Purchase Taxes and Charges Template",
|
|
||||||
"owner": "wasim@webnotestech.com",
|
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"amend": 0,
|
"email": 1,
|
||||||
"apply_user_permissions": 0,
|
"print": 1,
|
||||||
"cancel": 0,
|
"read": 1,
|
||||||
"create": 0,
|
"report": 1,
|
||||||
"delete": 0,
|
"role": "Purchase Manager"
|
||||||
"email": 1,
|
},
|
||||||
"export": 0,
|
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"is_custom": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
|
||||||
"read": 1,
|
|
||||||
"report": 1,
|
|
||||||
"role": "Purchase Manager",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 0,
|
|
||||||
"submit": 0,
|
|
||||||
"write": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"amend": 0,
|
"create": 1,
|
||||||
"apply_user_permissions": 0,
|
"delete": 1,
|
||||||
"cancel": 0,
|
"email": 1,
|
||||||
"create": 1,
|
"print": 1,
|
||||||
"delete": 1,
|
"read": 1,
|
||||||
"email": 1,
|
"report": 1,
|
||||||
"export": 0,
|
"role": "Purchase Master Manager",
|
||||||
"if_owner": 0,
|
"share": 1,
|
||||||
"import": 0,
|
|
||||||
"is_custom": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
|
||||||
"read": 1,
|
|
||||||
"report": 1,
|
|
||||||
"role": "Purchase Master Manager",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 0,
|
"read": 1,
|
||||||
"apply_user_permissions": 0,
|
"role": "Purchase User"
|
||||||
"cancel": 0,
|
|
||||||
"create": 0,
|
|
||||||
"delete": 0,
|
|
||||||
"email": 0,
|
|
||||||
"export": 0,
|
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"is_custom": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 0,
|
|
||||||
"read": 1,
|
|
||||||
"report": 0,
|
|
||||||
"role": "Purchase User",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 0,
|
|
||||||
"submit": 0,
|
|
||||||
"write": 0
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"quick_entry": 0,
|
"sort_order": "DESC",
|
||||||
"read_only": 0,
|
"track_changes": 1
|
||||||
"read_only_onload": 0,
|
|
||||||
"sort_order": "DESC",
|
|
||||||
"track_seen": 0
|
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,7 @@
|
|||||||
|
{% include "erpnext/regional/india/taxes.js" %}
|
||||||
|
|
||||||
|
erpnext.setup_auto_gst_taxation('Sales Invoice');
|
||||||
|
|
||||||
frappe.ui.form.on("Sales Invoice", {
|
frappe.ui.form.on("Sales Invoice", {
|
||||||
setup: function(frm) {
|
setup: function(frm) {
|
||||||
frm.set_query('transporter', function() {
|
frm.set_query('transporter', function() {
|
||||||
@@ -35,4 +39,5 @@ frappe.ui.form.on("Sales Invoice", {
|
|||||||
}, __("Make"));
|
}, __("Make"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -556,22 +556,11 @@ cur_frm.cscript.cost_center = function(doc, cdt, cdn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cur_frm.set_query("debit_to", function(doc) {
|
cur_frm.set_query("debit_to", function(doc) {
|
||||||
// filter on Account
|
return {
|
||||||
if (doc.customer) {
|
filters: {
|
||||||
return {
|
'account_type': 'Receivable',
|
||||||
filters: {
|
'is_group': 0,
|
||||||
'account_type': 'Receivable',
|
'company': doc.company
|
||||||
'is_group': 0,
|
|
||||||
'company': doc.company
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
filters: {
|
|
||||||
'report_type': 'Balance Sheet',
|
|
||||||
'is_group': 0,
|
|
||||||
'company': doc.company
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -697,8 +686,8 @@ frappe.ui.form.on('Sales Invoice', {
|
|||||||
if (frm.doc.company)
|
if (frm.doc.company)
|
||||||
{
|
{
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method:"frappe.contacts.doctype.address.address.get_default_address",
|
method:"erpnext.setup.doctype.company.company.get_default_company_address",
|
||||||
args:{ doctype:'Company',name:frm.doc.company},
|
args:{name:frm.doc.company, existing_address: frm.doc.company_address},
|
||||||
callback: function(r){
|
callback: function(r){
|
||||||
if (r.message){
|
if (r.message){
|
||||||
frm.set_value("company_address",r.message)
|
frm.set_value("company_address",r.message)
|
||||||
|
|||||||
@@ -535,10 +535,8 @@ class SalesInvoice(SellingController):
|
|||||||
for i in dic:
|
for i in dic:
|
||||||
if frappe.db.get_single_value('Selling Settings', dic[i][0]) == 'Yes':
|
if frappe.db.get_single_value('Selling Settings', dic[i][0]) == 'Yes':
|
||||||
for d in self.get('items'):
|
for d in self.get('items'):
|
||||||
is_stock_item = frappe.get_cached_value('Item', d.item_code, 'is_stock_item')
|
if (d.item_code and not d.get(i.lower().replace(' ','_')) and not self.get(dic[i][1])):
|
||||||
if (d.item_code and is_stock_item == 1\
|
msgprint(_("{0} is mandatory for Item {1}").format(i,d.item_code), raise_exception=1)
|
||||||
and not d.get(i.lower().replace(' ','_')) and not self.get(dic[i][1])):
|
|
||||||
msgprint(_("{0} is mandatory for Stock Item {1}").format(i,d.item_code), raise_exception=1)
|
|
||||||
|
|
||||||
|
|
||||||
def validate_proj_cust(self):
|
def validate_proj_cust(self):
|
||||||
@@ -953,7 +951,7 @@ class SalesInvoice(SellingController):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def make_gle_for_rounding_adjustment(self, gl_entries):
|
def make_gle_for_rounding_adjustment(self, gl_entries):
|
||||||
if flt(self.rounding_adjustment, self.precision("rounding_adjustment")):
|
if flt(self.rounding_adjustment, self.precision("rounding_adjustment")) and self.base_rounding_adjustment:
|
||||||
round_off_account, round_off_cost_center = \
|
round_off_account, round_off_cost_center = \
|
||||||
get_round_off_account_and_cost_center(self.company)
|
get_round_off_account_and_cost_center(self.company)
|
||||||
|
|
||||||
|
|||||||
@@ -1,299 +1,119 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"allow_import": 1,
|
||||||
"allow_import": 1,
|
"allow_rename": 1,
|
||||||
"allow_rename": 1,
|
"creation": "2013-01-10 16:34:09",
|
||||||
"autoname": "field:title",
|
"description": "Standard tax template that can be applied to all Sales Transactions. This template can contain list of tax heads and also other expense / income heads like \"Shipping\", \"Insurance\", \"Handling\" etc.\n\n#### Note\n\nThe tax rate you define here will be the standard tax rate for all **Items**. If there are **Items** that have different rates, they must be added in the **Item Tax** table in the **Item** master.\n\n#### Description of Columns\n\n1. Calculation Type: \n - This can be on **Net Total** (that is the sum of basic amount).\n - **On Previous Row Total / Amount** (for cumulative taxes or charges). If you select this option, the tax will be applied as a percentage of the previous row (in the tax table) amount or total.\n - **Actual** (as mentioned).\n2. Account Head: The Account ledger under which this tax will be booked\n3. Cost Center: If the tax / charge is an income (like shipping) or expense it needs to be booked against a Cost Center.\n4. Description: Description of the tax (that will be printed in invoices / quotes).\n5. Rate: Tax rate.\n6. Amount: Tax amount.\n7. Total: Cumulative total to this point.\n8. Enter Row: If based on \"Previous Row Total\" you can select the row number which will be taken as a base for this calculation (default is the previous row).\n9. Is this Tax included in Basic Rate?: If you check this, it means that this tax will not be shown below the item table, but will be included in the Basic Rate in your main item table. This is useful where you want give a flat price (inclusive of all taxes) price to customers.",
|
||||||
"beta": 0,
|
"doctype": "DocType",
|
||||||
"creation": "2013-01-10 16:34:09",
|
"document_type": "Setup",
|
||||||
"custom": 0,
|
"engine": "InnoDB",
|
||||||
"description": "Standard tax template that can be applied to all Sales Transactions. This template can contain list of tax heads and also other expense / income heads like \"Shipping\", \"Insurance\", \"Handling\" etc.\n\n#### Note\n\nThe tax rate you define here will be the standard tax rate for all **Items**. If there are **Items** that have different rates, they must be added in the **Item Tax** table in the **Item** master.\n\n#### Description of Columns\n\n1. Calculation Type: \n - This can be on **Net Total** (that is the sum of basic amount).\n - **On Previous Row Total / Amount** (for cumulative taxes or charges). If you select this option, the tax will be applied as a percentage of the previous row (in the tax table) amount or total.\n - **Actual** (as mentioned).\n2. Account Head: The Account ledger under which this tax will be booked\n3. Cost Center: If the tax / charge is an income (like shipping) or expense it needs to be booked against a Cost Center.\n4. Description: Description of the tax (that will be printed in invoices / quotes).\n5. Rate: Tax rate.\n6. Amount: Tax amount.\n7. Total: Cumulative total to this point.\n8. Enter Row: If based on \"Previous Row Total\" you can select the row number which will be taken as a base for this calculation (default is the previous row).\n9. Is this Tax included in Basic Rate?: If you check this, it means that this tax will not be shown below the item table, but will be included in the Basic Rate in your main item table. This is useful where you want give a flat price (inclusive of all taxes) price to customers.",
|
"field_order": [
|
||||||
"docstatus": 0,
|
"title",
|
||||||
"doctype": "DocType",
|
"is_default",
|
||||||
"document_type": "Setup",
|
"disabled",
|
||||||
"editable_grid": 0,
|
"column_break_3",
|
||||||
|
"company",
|
||||||
|
"tax_category",
|
||||||
|
"section_break_5",
|
||||||
|
"taxes"
|
||||||
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"fieldname": "title",
|
||||||
"bold": 0,
|
"fieldtype": "Data",
|
||||||
"collapsible": 0,
|
"label": "Title",
|
||||||
"columns": 0,
|
"no_copy": 1,
|
||||||
"fieldname": "title",
|
"oldfieldname": "title",
|
||||||
"fieldtype": "Data",
|
"oldfieldtype": "Data",
|
||||||
"hidden": 0,
|
"reqd": 1
|
||||||
"ignore_user_permissions": 0,
|
},
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 1,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Title",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 1,
|
|
||||||
"oldfieldname": "title",
|
|
||||||
"oldfieldtype": "Data",
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"default": "0",
|
||||||
"bold": 0,
|
"fieldname": "is_default",
|
||||||
"collapsible": 0,
|
"fieldtype": "Check",
|
||||||
"columns": 0,
|
"in_list_view": 1,
|
||||||
"fieldname": "is_default",
|
"label": "Default"
|
||||||
"fieldtype": "Check",
|
},
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_list_view": 1,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Default",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"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,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"default": "0",
|
||||||
"bold": 0,
|
"fieldname": "disabled",
|
||||||
"collapsible": 0,
|
"fieldtype": "Check",
|
||||||
"columns": 0,
|
"label": "Disabled"
|
||||||
"fieldname": "disabled",
|
},
|
||||||
"fieldtype": "Check",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Disabled",
|
|
||||||
"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,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"fieldname": "column_break_3",
|
||||||
"bold": 0,
|
"fieldtype": "Column Break"
|
||||||
"collapsible": 0,
|
},
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "column_break_3",
|
|
||||||
"fieldtype": "Column Break",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"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,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"fieldname": "company",
|
||||||
"bold": 0,
|
"fieldtype": "Link",
|
||||||
"collapsible": 0,
|
"in_list_view": 1,
|
||||||
"columns": 0,
|
"in_standard_filter": 1,
|
||||||
"fieldname": "company",
|
"label": "Company",
|
||||||
"fieldtype": "Link",
|
"oldfieldname": "company",
|
||||||
"hidden": 0,
|
"oldfieldtype": "Link",
|
||||||
"ignore_user_permissions": 0,
|
"options": "Company",
|
||||||
"ignore_xss_filter": 0,
|
"remember_last_selected_value": 1,
|
||||||
"in_filter": 1,
|
"reqd": 1
|
||||||
"in_list_view": 1,
|
},
|
||||||
"in_standard_filter": 1,
|
|
||||||
"label": "Company",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "company",
|
|
||||||
"oldfieldtype": "Link",
|
|
||||||
"options": "Company",
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 1,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"fieldname": "section_break_5",
|
||||||
"bold": 0,
|
"fieldtype": "Section Break"
|
||||||
"collapsible": 0,
|
},
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "section_break_5",
|
|
||||||
"fieldtype": "Section Break",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"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,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"description": "* Will be calculated in the transaction.",
|
||||||
"bold": 0,
|
"fieldname": "taxes",
|
||||||
"collapsible": 0,
|
"fieldtype": "Table",
|
||||||
"columns": 0,
|
"label": "Sales Taxes and Charges",
|
||||||
"description": "* Will be calculated in the transaction.",
|
"oldfieldname": "other_charges",
|
||||||
"fieldname": "taxes",
|
"oldfieldtype": "Table",
|
||||||
"fieldtype": "Table",
|
"options": "Sales Taxes and Charges"
|
||||||
"hidden": 0,
|
},
|
||||||
"ignore_user_permissions": 0,
|
{
|
||||||
"ignore_xss_filter": 0,
|
"fieldname": "tax_category",
|
||||||
"in_filter": 0,
|
"fieldtype": "Link",
|
||||||
"in_list_view": 0,
|
"label": "Tax Category",
|
||||||
"in_standard_filter": 0,
|
"options": "Tax Category"
|
||||||
"label": "Sales Taxes and Charges",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "other_charges",
|
|
||||||
"oldfieldtype": "Table",
|
|
||||||
"options": "Sales Taxes and Charges",
|
|
||||||
"permlevel": 0,
|
|
||||||
"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,
|
|
||||||
"unique": 0
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"hide_heading": 0,
|
"icon": "fa fa-money",
|
||||||
"hide_toolbar": 0,
|
"idx": 1,
|
||||||
"icon": "fa fa-money",
|
"modified": "2019-11-25 13:06:03.279099",
|
||||||
"idx": 1,
|
"modified_by": "Administrator",
|
||||||
"image_view": 0,
|
"module": "Accounts",
|
||||||
"in_create": 0,
|
"name": "Sales Taxes and Charges Template",
|
||||||
|
"owner": "Administrator",
|
||||||
"is_submittable": 0,
|
|
||||||
"issingle": 0,
|
|
||||||
"istable": 0,
|
|
||||||
"max_attachments": 0,
|
|
||||||
"modified": "2016-11-07 05:18:41.743257",
|
|
||||||
"modified_by": "Administrator",
|
|
||||||
"module": "Accounts",
|
|
||||||
"name": "Sales Taxes and Charges Template",
|
|
||||||
"owner": "Administrator",
|
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"amend": 0,
|
"email": 1,
|
||||||
"apply_user_permissions": 1,
|
"print": 1,
|
||||||
"cancel": 0,
|
"read": 1,
|
||||||
"create": 0,
|
"report": 1,
|
||||||
"delete": 0,
|
"role": "Sales User"
|
||||||
"email": 1,
|
},
|
||||||
"export": 0,
|
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"is_custom": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
|
||||||
"read": 1,
|
|
||||||
"report": 1,
|
|
||||||
"role": "Sales User",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 0,
|
|
||||||
"submit": 0,
|
|
||||||
"write": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"amend": 0,
|
"create": 1,
|
||||||
"apply_user_permissions": 0,
|
"delete": 1,
|
||||||
"cancel": 0,
|
"email": 1,
|
||||||
"create": 1,
|
"print": 1,
|
||||||
"delete": 1,
|
"read": 1,
|
||||||
"email": 1,
|
"report": 1,
|
||||||
"export": 0,
|
"role": "Accounts Manager",
|
||||||
"if_owner": 0,
|
"share": 1,
|
||||||
"import": 0,
|
|
||||||
"is_custom": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
|
||||||
"read": 1,
|
|
||||||
"report": 1,
|
|
||||||
"role": "Accounts Manager",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 0,
|
"create": 1,
|
||||||
"apply_user_permissions": 0,
|
"delete": 1,
|
||||||
"cancel": 0,
|
"email": 1,
|
||||||
"create": 1,
|
"print": 1,
|
||||||
"delete": 1,
|
"read": 1,
|
||||||
"email": 1,
|
"report": 1,
|
||||||
"export": 0,
|
"role": "Sales Master Manager",
|
||||||
"if_owner": 0,
|
"share": 1,
|
||||||
"import": 0,
|
|
||||||
"is_custom": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
|
||||||
"read": 1,
|
|
||||||
"report": 1,
|
|
||||||
"role": "Sales Master Manager",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"quick_entry": 0,
|
"sort_field": "modified",
|
||||||
"read_only": 0,
|
"sort_order": "ASC",
|
||||||
"read_only_onload": 0,
|
"track_changes": 1
|
||||||
"sort_order": "ASC",
|
|
||||||
"track_seen": 0
|
|
||||||
}
|
}
|
||||||
@@ -188,7 +188,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"modified": "2019-11-07 13:31:17.999744",
|
"modified": "2019-12-20 14:48:01.990600",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Share Transfer",
|
"name": "Share Transfer",
|
||||||
@@ -196,6 +196,7 @@
|
|||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"amend": 1,
|
"amend": 1,
|
||||||
|
"cancel": 1,
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
@@ -221,6 +222,7 @@
|
|||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"cancel": 1,
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
@@ -230,6 +232,7 @@
|
|||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "Accounts Manager",
|
"role": "Accounts Manager",
|
||||||
"share": 1,
|
"share": 1,
|
||||||
|
"submit": 1,
|
||||||
"write": 1
|
"write": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -90,8 +90,12 @@ def merge_similar_entries(gl_map):
|
|||||||
else:
|
else:
|
||||||
merged_gl_map.append(entry)
|
merged_gl_map.append(entry)
|
||||||
|
|
||||||
|
company = gl_map[0].company if gl_map else erpnext.get_default_company()
|
||||||
|
company_currency = erpnext.get_company_currency(company)
|
||||||
|
precision = get_field_precision(frappe.get_meta("GL Entry").get_field("debit"), company_currency)
|
||||||
|
|
||||||
# filter zero debit and credit entries
|
# filter zero debit and credit entries
|
||||||
merged_gl_map = filter(lambda x: flt(x.debit, 9)!=0 or flt(x.credit, 9)!=0, merged_gl_map)
|
merged_gl_map = filter(lambda x: flt(x.debit, precision)!=0 or flt(x.credit, precision)!=0, merged_gl_map)
|
||||||
merged_gl_map = list(merged_gl_map)
|
merged_gl_map = list(merged_gl_map)
|
||||||
|
|
||||||
return merged_gl_map
|
return merged_gl_map
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ class DuplicatePartyAccountError(frappe.ValidationError): pass
|
|||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_party_details(party=None, account=None, party_type="Customer", company=None, posting_date=None,
|
def get_party_details(party=None, account=None, party_type="Customer", company=None, posting_date=None,
|
||||||
bill_date=None, price_list=None, currency=None, doctype=None, ignore_permissions=False, fetch_payment_terms_template=True,
|
bill_date=None, price_list=None, currency=None, doctype=None, ignore_permissions=False, fetch_payment_terms_template=True,
|
||||||
party_address=None, shipping_address=None, pos_profile=None):
|
party_address=None, company_address=None, shipping_address=None, pos_profile=None):
|
||||||
|
|
||||||
if not party:
|
if not party:
|
||||||
return {}
|
return {}
|
||||||
@@ -31,14 +31,14 @@ def get_party_details(party=None, account=None, party_type="Customer", company=N
|
|||||||
frappe.throw(_("{0}: {1} does not exists").format(party_type, party))
|
frappe.throw(_("{0}: {1} does not exists").format(party_type, party))
|
||||||
return _get_party_details(party, account, party_type,
|
return _get_party_details(party, account, party_type,
|
||||||
company, posting_date, bill_date, price_list, currency, doctype, ignore_permissions,
|
company, posting_date, bill_date, price_list, currency, doctype, ignore_permissions,
|
||||||
fetch_payment_terms_template, party_address, shipping_address, pos_profile)
|
fetch_payment_terms_template, party_address, company_address, shipping_address, pos_profile)
|
||||||
|
|
||||||
def _get_party_details(party=None, account=None, party_type="Customer", company=None, posting_date=None,
|
def _get_party_details(party=None, account=None, party_type="Customer", company=None, posting_date=None,
|
||||||
bill_date=None, price_list=None, currency=None, doctype=None, ignore_permissions=False,
|
bill_date=None, price_list=None, currency=None, doctype=None, ignore_permissions=False,
|
||||||
fetch_payment_terms_template=True, party_address=None, shipping_address=None, pos_profile=None):
|
fetch_payment_terms_template=True, party_address=None, company_address=None,shipping_address=None, pos_profile=None):
|
||||||
|
|
||||||
out = frappe._dict(set_account_and_due_date(party, account, party_type, company, posting_date, bill_date, doctype))
|
party_details = frappe._dict(set_account_and_due_date(party, account, party_type, company, posting_date, bill_date, doctype))
|
||||||
party = out[party_type.lower()]
|
party = party_details[party_type.lower()]
|
||||||
|
|
||||||
if not ignore_permissions and not frappe.has_permission(party_type, "read", party):
|
if not ignore_permissions and not frappe.has_permission(party_type, "read", party):
|
||||||
frappe.throw(_("Not permitted for {0}").format(party), frappe.PermissionError)
|
frappe.throw(_("Not permitted for {0}").format(party), frappe.PermissionError)
|
||||||
@@ -46,76 +46,81 @@ def _get_party_details(party=None, account=None, party_type="Customer", company=
|
|||||||
party = frappe.get_doc(party_type, party)
|
party = frappe.get_doc(party_type, party)
|
||||||
currency = party.default_currency if party.get("default_currency") else get_company_currency(company)
|
currency = party.default_currency if party.get("default_currency") else get_company_currency(company)
|
||||||
|
|
||||||
party_address, shipping_address = set_address_details(out, party, party_type, doctype, company, party_address, shipping_address)
|
party_address, shipping_address = set_address_details(party_details, party, party_type, doctype, company, party_address, company_address, shipping_address)
|
||||||
set_contact_details(out, party, party_type)
|
set_contact_details(party_details, party, party_type)
|
||||||
set_other_values(out, party, party_type)
|
set_other_values(party_details, party, party_type)
|
||||||
set_price_list(out, party, party_type, price_list, pos_profile)
|
set_price_list(party_details, party, party_type, price_list, pos_profile)
|
||||||
|
|
||||||
out["tax_category"] = get_address_tax_category(party.get("tax_category"),
|
party_details["tax_category"] = get_address_tax_category(party.get("tax_category"),
|
||||||
party_address, shipping_address if party_type != "Supplier" else party_address)
|
party_address, shipping_address if party_type != "Supplier" else party_address)
|
||||||
out["taxes_and_charges"] = set_taxes(party.name, party_type, posting_date, company,
|
|
||||||
customer_group=out.customer_group, supplier_group=out.supplier_group, tax_category=out.tax_category,
|
if not party_details.get("taxes_and_charges"):
|
||||||
billing_address=party_address, shipping_address=shipping_address)
|
party_details["taxes_and_charges"] = set_taxes(party.name, party_type, posting_date, company,
|
||||||
|
customer_group=party_details.customer_group, supplier_group=party_details.supplier_group, tax_category=party_details.tax_category,
|
||||||
|
billing_address=party_address, shipping_address=shipping_address)
|
||||||
|
|
||||||
if fetch_payment_terms_template:
|
if fetch_payment_terms_template:
|
||||||
out["payment_terms_template"] = get_pyt_term_template(party.name, party_type, company)
|
party_details["payment_terms_template"] = get_pyt_term_template(party.name, party_type, company)
|
||||||
|
|
||||||
if not out.get("currency"):
|
if not party_details.get("currency"):
|
||||||
out["currency"] = currency
|
party_details["currency"] = currency
|
||||||
|
|
||||||
# sales team
|
# sales team
|
||||||
if party_type=="Customer":
|
if party_type=="Customer":
|
||||||
out["sales_team"] = [{
|
party_details["sales_team"] = [{
|
||||||
"sales_person": d.sales_person,
|
"sales_person": d.sales_person,
|
||||||
"allocated_percentage": d.allocated_percentage or None
|
"allocated_percentage": d.allocated_percentage or None
|
||||||
} for d in party.get("sales_team")]
|
} for d in party.get("sales_team")]
|
||||||
|
|
||||||
# supplier tax withholding category
|
# supplier tax withholding category
|
||||||
if party_type == "Supplier" and party:
|
if party_type == "Supplier" and party:
|
||||||
out["supplier_tds"] = frappe.get_value(party_type, party.name, "tax_withholding_category")
|
party_details["supplier_tds"] = frappe.get_value(party_type, party.name, "tax_withholding_category")
|
||||||
|
|
||||||
return out
|
return party_details
|
||||||
|
|
||||||
def set_address_details(out, party, party_type, doctype=None, company=None, party_address=None, shipping_address=None):
|
def set_address_details(party_details, party, party_type, doctype=None, company=None, party_address=None, company_address=None, shipping_address=None):
|
||||||
billing_address_field = "customer_address" if party_type == "Lead" \
|
billing_address_field = "customer_address" if party_type == "Lead" \
|
||||||
else party_type.lower() + "_address"
|
else party_type.lower() + "_address"
|
||||||
out[billing_address_field] = party_address or get_default_address(party_type, party.name)
|
party_details[billing_address_field] = party_address or get_default_address(party_type, party.name)
|
||||||
if doctype:
|
if doctype:
|
||||||
out.update(get_fetch_values(doctype, billing_address_field, out[billing_address_field]))
|
party_details.update(get_fetch_values(doctype, billing_address_field, party_details[billing_address_field]))
|
||||||
# address display
|
# address display
|
||||||
out.address_display = get_address_display(out[billing_address_field])
|
party_details.address_display = get_address_display(party_details[billing_address_field])
|
||||||
# shipping address
|
# shipping address
|
||||||
if party_type in ["Customer", "Lead"]:
|
if party_type in ["Customer", "Lead"]:
|
||||||
out.shipping_address_name = shipping_address or get_party_shipping_address(party_type, party.name)
|
party_details.shipping_address_name = shipping_address or get_party_shipping_address(party_type, party.name)
|
||||||
out.shipping_address = get_address_display(out["shipping_address_name"])
|
party_details.shipping_address = get_address_display(party_details["shipping_address_name"])
|
||||||
if doctype:
|
if doctype:
|
||||||
out.update(get_fetch_values(doctype, 'shipping_address_name', out.shipping_address_name))
|
party_details.update(get_fetch_values(doctype, 'shipping_address_name', party_details.shipping_address_name))
|
||||||
|
|
||||||
if doctype and doctype in ['Delivery Note', 'Sales Invoice']:
|
if company_address:
|
||||||
out.update(get_company_address(company))
|
party_details.update({'company_address': company_address})
|
||||||
if out.company_address:
|
else:
|
||||||
out.update(get_fetch_values(doctype, 'company_address', out.company_address))
|
party_details.update(get_company_address(company))
|
||||||
get_regional_address_details(out, doctype, company)
|
|
||||||
|
|
||||||
elif doctype and doctype == "Purchase Invoice":
|
if doctype and doctype in ['Delivery Note', 'Sales Invoice', 'Sales Order']:
|
||||||
out.update(get_company_address(company))
|
if party_details.company_address:
|
||||||
if out.company_address:
|
party_details.update(get_fetch_values(doctype, 'company_address', party_details.company_address))
|
||||||
out["shipping_address"] = shipping_address or out["company_address"]
|
get_regional_address_details(party_details, doctype, company)
|
||||||
out.shipping_address_display = get_address_display(out["shipping_address"])
|
|
||||||
out.update(get_fetch_values(doctype, 'shipping_address', out.shipping_address))
|
|
||||||
get_regional_address_details(out, doctype, company)
|
|
||||||
|
|
||||||
return out.get(billing_address_field), out.shipping_address_name
|
elif doctype and doctype in ["Purchase Invoice", "Purchase Order", "Purchase Receipt"]:
|
||||||
|
if party_details.company_address:
|
||||||
|
party_details["shipping_address"] = shipping_address or party_details["company_address"]
|
||||||
|
party_details.shipping_address_display = get_address_display(party_details["shipping_address"])
|
||||||
|
party_details.update(get_fetch_values(doctype, 'shipping_address', party_details.shipping_address))
|
||||||
|
get_regional_address_details(party_details, doctype, company)
|
||||||
|
|
||||||
|
return party_details.get(billing_address_field), party_details.shipping_address_name
|
||||||
|
|
||||||
@erpnext.allow_regional
|
@erpnext.allow_regional
|
||||||
def get_regional_address_details(out, doctype, company):
|
def get_regional_address_details(party_details, doctype, company):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def set_contact_details(out, party, party_type):
|
def set_contact_details(party_details, party, party_type):
|
||||||
out.contact_person = get_default_contact(party_type, party.name)
|
party_details.contact_person = get_default_contact(party_type, party.name)
|
||||||
|
|
||||||
if not out.contact_person:
|
if not party_details.contact_person:
|
||||||
out.update({
|
party_details.update({
|
||||||
"contact_person": None,
|
"contact_person": None,
|
||||||
"contact_display": None,
|
"contact_display": None,
|
||||||
"contact_email": None,
|
"contact_email": None,
|
||||||
@@ -125,22 +130,22 @@ def set_contact_details(out, party, party_type):
|
|||||||
"contact_department": None
|
"contact_department": None
|
||||||
})
|
})
|
||||||
else:
|
else:
|
||||||
out.update(get_contact_details(out.contact_person))
|
party_details.update(get_contact_details(party_details.contact_person))
|
||||||
|
|
||||||
def set_other_values(out, party, party_type):
|
def set_other_values(party_details, party, party_type):
|
||||||
# copy
|
# copy
|
||||||
if party_type=="Customer":
|
if party_type=="Customer":
|
||||||
to_copy = ["customer_name", "customer_group", "territory", "language"]
|
to_copy = ["customer_name", "customer_group", "territory", "language"]
|
||||||
else:
|
else:
|
||||||
to_copy = ["supplier_name", "supplier_group", "language"]
|
to_copy = ["supplier_name", "supplier_group", "language"]
|
||||||
for f in to_copy:
|
for f in to_copy:
|
||||||
out[f] = party.get(f)
|
party_details[f] = party.get(f)
|
||||||
|
|
||||||
# fields prepended with default in Customer doctype
|
# fields prepended with default in Customer doctype
|
||||||
for f in ['currency'] \
|
for f in ['currency'] \
|
||||||
+ (['sales_partner', 'commission_rate'] if party_type=="Customer" else []):
|
+ (['sales_partner', 'commission_rate'] if party_type=="Customer" else []):
|
||||||
if party.get("default_" + f):
|
if party.get("default_" + f):
|
||||||
out[f] = party.get("default_" + f)
|
party_details[f] = party.get("default_" + f)
|
||||||
|
|
||||||
def get_default_price_list(party):
|
def get_default_price_list(party):
|
||||||
"""Return default price list for party (Document object)"""
|
"""Return default price list for party (Document object)"""
|
||||||
@@ -155,7 +160,7 @@ def get_default_price_list(party):
|
|||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def set_price_list(out, party, party_type, given_price_list, pos=None):
|
def set_price_list(party_details, party, party_type, given_price_list, pos=None):
|
||||||
# price list
|
# price list
|
||||||
price_list = get_permitted_documents('Price List')
|
price_list = get_permitted_documents('Price List')
|
||||||
|
|
||||||
@@ -173,9 +178,9 @@ def set_price_list(out, party, party_type, given_price_list, pos=None):
|
|||||||
price_list = get_default_price_list(party) or given_price_list
|
price_list = get_default_price_list(party) or given_price_list
|
||||||
|
|
||||||
if price_list:
|
if price_list:
|
||||||
out.price_list_currency = frappe.db.get_value("Price List", price_list, "currency", cache=True)
|
party_details.price_list_currency = frappe.db.get_value("Price List", price_list, "currency", cache=True)
|
||||||
|
|
||||||
out["selling_price_list" if party.doctype=="Customer" else "buying_price_list"] = price_list
|
party_details["selling_price_list" if party.doctype=="Customer" else "buying_price_list"] = price_list
|
||||||
|
|
||||||
|
|
||||||
def set_account_and_due_date(party, account, party_type, company, posting_date, bill_date, doctype):
|
def set_account_and_due_date(party, account, party_type, company, posting_date, bill_date, doctype):
|
||||||
|
|||||||
@@ -171,7 +171,7 @@ class ReceivablePayableReport(object):
|
|||||||
row.outstanding = flt(row.invoiced - row.paid - row.credit_note, self.currency_precision)
|
row.outstanding = flt(row.invoiced - row.paid - row.credit_note, self.currency_precision)
|
||||||
row.invoice_grand_total = row.invoiced
|
row.invoice_grand_total = row.invoiced
|
||||||
|
|
||||||
if abs(row.outstanding) > 0.1/10 ** self.currency_precision:
|
if abs(row.outstanding) > 1.0/10 ** self.currency_precision:
|
||||||
# non-zero oustanding, we must consider this row
|
# non-zero oustanding, we must consider this row
|
||||||
|
|
||||||
if self.is_invoice(row) and self.filters.based_on_payment_terms:
|
if self.is_invoice(row) and self.filters.based_on_payment_terms:
|
||||||
@@ -285,7 +285,7 @@ class ReceivablePayableReport(object):
|
|||||||
|
|
||||||
def set_party_details(self, row):
|
def set_party_details(self, row):
|
||||||
# customer / supplier name
|
# customer / supplier name
|
||||||
party_details = self.get_party_details(row.party)
|
party_details = self.get_party_details(row.party) or {}
|
||||||
row.update(party_details)
|
row.update(party_details)
|
||||||
if self.filters.get(scrub(self.filters.party_type)):
|
if self.filters.get(scrub(self.filters.party_type)):
|
||||||
row.currency = row.account_currency
|
row.currency = row.account_currency
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
{%
|
{%
|
||||||
var report_columns = report.get_columns_for_print();
|
var report_columns = report.get_columns_for_print();
|
||||||
|
report_columns = report_columns.filter(col => !col.hidden);
|
||||||
|
|
||||||
if (report_columns.length > 8) {
|
if (report_columns.length > 8) {
|
||||||
frappe.throw(__("Too many columns. Export the report and print it using a spreadsheet application."));
|
frappe.throw(__("Too many columns. Export the report and print it using a spreadsheet application."));
|
||||||
@@ -15,34 +16,35 @@
|
|||||||
height: 37px;
|
height: 37px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
{% var letterhead= filters.letter_head || (frappe.get_doc(":Company", filters.company) && frappe.get_doc(":Company", filters.company).default_letter_head) %}
|
|
||||||
{% if(letterhead) { %}
|
|
||||||
<div style="margin-bottom: 7px;" class="text-center">
|
|
||||||
{%= frappe.boot.letter_heads[letterhead].header %}
|
|
||||||
</div>
|
|
||||||
{% } %}
|
|
||||||
<h2 class="text-center">{%= __(report.report_name) %}</h2>
|
<h2 class="text-center">{%= __(report.report_name) %}</h2>
|
||||||
<h3 class="text-center">{%= filters.company %}</h3>
|
<h3 class="text-center">{%= filters.company %}</h3>
|
||||||
|
|
||||||
{% if 'cost_center' in filters %}
|
{% if 'cost_center' in filters %}
|
||||||
<h3 class="text-center">{%= filters.cost_center %}</h3>
|
<h3 class="text-center">{%= filters.cost_center %}</h3>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<h3 class="text-center">{%= filters.fiscal_year %}</h3>
|
<h3 class="text-center">{%= filters.fiscal_year %}</h3>
|
||||||
<h5 class="text-center">{%= __("Currency") %} : {%= filters.presentation_currency || erpnext.get_currency(filters.company) %} </h4>
|
<h5 class="text-center">
|
||||||
|
{%= __("Currency") %} : {%= filters.presentation_currency || erpnext.get_currency(filters.company) %}
|
||||||
|
</h5>
|
||||||
{% if (filters.from_date) { %}
|
{% if (filters.from_date) { %}
|
||||||
<h4 class="text-center">{%= frappe.datetime.str_to_user(filters.from_date) %} - {%= frappe.datetime.str_to_user(filters.to_date) %}</h3>
|
<h5 class="text-center">
|
||||||
|
{%= frappe.datetime.str_to_user(filters.from_date) %} - {%= frappe.datetime.str_to_user(filters.to_date) %}
|
||||||
|
</h5>
|
||||||
{% } %}
|
{% } %}
|
||||||
<hr>
|
<hr>
|
||||||
<table class="table table-bordered">
|
<table class="table table-bordered">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th style="width: {%= 100 - (report_columns.length - 2) * 13 %}%"></th>
|
<th style="width: {%= 100 - (report_columns.length - 1) * 13 %}%"></th>
|
||||||
{% for(var i=2, l=report_columns.length; i<l; i++) { %}
|
{% for (let i=1, l=report_columns.length; i<l; i++) { %}
|
||||||
<th class="text-right">{%= report_columns[i].label %}</th>
|
<th class="text-right">{%= report_columns[i].label %}</th>
|
||||||
{% } %}
|
{% } %}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for(var j=0, k=data.length-1; j<k; j++) { %}
|
{% for(let j=0, k=data.length-1; j<k; j++) { %}
|
||||||
{%
|
{%
|
||||||
var row = data[j];
|
var row = data[j];
|
||||||
var row_class = data[j].parent_account ? "" : "financial-statements-important";
|
var row_class = data[j].parent_account ? "" : "financial-statements-important";
|
||||||
@@ -52,11 +54,11 @@
|
|||||||
<td>
|
<td>
|
||||||
<span style="padding-left: {%= cint(data[j].indent) * 2 %}em">{%= row.account_name %}</span>
|
<span style="padding-left: {%= cint(data[j].indent) * 2 %}em">{%= row.account_name %}</span>
|
||||||
</td>
|
</td>
|
||||||
{% for(var i=2, l=report_columns.length; i<l; i++) { %}
|
{% for(let i=1, l=report_columns.length; i<l; i++) { %}
|
||||||
<td class="text-right">
|
<td class="text-right">
|
||||||
{% var fieldname = report_columns[i].fieldname; %}
|
{% const fieldname = report_columns[i].fieldname; %}
|
||||||
{% if (!is_null(row[fieldname])) { %}
|
{% if (!is_null(row[fieldname])) { %}
|
||||||
{%= format_currency(row[fieldname], filters.presentation_currency) %}
|
{%= frappe.format(row[fieldname], report_columns[i], {}, row) %}
|
||||||
{% } %}
|
{% } %}
|
||||||
</td>
|
</td>
|
||||||
{% } %}
|
{% } %}
|
||||||
@@ -64,4 +66,6 @@
|
|||||||
{% } %}
|
{% } %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<p class="text-right text-muted">Printed On {%= frappe.datetime.str_to_user(frappe.datetime.get_datetime_as_string()) %}</p>
|
<p class="text-right text-muted">
|
||||||
|
Printed On {%= frappe.datetime.str_to_user(frappe.datetime.get_datetime_as_string()) %}
|
||||||
|
</p>
|
||||||
|
|||||||
@@ -264,8 +264,8 @@ def filter_out_zero_value_rows(data, parent_children_map, show_zero_values=False
|
|||||||
|
|
||||||
def add_total_row(out, root_type, balance_must_be, period_list, company_currency):
|
def add_total_row(out, root_type, balance_must_be, period_list, company_currency):
|
||||||
total_row = {
|
total_row = {
|
||||||
"account_name": "'" + _("Total {0} ({1})").format(_(root_type), _(balance_must_be)) + "'",
|
"account_name": _("Total {0} ({1})").format(_(root_type), _(balance_must_be)),
|
||||||
"account": "'" + _("Total {0} ({1})").format(_(root_type), _(balance_must_be)) + "'",
|
"account": _("Total {0} ({1})").format(_(root_type), _(balance_must_be)),
|
||||||
"currency": company_currency
|
"currency": company_currency
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,32 +38,46 @@ def _execute(filters, additional_table_columns=None, additional_query_columns=No
|
|||||||
cost_center = list(set(invoice_cc_wh_map.get(inv.name, {}).get("cost_center", [])))
|
cost_center = list(set(invoice_cc_wh_map.get(inv.name, {}).get("cost_center", [])))
|
||||||
warehouse = list(set(invoice_cc_wh_map.get(inv.name, {}).get("warehouse", [])))
|
warehouse = list(set(invoice_cc_wh_map.get(inv.name, {}).get("warehouse", [])))
|
||||||
|
|
||||||
row = [
|
row = {
|
||||||
inv.name, inv.posting_date, inv.customer, inv.customer_name
|
'invoice': inv.name,
|
||||||
]
|
'posting_date': inv.posting_date,
|
||||||
|
'customer': inv.customer,
|
||||||
|
'customer_name': inv.customer_name
|
||||||
|
}
|
||||||
|
|
||||||
if additional_query_columns:
|
if additional_query_columns:
|
||||||
for col in additional_query_columns:
|
for col in additional_query_columns:
|
||||||
row.append(inv.get(col))
|
row.update({
|
||||||
|
col: inv.get(col)
|
||||||
|
})
|
||||||
|
|
||||||
|
row.update({
|
||||||
|
'customer_group': inv.get("customer_group"),
|
||||||
|
'territory': inv.get("territory"),
|
||||||
|
'tax_id': inv.get("tax_id"),
|
||||||
|
'receivable_account': inv.debit_to,
|
||||||
|
'mode_of_payment': ", ".join(mode_of_payments.get(inv.name, [])),
|
||||||
|
'project': inv.project,
|
||||||
|
'owner': inv.owner,
|
||||||
|
'remarks': inv.remarks,
|
||||||
|
'sales_order': ", ".join(sales_order),
|
||||||
|
'delivery_note': ", ".join(delivery_note),
|
||||||
|
'cost_center': ", ".join(cost_center),
|
||||||
|
'warehouse': ", ".join(warehouse),
|
||||||
|
'currency': company_currency
|
||||||
|
})
|
||||||
|
|
||||||
row +=[
|
|
||||||
inv.get("customer_group"),
|
|
||||||
inv.get("territory"),
|
|
||||||
inv.get("tax_id"),
|
|
||||||
inv.debit_to, ", ".join(mode_of_payments.get(inv.name, [])),
|
|
||||||
inv.project, inv.owner, inv.remarks,
|
|
||||||
", ".join(sales_order), ", ".join(delivery_note),", ".join(cost_center),
|
|
||||||
", ".join(warehouse), company_currency
|
|
||||||
]
|
|
||||||
# map income values
|
# map income values
|
||||||
base_net_total = 0
|
base_net_total = 0
|
||||||
for income_acc in income_accounts:
|
for income_acc in income_accounts:
|
||||||
income_amount = flt(invoice_income_map.get(inv.name, {}).get(income_acc))
|
income_amount = flt(invoice_income_map.get(inv.name, {}).get(income_acc))
|
||||||
base_net_total += income_amount
|
base_net_total += income_amount
|
||||||
row.append(income_amount)
|
row.update({
|
||||||
|
frappe.scrub(income_acc): income_amount
|
||||||
|
})
|
||||||
|
|
||||||
# net total
|
# net total
|
||||||
row.append(base_net_total or inv.base_net_total)
|
row.update({'net_total': base_net_total or inv.base_net_total})
|
||||||
|
|
||||||
# tax account
|
# tax account
|
||||||
total_tax = 0
|
total_tax = 0
|
||||||
@@ -72,10 +86,18 @@ def _execute(filters, additional_table_columns=None, additional_query_columns=No
|
|||||||
tax_amount_precision = get_field_precision(frappe.get_meta("Sales Taxes and Charges").get_field("tax_amount"), currency=company_currency) or 2
|
tax_amount_precision = get_field_precision(frappe.get_meta("Sales Taxes and Charges").get_field("tax_amount"), currency=company_currency) or 2
|
||||||
tax_amount = flt(invoice_tax_map.get(inv.name, {}).get(tax_acc), tax_amount_precision)
|
tax_amount = flt(invoice_tax_map.get(inv.name, {}).get(tax_acc), tax_amount_precision)
|
||||||
total_tax += tax_amount
|
total_tax += tax_amount
|
||||||
row.append(tax_amount)
|
row.update({
|
||||||
|
frappe.scrub(tax_acc): tax_amount
|
||||||
|
})
|
||||||
|
|
||||||
# total tax, grand total, outstanding amount & rounded total
|
# total tax, grand total, outstanding amount & rounded total
|
||||||
row += [total_tax, inv.base_grand_total, inv.base_rounded_total, inv.outstanding_amount]
|
|
||||||
|
row.update({
|
||||||
|
'tax_total': total_tax,
|
||||||
|
'grand_total': inv.base_grand_total,
|
||||||
|
'rounded_total': inv.base_rounded_total,
|
||||||
|
'outstanding_amount': inv.outstanding_amount
|
||||||
|
})
|
||||||
|
|
||||||
data.append(row)
|
data.append(row)
|
||||||
|
|
||||||
@@ -84,19 +106,118 @@ def _execute(filters, additional_table_columns=None, additional_query_columns=No
|
|||||||
def get_columns(invoice_list, additional_table_columns):
|
def get_columns(invoice_list, additional_table_columns):
|
||||||
"""return columns based on filters"""
|
"""return columns based on filters"""
|
||||||
columns = [
|
columns = [
|
||||||
_("Invoice") + ":Link/Sales Invoice:120", _("Posting Date") + ":Date:80",
|
{
|
||||||
_("Customer") + ":Link/Customer:120", _("Customer Name") + "::120"
|
'label': _("Invoice"),
|
||||||
|
'fieldname': 'invoice',
|
||||||
|
'fieldtype': 'Link',
|
||||||
|
'options': 'Sales Invoice',
|
||||||
|
'width': 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': _("Posting Date"),
|
||||||
|
'fieldname': 'posting_date',
|
||||||
|
'fieldtype': 'Date',
|
||||||
|
'width': 80
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': _("Customer"),
|
||||||
|
'fieldname': 'customer',
|
||||||
|
'fieldtype': 'Link',
|
||||||
|
'options': 'Customer',
|
||||||
|
'width': 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': _("Customer Name"),
|
||||||
|
'fieldname': 'customer_name',
|
||||||
|
'fieldtype': 'Data',
|
||||||
|
'width': 120
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
if additional_table_columns:
|
if additional_table_columns:
|
||||||
columns += additional_table_columns
|
columns += additional_table_columns
|
||||||
|
|
||||||
columns +=[
|
columns +=[
|
||||||
_("Customer Group") + ":Link/Customer Group:120", _("Territory") + ":Link/Territory:80",
|
{
|
||||||
_("Tax Id") + "::80", _("Receivable Account") + ":Link/Account:120", _("Mode of Payment") + "::120",
|
'label': _("Custmer Group"),
|
||||||
_("Project") +":Link/Project:80", _("Owner") + "::150", _("Remarks") + "::150",
|
'fieldname': 'customer_group',
|
||||||
_("Sales Order") + ":Link/Sales Order:100", _("Delivery Note") + ":Link/Delivery Note:100",
|
'fieldtype': 'Link',
|
||||||
_("Cost Center") + ":Link/Cost Center:100", _("Warehouse") + ":Link/Warehouse:100",
|
'options': 'Customer Group',
|
||||||
|
'width': 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': _("Territory"),
|
||||||
|
'fieldname': 'territory',
|
||||||
|
'fieldtype': 'Link',
|
||||||
|
'options': 'Territory',
|
||||||
|
'width': 80
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': _("Tax Id"),
|
||||||
|
'fieldname': 'tax_id',
|
||||||
|
'fieldtype': 'Data',
|
||||||
|
'width': 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': _("Receivable Account"),
|
||||||
|
'fieldname': 'receivable_account',
|
||||||
|
'fieldtype': 'Link',
|
||||||
|
'options': 'Account',
|
||||||
|
'width': 80
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': _("Mode Of Payment"),
|
||||||
|
'fieldname': 'mode_of_payment',
|
||||||
|
'fieldtype': 'Data',
|
||||||
|
'width': 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': _("Project"),
|
||||||
|
'fieldname': 'project',
|
||||||
|
'fieldtype': 'Link',
|
||||||
|
'options': 'project',
|
||||||
|
'width': 80
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': _("Owner"),
|
||||||
|
'fieldname': 'owner',
|
||||||
|
'fieldtype': 'Data',
|
||||||
|
'width': 150
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': _("Remarks"),
|
||||||
|
'fieldname': 'remarks',
|
||||||
|
'fieldtype': 'Data',
|
||||||
|
'width': 150
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': _("Sales Order"),
|
||||||
|
'fieldname': 'sales_order',
|
||||||
|
'fieldtype': 'Link',
|
||||||
|
'options': 'Sales Order',
|
||||||
|
'width': 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': _("Delivery Note"),
|
||||||
|
'fieldname': 'delivery_note',
|
||||||
|
'fieldtype': 'Link',
|
||||||
|
'options': 'Delivery Note',
|
||||||
|
'width': 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': _("Cost Center"),
|
||||||
|
'fieldname': 'cost_center',
|
||||||
|
'fieldtype': 'Link',
|
||||||
|
'options': 'Cost Center',
|
||||||
|
'width': 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': _("Warehouse"),
|
||||||
|
'fieldname': 'warehouse',
|
||||||
|
'fieldtype': 'Link',
|
||||||
|
'options': 'Warehouse',
|
||||||
|
'width': 100
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "currency",
|
"fieldname": "currency",
|
||||||
"label": _("Currency"),
|
"label": _("Currency"),
|
||||||
@@ -105,7 +226,10 @@ def get_columns(invoice_list, additional_table_columns):
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
income_accounts = tax_accounts = income_columns = tax_columns = []
|
income_accounts = []
|
||||||
|
tax_accounts = []
|
||||||
|
income_columns = []
|
||||||
|
tax_columns = []
|
||||||
|
|
||||||
if invoice_list:
|
if invoice_list:
|
||||||
income_accounts = frappe.db.sql_list("""select distinct income_account
|
income_accounts = frappe.db.sql_list("""select distinct income_account
|
||||||
@@ -119,14 +243,65 @@ def get_columns(invoice_list, additional_table_columns):
|
|||||||
and parent in (%s) order by account_head""" %
|
and parent in (%s) order by account_head""" %
|
||||||
', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]))
|
', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]))
|
||||||
|
|
||||||
income_columns = [(account + ":Currency/currency:120") for account in income_accounts]
|
for account in income_accounts:
|
||||||
|
income_columns.append({
|
||||||
|
"label": account,
|
||||||
|
"fieldname": frappe.scrub(account),
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"options": 'currency',
|
||||||
|
"width": 120
|
||||||
|
})
|
||||||
|
|
||||||
for account in tax_accounts:
|
for account in tax_accounts:
|
||||||
if account not in income_accounts:
|
if account not in income_accounts:
|
||||||
tax_columns.append(account + ":Currency/currency:120")
|
tax_columns.append({
|
||||||
|
"label": account,
|
||||||
|
"fieldname": frappe.scrub(account),
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"options": 'currency',
|
||||||
|
"width": 120
|
||||||
|
})
|
||||||
|
|
||||||
columns = columns + income_columns + [_("Net Total") + ":Currency/currency:120"] + tax_columns + \
|
net_total_column = [{
|
||||||
[_("Total Tax") + ":Currency/currency:120", _("Grand Total") + ":Currency/currency:120",
|
"label": _("Net Total"),
|
||||||
_("Rounded Total") + ":Currency/currency:120", _("Outstanding Amount") + ":Currency/currency:120"]
|
"fieldname": "net_total",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"options": 'currency',
|
||||||
|
"width": 120
|
||||||
|
}]
|
||||||
|
|
||||||
|
total_columns = [
|
||||||
|
{
|
||||||
|
"label": _("Tax Total"),
|
||||||
|
"fieldname": "tax_total",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"options": 'currency',
|
||||||
|
"width": 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Grand Total"),
|
||||||
|
"fieldname": "grand_total",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"options": 'currency',
|
||||||
|
"width": 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Rounded Total"),
|
||||||
|
"fieldname": "rounded_total",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"options": 'currency',
|
||||||
|
"width": 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Outstanding Amount"),
|
||||||
|
"fieldname": "outstanding_amount",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"options": 'currency',
|
||||||
|
"width": 120
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
columns = columns + income_columns + net_total_column + tax_columns + total_columns
|
||||||
|
|
||||||
return columns, income_accounts, tax_accounts
|
return columns, income_accounts, tax_accounts
|
||||||
|
|
||||||
|
|||||||
@@ -144,6 +144,10 @@ frappe.ui.form.on('Asset', {
|
|||||||
frm.set_df_property('purchase_invoice', 'read_only', 1);
|
frm.set_df_property('purchase_invoice', 'read_only', 1);
|
||||||
frm.set_df_property('purchase_receipt', 'read_only', 1);
|
frm.set_df_property('purchase_receipt', 'read_only', 1);
|
||||||
}
|
}
|
||||||
|
else if (frm.doc.is_existing_asset) {
|
||||||
|
frm.toggle_reqd('purchase_receipt', 0);
|
||||||
|
frm.toggle_reqd('purchase_invoice', 0);
|
||||||
|
}
|
||||||
else if (frm.doc.purchase_receipt) {
|
else if (frm.doc.purchase_receipt) {
|
||||||
// if purchase receipt link is set then set PI disabled
|
// if purchase receipt link is set then set PI disabled
|
||||||
frm.toggle_reqd('purchase_invoice', 0);
|
frm.toggle_reqd('purchase_invoice', 0);
|
||||||
@@ -256,6 +260,7 @@ frappe.ui.form.on('Asset', {
|
|||||||
},
|
},
|
||||||
|
|
||||||
is_existing_asset: function(frm) {
|
is_existing_asset: function(frm) {
|
||||||
|
frm.trigger("toggle_reference_doc");
|
||||||
// frm.toggle_reqd("next_depreciation_date", (!frm.doc.is_existing_asset && frm.doc.calculate_depreciation));
|
// frm.toggle_reqd("next_depreciation_date", (!frm.doc.is_existing_asset && frm.doc.calculate_depreciation));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -610,13 +610,19 @@ def get_asset_account(account_name, asset=None, asset_category=None, company=Non
|
|||||||
if asset:
|
if asset:
|
||||||
account = get_asset_category_account(account_name, asset=asset,
|
account = get_asset_category_account(account_name, asset=asset,
|
||||||
asset_category = asset_category, company = company)
|
asset_category = asset_category, company = company)
|
||||||
|
|
||||||
|
if not asset and not account:
|
||||||
|
account = get_asset_category_account(account_name, asset_category = asset_category, company = company)
|
||||||
|
|
||||||
if not account:
|
if not account:
|
||||||
account = frappe.get_cached_value('Company', company, account_name)
|
account = frappe.get_cached_value('Company', company, account_name)
|
||||||
|
|
||||||
if not account:
|
if not account:
|
||||||
frappe.throw(_("Set {0} in asset category {1} or company {2}")
|
if not asset_category:
|
||||||
.format(account_name.replace('_', ' ').title(), asset_category, company))
|
frappe.throw(_("Set {0} in company {2}").format(account_name.replace('_', ' ').title(), company))
|
||||||
|
else:
|
||||||
|
frappe.throw(_("Set {0} in asset category {1} or company {2}")
|
||||||
|
.format(account_name.replace('_', ' ').title(), asset_category, company))
|
||||||
|
|
||||||
return account
|
return account
|
||||||
|
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ class AssetMovement(Document):
|
|||||||
ORDER BY
|
ORDER BY
|
||||||
asm.transaction_date asc
|
asm.transaction_date asc
|
||||||
""", (d.asset, self.company, 'Receipt'), as_dict=1)
|
""", (d.asset, self.company, 'Receipt'), as_dict=1)
|
||||||
if auto_gen_movement_entry[0].get('name') == self.name:
|
if auto_gen_movement_entry and auto_gen_movement_entry[0].get('name') == self.name:
|
||||||
frappe.throw(_('{0} will be cancelled automatically on asset cancellation as it was \
|
frappe.throw(_('{0} will be cancelled automatically on asset cancellation as it was \
|
||||||
auto generated for Asset {1}').format(self.name, d.asset))
|
auto generated for Asset {1}').format(self.name, d.asset))
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"actions": [],
|
||||||
"allow_import": 1,
|
"allow_import": 1,
|
||||||
"autoname": "naming_series:",
|
"autoname": "naming_series:",
|
||||||
"creation": "2013-05-21 16:16:39",
|
"creation": "2013-05-21 16:16:39",
|
||||||
@@ -47,6 +48,7 @@
|
|||||||
"ignore_pricing_rule",
|
"ignore_pricing_rule",
|
||||||
"sec_warehouse",
|
"sec_warehouse",
|
||||||
"set_warehouse",
|
"set_warehouse",
|
||||||
|
"set_reserve_warehouse",
|
||||||
"col_break_warehouse",
|
"col_break_warehouse",
|
||||||
"is_subcontracted",
|
"is_subcontracted",
|
||||||
"supplier_warehouse",
|
"supplier_warehouse",
|
||||||
@@ -1039,12 +1041,20 @@
|
|||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Tax Category",
|
"label": "Tax Category",
|
||||||
"options": "Tax Category"
|
"options": "Tax Category"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "supplied_items",
|
||||||
|
"fieldname": "set_reserve_warehouse",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Set Reserve Warehouse",
|
||||||
|
"options": "Warehouse"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "fa fa-file-text",
|
"icon": "fa fa-file-text",
|
||||||
"idx": 105,
|
"idx": 105,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"modified": "2019-07-11 18:25:49.509343",
|
"links": [],
|
||||||
|
"modified": "2019-12-18 13:13:22.852412",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Buying",
|
"module": "Buying",
|
||||||
"name": "Purchase Order",
|
"name": "Purchase Order",
|
||||||
|
|||||||
3
erpnext/buying/doctype/purchase_order/regional/india.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{% include "erpnext/regional/india/taxes.js" %}
|
||||||
|
|
||||||
|
erpnext.setup_auto_gst_taxation('Purchase Order');
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"actions": [],
|
||||||
"autoname": "hash",
|
"autoname": "hash",
|
||||||
"creation": "2013-05-24 19:29:06",
|
"creation": "2013-05-24 19:29:06",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
@@ -43,6 +44,7 @@
|
|||||||
"base_amount",
|
"base_amount",
|
||||||
"pricing_rules",
|
"pricing_rules",
|
||||||
"is_free_item",
|
"is_free_item",
|
||||||
|
"is_fixed_asset",
|
||||||
"section_break_29",
|
"section_break_29",
|
||||||
"net_rate",
|
"net_rate",
|
||||||
"net_amount",
|
"net_amount",
|
||||||
@@ -708,11 +710,20 @@
|
|||||||
"fieldname": "against_blanket_order",
|
"fieldname": "against_blanket_order",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Against Blanket Order"
|
"label": "Against Blanket Order"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fetch_from": "item_code.is_fixed_asset",
|
||||||
|
"fieldname": "is_fixed_asset",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Is Fixed Asset",
|
||||||
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"modified": "2019-11-19 14:10:52.865006",
|
"links": [],
|
||||||
|
"modified": "2019-12-06 13:17:12.142799",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Buying",
|
"module": "Buying",
|
||||||
"name": "Purchase Order Item",
|
"name": "Purchase Order Item",
|
||||||
|
|||||||
@@ -3,18 +3,19 @@
|
|||||||
"app": "ERPNext",
|
"app": "ERPNext",
|
||||||
"creation": "2019-11-15 14:45:32.626641",
|
"creation": "2019-11-15 14:45:32.626641",
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "Setup Wizard Slide",
|
"doctype": "Onboarding Slide",
|
||||||
"domains": [],
|
"domains": [],
|
||||||
"help_links": [
|
"help_links": [
|
||||||
{
|
{
|
||||||
"label": "Supplier",
|
"label": "Learn More",
|
||||||
"video_id": "zsrrVDk6VBs"
|
"video_id": "zsrrVDk6VBs"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
"image_src": "/assets/erpnext/images/illustrations/supplier.png",
|
"image_src": "",
|
||||||
|
"is_completed": 0,
|
||||||
"max_count": 3,
|
"max_count": 3,
|
||||||
"modified": "2019-11-26 18:26:25.498325",
|
"modified": "2019-12-09 17:54:18.452038",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"name": "Add A Few Suppliers",
|
"name": "Add A Few Suppliers",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
@@ -44,6 +45,5 @@
|
|||||||
],
|
],
|
||||||
"slide_order": 50,
|
"slide_order": 50,
|
||||||
"slide_title": "Add A Few Suppliers",
|
"slide_title": "Add A Few Suppliers",
|
||||||
"slide_type": "Create",
|
"slide_type": "Create"
|
||||||
"submit_method": ""
|
|
||||||
}
|
}
|
||||||
@@ -319,8 +319,8 @@ class AccountsController(TransactionBase):
|
|||||||
if item.get('discount_amount'):
|
if item.get('discount_amount'):
|
||||||
item.rate = item.price_list_rate - item.discount_amount
|
item.rate = item.price_list_rate - item.discount_amount
|
||||||
|
|
||||||
elif pricing_rule_args.get('free_item'):
|
elif pricing_rule_args.get('free_item_data'):
|
||||||
apply_pricing_rule_for_free_items(self, pricing_rule_args)
|
apply_pricing_rule_for_free_items(self, pricing_rule_args.get('free_item_data'))
|
||||||
|
|
||||||
elif pricing_rule_args.get("validate_applied_rule"):
|
elif pricing_rule_args.get("validate_applied_rule"):
|
||||||
for pricing_rule in get_applied_pricing_rules(item):
|
for pricing_rule in get_applied_pricing_rules(item):
|
||||||
|
|||||||
@@ -265,16 +265,17 @@ class BuyingController(StockController):
|
|||||||
|
|
||||||
fg_yet_to_be_received = qty_to_be_received_map.get(item_key)
|
fg_yet_to_be_received = qty_to_be_received_map.get(item_key)
|
||||||
|
|
||||||
raw_material_data = backflushed_raw_materials_map.get(item_key, {})
|
|
||||||
|
|
||||||
consumed_qty = raw_material_data.get('qty', 0)
|
|
||||||
consumed_serial_nos = raw_material_data.get('serial_nos', '')
|
|
||||||
consumed_batch_nos = raw_material_data.get('batch_nos', '')
|
|
||||||
|
|
||||||
transferred_batch_qty_map = get_transferred_batch_qty_map(item.purchase_order, item.item_code)
|
transferred_batch_qty_map = get_transferred_batch_qty_map(item.purchase_order, item.item_code)
|
||||||
backflushed_batch_qty_map = get_backflushed_batch_qty_map(item.purchase_order, item.item_code)
|
backflushed_batch_qty_map = get_backflushed_batch_qty_map(item.purchase_order, item.item_code)
|
||||||
|
|
||||||
for raw_material in transferred_raw_materials + non_stock_items:
|
for raw_material in transferred_raw_materials + non_stock_items:
|
||||||
|
rm_item_key = '{}{}'.format(raw_material.rm_item_code, item.purchase_order)
|
||||||
|
raw_material_data = backflushed_raw_materials_map.get(rm_item_key, {})
|
||||||
|
|
||||||
|
consumed_qty = raw_material_data.get('qty', 0)
|
||||||
|
consumed_serial_nos = raw_material_data.get('serial_nos', '')
|
||||||
|
consumed_batch_nos = raw_material_data.get('batch_nos', '')
|
||||||
|
|
||||||
transferred_qty = raw_material.qty
|
transferred_qty = raw_material.qty
|
||||||
|
|
||||||
rm_qty_to_be_consumed = transferred_qty - consumed_qty
|
rm_qty_to_be_consumed = transferred_qty - consumed_qty
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ from datetime import timedelta
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.utils import get_url
|
from frappe.utils import get_url, getdate
|
||||||
from frappe.utils.verified_command import verify_request, get_signed_params
|
from frappe.utils.verified_command import verify_request, get_signed_params
|
||||||
|
|
||||||
|
|
||||||
@@ -117,7 +117,7 @@ class Appointment(Document):
|
|||||||
if self._assign:
|
if self._assign:
|
||||||
return
|
return
|
||||||
available_agents = _get_agents_sorted_by_asc_workload(
|
available_agents = _get_agents_sorted_by_asc_workload(
|
||||||
self.scheduled_time.date())
|
getdate(self.scheduled_time))
|
||||||
for agent in available_agents:
|
for agent in available_agents:
|
||||||
if(_check_agent_availability(agent, self.scheduled_time)):
|
if(_check_agent_availability(agent, self.scheduled_time)):
|
||||||
agent = agent[0]
|
agent = agent[0]
|
||||||
@@ -171,7 +171,7 @@ class Appointment(Document):
|
|||||||
self.save(ignore_permissions=True)
|
self.save(ignore_permissions=True)
|
||||||
|
|
||||||
def _get_verify_url(self):
|
def _get_verify_url(self):
|
||||||
verify_route = '/book-appointment/verify'
|
verify_route = '/book_appointment/verify'
|
||||||
params = {
|
params = {
|
||||||
'email': self.customer_email,
|
'email': self.customer_email,
|
||||||
'appointment': self.name
|
'appointment': self.name
|
||||||
@@ -189,7 +189,7 @@ def _get_agents_sorted_by_asc_workload(date):
|
|||||||
assigned_to = frappe.parse_json(appointment._assign)
|
assigned_to = frappe.parse_json(appointment._assign)
|
||||||
if not assigned_to:
|
if not assigned_to:
|
||||||
continue
|
continue
|
||||||
if (assigned_to[0] in agent_list) and appointment.scheduled_time.date() == date:
|
if (assigned_to[0] in agent_list) and getdate(appointment.scheduled_time) == date:
|
||||||
appointment_counter[assigned_to[0]] += 1
|
appointment_counter[assigned_to[0]] += 1
|
||||||
sorted_agent_list = appointment_counter.most_common()
|
sorted_agent_list = appointment_counter.most_common()
|
||||||
sorted_agent_list.reverse()
|
sorted_agent_list.reverse()
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ frappe.ui.form.on('Student', {
|
|||||||
frappe.ui.form.on('Student Guardian', {
|
frappe.ui.form.on('Student Guardian', {
|
||||||
guardians_add: function(frm){
|
guardians_add: function(frm){
|
||||||
frm.fields_dict['guardians'].grid.get_field('guardian').get_query = function(doc){
|
frm.fields_dict['guardians'].grid.get_field('guardian').get_query = function(doc){
|
||||||
var guardian_list = [];
|
let guardian_list = [];
|
||||||
if(!doc.__islocal) guardian_list.push(doc.guardian);
|
if(!doc.__islocal) guardian_list.push(doc.guardian);
|
||||||
$.each(doc.guardians, function(idx, val){
|
$.each(doc.guardians, function(idx, val){
|
||||||
if (val.guardian) guardian_list.push(val.guardian);
|
if (val.guardian) guardian_list.push(val.guardian);
|
||||||
@@ -40,3 +40,18 @@ frappe.ui.form.on('Student Guardian', {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
frappe.ui.form.on('Student Sibling', {
|
||||||
|
siblings_add: function(frm){
|
||||||
|
frm.fields_dict['siblings'].grid.get_field('student').get_query = function(doc){
|
||||||
|
let sibling_list = [frm.doc.name];
|
||||||
|
$.each(doc.siblings, function(idx, val){
|
||||||
|
if (val.student && val.studying_in_same_institute == 'YES') {
|
||||||
|
sibling_list.push(val.student);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return { filters: [['Student', 'name', 'not in', sibling_list]] };
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -5,12 +5,14 @@
|
|||||||
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.utils import getdate,today
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.desk.form.linked_with import get_linked_doctypes
|
from frappe.desk.form.linked_with import get_linked_doctypes
|
||||||
from erpnext.education.utils import check_content_completion, check_quiz_completion
|
from erpnext.education.utils import check_content_completion, check_quiz_completion
|
||||||
class Student(Document):
|
class Student(Document):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.title = " ".join(filter(None, [self.first_name, self.middle_name, self.last_name]))
|
self.title = " ".join(filter(None, [self.first_name, self.middle_name, self.last_name]))
|
||||||
|
self.validate_dates()
|
||||||
|
|
||||||
if self.student_applicant:
|
if self.student_applicant:
|
||||||
self.check_unique()
|
self.check_unique()
|
||||||
@@ -19,6 +21,10 @@ class Student(Document):
|
|||||||
if frappe.get_value("Student", self.name, "title") != self.title:
|
if frappe.get_value("Student", self.name, "title") != self.title:
|
||||||
self.update_student_name_in_linked_doctype()
|
self.update_student_name_in_linked_doctype()
|
||||||
|
|
||||||
|
def validate_dates(self):
|
||||||
|
if self.date_of_birth and getdate(self.date_of_birth) >= getdate(today()):
|
||||||
|
frappe.throw(_("Date of Birth cannot be greater than today."))
|
||||||
|
|
||||||
def update_student_name_in_linked_doctype(self):
|
def update_student_name_in_linked_doctype(self):
|
||||||
linked_doctypes = get_linked_doctypes("Student")
|
linked_doctypes = get_linked_doctypes("Student")
|
||||||
for d in linked_doctypes:
|
for d in linked_doctypes:
|
||||||
|
|||||||
@@ -180,6 +180,7 @@ standard_portal_menu_items = [
|
|||||||
{"title": _("Admission"), "route": "/admissions", "reference_doctype": "Student Admission", "role": "Student"},
|
{"title": _("Admission"), "route": "/admissions", "reference_doctype": "Student Admission", "role": "Student"},
|
||||||
{"title": _("Certification"), "route": "/certification", "reference_doctype": "Certification Application", "role": "Non Profit Portal User"},
|
{"title": _("Certification"), "route": "/certification", "reference_doctype": "Certification Application", "role": "Non Profit Portal User"},
|
||||||
{"title": _("Material Request"), "route": "/material-requests", "reference_doctype": "Material Request", "role": "Customer"},
|
{"title": _("Material Request"), "route": "/material-requests", "reference_doctype": "Material Request", "role": "Customer"},
|
||||||
|
{"title": _("Appointment Booking"), "route": "/book_appointment"},
|
||||||
]
|
]
|
||||||
|
|
||||||
default_roles = [
|
default_roles = [
|
||||||
@@ -246,10 +247,10 @@ doc_events = {
|
|||||||
"on_trash": "erpnext.regional.check_deletion_permission"
|
"on_trash": "erpnext.regional.check_deletion_permission"
|
||||||
},
|
},
|
||||||
'Address': {
|
'Address': {
|
||||||
'validate': ['erpnext.regional.india.utils.validate_gstin_for_india', 'erpnext.regional.italy.utils.set_state_code']
|
'validate': ['erpnext.regional.india.utils.validate_gstin_for_india', 'erpnext.regional.italy.utils.set_state_code', 'erpnext.regional.india.utils.update_gst_category']
|
||||||
},
|
},
|
||||||
('Sales Invoice', 'Purchase Invoice', 'Delivery Note'): {
|
('Sales Invoice', 'Sales Order', 'Delivery Note', 'Purchase Invoice', 'Purchase Order', 'Purchase Receipt'): {
|
||||||
'validate': 'erpnext.regional.india.utils.set_place_of_supply'
|
'validate': ['erpnext.regional.india.utils.set_place_of_supply']
|
||||||
},
|
},
|
||||||
"Contact": {
|
"Contact": {
|
||||||
"on_trash": "erpnext.support.doctype.issue.issue.update_issue",
|
"on_trash": "erpnext.support.doctype.issue.issue.update_issue",
|
||||||
|
|||||||
@@ -5,9 +5,10 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.utils import date_diff, add_days, getdate
|
from frappe.utils import date_diff, add_days, getdate, cint
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from erpnext.hr.utils import validate_dates, validate_overlap, get_leave_period, get_holidays_for_employee
|
from erpnext.hr.utils import validate_dates, validate_overlap, get_leave_period, \
|
||||||
|
get_holidays_for_employee, create_additional_leave_ledger_entry
|
||||||
|
|
||||||
class CompensatoryLeaveRequest(Document):
|
class CompensatoryLeaveRequest(Document):
|
||||||
|
|
||||||
@@ -25,16 +26,14 @@ class CompensatoryLeaveRequest(Document):
|
|||||||
frappe.throw(_("Leave Type is madatory"))
|
frappe.throw(_("Leave Type is madatory"))
|
||||||
|
|
||||||
def validate_attendance(self):
|
def validate_attendance(self):
|
||||||
query = """select attendance_date, status
|
attendance = frappe.get_all('Attendance',
|
||||||
from `tabAttendance` where
|
filters={
|
||||||
attendance_date between %(work_from_date)s and %(work_end_date)s
|
'attendance_date': ['between', (self.work_from_date, self.work_end_date)],
|
||||||
and docstatus=1 and status = 'Present' and employee=%(employee)s"""
|
'status': 'Present',
|
||||||
|
'docstatus': 1,
|
||||||
|
'employee': self.employee
|
||||||
|
}, fields=['attendance_date', 'status'])
|
||||||
|
|
||||||
attendance = frappe.db.sql(query, {
|
|
||||||
"work_from_date": self.work_from_date,
|
|
||||||
"work_end_date": self.work_end_date,
|
|
||||||
"employee": self.employee
|
|
||||||
}, as_dict=True)
|
|
||||||
if len(attendance) < date_diff(self.work_end_date, self.work_from_date) + 1:
|
if len(attendance) < date_diff(self.work_end_date, self.work_from_date) + 1:
|
||||||
frappe.throw(_("You are not present all day(s) between compensatory leave request days"))
|
frappe.throw(_("You are not present all day(s) between compensatory leave request days"))
|
||||||
|
|
||||||
@@ -50,13 +49,19 @@ class CompensatoryLeaveRequest(Document):
|
|||||||
date_difference -= 0.5
|
date_difference -= 0.5
|
||||||
leave_period = get_leave_period(self.work_from_date, self.work_end_date, company)
|
leave_period = get_leave_period(self.work_from_date, self.work_end_date, company)
|
||||||
if leave_period:
|
if leave_period:
|
||||||
leave_allocation = self.exists_allocation_for_period(leave_period)
|
leave_allocation = self.get_existing_allocation_for_period(leave_period)
|
||||||
if leave_allocation:
|
if leave_allocation:
|
||||||
leave_allocation.new_leaves_allocated += date_difference
|
leave_allocation.new_leaves_allocated += date_difference
|
||||||
leave_allocation.submit()
|
leave_allocation.validate()
|
||||||
|
leave_allocation.db_set("new_leaves_allocated", leave_allocation.total_leaves_allocated)
|
||||||
|
leave_allocation.db_set("total_leaves_allocated", leave_allocation.total_leaves_allocated)
|
||||||
|
|
||||||
|
# generate additional ledger entry for the new compensatory leaves off
|
||||||
|
create_additional_leave_ledger_entry(leave_allocation, date_difference, add_days(self.work_end_date, 1))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
leave_allocation = self.create_leave_allocation(leave_period, date_difference)
|
leave_allocation = self.create_leave_allocation(leave_period, date_difference)
|
||||||
self.db_set("leave_allocation", leave_allocation.name)
|
self.leave_allocation=leave_allocation.name
|
||||||
else:
|
else:
|
||||||
frappe.throw(_("There is no leave period in between {0} and {1}").format(self.work_from_date, self.work_end_date))
|
frappe.throw(_("There is no leave period in between {0} and {1}").format(self.work_from_date, self.work_end_date))
|
||||||
|
|
||||||
@@ -68,11 +73,16 @@ class CompensatoryLeaveRequest(Document):
|
|||||||
leave_allocation = frappe.get_doc("Leave Allocation", self.leave_allocation)
|
leave_allocation = frappe.get_doc("Leave Allocation", self.leave_allocation)
|
||||||
if leave_allocation:
|
if leave_allocation:
|
||||||
leave_allocation.new_leaves_allocated -= date_difference
|
leave_allocation.new_leaves_allocated -= date_difference
|
||||||
if leave_allocation.total_leaves_allocated - date_difference <= 0:
|
if leave_allocation.new_leaves_allocated - date_difference <= 0:
|
||||||
leave_allocation.total_leaves_allocated = 0
|
leave_allocation.new_leaves_allocated = 0
|
||||||
leave_allocation.submit()
|
leave_allocation.validate()
|
||||||
|
leave_allocation.db_set("new_leaves_allocated", leave_allocation.total_leaves_allocated)
|
||||||
|
leave_allocation.db_set("total_leaves_allocated", leave_allocation.total_leaves_allocated)
|
||||||
|
|
||||||
def exists_allocation_for_period(self, leave_period):
|
# create reverse entry on cancelation
|
||||||
|
create_additional_leave_ledger_entry(leave_allocation, date_difference * -1, add_days(self.work_end_date, 1))
|
||||||
|
|
||||||
|
def get_existing_allocation_for_period(self, leave_period):
|
||||||
leave_allocation = frappe.db.sql("""
|
leave_allocation = frappe.db.sql("""
|
||||||
select name
|
select name
|
||||||
from `tabLeave Allocation`
|
from `tabLeave Allocation`
|
||||||
@@ -95,17 +105,18 @@ class CompensatoryLeaveRequest(Document):
|
|||||||
|
|
||||||
def create_leave_allocation(self, leave_period, date_difference):
|
def create_leave_allocation(self, leave_period, date_difference):
|
||||||
is_carry_forward = frappe.db.get_value("Leave Type", self.leave_type, "is_carry_forward")
|
is_carry_forward = frappe.db.get_value("Leave Type", self.leave_type, "is_carry_forward")
|
||||||
allocation = frappe.new_doc("Leave Allocation")
|
allocation = frappe.get_doc(dict(
|
||||||
allocation.employee = self.employee
|
doctype="Leave Allocation",
|
||||||
allocation.employee_name = self.employee_name
|
employee=self.employee,
|
||||||
allocation.leave_type = self.leave_type
|
employee_name=self.employee_name,
|
||||||
allocation.from_date = add_days(self.work_end_date, 1)
|
leave_type=self.leave_type,
|
||||||
allocation.to_date = leave_period[0].to_date
|
from_date=add_days(self.work_end_date, 1),
|
||||||
allocation.new_leaves_allocated = date_difference
|
to_date=leave_period[0].to_date,
|
||||||
allocation.total_leaves_allocated = date_difference
|
carry_forward=cint(is_carry_forward),
|
||||||
allocation.description = self.reason
|
new_leaves_allocated=date_difference,
|
||||||
if is_carry_forward == 1:
|
total_leaves_allocated=date_difference,
|
||||||
allocation.carry_forward = True
|
description=self.reason
|
||||||
allocation.save(ignore_permissions = True)
|
))
|
||||||
|
allocation.insert(ignore_permissions=True)
|
||||||
allocation.submit()
|
allocation.submit()
|
||||||
return allocation
|
return allocation
|
||||||
@@ -5,37 +5,128 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
import unittest
|
import unittest
|
||||||
|
from frappe.utils import today, add_months, add_days
|
||||||
|
from erpnext.hr.doctype.attendance_request.test_attendance_request import get_employee
|
||||||
|
from erpnext.hr.doctype.leave_period.test_leave_period import create_leave_period
|
||||||
|
from erpnext.hr.doctype.leave_application.leave_application import get_leave_balance_on
|
||||||
|
|
||||||
# class TestCompensatoryLeaveRequest(unittest.TestCase):
|
class TestCompensatoryLeaveRequest(unittest.TestCase):
|
||||||
# def get_compensatory_leave_request(self):
|
def setUp(self):
|
||||||
# return frappe.get_doc('Compensatory Leave Request', dict(
|
frappe.db.sql(''' delete from `tabCompensatory Leave Request`''')
|
||||||
# employee = employee,
|
frappe.db.sql(''' delete from `tabLeave Ledger Entry`''')
|
||||||
# work_from_date = today,
|
frappe.db.sql(''' delete from `tabLeave Allocation`''')
|
||||||
# work_to_date = today,
|
frappe.db.sql(''' delete from `tabAttendance` where attendance_date in {0} '''.format((today(), add_days(today(), -1)))) #nosec
|
||||||
# reason = 'test'
|
create_leave_period(add_months(today(), -3), add_months(today(), 3), "_Test Company")
|
||||||
# )).insert()
|
create_holiday_list()
|
||||||
#
|
|
||||||
# def test_creation_of_leave_allocation(self):
|
employee = get_employee()
|
||||||
# employee = get_employee()
|
employee.holiday_list = "_Test Compensatory Leave"
|
||||||
# today = get_today()
|
employee.save()
|
||||||
#
|
|
||||||
# compensatory_leave_request = self.get_compensatory_leave_request(today)
|
def test_leave_balance_on_submit(self):
|
||||||
#
|
''' check creation of leave allocation on submission of compensatory leave request '''
|
||||||
# before = get_leave_balance(employee, compensatory_leave_request.leave_type)
|
employee = get_employee()
|
||||||
#
|
mark_attendance(employee)
|
||||||
# compensatory_leave_request.submit()
|
compensatory_leave_request = get_compensatory_leave_request(employee.name)
|
||||||
#
|
|
||||||
# self.assertEqual(get_leave_balance(employee, compensatory_leave_request.leave_type), before + 1)
|
before = get_leave_balance_on(employee.name, compensatory_leave_request.leave_type, today())
|
||||||
#
|
compensatory_leave_request.submit()
|
||||||
# def test_max_compensatory_leave(self):
|
|
||||||
# employee = get_employee()
|
self.assertEqual(get_leave_balance_on(employee.name, compensatory_leave_request.leave_type, add_days(today(), 1)), before + 1)
|
||||||
# today = get_today()
|
|
||||||
#
|
def test_leave_allocation_update_on_submit(self):
|
||||||
# compensatory_leave_request = self.get_compensatory_leave_request()
|
employee = get_employee()
|
||||||
#
|
mark_attendance(employee, date=add_days(today(), -1))
|
||||||
# frappe.db.set_value('Leave Type', compensatory_leave_request.leave_type, 'max_leaves_allowed', 0)
|
compensatory_leave_request = get_compensatory_leave_request(employee.name, leave_date=add_days(today(), -1))
|
||||||
#
|
compensatory_leave_request.submit()
|
||||||
# self.assertRaises(MaxLeavesLimitCrossed, compensatory_leave_request.submit)
|
|
||||||
#
|
# leave allocation creation on submit
|
||||||
# frappe.db.set_value('Leave Type', compensatory_leave_request.leave_type, 'max_leaves_allowed', 10)
|
leaves_allocated = frappe.db.get_value('Leave Allocation', {
|
||||||
#
|
'name': compensatory_leave_request.leave_allocation
|
||||||
|
}, ['total_leaves_allocated'])
|
||||||
|
self.assertEqual(leaves_allocated, 1)
|
||||||
|
|
||||||
|
mark_attendance(employee)
|
||||||
|
compensatory_leave_request = get_compensatory_leave_request(employee.name)
|
||||||
|
compensatory_leave_request.submit()
|
||||||
|
|
||||||
|
# leave allocation updates on submission of second compensatory leave request
|
||||||
|
leaves_allocated = frappe.db.get_value('Leave Allocation', {
|
||||||
|
'name': compensatory_leave_request.leave_allocation
|
||||||
|
}, ['total_leaves_allocated'])
|
||||||
|
self.assertEqual(leaves_allocated, 2)
|
||||||
|
|
||||||
|
def test_creation_of_leave_ledger_entry_on_submit(self):
|
||||||
|
''' check creation of leave ledger entry on submission of leave request '''
|
||||||
|
employee = get_employee()
|
||||||
|
mark_attendance(employee)
|
||||||
|
compensatory_leave_request = get_compensatory_leave_request(employee.name)
|
||||||
|
compensatory_leave_request.submit()
|
||||||
|
|
||||||
|
filters = dict(transaction_name=compensatory_leave_request.leave_allocation)
|
||||||
|
leave_ledger_entry = frappe.get_all('Leave Ledger Entry', fields='*', filters=filters)
|
||||||
|
|
||||||
|
self.assertEquals(len(leave_ledger_entry), 1)
|
||||||
|
self.assertEquals(leave_ledger_entry[0].employee, compensatory_leave_request.employee)
|
||||||
|
self.assertEquals(leave_ledger_entry[0].leave_type, compensatory_leave_request.leave_type)
|
||||||
|
self.assertEquals(leave_ledger_entry[0].leaves, 1)
|
||||||
|
|
||||||
|
# check reverse leave ledger entry on cancellation
|
||||||
|
compensatory_leave_request.cancel()
|
||||||
|
leave_ledger_entry = frappe.get_all('Leave Ledger Entry', fields='*', filters=filters, order_by = 'creation desc')
|
||||||
|
|
||||||
|
self.assertEquals(len(leave_ledger_entry), 2)
|
||||||
|
self.assertEquals(leave_ledger_entry[0].employee, compensatory_leave_request.employee)
|
||||||
|
self.assertEquals(leave_ledger_entry[0].leave_type, compensatory_leave_request.leave_type)
|
||||||
|
self.assertEquals(leave_ledger_entry[0].leaves, -1)
|
||||||
|
|
||||||
|
def get_compensatory_leave_request(employee, leave_date=today()):
|
||||||
|
prev_comp_leave_req = frappe.db.get_value('Compensatory Leave Request',
|
||||||
|
dict(leave_type='Compensatory Off',
|
||||||
|
work_from_date=leave_date,
|
||||||
|
work_end_date=leave_date,
|
||||||
|
employee=employee), 'name')
|
||||||
|
if prev_comp_leave_req:
|
||||||
|
return frappe.get_doc('Compensatory Leave Request', prev_comp_leave_req)
|
||||||
|
|
||||||
|
return frappe.get_doc(dict(
|
||||||
|
doctype='Compensatory Leave Request',
|
||||||
|
employee=employee,
|
||||||
|
leave_type='Compensatory Off',
|
||||||
|
work_from_date=leave_date,
|
||||||
|
work_end_date=leave_date,
|
||||||
|
reason='test'
|
||||||
|
)).insert()
|
||||||
|
|
||||||
|
def mark_attendance(employee, date=today(), status='Present'):
|
||||||
|
if not frappe.db.exists(dict(doctype='Attendance', employee=employee.name, attendance_date=date, status='Present')):
|
||||||
|
attendance = frappe.get_doc({
|
||||||
|
"doctype": "Attendance",
|
||||||
|
"employee": employee.name,
|
||||||
|
"attendance_date": date,
|
||||||
|
"status": status
|
||||||
|
})
|
||||||
|
attendance.save()
|
||||||
|
attendance.submit()
|
||||||
|
|
||||||
|
def create_holiday_list():
|
||||||
|
if frappe.db.exists("Holiday List", "_Test Compensatory Leave"):
|
||||||
|
return
|
||||||
|
|
||||||
|
holiday_list = frappe.get_doc({
|
||||||
|
"doctype": "Holiday List",
|
||||||
|
"from_date": add_months(today(), -3),
|
||||||
|
"to_date": add_months(today(), 3),
|
||||||
|
"holidays": [
|
||||||
|
{
|
||||||
|
"description": "Test Holiday",
|
||||||
|
"holiday_date": today()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Test Holiday 1",
|
||||||
|
"holiday_date": add_days(today(), -1)
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"holiday_list_name": "_Test Compensatory Leave"
|
||||||
|
})
|
||||||
|
holiday_list.save()
|
||||||
@@ -232,7 +232,6 @@
|
|||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "You can enter any date manually",
|
|
||||||
"fieldname": "date_of_birth",
|
"fieldname": "date_of_birth",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"label": "Date of Birth",
|
"label": "Date of Birth",
|
||||||
@@ -831,4 +830,4 @@
|
|||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"title_field": "employee_name",
|
"title_field": "employee_name",
|
||||||
"track_changes": 1
|
"track_changes": 1
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,14 @@ frappe.ui.form.on('Employee Onboarding', {
|
|||||||
frm.add_fetch("employee_onboarding_template", "department", "department");
|
frm.add_fetch("employee_onboarding_template", "department", "department");
|
||||||
frm.add_fetch("employee_onboarding_template", "designation", "designation");
|
frm.add_fetch("employee_onboarding_template", "designation", "designation");
|
||||||
frm.add_fetch("employee_onboarding_template", "employee_grade", "employee_grade");
|
frm.add_fetch("employee_onboarding_template", "employee_grade", "employee_grade");
|
||||||
|
|
||||||
|
frm.set_query('job_offer', function () {
|
||||||
|
return {
|
||||||
|
filters: {
|
||||||
|
'job_applicant': frm.doc.job_applicant
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
refresh: function(frm) {
|
refresh: function(frm) {
|
||||||
|
|||||||
@@ -42,12 +42,6 @@ cur_frm.cscript.onload = function(doc) {
|
|||||||
cur_frm.set_value("posting_date", frappe.datetime.get_today());
|
cur_frm.set_value("posting_date", frappe.datetime.get_today());
|
||||||
cur_frm.cscript.clear_sanctioned(doc);
|
cur_frm.cscript.clear_sanctioned(doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
cur_frm.fields_dict.employee.get_query = function() {
|
|
||||||
return {
|
|
||||||
query: "erpnext.controllers.queries.employee_query"
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
cur_frm.cscript.clear_sanctioned = function(doc) {
|
cur_frm.cscript.clear_sanctioned = function(doc) {
|
||||||
@@ -119,7 +113,7 @@ cur_frm.cscript.calculate_total_amount = function(doc,cdt,cdn){
|
|||||||
};
|
};
|
||||||
|
|
||||||
erpnext.expense_claim = {
|
erpnext.expense_claim = {
|
||||||
set_title :function(frm) {
|
set_title: function(frm) {
|
||||||
if (!frm.doc.task) {
|
if (!frm.doc.task) {
|
||||||
frm.set_value("title", frm.doc.employee_name);
|
frm.set_value("title", frm.doc.employee_name);
|
||||||
}
|
}
|
||||||
@@ -131,20 +125,20 @@ erpnext.expense_claim = {
|
|||||||
|
|
||||||
frappe.ui.form.on("Expense Claim", {
|
frappe.ui.form.on("Expense Claim", {
|
||||||
setup: function(frm) {
|
setup: function(frm) {
|
||||||
frm.trigger("set_query_for_cost_center");
|
|
||||||
frm.trigger("set_query_for_payable_account");
|
|
||||||
frm.add_fetch("company", "cost_center", "cost_center");
|
frm.add_fetch("company", "cost_center", "cost_center");
|
||||||
frm.add_fetch("company", "default_expense_claim_payable_account", "payable_account");
|
frm.add_fetch("company", "default_expense_claim_payable_account", "payable_account");
|
||||||
frm.set_query("employee_advance", "advances", function(doc) {
|
|
||||||
|
frm.set_query("employee_advance", "advances", function() {
|
||||||
return {
|
return {
|
||||||
filters: [
|
filters: [
|
||||||
['docstatus', '=', 1],
|
['docstatus', '=', 1],
|
||||||
['employee', '=', doc.employee],
|
['employee', '=', frm.doc.employee],
|
||||||
['paid_amount', '>', 0],
|
['paid_amount', '>', 0],
|
||||||
['paid_amount', '>', 'claimed_amount']
|
['paid_amount', '>', 'claimed_amount']
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
frm.set_query("expense_approver", function() {
|
frm.set_query("expense_approver", function() {
|
||||||
return {
|
return {
|
||||||
query: "erpnext.hr.doctype.department_approver.department_approver.get_approvers",
|
query: "erpnext.hr.doctype.department_approver.department_approver.get_approvers",
|
||||||
@@ -154,14 +148,49 @@ frappe.ui.form.on("Expense Claim", {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
frm.set_query("account_head", "taxes", function(doc) {
|
|
||||||
|
frm.set_query("account_head", "taxes", function() {
|
||||||
return {
|
return {
|
||||||
filters: [
|
filters: [
|
||||||
['company', '=', doc.company],
|
['company', '=', frm.doc.company],
|
||||||
['account_type', 'in', ["Tax", "Chargeable", "Income Account", "Expenses Included In Valuation"]]
|
['account_type', 'in', ["Tax", "Chargeable", "Income Account", "Expenses Included In Valuation"]]
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
frm.set_query("cost_center", "expenses", function() {
|
||||||
|
return {
|
||||||
|
filters: {
|
||||||
|
"company": frm.doc.company,
|
||||||
|
"is_group": 0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
frm.set_query("payable_account", function() {
|
||||||
|
return {
|
||||||
|
filters: {
|
||||||
|
"report_type": "Balance Sheet",
|
||||||
|
"account_type": "Payable",
|
||||||
|
"company": frm.doc.company,
|
||||||
|
"is_group": 0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
frm.set_query("task", function() {
|
||||||
|
return {
|
||||||
|
filters: {
|
||||||
|
'project': frm.doc.project
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
frm.set_query("employee", function() {
|
||||||
|
return {
|
||||||
|
query: "erpnext.controllers.queries.employee_query"
|
||||||
|
};
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
onload: function(frm) {
|
onload: function(frm) {
|
||||||
@@ -244,30 +273,6 @@ frappe.ui.form.on("Expense Claim", {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
set_query_for_cost_center: function(frm) {
|
|
||||||
frm.fields_dict["cost_center"].get_query = function() {
|
|
||||||
return {
|
|
||||||
filters: {
|
|
||||||
"company": frm.doc.company,
|
|
||||||
"is_group": 0
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
set_query_for_payable_account: function(frm) {
|
|
||||||
frm.fields_dict["payable_account"].get_query = function() {
|
|
||||||
return {
|
|
||||||
filters: {
|
|
||||||
"report_type": "Balance Sheet",
|
|
||||||
"account_type": "Payable",
|
|
||||||
"company": frm.doc.company,
|
|
||||||
"is_group": 0
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
is_paid: function(frm) {
|
is_paid: function(frm) {
|
||||||
frm.trigger("toggle_fields");
|
frm.trigger("toggle_fields");
|
||||||
},
|
},
|
||||||
@@ -329,6 +334,10 @@ frappe.ui.form.on("Expense Claim", {
|
|||||||
});
|
});
|
||||||
|
|
||||||
frappe.ui.form.on("Expense Claim Detail", {
|
frappe.ui.form.on("Expense Claim Detail", {
|
||||||
|
expenses_add: function(frm, cdt, cdn) {
|
||||||
|
var row = frappe.get_doc(cdt, cdn);
|
||||||
|
frm.script_manager.copy_from_first_row("expenses", row, ["cost_center"]);
|
||||||
|
},
|
||||||
amount: function(frm, cdt, cdn) {
|
amount: function(frm, cdt, cdn) {
|
||||||
var child = locals[cdt][cdn];
|
var child = locals[cdt][cdn];
|
||||||
var doc = frm.doc;
|
var doc = frm.doc;
|
||||||
@@ -341,6 +350,9 @@ frappe.ui.form.on("Expense Claim Detail", {
|
|||||||
cur_frm.cscript.calculate_total(doc,cdt,cdn);
|
cur_frm.cscript.calculate_total(doc,cdt,cdn);
|
||||||
frm.trigger("get_taxes");
|
frm.trigger("get_taxes");
|
||||||
frm.trigger("calculate_grand_total");
|
frm.trigger("calculate_grand_total");
|
||||||
|
},
|
||||||
|
cost_center: function(frm, cdt, cdn) {
|
||||||
|
erpnext.utils.copy_value_in_all_rows(frm.doc, cdt, cdn, "expenses", "cost_center");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -411,12 +423,4 @@ frappe.ui.form.on("Expense Taxes and Charges", {
|
|||||||
tax_amount: function(frm, cdt, cdn) {
|
tax_amount: function(frm, cdt, cdn) {
|
||||||
frm.trigger("calculate_total_tax", cdt, cdn);
|
frm.trigger("calculate_total_tax", cdt, cdn);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
cur_frm.fields_dict['task'].get_query = function(doc) {
|
|
||||||
return {
|
|
||||||
filters:{
|
|
||||||
'project': doc.project
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@@ -43,7 +43,6 @@
|
|||||||
"accounting_dimensions_section",
|
"accounting_dimensions_section",
|
||||||
"project",
|
"project",
|
||||||
"dimension_col_break",
|
"dimension_col_break",
|
||||||
"cost_center",
|
|
||||||
"more_details",
|
"more_details",
|
||||||
"status",
|
"status",
|
||||||
"amended_from",
|
"amended_from",
|
||||||
@@ -366,7 +365,7 @@
|
|||||||
"icon": "fa fa-money",
|
"icon": "fa fa-money",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"modified": "2019-11-08 14:13:08.964547",
|
"modified": "2019-11-09 14:13:08.964547",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "Expense Claim",
|
"name": "Expense Claim",
|
||||||
|
|||||||
@@ -43,9 +43,9 @@ class ExpenseClaim(AccountsController):
|
|||||||
}[cstr(self.docstatus or 0)]
|
}[cstr(self.docstatus or 0)]
|
||||||
|
|
||||||
paid_amount = flt(self.total_amount_reimbursed) + flt(self.total_advance_amount)
|
paid_amount = flt(self.total_amount_reimbursed) + flt(self.total_advance_amount)
|
||||||
precision = self.precision("total_sanctioned_amount")
|
precision = self.precision("grand_total")
|
||||||
if (self.is_paid or (flt(self.total_sanctioned_amount) > 0
|
if (self.is_paid or (flt(self.total_sanctioned_amount) > 0
|
||||||
and flt(self.total_sanctioned_amount, precision) == flt(paid_amount, precision))) \
|
and flt(self.grand_total, precision) == flt(paid_amount, precision))) \
|
||||||
and self.docstatus == 1 and self.approval_status == 'Approved':
|
and self.docstatus == 1 and self.approval_status == 'Approved':
|
||||||
self.status = "Paid"
|
self.status = "Paid"
|
||||||
elif flt(self.total_sanctioned_amount) > 0 and self.docstatus == 1 and self.approval_status == 'Approved':
|
elif flt(self.total_sanctioned_amount) > 0 and self.docstatus == 1 and self.approval_status == 'Approved':
|
||||||
@@ -127,7 +127,7 @@ class ExpenseClaim(AccountsController):
|
|||||||
"debit": data.sanctioned_amount,
|
"debit": data.sanctioned_amount,
|
||||||
"debit_in_account_currency": data.sanctioned_amount,
|
"debit_in_account_currency": data.sanctioned_amount,
|
||||||
"against": self.employee,
|
"against": self.employee,
|
||||||
"cost_center": self.cost_center
|
"cost_center": data.cost_center
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -190,8 +190,9 @@ class ExpenseClaim(AccountsController):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def validate_account_details(self):
|
def validate_account_details(self):
|
||||||
if not self.cost_center:
|
for data in self.expenses:
|
||||||
frappe.throw(_("Cost center is required to book an expense claim"))
|
if not data.cost_center:
|
||||||
|
frappe.throw(_("Cost center is required to book an expense claim"))
|
||||||
|
|
||||||
if self.is_paid:
|
if self.is_paid:
|
||||||
if not self.mode_of_payment:
|
if not self.mode_of_payment:
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ def generate_taxes():
|
|||||||
|
|
||||||
def make_expense_claim(payable_account, amount, sanctioned_amount, company, account, project=None, task_name=None, do_not_submit=False, taxes=None):
|
def make_expense_claim(payable_account, amount, sanctioned_amount, company, account, project=None, task_name=None, do_not_submit=False, taxes=None):
|
||||||
employee = frappe.db.get_value("Employee", {"status": "Active"})
|
employee = frappe.db.get_value("Employee", {"status": "Active"})
|
||||||
currency = frappe.db.get_value('Company', company, 'default_currency')
|
currency, cost_center = frappe.db.get_value('Company', company, ['default_currency', 'cost_center'])
|
||||||
expense_claim = {
|
expense_claim = {
|
||||||
"doctype": "Expense Claim",
|
"doctype": "Expense Claim",
|
||||||
"employee": employee,
|
"employee": employee,
|
||||||
@@ -134,12 +134,15 @@ def make_expense_claim(payable_account, amount, sanctioned_amount, company, acco
|
|||||||
"approval_status": "Approved",
|
"approval_status": "Approved",
|
||||||
"company": company,
|
"company": company,
|
||||||
'currency': currency,
|
'currency': currency,
|
||||||
"expenses":
|
"expenses": [{
|
||||||
[{"expense_type": "Travel",
|
"expense_type": "Travel",
|
||||||
"default_account": account,
|
"default_account": account,
|
||||||
'currency': currency,
|
"currency": currency,
|
||||||
"amount": amount,
|
"amount": amount,
|
||||||
"sanctioned_amount": sanctioned_amount}]}
|
"sanctioned_amount": sanctioned_amount,
|
||||||
|
"cost_center": cost_center
|
||||||
|
}]
|
||||||
|
}
|
||||||
if taxes:
|
if taxes:
|
||||||
expense_claim.update(taxes)
|
expense_claim.update(taxes)
|
||||||
|
|
||||||
|
|||||||
@@ -1,378 +1,118 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
|
||||||
"allow_events_in_timeline": 0,
|
|
||||||
"allow_guest_to_view": 0,
|
|
||||||
"allow_import": 0,
|
|
||||||
"allow_rename": 0,
|
|
||||||
"beta": 0,
|
|
||||||
"creation": "2013-02-22 01:27:46",
|
"creation": "2013-02-22 01:27:46",
|
||||||
"custom": 0,
|
|
||||||
"docstatus": 0,
|
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"expense_date",
|
||||||
|
"column_break_2",
|
||||||
|
"expense_type",
|
||||||
|
"default_account",
|
||||||
|
"section_break_4",
|
||||||
|
"description",
|
||||||
|
"section_break_6",
|
||||||
|
"amount",
|
||||||
|
"column_break_8",
|
||||||
|
"sanctioned_amount",
|
||||||
|
"cost_center"
|
||||||
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "expense_date",
|
"fieldname": "expense_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Expense Date",
|
"label": "Expense Date",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "expense_date",
|
"oldfieldname": "expense_date",
|
||||||
"oldfieldtype": "Date",
|
"oldfieldtype": "Date",
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"print_width": "150px",
|
"print_width": "150px",
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0,
|
|
||||||
"width": "150px"
|
"width": "150px"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "column_break_2",
|
"fieldname": "column_break_2",
|
||||||
"fieldtype": "Column Break",
|
"fieldtype": "Column Break"
|
||||||
"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,
|
|
||||||
"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,
|
|
||||||
"fieldname": "expense_type",
|
"fieldname": "expense_type",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Expense Claim Type",
|
"label": "Expense Claim Type",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "expense_type",
|
"oldfieldname": "expense_type",
|
||||||
"oldfieldtype": "Link",
|
"oldfieldtype": "Link",
|
||||||
"options": "Expense Claim Type",
|
"options": "Expense Claim Type",
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"print_width": "150px",
|
"print_width": "150px",
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0,
|
|
||||||
"width": "150px"
|
"width": "150px"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"depends_on": "expense_type",
|
"depends_on": "expense_type",
|
||||||
"fieldname": "default_account",
|
"fieldname": "default_account",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Default Account",
|
"label": "Default Account",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Account",
|
"options": "Account",
|
||||||
"permlevel": 0,
|
"read_only": 1
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 1,
|
|
||||||
"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,
|
|
||||||
"fieldname": "section_break_4",
|
"fieldname": "section_break_4",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break"
|
||||||
"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,
|
|
||||||
"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_from": "",
|
|
||||||
"fieldname": "description",
|
"fieldname": "description",
|
||||||
"fieldtype": "Text Editor",
|
"fieldtype": "Text Editor",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Description",
|
"label": "Description",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "description",
|
"oldfieldname": "description",
|
||||||
"oldfieldtype": "Small Text",
|
"oldfieldtype": "Small Text",
|
||||||
"options": "",
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"print_width": "300px",
|
"print_width": "300px",
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0,
|
|
||||||
"width": "300px"
|
"width": "300px"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "section_break_6",
|
"fieldname": "section_break_6",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break"
|
||||||
"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,
|
|
||||||
"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,
|
|
||||||
"fieldname": "amount",
|
"fieldname": "amount",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Amount",
|
"label": "Amount",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "claim_amount",
|
"oldfieldname": "claim_amount",
|
||||||
"oldfieldtype": "Currency",
|
"oldfieldtype": "Currency",
|
||||||
"options": "Company:company:default_currency",
|
"options": "Company:company:default_currency",
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"print_width": "150px",
|
"print_width": "150px",
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0,
|
|
||||||
"width": "150px"
|
"width": "150px"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "column_break_8",
|
"fieldname": "column_break_8",
|
||||||
"fieldtype": "Column Break",
|
"fieldtype": "Column Break"
|
||||||
"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,
|
|
||||||
"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,
|
|
||||||
"fieldname": "sanctioned_amount",
|
"fieldname": "sanctioned_amount",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Sanctioned Amount",
|
"label": "Sanctioned Amount",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"oldfieldname": "sanctioned_amount",
|
"oldfieldname": "sanctioned_amount",
|
||||||
"oldfieldtype": "Currency",
|
"oldfieldtype": "Currency",
|
||||||
"options": "Company:company:default_currency",
|
"options": "Company:company:default_currency",
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"print_width": "150px",
|
"print_width": "150px",
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0,
|
|
||||||
"width": "150px"
|
"width": "150px"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "cost_center",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Cost Center",
|
||||||
|
"options": "Cost Center"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_web_view": 0,
|
|
||||||
"hide_heading": 0,
|
|
||||||
"hide_toolbar": 0,
|
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"image_view": 0,
|
|
||||||
"in_create": 0,
|
|
||||||
"is_submittable": 0,
|
|
||||||
"issingle": 0,
|
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"max_attachments": 0,
|
"modified": "2019-11-22 11:57:25.110942",
|
||||||
"modified": "2019-06-10 08:41:36.122565",
|
"modified_by": "jangeles@bai.ph",
|
||||||
"modified_by": "Administrator",
|
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "Expense Claim Detail",
|
"name": "Expense Claim Detail",
|
||||||
"owner": "harshada@webnotestech.com",
|
"owner": "harshada@webnotestech.com",
|
||||||
"permissions": [],
|
"permissions": [],
|
||||||
"quick_entry": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"read_only_onload": 0,
|
|
||||||
"show_name_in_global_search": 0,
|
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC"
|
||||||
"track_changes": 0,
|
|
||||||
"track_seen": 0,
|
|
||||||
"track_views": 0
|
|
||||||
}
|
}
|
||||||
@@ -8,7 +8,7 @@ frappe.ui.form.on("Expense Claim Type", {
|
|||||||
return{
|
return{
|
||||||
filters: {
|
filters: {
|
||||||
"is_group": 0,
|
"is_group": 0,
|
||||||
"root_type": "Expense",
|
"root_type": frm.doc.deferred_expense_account ? "Asset" : "Expense",
|
||||||
'company': d.company
|
'company': d.company
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,181 +1,72 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"allow_import": 1,
|
||||||
"allow_guest_to_view": 0,
|
"allow_rename": 1,
|
||||||
"allow_import": 1,
|
"autoname": "field:expense_type",
|
||||||
"allow_rename": 1,
|
"creation": "2012-03-27 14:35:55",
|
||||||
"autoname": "field:expense_type",
|
"doctype": "DocType",
|
||||||
"beta": 0,
|
"document_type": "Setup",
|
||||||
"creation": "2012-03-27 14:35:55",
|
"engine": "InnoDB",
|
||||||
"custom": 0,
|
"field_order": [
|
||||||
"docstatus": 0,
|
"deferred_expense_account",
|
||||||
"doctype": "DocType",
|
"expense_type",
|
||||||
"document_type": "Setup",
|
"description",
|
||||||
"editable_grid": 0,
|
"accounts"
|
||||||
"engine": "InnoDB",
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "expense_type",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Data",
|
||||||
"allow_on_submit": 0,
|
"in_list_view": 1,
|
||||||
"bold": 0,
|
"label": "Expense Claim Type",
|
||||||
"collapsible": 0,
|
"oldfieldname": "expense_type",
|
||||||
"columns": 0,
|
"oldfieldtype": "Data",
|
||||||
"fieldname": "expense_type",
|
"reqd": 1,
|
||||||
"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": "Expense Claim Type",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "expense_type",
|
|
||||||
"oldfieldtype": "Data",
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 1
|
"unique": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "description",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Small Text",
|
||||||
"allow_on_submit": 0,
|
"label": "Description",
|
||||||
"bold": 0,
|
"oldfieldname": "description",
|
||||||
"collapsible": 0,
|
"oldfieldtype": "Small Text",
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "description",
|
|
||||||
"fieldtype": "Small Text",
|
|
||||||
"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": "Description",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "description",
|
|
||||||
"oldfieldtype": "Small Text",
|
|
||||||
"permlevel": 0,
|
|
||||||
"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,
|
|
||||||
"width": "300px"
|
"width": "300px"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "accounts",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Table",
|
||||||
"allow_on_submit": 0,
|
"label": "Accounts",
|
||||||
"bold": 0,
|
"options": "Expense Claim Account"
|
||||||
"collapsible": 0,
|
},
|
||||||
"columns": 0,
|
{
|
||||||
"fieldname": "accounts",
|
"default": "0",
|
||||||
"fieldtype": "Table",
|
"fieldname": "deferred_expense_account",
|
||||||
"hidden": 0,
|
"fieldtype": "Check",
|
||||||
"ignore_user_permissions": 0,
|
"label": "Deferred Expense Account"
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Accounts",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Expense Claim Account",
|
|
||||||
"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
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_web_view": 0,
|
"icon": "fa fa-flag",
|
||||||
"hide_heading": 0,
|
"idx": 1,
|
||||||
"hide_toolbar": 0,
|
"modified": "2019-11-22 12:00:18.710408",
|
||||||
"icon": "fa fa-flag",
|
"modified_by": "jangeles@bai.ph",
|
||||||
"idx": 1,
|
"module": "HR",
|
||||||
"image_view": 0,
|
"name": "Expense Claim Type",
|
||||||
"in_create": 0,
|
"owner": "harshada@webnotestech.com",
|
||||||
"is_submittable": 0,
|
|
||||||
"issingle": 0,
|
|
||||||
"istable": 0,
|
|
||||||
"max_attachments": 0,
|
|
||||||
"modified": "2018-09-18 14:13:43.770829",
|
|
||||||
"modified_by": "Administrator",
|
|
||||||
"module": "HR",
|
|
||||||
"name": "Expense Claim Type",
|
|
||||||
"owner": "harshada@webnotestech.com",
|
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"amend": 0,
|
"create": 1,
|
||||||
"cancel": 0,
|
"email": 1,
|
||||||
"create": 1,
|
"print": 1,
|
||||||
"delete": 0,
|
"read": 1,
|
||||||
"email": 1,
|
"report": 1,
|
||||||
"export": 0,
|
"role": "HR Manager",
|
||||||
"if_owner": 0,
|
"share": 1,
|
||||||
"import": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
|
||||||
"read": 1,
|
|
||||||
"report": 1,
|
|
||||||
"role": "HR Manager",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 0,
|
"read": 1,
|
||||||
"cancel": 0,
|
"role": "Employee"
|
||||||
"create": 0,
|
|
||||||
"delete": 0,
|
|
||||||
"email": 0,
|
|
||||||
"export": 0,
|
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 0,
|
|
||||||
"read": 1,
|
|
||||||
"report": 0,
|
|
||||||
"role": "Employee",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 0,
|
|
||||||
"submit": 0,
|
|
||||||
"write": 0
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"quick_entry": 0,
|
"sort_field": "modified",
|
||||||
"read_only": 0,
|
"sort_order": "ASC"
|
||||||
"read_only_onload": 0,
|
|
||||||
"show_name_in_global_search": 0,
|
|
||||||
"sort_order": "ASC",
|
|
||||||
"track_changes": 0,
|
|
||||||
"track_seen": 0,
|
|
||||||
"track_views": 0
|
|
||||||
}
|
}
|
||||||
@@ -69,10 +69,14 @@ class LeaveAllocation(Document):
|
|||||||
|
|
||||||
def validate_allocation_overlap(self):
|
def validate_allocation_overlap(self):
|
||||||
leave_allocation = frappe.db.sql("""
|
leave_allocation = frappe.db.sql("""
|
||||||
select name from `tabLeave Allocation`
|
SELECT
|
||||||
where employee=%s and leave_type=%s and docstatus=1
|
name
|
||||||
and to_date >= %s and from_date <= %s""",
|
FROM `tabLeave Allocation`
|
||||||
(self.employee, self.leave_type, self.from_date, self.to_date))
|
WHERE
|
||||||
|
employee=%s AND leave_type=%s
|
||||||
|
AND name <> %s AND docstatus=1
|
||||||
|
AND to_date >= %s AND from_date <= %s""",
|
||||||
|
(self.employee, self.leave_type, self.name, self.from_date, self.to_date))
|
||||||
|
|
||||||
if leave_allocation:
|
if leave_allocation:
|
||||||
frappe.msgprint(_("{0} already allocated for Employee {1} for period {2} to {3}")
|
frappe.msgprint(_("{0} already allocated for Employee {1} for period {2} to {3}")
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ frappe.ui.form.on("Leave Application", {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
$("div").remove(".form-dashboard-section.custom");
|
||||||
frm.dashboard.add_section(
|
frm.dashboard.add_section(
|
||||||
frappe.render_template('leave_application_dashboard', {
|
frappe.render_template('leave_application_dashboard', {
|
||||||
data: leave_details
|
data: leave_details
|
||||||
@@ -170,7 +171,7 @@ frappe.ui.form.on("Leave Application", {
|
|||||||
frm.set_value('to_date', '');
|
frm.set_value('to_date', '');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// server call is done to include holidays in leave days calculations
|
// server call is done to include holidays in leave days calculations
|
||||||
return frappe.call({
|
return frappe.call({
|
||||||
method: 'erpnext.hr.doctype.leave_application.leave_application.get_number_of_leave_days',
|
method: 'erpnext.hr.doctype.leave_application.leave_application.get_number_of_leave_days',
|
||||||
args: {
|
args: {
|
||||||
@@ -193,7 +194,7 @@ frappe.ui.form.on("Leave Application", {
|
|||||||
|
|
||||||
set_leave_approver: function(frm) {
|
set_leave_approver: function(frm) {
|
||||||
if(frm.doc.employee) {
|
if(frm.doc.employee) {
|
||||||
// server call is done to include holidays in leave days calculations
|
// server call is done to include holidays in leave days calculations
|
||||||
return frappe.call({
|
return frappe.call({
|
||||||
method: 'erpnext.hr.doctype.leave_application.leave_application.get_leave_approver',
|
method: 'erpnext.hr.doctype.leave_application.leave_application.get_leave_approver',
|
||||||
args: {
|
args: {
|
||||||
|
|||||||
@@ -54,9 +54,11 @@ class LeaveApplication(Document):
|
|||||||
self.create_leave_ledger_entry()
|
self.create_leave_ledger_entry()
|
||||||
self.reload()
|
self.reload()
|
||||||
|
|
||||||
|
def before_cancel(self):
|
||||||
|
self.status = "Cancelled"
|
||||||
|
|
||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
self.create_leave_ledger_entry(submit=False)
|
self.create_leave_ledger_entry(submit=False)
|
||||||
self.status = "Cancelled"
|
|
||||||
# notify leave applier about cancellation
|
# notify leave applier about cancellation
|
||||||
self.notify_employee()
|
self.notify_employee()
|
||||||
self.cancel_attendance()
|
self.cancel_attendance()
|
||||||
@@ -351,7 +353,7 @@ class LeaveApplication(Document):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def create_leave_ledger_entry(self, submit=True):
|
def create_leave_ledger_entry(self, submit=True):
|
||||||
if self.status != 'Approved':
|
if self.status != 'Approved' and submit:
|
||||||
return
|
return
|
||||||
|
|
||||||
expiry_date = get_allocation_expiry(self.employee, self.leave_type,
|
expiry_date = get_allocation_expiry(self.employee, self.leave_type,
|
||||||
@@ -549,10 +551,10 @@ def get_leaves_for_period(employee, leave_type, from_date, to_date):
|
|||||||
leave_days += leave_entry.leaves
|
leave_days += leave_entry.leaves
|
||||||
|
|
||||||
elif inclusive_period and leave_entry.transaction_type == 'Leave Allocation' \
|
elif inclusive_period and leave_entry.transaction_type == 'Leave Allocation' \
|
||||||
and not skip_expiry_leaves(leave_entry, to_date):
|
and leave_entry.is_expired and not skip_expiry_leaves(leave_entry, to_date):
|
||||||
leave_days += leave_entry.leaves
|
leave_days += leave_entry.leaves
|
||||||
|
|
||||||
else:
|
elif leave_entry.transaction_type == 'Leave Application':
|
||||||
if leave_entry.from_date < getdate(from_date):
|
if leave_entry.from_date < getdate(from_date):
|
||||||
leave_entry.from_date = from_date
|
leave_entry.from_date = from_date
|
||||||
if leave_entry.to_date > getdate(to_date):
|
if leave_entry.to_date > getdate(to_date):
|
||||||
@@ -579,14 +581,15 @@ def skip_expiry_leaves(leave_entry, date):
|
|||||||
def get_leave_entries(employee, leave_type, from_date, to_date):
|
def get_leave_entries(employee, leave_type, from_date, to_date):
|
||||||
''' Returns leave entries between from_date and to_date '''
|
''' Returns leave entries between from_date and to_date '''
|
||||||
return frappe.db.sql("""
|
return frappe.db.sql("""
|
||||||
select employee, leave_type, from_date, to_date, leaves, transaction_type, is_carry_forward, transaction_name
|
SELECT
|
||||||
from `tabLeave Ledger Entry`
|
employee, leave_type, from_date, to_date, leaves, transaction_name, transaction_type,
|
||||||
where employee=%(employee)s and leave_type=%(leave_type)s
|
is_carry_forward, is_expired
|
||||||
and docstatus=1
|
FROM `tabLeave Ledger Entry`
|
||||||
and leaves<0
|
WHERE employee=%(employee)s AND leave_type=%(leave_type)s
|
||||||
and (from_date between %(from_date)s and %(to_date)s
|
AND docstatus=1 AND leaves<0
|
||||||
or to_date between %(from_date)s and %(to_date)s
|
AND (from_date between %(from_date)s AND %(to_date)s
|
||||||
or (from_date < %(from_date)s and to_date > %(to_date)s))
|
OR to_date between %(from_date)s AND %(to_date)s
|
||||||
|
OR (from_date < %(from_date)s AND to_date > %(to_date)s))
|
||||||
""", {
|
""", {
|
||||||
"from_date": from_date,
|
"from_date": from_date,
|
||||||
"to_date": to_date,
|
"to_date": to_date,
|
||||||
@@ -773,4 +776,4 @@ def get_leave_approver(employee):
|
|||||||
leave_approver = frappe.db.get_value('Department Approver', {'parent': department,
|
leave_approver = frappe.db.get_value('Department Approver', {'parent': department,
|
||||||
'parentfield': 'leave_approvers', 'idx': 1}, 'approver')
|
'parentfield': 'leave_approvers', 'idx': 1}, 'approver')
|
||||||
|
|
||||||
return leave_approver
|
return leave_approver
|
||||||
@@ -301,7 +301,7 @@ class TestLeaveApplication(unittest.TestCase):
|
|||||||
to_date = add_days(date, 2),
|
to_date = add_days(date, 2),
|
||||||
company = "_Test Company",
|
company = "_Test Company",
|
||||||
docstatus = 1,
|
docstatus = 1,
|
||||||
status = "Approved"
|
status = "Approved"
|
||||||
))
|
))
|
||||||
leave_application.submit()
|
leave_application.submit()
|
||||||
|
|
||||||
@@ -314,7 +314,7 @@ class TestLeaveApplication(unittest.TestCase):
|
|||||||
to_date = add_days(date, 8),
|
to_date = add_days(date, 8),
|
||||||
company = "_Test Company",
|
company = "_Test Company",
|
||||||
docstatus = 1,
|
docstatus = 1,
|
||||||
status = "Approved"
|
status = "Approved"
|
||||||
))
|
))
|
||||||
self.assertRaises(frappe.ValidationError, leave_application.insert)
|
self.assertRaises(frappe.ValidationError, leave_application.insert)
|
||||||
|
|
||||||
|
|||||||
@@ -43,10 +43,18 @@ class TestLeavePeriod(unittest.TestCase):
|
|||||||
leave_period.grant_leave_allocation(employee=employee_doc_name)
|
leave_period.grant_leave_allocation(employee=employee_doc_name)
|
||||||
self.assertEqual(get_leave_balance_on(employee_doc_name, leave_type, today()), 20)
|
self.assertEqual(get_leave_balance_on(employee_doc_name, leave_type, today()), 20)
|
||||||
|
|
||||||
def create_leave_period(from_date, to_date):
|
def create_leave_period(from_date, to_date, company=None):
|
||||||
|
leave_period = frappe.db.get_value('Leave Period',
|
||||||
|
dict(company=company or erpnext.get_default_company(),
|
||||||
|
from_date=from_date,
|
||||||
|
to_date=to_date,
|
||||||
|
is_active=1), 'name')
|
||||||
|
if leave_period:
|
||||||
|
return frappe.get_doc("Leave Period", leave_period)
|
||||||
|
|
||||||
leave_period = frappe.get_doc({
|
leave_period = frappe.get_doc({
|
||||||
"doctype": "Leave Period",
|
"doctype": "Leave Period",
|
||||||
"company": erpnext.get_default_company(),
|
"company": company or erpnext.get_default_company(),
|
||||||
"from_date": from_date,
|
"from_date": from_date,
|
||||||
"to_date": to_date,
|
"to_date": to_date,
|
||||||
"is_active": 1
|
"is_active": 1
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ class PayrollEntry(Document):
|
|||||||
"""
|
"""
|
||||||
cond = self.get_filter_condition()
|
cond = self.get_filter_condition()
|
||||||
return frappe.db.sql(""" select eld.loan_account, eld.loan,
|
return frappe.db.sql(""" select eld.loan_account, eld.loan,
|
||||||
eld.interest_income_account, eld.principal_amount, eld.interest_amount, eld.total_payment
|
eld.interest_income_account, eld.principal_amount, eld.interest_amount, eld.total_payment,t1.employee
|
||||||
from
|
from
|
||||||
`tabSalary Slip` t1, `tabSalary Slip Loan` eld
|
`tabSalary Slip` t1, `tabSalary Slip Loan` eld
|
||||||
where
|
where
|
||||||
@@ -246,6 +246,7 @@ class PayrollEntry(Document):
|
|||||||
accounts.append({
|
accounts.append({
|
||||||
"account": acc,
|
"account": acc,
|
||||||
"debit_in_account_currency": flt(amount, precision),
|
"debit_in_account_currency": flt(amount, precision),
|
||||||
|
"party_type": '',
|
||||||
"cost_center": self.cost_center,
|
"cost_center": self.cost_center,
|
||||||
"project": self.project
|
"project": self.project
|
||||||
})
|
})
|
||||||
@@ -257,6 +258,7 @@ class PayrollEntry(Document):
|
|||||||
"account": acc,
|
"account": acc,
|
||||||
"credit_in_account_currency": flt(amount, precision),
|
"credit_in_account_currency": flt(amount, precision),
|
||||||
"cost_center": self.cost_center,
|
"cost_center": self.cost_center,
|
||||||
|
"party_type": '',
|
||||||
"project": self.project
|
"project": self.project
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -264,7 +266,9 @@ class PayrollEntry(Document):
|
|||||||
for data in loan_details:
|
for data in loan_details:
|
||||||
accounts.append({
|
accounts.append({
|
||||||
"account": data.loan_account,
|
"account": data.loan_account,
|
||||||
"credit_in_account_currency": data.principal_amount
|
"credit_in_account_currency": data.principal_amount,
|
||||||
|
"party_type": "Employee",
|
||||||
|
"party": data.employee
|
||||||
})
|
})
|
||||||
|
|
||||||
if data.interest_amount and not data.interest_income_account:
|
if data.interest_amount and not data.interest_income_account:
|
||||||
@@ -275,14 +279,17 @@ class PayrollEntry(Document):
|
|||||||
"account": data.interest_income_account,
|
"account": data.interest_income_account,
|
||||||
"credit_in_account_currency": data.interest_amount,
|
"credit_in_account_currency": data.interest_amount,
|
||||||
"cost_center": self.cost_center,
|
"cost_center": self.cost_center,
|
||||||
"project": self.project
|
"project": self.project,
|
||||||
|
"party_type": "Employee",
|
||||||
|
"party": data.employee
|
||||||
})
|
})
|
||||||
payable_amount -= flt(data.total_payment, precision)
|
payable_amount -= flt(data.total_payment, precision)
|
||||||
|
|
||||||
# Payable amount
|
# Payable amount
|
||||||
accounts.append({
|
accounts.append({
|
||||||
"account": default_payroll_payable_account,
|
"account": default_payroll_payable_account,
|
||||||
"credit_in_account_currency": flt(payable_amount, precision)
|
"credit_in_account_currency": flt(payable_amount, precision),
|
||||||
|
"party_type": '',
|
||||||
})
|
})
|
||||||
|
|
||||||
journal_entry.set("accounts", accounts)
|
journal_entry.set("accounts", accounts)
|
||||||
@@ -546,7 +553,6 @@ def submit_salary_slips_for_employees(payroll_entry, salary_slips, publish_progr
|
|||||||
count += 1
|
count += 1
|
||||||
if publish_progress:
|
if publish_progress:
|
||||||
frappe.publish_progress(count*100/len(salary_slips), title = _("Submitting Salary Slips..."))
|
frappe.publish_progress(count*100/len(salary_slips), title = _("Submitting Salary Slips..."))
|
||||||
|
|
||||||
if submitted_ss:
|
if submitted_ss:
|
||||||
payroll_entry.make_accrual_jv_entry()
|
payroll_entry.make_accrual_jv_entry()
|
||||||
frappe.msgprint(_("Salary Slip submitted for period from {0} to {1}")
|
frappe.msgprint(_("Salary Slip submitted for period from {0} to {1}")
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ frappe.ui.form.on('Salary Structure', {
|
|||||||
frm.trigger("toggle_fields");
|
frm.trigger("toggle_fields");
|
||||||
frm.fields_dict['earnings'].grid.set_column_disp("default_amount", false);
|
frm.fields_dict['earnings'].grid.set_column_disp("default_amount", false);
|
||||||
frm.fields_dict['deductions'].grid.set_column_disp("default_amount", false);
|
frm.fields_dict['deductions'].grid.set_column_disp("default_amount", false);
|
||||||
|
|
||||||
if(frm.doc.docstatus === 1) {
|
if(frm.doc.docstatus === 1) {
|
||||||
frm.add_custom_button(__("Preview Salary Slip"), function() {
|
frm.add_custom_button(__("Preview Salary Slip"), function() {
|
||||||
frm.trigger('preview_salary_slip');
|
frm.trigger('preview_salary_slip');
|
||||||
@@ -119,47 +119,52 @@ frappe.ui.form.on('Salary Structure', {
|
|||||||
},
|
},
|
||||||
callback: function(r) {
|
callback: function(r) {
|
||||||
var employees = r.message;
|
var employees = r.message;
|
||||||
var d = new frappe.ui.Dialog({
|
if(!employees) return;
|
||||||
title: __("Preview Salary Slip"),
|
if (employees.length == 1){
|
||||||
fields: [
|
frm.events.open_salary_slip(frm, employees[0]);
|
||||||
{
|
} else {
|
||||||
"label":__("Employee"),
|
var d = new frappe.ui.Dialog({
|
||||||
"fieldname":"employee",
|
title: __("Preview Salary Slip"),
|
||||||
"fieldtype":"Select",
|
fields: [
|
||||||
"reqd": true,
|
{
|
||||||
options: employees
|
"label":__("Employee"),
|
||||||
}, {
|
"fieldname":"employee",
|
||||||
fieldname:"fetch",
|
"fieldtype":"Select",
|
||||||
"label":__("Show Salary Slip"),
|
"reqd": true,
|
||||||
"fieldtype":"Button"
|
options: employees
|
||||||
}
|
}, {
|
||||||
]
|
fieldname:"fetch",
|
||||||
});
|
"label":__("Show Salary Slip"),
|
||||||
d.get_input("fetch").on("click", function() {
|
"fieldtype":"Button"
|
||||||
var values = d.get_values();
|
}
|
||||||
if(!values) return;
|
]
|
||||||
var print_format;
|
|
||||||
frm.doc.salary_slip_based_on_timesheet ?
|
|
||||||
print_format="Salary Slip based on Timesheet" :
|
|
||||||
print_format="Salary Slip Standard";
|
|
||||||
|
|
||||||
frappe.call({
|
|
||||||
method: "erpnext.hr.doctype.salary_structure.salary_structure.make_salary_slip",
|
|
||||||
args: {
|
|
||||||
source_name: frm.doc.name,
|
|
||||||
employee: values.employee,
|
|
||||||
as_print: 1,
|
|
||||||
print_format: print_format,
|
|
||||||
for_preview: 1
|
|
||||||
},
|
|
||||||
callback: function(r) {
|
|
||||||
var new_window = window.open();
|
|
||||||
new_window.document.write(r.message);
|
|
||||||
// frappe.msgprint(r.message);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
d.get_input("fetch").on("click", function() {
|
||||||
d.show();
|
var values = d.get_values();
|
||||||
|
if(!values) return;
|
||||||
|
frm.events.open_salary_slip(frm, values.employee)
|
||||||
|
|
||||||
|
});
|
||||||
|
d.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
open_salary_slip: function(frm, employee){
|
||||||
|
var print_format = frm.doc.salary_slip_based_on_timesheet ? "Salary Slip based on Timesheet" : "Salary Slip Standard";
|
||||||
|
frappe.call({
|
||||||
|
method: "erpnext.hr.doctype.salary_structure.salary_structure.make_salary_slip",
|
||||||
|
args: {
|
||||||
|
source_name: frm.doc.name,
|
||||||
|
employee: employee,
|
||||||
|
as_print: 1,
|
||||||
|
print_format: print_format,
|
||||||
|
for_preview: 1
|
||||||
|
},
|
||||||
|
callback: function(r) {
|
||||||
|
var new_window = window.open();
|
||||||
|
new_window.document.write(r.message);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,9 +1,44 @@
|
|||||||
<h3>{{_("Training Event")}}</h3>
|
<table class="panel-header" border="0" cellpadding="0" cellspacing="0" width="100%">
|
||||||
|
<tr height="10"></tr>
|
||||||
|
<tr>
|
||||||
|
<td width="15"></td>
|
||||||
|
<td>
|
||||||
|
<div class="text-medium text-muted">
|
||||||
|
<span>{{_("Training Event:")}} {{ doc.event_name }}</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td width="15"></td>
|
||||||
|
</tr>
|
||||||
|
<tr height="10"></tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
<p>{{ doc.introduction }}</p>
|
<table class="panel-body" border="0" cellpadding="0" cellspacing="0" width="100%">
|
||||||
|
<tr height="10"></tr>
|
||||||
<h4>{{_("Details")}}</h4>
|
<tr>
|
||||||
{{_("Event Name")}}: {{ frappe.utils.get_link_to_form(doc.doctype, doc.name) }}
|
<td width="15"></td>
|
||||||
<br>{{_("Event Location")}}: {{ doc.location }}
|
<td>
|
||||||
<br>{{_("Start Time")}}: {{ doc.start_time }}
|
<div>
|
||||||
<br>{{_("End Time")}}: {{ doc.end_time }}
|
{{ doc.introduction }}
|
||||||
|
<ul class="list-unstyled" style="line-height: 1.7">
|
||||||
|
<li>{{_("Event Location")}}: <b>{{ doc.location }}</b></li>
|
||||||
|
{% set start = frappe.utils.get_datetime(doc.start_time) %}
|
||||||
|
{% set end = frappe.utils.get_datetime(doc.end_time) %}
|
||||||
|
{% if start.date() == end.date() %}
|
||||||
|
<li>{{_("Date")}}: <b>{{ start.strftime("%A, %d %b %Y") }}</b></li>
|
||||||
|
<li>
|
||||||
|
{{_("Timing")}}: <b>{{ start.strftime("%I:%M %p") + ' to ' + end.strftime("%I:%M %p") }}</b>
|
||||||
|
</li>
|
||||||
|
{% else %}
|
||||||
|
<li>{{_("Start Time")}}: <b>{{ start.strftime("%A, %d %b %Y at %I:%M %p") }}</b>
|
||||||
|
</li>
|
||||||
|
<li>{{_("End Time")}}: <b>{{ end.strftime("%A, %d %b %Y at %I:%M %p") }}</b>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
<li>{{ _('Event Link') }}: {{ frappe.utils.get_link_to_form(doc.doctype, doc.name) }}</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td width="15"></td>
|
||||||
|
</tr>
|
||||||
|
<tr height="10"></tr>
|
||||||
|
</table>
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
{
|
{
|
||||||
"attach_print": 0,
|
"attach_print": 0,
|
||||||
|
"channel": "Email",
|
||||||
|
"condition": "",
|
||||||
"creation": "2017-08-11 03:13:40.519614",
|
"creation": "2017-08-11 03:13:40.519614",
|
||||||
"days_in_advance": 0,
|
"days_in_advance": 0,
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
@@ -9,8 +11,8 @@
|
|||||||
"event": "Submit",
|
"event": "Submit",
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
"is_standard": 1,
|
"is_standard": 1,
|
||||||
"message": "<h3>{{_(\"Training Event\")}}</h3>\n\n<p>{{ doc.introduction }}</p>\n\n<h4>{{_(\"Details\")}}</h4>\n{{_(\"Event Name\")}}: {{ frappe.utils.get_link_to_form(doc.doctype, doc.name) }}\n<br>{{_(\"Event Location\")}}: {{ doc.location }}\n<br>{{_(\"Start Time\")}}: {{ doc.start_time }}\n<br>{{_(\"End Time\")}}: {{ doc.end_time }}\n",
|
"message": "<table class=\"panel-header\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\">\n <tr height=\"10\"></tr>\n <tr>\n <td width=\"15\"></td>\n <td>\n <div class=\"text-medium text-muted\">\n <span>{{_(\"Training Event:\")}} {{ doc.event_name }}</span>\n </div>\n </td>\n <td width=\"15\"></td>\n </tr>\n <tr height=\"10\"></tr>\n</table>\n\n<table class=\"panel-body\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\">\n <tr height=\"10\"></tr>\n <tr>\n <td width=\"15\"></td>\n <td>\n <div>\n <ul class=\"list-unstyled\" style=\"line-height: 1.7\">\n <li>{{ doc.introduction }}</li>\n <li>{{_(\"Event Location\")}}: <b>{{ doc.location }}</b></li>\n {% set start = frappe.utils.get_datetime(doc.start_time) %}\n {% set end = frappe.utils.get_datetime(doc.end_time) %}\n {% if start.date() == end.date() %}\n <li>{{_(\"Date\")}}: <b>{{ start.strftime(\"%A, %d %b %Y\") }}</b></li>\n <li>\n {{_(\"Timing\")}}: <b>{{ start.strftime(\"%I:%M %p\") + ' to ' + end.strftime(\"%I:%M %p\") }}</b>\n </li>\n {% else %}\n <li>{{_(\"Start Time\")}}: <b>{{ start.strftime(\"%A, %d %b %Y at %I:%M %p\") }}</b>\n </li>\n <li>{{_(\"End Time\")}}: <b>{{ end.strftime(\"%A, %d %b %Y at %I:%M %p\") }}</b>\n </li>\n {% endif %}\n </ul>\n {{ _('Event Link') }}: {{ frappe.utils.get_link_to_form(doc.doctype, doc.name) }}\n </div>\n </td>\n <td width=\"15\"></td>\n </tr>\n <tr height=\"10\"></tr>\n</table>",
|
||||||
"modified": "2017-08-13 22:49:42.338881",
|
"modified": "2019-11-29 15:38:31.805409",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "Training Scheduled",
|
"name": "Training Scheduled",
|
||||||
|
|||||||
@@ -1,9 +1,44 @@
|
|||||||
<h3>{{_("Training Event")}}</h3>
|
<table class="panel-header" border="0" cellpadding="0" cellspacing="0" width="100%">
|
||||||
<p>{{ message }}</p>
|
<tr height="10"></tr>
|
||||||
|
<tr>
|
||||||
|
<td width="15"></td>
|
||||||
|
<td>
|
||||||
|
<div class="text-medium text-muted">
|
||||||
|
<span>{{_("Training Event:")}} {{ doc.event_name }}</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td width="15"></td>
|
||||||
|
</tr>
|
||||||
|
<tr height="10"></tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
<h4>{{_("Details")}}</h4>
|
<table class="panel-body" border="0" cellpadding="0" cellspacing="0" width="100%">
|
||||||
{{_("Event Name")}}: <a href="{{ event_link }}">{{ name }}</a>
|
<tr height="10"></tr>
|
||||||
<br>{{_("Event Location")}}: {{ location }}
|
<tr>
|
||||||
<br>{{_("Start Time")}}: {{ start_time }}
|
<td width="15"></td>
|
||||||
<br>{{_("End Time")}}: {{ end_time }}
|
<td>
|
||||||
<br>{{_("Attendance")}}: {{ attendance }}
|
<div>
|
||||||
|
{{ doc.introduction }}
|
||||||
|
<ul class="list-unstyled" style="line-height: 1.7">
|
||||||
|
<li>{{_("Event Location")}}: <b>{{ doc.location }}</b></li>
|
||||||
|
{% set start = frappe.utils.get_datetime(doc.start_time) %}
|
||||||
|
{% set end = frappe.utils.get_datetime(doc.end_time) %}
|
||||||
|
{% if start.date() == end.date() %}
|
||||||
|
<li>{{_("Date")}}: <b>{{ start.strftime("%A, %d %b %Y") }}</b></li>
|
||||||
|
<li>
|
||||||
|
{{_("Timing")}}: <b>{{ start.strftime("%I:%M %p") + ' to ' + end.strftime("%I:%M %p") }}</b>
|
||||||
|
</li>
|
||||||
|
{% else %}
|
||||||
|
<li>{{_("Start Time")}}: <b>{{ start.strftime("%A, %d %b %Y at %I:%M %p") }}</b>
|
||||||
|
</li>
|
||||||
|
<li>{{_("End Time")}}: <b>{{ end.strftime("%A, %d %b %Y at %I:%M %p") }}</b>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
<li>{{ _('Event Link') }}: {{ frappe.utils.get_link_to_form(doc.doctype, doc.name) }}</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td width="15"></td>
|
||||||
|
</tr>
|
||||||
|
<tr height="10"></tr>
|
||||||
|
</table>
|
||||||
@@ -321,11 +321,11 @@ def allocate_earned_leaves():
|
|||||||
if new_allocation == allocation.total_leaves_allocated:
|
if new_allocation == allocation.total_leaves_allocated:
|
||||||
continue
|
continue
|
||||||
allocation.db_set("total_leaves_allocated", new_allocation, update_modified=False)
|
allocation.db_set("total_leaves_allocated", new_allocation, update_modified=False)
|
||||||
create_earned_leave_ledger_entry(allocation, earned_leaves, today)
|
create_additional_leave_ledger_entry(allocation, earned_leaves, today)
|
||||||
|
|
||||||
def create_earned_leave_ledger_entry(allocation, earned_leaves, date):
|
def create_additional_leave_ledger_entry(allocation, leaves, date):
|
||||||
''' Create leave ledger entry based on the earned leave frequency '''
|
''' Create leave ledger entry for leave types '''
|
||||||
allocation.new_leaves_allocated = earned_leaves
|
allocation.new_leaves_allocated = leaves
|
||||||
allocation.from_date = date
|
allocation.from_date = date
|
||||||
allocation.unused_leaves = 0
|
allocation.unused_leaves = 0
|
||||||
allocation.create_leave_ledger_entry()
|
allocation.create_leave_ledger_entry()
|
||||||
@@ -389,6 +389,7 @@ def get_sal_slip_total_benefit_given(employee, payroll_period, component=False):
|
|||||||
|
|
||||||
def get_holidays_for_employee(employee, start_date, end_date):
|
def get_holidays_for_employee(employee, start_date, end_date):
|
||||||
holiday_list = get_holiday_list_for_employee(employee)
|
holiday_list = get_holiday_list_for_employee(employee)
|
||||||
|
|
||||||
holidays = frappe.db.sql_list('''select holiday_date from `tabHoliday`
|
holidays = frappe.db.sql_list('''select holiday_date from `tabHoliday`
|
||||||
where
|
where
|
||||||
parent=%(holiday_list)s
|
parent=%(holiday_list)s
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ frappe.provide("erpnext.bom");
|
|||||||
frappe.ui.form.on("BOM", {
|
frappe.ui.form.on("BOM", {
|
||||||
setup: function(frm) {
|
setup: function(frm) {
|
||||||
frm.custom_make_buttons = {
|
frm.custom_make_buttons = {
|
||||||
'BOM': 'Duplicate BOM',
|
|
||||||
'Work Order': 'Work Order',
|
'Work Order': 'Work Order',
|
||||||
'Quality Inspection': 'Quality Inspection'
|
'Quality Inspection': 'Quality Inspection'
|
||||||
};
|
};
|
||||||
@@ -91,10 +90,6 @@ frappe.ui.form.on("BOM", {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(frm.doc.docstatus!=0) {
|
if(frm.doc.docstatus!=0) {
|
||||||
frm.add_custom_button(__("Duplicate BOM"), function() {
|
|
||||||
frm.copy_doc();
|
|
||||||
}, __("Create"));
|
|
||||||
|
|
||||||
frm.add_custom_button(__("Work Order"), function() {
|
frm.add_custom_button(__("Work Order"), function() {
|
||||||
frm.trigger("make_work_order");
|
frm.trigger("make_work_order");
|
||||||
}, __("Create"));
|
}, __("Create"));
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ class BOM(WebsiteGenerator):
|
|||||||
context.parents = [{'name': 'boms', 'title': _('All BOMs') }]
|
context.parents = [{'name': 'boms', 'title': _('All BOMs') }]
|
||||||
|
|
||||||
def on_update(self):
|
def on_update(self):
|
||||||
|
frappe.cache().hdel('bom_children', self.name)
|
||||||
self.check_recursion()
|
self.check_recursion()
|
||||||
self.update_stock_qty()
|
self.update_stock_qty()
|
||||||
self.update_exploded_items()
|
self.update_exploded_items()
|
||||||
|
|||||||
@@ -25,5 +25,5 @@ def get_data():
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
'disable_create_buttons': ["Item", "Purchase Order", "Purchase Receipt",
|
'disable_create_buttons': ["Item", "Purchase Order", "Purchase Receipt",
|
||||||
"Purchase Invoice", "Job Card", "Stock Entry"]
|
"Purchase Invoice", "Job Card", "Stock Entry", "BOM"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,9 @@
|
|||||||
|
|
||||||
frappe.ui.form.on('Job Card', {
|
frappe.ui.form.on('Job Card', {
|
||||||
refresh: function(frm) {
|
refresh: function(frm) {
|
||||||
|
frappe.flags.pause_job = 0;
|
||||||
|
frappe.flags.resume_job = 0;
|
||||||
|
|
||||||
if(!frm.doc.__islocal && frm.doc.items && frm.doc.items.length) {
|
if(!frm.doc.__islocal && frm.doc.items && frm.doc.items.length) {
|
||||||
if (frm.doc.for_quantity != frm.doc.transferred_qty) {
|
if (frm.doc.for_quantity != frm.doc.transferred_qty) {
|
||||||
frm.add_custom_button(__("Material Request"), () => {
|
frm.add_custom_button(__("Material Request"), () => {
|
||||||
@@ -13,44 +16,99 @@ frappe.ui.form.on('Job Card', {
|
|||||||
if (frm.doc.for_quantity != frm.doc.transferred_qty) {
|
if (frm.doc.for_quantity != frm.doc.transferred_qty) {
|
||||||
frm.add_custom_button(__("Material Transfer"), () => {
|
frm.add_custom_button(__("Material Transfer"), () => {
|
||||||
frm.trigger("make_stock_entry");
|
frm.trigger("make_stock_entry");
|
||||||
});
|
}).addClass("btn-primary");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (frm.doc.docstatus == 0) {
|
if (frm.doc.docstatus == 0 && frm.doc.for_quantity > frm.doc.total_completed_qty
|
||||||
frm.trigger("make_dashboard");
|
&& (!frm.doc.items.length || frm.doc.for_quantity == frm.doc.transferred_qty)) {
|
||||||
|
frm.trigger("prepare_timer_buttons");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
if (!frm.doc.job_started) {
|
prepare_timer_buttons: function(frm) {
|
||||||
frm.add_custom_button(__("Start Job"), () => {
|
frm.trigger("make_dashboard");
|
||||||
let row = frappe.model.add_child(frm.doc, 'Job Card Time Log', 'time_logs');
|
if (!frm.doc.job_started) {
|
||||||
row.from_time = frappe.datetime.now_datetime();
|
frm.add_custom_button(__("Start"), () => {
|
||||||
frm.set_value('job_started', 1);
|
if (!frm.doc.employee) {
|
||||||
frm.set_value('started_time' , row.from_time);
|
frappe.prompt({fieldtype: 'Link', label: __('Employee'), options: "Employee",
|
||||||
frm.save();
|
fieldname: 'employee'}, d => {
|
||||||
});
|
if (d.employee) {
|
||||||
} else {
|
frm.set_value("employee", d.employee);
|
||||||
frm.add_custom_button(__("Complete Job"), () => {
|
|
||||||
let completed_time = frappe.datetime.now_datetime();
|
|
||||||
frm.doc.time_logs.forEach(d => {
|
|
||||||
if (d.from_time && !d.to_time) {
|
|
||||||
d.to_time = completed_time;
|
|
||||||
frm.set_value('started_time' , '');
|
|
||||||
frm.set_value('job_started', 0);
|
|
||||||
frm.save();
|
|
||||||
}
|
}
|
||||||
})
|
|
||||||
});
|
frm.events.start_job(frm);
|
||||||
}
|
}, __("Enter Value"), __("Start"));
|
||||||
|
} else {
|
||||||
|
frm.events.start_job(frm);
|
||||||
|
}
|
||||||
|
}).addClass("btn-primary");
|
||||||
|
} else if (frm.doc.status == "On Hold") {
|
||||||
|
frm.add_custom_button(__("Resume"), () => {
|
||||||
|
frappe.flags.resume_job = 1;
|
||||||
|
frm.events.start_job(frm);
|
||||||
|
}).addClass("btn-primary");
|
||||||
|
} else {
|
||||||
|
frm.add_custom_button(__("Pause"), () => {
|
||||||
|
frappe.flags.pause_job = 1;
|
||||||
|
frm.set_value("status", "On Hold");
|
||||||
|
frm.events.complete_job(frm);
|
||||||
|
});
|
||||||
|
|
||||||
|
frm.add_custom_button(__("Complete"), () => {
|
||||||
|
let completed_time = frappe.datetime.now_datetime();
|
||||||
|
frm.trigger("hide_timer");
|
||||||
|
|
||||||
|
frappe.prompt({fieldtype: 'Float', label: __('Completed Quantity'),
|
||||||
|
fieldname: 'qty', reqd: 1, default: frm.doc.for_quantity}, data => {
|
||||||
|
frm.events.complete_job(frm, completed_time, data.qty);
|
||||||
|
}, __("Enter Value"), __("Complete"));
|
||||||
|
}).addClass("btn-primary");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
start_job: function(frm) {
|
||||||
|
let row = frappe.model.add_child(frm.doc, 'Job Card Time Log', 'time_logs');
|
||||||
|
row.from_time = frappe.datetime.now_datetime();
|
||||||
|
frm.set_value('job_started', 1);
|
||||||
|
frm.set_value('started_time' , row.from_time);
|
||||||
|
frm.set_value("status", "Work In Progress");
|
||||||
|
|
||||||
|
if (!frappe.flags.resume_job) {
|
||||||
|
frm.set_value('current_time' , 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
frm.save();
|
||||||
|
},
|
||||||
|
|
||||||
|
complete_job: function(frm, completed_time, completed_qty) {
|
||||||
|
frm.doc.time_logs.forEach(d => {
|
||||||
|
if (d.from_time && !d.to_time) {
|
||||||
|
d.to_time = completed_time || frappe.datetime.now_datetime();
|
||||||
|
d.completed_qty = completed_qty || 0;
|
||||||
|
|
||||||
|
if(frappe.flags.pause_job) {
|
||||||
|
let currentIncrement = moment(d.to_time).diff(moment(d.from_time),"seconds") || 0;
|
||||||
|
frm.set_value('current_time' , currentIncrement + (frm.doc.current_time || 0));
|
||||||
|
} else {
|
||||||
|
frm.set_value('started_time' , '');
|
||||||
|
frm.set_value('job_started', 0);
|
||||||
|
frm.set_value('current_time' , 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
frm.save();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
make_dashboard: function(frm) {
|
make_dashboard: function(frm) {
|
||||||
if(frm.doc.__islocal)
|
if(frm.doc.__islocal)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
frm.dashboard.refresh();
|
frm.dashboard.refresh();
|
||||||
const timer = `
|
const timer = `
|
||||||
<div class="stopwatch" style="font-weight:bold">
|
<div class="stopwatch" style="font-weight:bold;margin:0px 13px 0px 2px;
|
||||||
|
color:#545454;font-size:18px;display:inline-block;vertical-align:text-bottom;>
|
||||||
<span class="hours">00</span>
|
<span class="hours">00</span>
|
||||||
<span class="colon">:</span>
|
<span class="colon">:</span>
|
||||||
<span class="minutes">00</span>
|
<span class="minutes">00</span>
|
||||||
@@ -58,11 +116,16 @@ frappe.ui.form.on('Job Card', {
|
|||||||
<span class="seconds">00</span>
|
<span class="seconds">00</span>
|
||||||
</div>`;
|
</div>`;
|
||||||
|
|
||||||
var section = frm.dashboard.add_section(timer);
|
var section = frm.toolbar.page.add_inner_message(timer);
|
||||||
|
|
||||||
if (frm.doc.started_time) {
|
let currentIncrement = frm.doc.current_time || 0;
|
||||||
let currentIncrement = moment(frappe.datetime.now_datetime()).diff(moment(frm.doc.started_time),"seconds");
|
if (frm.doc.started_time || frm.doc.current_time) {
|
||||||
initialiseTimer();
|
if (frm.doc.status == "On Hold") {
|
||||||
|
updateStopwatch(currentIncrement);
|
||||||
|
} else {
|
||||||
|
currentIncrement += moment(frappe.datetime.now_datetime()).diff(moment(frm.doc.started_time),"seconds");
|
||||||
|
initialiseTimer();
|
||||||
|
}
|
||||||
|
|
||||||
function initialiseTimer() {
|
function initialiseTimer() {
|
||||||
const interval = setInterval(function() {
|
const interval = setInterval(function() {
|
||||||
@@ -70,12 +133,12 @@ frappe.ui.form.on('Job Card', {
|
|||||||
updateStopwatch(current);
|
updateStopwatch(current);
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateStopwatch(increment) {
|
function updateStopwatch(increment) {
|
||||||
var hours = Math.floor(increment / 3600);
|
var hours = Math.floor(increment / 3600);
|
||||||
var minutes = Math.floor((increment - (hours * 3600)) / 60);
|
var minutes = Math.floor((increment - (hours * 3600)) / 60);
|
||||||
var seconds = increment - (hours * 3600) - (minutes * 60);
|
var seconds = increment - (hours * 3600) - (minutes * 60);
|
||||||
|
|
||||||
$(section).find(".hours").text(hours < 10 ? ("0" + hours.toString()) : hours.toString());
|
$(section).find(".hours").text(hours < 10 ? ("0" + hours.toString()) : hours.toString());
|
||||||
$(section).find(".minutes").text(minutes < 10 ? ("0" + minutes.toString()) : minutes.toString());
|
$(section).find(".minutes").text(minutes < 10 ? ("0" + minutes.toString()) : minutes.toString());
|
||||||
$(section).find(".seconds").text(seconds < 10 ? ("0" + seconds.toString()) : seconds.toString());
|
$(section).find(".seconds").text(seconds < 10 ? ("0" + seconds.toString()) : seconds.toString());
|
||||||
@@ -88,6 +151,10 @@ frappe.ui.form.on('Job Card', {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
hide_timer: function(frm) {
|
||||||
|
frm.toolbar.page.inner_toolbar.find(".stopwatch").remove();
|
||||||
|
},
|
||||||
|
|
||||||
for_quantity: function(frm) {
|
for_quantity: function(frm) {
|
||||||
frm.doc.items = [];
|
frm.doc.items = [];
|
||||||
frm.call({
|
frm.call({
|
||||||
@@ -117,5 +184,22 @@ frappe.ui.form.on('Job Card', {
|
|||||||
|
|
||||||
timer: function(frm) {
|
timer: function(frm) {
|
||||||
return `<button> Start </button>`
|
return `<button> Start </button>`
|
||||||
|
},
|
||||||
|
|
||||||
|
set_total_completed_qty: function(frm) {
|
||||||
|
frm.doc.total_completed_qty = 0;
|
||||||
|
frm.doc.time_logs.forEach(d => {
|
||||||
|
if (d.completed_qty) {
|
||||||
|
frm.doc.total_completed_qty += d.completed_qty;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
refresh_field("total_completed_qty");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
frappe.ui.form.on('Job Card Time Log', {
|
||||||
|
completed_qty: function(frm) {
|
||||||
|
frm.events.set_total_completed_qty(frm);
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -13,10 +13,18 @@
|
|||||||
"column_break_4",
|
"column_break_4",
|
||||||
"posting_date",
|
"posting_date",
|
||||||
"company",
|
"company",
|
||||||
|
"remarks",
|
||||||
|
"production_section",
|
||||||
|
"production_item",
|
||||||
|
"item_name",
|
||||||
"for_quantity",
|
"for_quantity",
|
||||||
"wip_warehouse",
|
"wip_warehouse",
|
||||||
"timing_detail",
|
"column_break_12",
|
||||||
"employee",
|
"employee",
|
||||||
|
"employee_name",
|
||||||
|
"status",
|
||||||
|
"project",
|
||||||
|
"timing_detail",
|
||||||
"time_logs",
|
"time_logs",
|
||||||
"section_break_13",
|
"section_break_13",
|
||||||
"total_completed_qty",
|
"total_completed_qty",
|
||||||
@@ -28,12 +36,11 @@
|
|||||||
"operation_id",
|
"operation_id",
|
||||||
"transferred_qty",
|
"transferred_qty",
|
||||||
"requested_qty",
|
"requested_qty",
|
||||||
"project",
|
|
||||||
"remarks",
|
|
||||||
"column_break_20",
|
"column_break_20",
|
||||||
"status",
|
"barcode",
|
||||||
"job_started",
|
"job_started",
|
||||||
"started_time",
|
"started_time",
|
||||||
|
"current_time",
|
||||||
"amended_from"
|
"amended_from"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
@@ -41,13 +48,14 @@
|
|||||||
"fieldname": "work_order",
|
"fieldname": "work_order",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
|
"in_standard_filter": 1,
|
||||||
"label": "Work Order",
|
"label": "Work Order",
|
||||||
"options": "Work Order",
|
"options": "Work Order",
|
||||||
"read_only": 1,
|
|
||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
"search_index": 1
|
"search_index": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"fetch_from": "work_order.bom_no",
|
||||||
"fieldname": "bom_no",
|
"fieldname": "bom_no",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "BOM No",
|
"label": "BOM No",
|
||||||
@@ -91,7 +99,7 @@
|
|||||||
"fieldname": "for_quantity",
|
"fieldname": "for_quantity",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "For Quantity",
|
"label": "Qty To Manufacture",
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -109,6 +117,7 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "employee",
|
"fieldname": "employee",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
|
"in_standard_filter": 1,
|
||||||
"label": "Employee",
|
"label": "Employee",
|
||||||
"options": "Employee"
|
"options": "Employee"
|
||||||
},
|
},
|
||||||
@@ -198,7 +207,7 @@
|
|||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"label": "Status",
|
"label": "Status",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "Open\nWork In Progress\nMaterial Transferred\nSubmitted\nCancelled\nCompleted",
|
"options": "Open\nWork In Progress\nMaterial Transferred\nOn Hold\nSubmitted\nCancelled\nCompleted",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -236,10 +245,52 @@
|
|||||||
"label": "Naming Series",
|
"label": "Naming Series",
|
||||||
"options": "PO-JOB.#####",
|
"options": "PO-JOB.#####",
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fetch_from": "employee.employee_name",
|
||||||
|
"fieldname": "employee_name",
|
||||||
|
"fieldtype": "Read Only",
|
||||||
|
"label": "Employee Name"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "production_section",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Production"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_12",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fetch_from": "work_order.production_item",
|
||||||
|
"fieldname": "production_item",
|
||||||
|
"fieldtype": "Read Only",
|
||||||
|
"label": "Production Item"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "barcode",
|
||||||
|
"fieldtype": "Barcode",
|
||||||
|
"label": "Barcode",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fetch_from": "work_order.item_name",
|
||||||
|
"fieldname": "item_name",
|
||||||
|
"fieldtype": "Read Only",
|
||||||
|
"label": "Item Name"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "current_time",
|
||||||
|
"fieldtype": "Int",
|
||||||
|
"hidden": 1,
|
||||||
|
"label": "Current Time",
|
||||||
|
"no_copy": 1,
|
||||||
|
"print_hide": 1,
|
||||||
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"modified": "2019-10-30 01:49:19.606178",
|
"modified": "2019-12-03 13:08:57.926201",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Manufacturing",
|
"module": "Manufacturing",
|
||||||
"name": "Job Card",
|
"name": "Job Card",
|
||||||
|
|||||||
@@ -194,8 +194,9 @@ class JobCard(Document):
|
|||||||
if self.total_completed_qty <= 0.0:
|
if self.total_completed_qty <= 0.0:
|
||||||
frappe.throw(_("Total completed qty must be greater than zero"))
|
frappe.throw(_("Total completed qty must be greater than zero"))
|
||||||
|
|
||||||
if self.total_completed_qty > self.for_quantity:
|
if self.total_completed_qty != self.for_quantity:
|
||||||
frappe.throw(_("Total completed qty can not be greater than for quantity"))
|
frappe.throw(_("The total completed qty({0}) must be equal to qty to manufacture({1})"
|
||||||
|
.format(frappe.bold(self.total_completed_qty),frappe.bold(self.for_quantity))))
|
||||||
|
|
||||||
def update_work_order(self):
|
def update_work_order(self):
|
||||||
if not self.work_order:
|
if not self.work_order:
|
||||||
@@ -271,6 +272,8 @@ class JobCard(Document):
|
|||||||
self.set_status(update_status)
|
self.set_status(update_status)
|
||||||
|
|
||||||
def set_status(self, update_status=False):
|
def set_status(self, update_status=False):
|
||||||
|
if self.status == "On Hold": return
|
||||||
|
|
||||||
self.status = {
|
self.status = {
|
||||||
0: "Open",
|
0: "Open",
|
||||||
1: "Submitted",
|
1: "Submitted",
|
||||||
@@ -329,6 +332,7 @@ def make_stock_entry(source_name, target_doc=None):
|
|||||||
target.fg_completed_qty = source.get('for_quantity', 0) - source.get('transferred_qty', 0)
|
target.fg_completed_qty = source.get('for_quantity', 0) - source.get('transferred_qty', 0)
|
||||||
target.calculate_rate_and_amount()
|
target.calculate_rate_and_amount()
|
||||||
target.set_missing_values()
|
target.set_missing_values()
|
||||||
|
target.set_stock_entry_type()
|
||||||
|
|
||||||
doclist = get_mapped_doc("Job Card", source_name, {
|
doclist = get_mapped_doc("Job Card", source_name, {
|
||||||
"Job Card": {
|
"Job Card": {
|
||||||
@@ -352,4 +356,46 @@ def make_stock_entry(source_name, target_doc=None):
|
|||||||
return doclist
|
return doclist
|
||||||
|
|
||||||
def time_diff_in_minutes(string_ed_date, string_st_date):
|
def time_diff_in_minutes(string_ed_date, string_st_date):
|
||||||
return time_diff(string_ed_date, string_st_date).total_seconds() / 60
|
return time_diff(string_ed_date, string_st_date).total_seconds() / 60
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def get_job_details(start, end, filters=None):
|
||||||
|
events = []
|
||||||
|
|
||||||
|
event_color = {
|
||||||
|
"Completed": "#cdf5a6",
|
||||||
|
"Material Transferred": "#ffdd9e",
|
||||||
|
"Work In Progress": "#D3D3D3"
|
||||||
|
}
|
||||||
|
|
||||||
|
from frappe.desk.reportview import get_filters_cond
|
||||||
|
conditions = get_filters_cond("Job Card", filters, [])
|
||||||
|
|
||||||
|
job_cards = frappe.db.sql(""" SELECT `tabJob Card`.name, `tabJob Card`.work_order,
|
||||||
|
`tabJob Card`.employee_name, `tabJob Card`.status, ifnull(`tabJob Card`.remarks, ''),
|
||||||
|
min(`tabJob Card Time Log`.from_time) as from_time,
|
||||||
|
max(`tabJob Card Time Log`.to_time) as to_time
|
||||||
|
FROM `tabJob Card` , `tabJob Card Time Log`
|
||||||
|
WHERE
|
||||||
|
`tabJob Card`.name = `tabJob Card Time Log`.parent {0}
|
||||||
|
group by `tabJob Card`.name""".format(conditions), as_dict=1)
|
||||||
|
|
||||||
|
for d in job_cards:
|
||||||
|
subject_data = []
|
||||||
|
for field in ["name", "work_order", "remarks", "employee_name"]:
|
||||||
|
if not d.get(field): continue
|
||||||
|
|
||||||
|
subject_data.append(d.get(field))
|
||||||
|
|
||||||
|
color = event_color.get(d.status)
|
||||||
|
job_card_data = {
|
||||||
|
'from_time': d.from_time,
|
||||||
|
'to_time': d.to_time,
|
||||||
|
'name': d.name,
|
||||||
|
'subject': '\n'.join(subject_data),
|
||||||
|
'color': color if color else "#89bcde"
|
||||||
|
}
|
||||||
|
|
||||||
|
events.append(job_card_data)
|
||||||
|
|
||||||
|
return events
|
||||||
|
|||||||
21
erpnext/manufacturing/doctype/job_card/job_card_calendar.js
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
frappe.views.calendar["Job Card"] = {
|
||||||
|
field_map: {
|
||||||
|
"start": "from_time",
|
||||||
|
"end": "to_time",
|
||||||
|
"id": "name",
|
||||||
|
"title": "subject",
|
||||||
|
"color": "color",
|
||||||
|
"allDay": "allDay",
|
||||||
|
"progress": "progress"
|
||||||
|
},
|
||||||
|
gantt: true,
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"fieldname": "employee",
|
||||||
|
"options": "Employee",
|
||||||
|
"label": __("Employee")
|
||||||
|
}
|
||||||
|
],
|
||||||
|
get_events_method: "erpnext.manufacturing.doctype.job_card.job_card.get_job_details"
|
||||||
|
};
|
||||||
@@ -1,208 +1,57 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"creation": "2019-03-08 23:56:43.187569",
|
||||||
"allow_events_in_timeline": 0,
|
"doctype": "DocType",
|
||||||
"allow_guest_to_view": 0,
|
"editable_grid": 1,
|
||||||
"allow_import": 0,
|
"engine": "InnoDB",
|
||||||
"allow_rename": 0,
|
"field_order": [
|
||||||
"beta": 0,
|
"from_time",
|
||||||
"creation": "2019-03-08 23:56:43.187569",
|
"to_time",
|
||||||
"custom": 0,
|
"column_break_2",
|
||||||
"docstatus": 0,
|
"time_in_mins",
|
||||||
"doctype": "DocType",
|
"completed_qty"
|
||||||
"document_type": "",
|
],
|
||||||
"editable_grid": 1,
|
|
||||||
"engine": "InnoDB",
|
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "from_time",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Datetime",
|
||||||
"allow_on_submit": 0,
|
"in_list_view": 1,
|
||||||
"bold": 0,
|
"label": "From Time"
|
||||||
"collapsible": 0,
|
},
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "from_time",
|
|
||||||
"fieldtype": "Datetime",
|
|
||||||
"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": "From Time",
|
|
||||||
"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,
|
"fieldname": "to_time",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Datetime",
|
||||||
"allow_on_submit": 0,
|
"in_list_view": 1,
|
||||||
"bold": 0,
|
"label": "To Time"
|
||||||
"collapsible": 0,
|
},
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "to_time",
|
|
||||||
"fieldtype": "Datetime",
|
|
||||||
"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": "To Time",
|
|
||||||
"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,
|
"fieldname": "column_break_2",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Column Break"
|
||||||
"allow_on_submit": 0,
|
},
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "column_break_2",
|
|
||||||
"fieldtype": "Column Break",
|
|
||||||
"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,
|
|
||||||
"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,
|
"fieldname": "time_in_mins",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Float",
|
||||||
"allow_on_submit": 0,
|
"in_list_view": 1,
|
||||||
"bold": 0,
|
"label": "Time In Mins",
|
||||||
"collapsible": 0,
|
"read_only": 1
|
||||||
"columns": 0,
|
},
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "time_in_mins",
|
|
||||||
"fieldtype": "Float",
|
|
||||||
"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": "Time In Mins",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 1,
|
|
||||||
"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,
|
"default": "0",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldname": "completed_qty",
|
||||||
"allow_on_submit": 0,
|
"fieldtype": "Float",
|
||||||
"bold": 0,
|
"in_list_view": 1,
|
||||||
"collapsible": 0,
|
"label": "Completed Qty",
|
||||||
"columns": 0,
|
"reqd": 1
|
||||||
"default": "0",
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "completed_qty",
|
|
||||||
"fieldtype": "Float",
|
|
||||||
"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": "Completed Qty",
|
|
||||||
"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": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_web_view": 0,
|
"istable": 1,
|
||||||
"hide_heading": 0,
|
"modified": "2019-12-03 12:56:02.285448",
|
||||||
"hide_toolbar": 0,
|
"modified_by": "Administrator",
|
||||||
"idx": 0,
|
"module": "Manufacturing",
|
||||||
"image_view": 0,
|
"name": "Job Card Time Log",
|
||||||
"in_create": 0,
|
"owner": "Administrator",
|
||||||
"is_submittable": 0,
|
"permissions": [],
|
||||||
"issingle": 0,
|
"quick_entry": 1,
|
||||||
"istable": 1,
|
"sort_field": "modified",
|
||||||
"max_attachments": 0,
|
"sort_order": "ASC",
|
||||||
"modified": "2019-03-10 17:08:46.504910",
|
"track_changes": 1
|
||||||
"modified_by": "Administrator",
|
|
||||||
"module": "Manufacturing",
|
|
||||||
"name": "Job Card Time Log",
|
|
||||||
"name_case": "",
|
|
||||||
"owner": "Administrator",
|
|
||||||
"permissions": [],
|
|
||||||
"quick_entry": 1,
|
|
||||||
"read_only": 0,
|
|
||||||
"read_only_onload": 0,
|
|
||||||
"show_name_in_global_search": 0,
|
|
||||||
"sort_field": "modified",
|
|
||||||
"sort_order": "ASC",
|
|
||||||
"track_changes": 1,
|
|
||||||
"track_seen": 0,
|
|
||||||
"track_views": 0
|
|
||||||
}
|
}
|
||||||
@@ -71,12 +71,13 @@ frappe.ui.form.on('Production Plan', {
|
|||||||
}, __('Create'));
|
}, __('Create'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
frm.page.set_inner_btn_group_as_primary(__('Create'));
|
||||||
frm.trigger("material_requirement");
|
frm.trigger("material_requirement");
|
||||||
|
|
||||||
const projected_qty_formula = ` <table class="table table-bordered" style="background-color: #f9f9f9;">
|
const projected_qty_formula = ` <table class="table table-bordered" style="background-color: #f9f9f9;">
|
||||||
<tr><td style="padding-left:25px">
|
<tr><td style="padding-left:25px">
|
||||||
<div>
|
<div>
|
||||||
<h3>
|
<h3 style="text-decoration: underline;">
|
||||||
<a href = "https://erpnext.com/docs/user/manual/en/stock/projected-quantity">
|
<a href = "https://erpnext.com/docs/user/manual/en/stock/projected-quantity">
|
||||||
${__("Projected Quantity Formula")}
|
${__("Projected Quantity Formula")}
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -615,6 +615,9 @@ def get_items_for_material_requests(doc, ignore_existing_ordered_qty=None):
|
|||||||
|
|
||||||
doc['mr_items'] = []
|
doc['mr_items'] = []
|
||||||
po_items = doc.get('po_items') if doc.get('po_items') else doc.get('items')
|
po_items = doc.get('po_items') if doc.get('po_items') else doc.get('items')
|
||||||
|
if not po_items:
|
||||||
|
frappe.throw(_("Items are required to pull the raw materials which is associated with it."))
|
||||||
|
|
||||||
company = doc.get('company')
|
company = doc.get('company')
|
||||||
warehouse = doc.get('for_warehouse')
|
warehouse = doc.get('for_warehouse')
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ def get_data():
|
|||||||
'fieldname': 'production_plan',
|
'fieldname': 'production_plan',
|
||||||
'transactions': [
|
'transactions': [
|
||||||
{
|
{
|
||||||
'label': _('Related'),
|
'label': _('Transactions'),
|
||||||
'items': ['Work Order', 'Material Request']
|
'items': ['Work Order', 'Material Request']
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ frappe.ui.form.on("Work Order", {
|
|||||||
frm.custom_make_buttons = {
|
frm.custom_make_buttons = {
|
||||||
'Stock Entry': 'Start',
|
'Stock Entry': 'Start',
|
||||||
'Pick List': 'Create Pick List',
|
'Pick List': 'Create Pick List',
|
||||||
|
'Job Card': 'Create Job Card'
|
||||||
};
|
};
|
||||||
|
|
||||||
// Set query for warehouses
|
// Set query for warehouses
|
||||||
@@ -131,7 +132,8 @@ frappe.ui.form.on("Work Order", {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (frm.doc.docstatus===1) {
|
if (frm.doc.docstatus===1) {
|
||||||
frm.trigger('show_progress');
|
frm.trigger('show_progress_for_items');
|
||||||
|
frm.trigger('show_progress_for_operations');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (frm.doc.docstatus === 1
|
if (frm.doc.docstatus === 1
|
||||||
@@ -179,89 +181,72 @@ frappe.ui.form.on("Work Order", {
|
|||||||
|
|
||||||
make_job_card: function(frm) {
|
make_job_card: function(frm) {
|
||||||
let qty = 0;
|
let qty = 0;
|
||||||
const fields = [{
|
let operations_data = [];
|
||||||
fieldtype: "Link",
|
|
||||||
fieldname: "operation",
|
|
||||||
options: "Operation",
|
|
||||||
label: __("Operation"),
|
|
||||||
get_query: () => {
|
|
||||||
const filter_workstation = frm.doc.operations.filter(d => {
|
|
||||||
if (d.status != "Completed") {
|
|
||||||
return d;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
const dialog = frappe.prompt({fieldname: 'operations', fieldtype: 'Table', label: __('Operations'),
|
||||||
filters: {
|
fields: [
|
||||||
name: ["in", (filter_workstation || []).map(d => d.operation)]
|
{
|
||||||
}
|
fieldtype:'Link',
|
||||||
};
|
fieldname:'operation',
|
||||||
},
|
label: __('Operation'),
|
||||||
reqd: true
|
read_only:1,
|
||||||
}, {
|
in_list_view:1
|
||||||
fieldtype: "Link",
|
},
|
||||||
fieldname: "workstation",
|
{
|
||||||
options: "Workstation",
|
fieldtype:'Link',
|
||||||
label: __("Workstation"),
|
fieldname:'workstation',
|
||||||
get_query: () => {
|
label: __('Workstation'),
|
||||||
const operation = dialog.get_value("operation");
|
read_only:1,
|
||||||
const filter_workstation = frm.doc.operations.filter(d => {
|
in_list_view:1
|
||||||
if (d.operation == operation) {
|
},
|
||||||
return d;
|
{
|
||||||
}
|
fieldtype:'Data',
|
||||||
});
|
fieldname:'name',
|
||||||
|
label: __('Operation Id')
|
||||||
return {
|
},
|
||||||
filters: {
|
{
|
||||||
name: ["in", (filter_workstation || []).map(d => d.workstation)]
|
fieldtype:'Float',
|
||||||
}
|
fieldname:'pending_qty',
|
||||||
};
|
label: __('Pending Qty'),
|
||||||
},
|
},
|
||||||
onchange: () => {
|
{
|
||||||
const operation = dialog.get_value("operation");
|
fieldtype:'Float',
|
||||||
const workstation = dialog.get_value("workstation");
|
fieldname:'qty',
|
||||||
if (operation && workstation) {
|
label: __('Quantity to Manufacture'),
|
||||||
const row = frm.doc.operations.filter(d => d.operation == operation && d.workstation == workstation)[0];
|
read_only:0,
|
||||||
qty = frm.doc.qty - row.completed_qty;
|
in_list_view:1,
|
||||||
|
},
|
||||||
if (qty > 0) {
|
],
|
||||||
dialog.set_value("qty", qty);
|
data: operations_data,
|
||||||
}
|
in_place_edit: true,
|
||||||
}
|
get_data: function() {
|
||||||
},
|
return operations_data;
|
||||||
reqd: true
|
|
||||||
}, {
|
|
||||||
fieldtype: "Float",
|
|
||||||
fieldname: "qty",
|
|
||||||
label: __("For Quantity"),
|
|
||||||
reqd: true
|
|
||||||
}];
|
|
||||||
|
|
||||||
const dialog = frappe.prompt(fields, function(data) {
|
|
||||||
if (data.qty > qty) {
|
|
||||||
frappe.throw(__("For Quantity must be less than quantity {0}", [qty]));
|
|
||||||
}
|
}
|
||||||
|
}, function(data) {
|
||||||
if (data.qty <= 0) {
|
|
||||||
frappe.throw(__("For Quantity must be greater than zero"));
|
|
||||||
}
|
|
||||||
|
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: "erpnext.manufacturing.doctype.work_order.work_order.make_job_card",
|
method: "erpnext.manufacturing.doctype.work_order.work_order.make_job_card",
|
||||||
args: {
|
args: {
|
||||||
work_order: frm.doc.name,
|
work_order: frm.doc.name,
|
||||||
operation: data.operation,
|
operations: data.operations,
|
||||||
workstation: data.workstation,
|
|
||||||
qty: data.qty
|
|
||||||
},
|
|
||||||
callback: function(r){
|
|
||||||
if (r.message) {
|
|
||||||
var doc = frappe.model.sync(r.message)[0];
|
|
||||||
frappe.set_route("Form", doc.doctype, doc.name);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, __("For Job Card"));
|
}, __("Job Card"), __("Create"));
|
||||||
|
|
||||||
|
var pending_qty = 0;
|
||||||
|
frm.doc.operations.forEach(data => {
|
||||||
|
if(data.completed_qty != frm.doc.qty) {
|
||||||
|
pending_qty = frm.doc.qty - flt(data.completed_qty);
|
||||||
|
|
||||||
|
dialog.fields_dict.operations.df.data.push({
|
||||||
|
'name': data.name,
|
||||||
|
'operation': data.operation,
|
||||||
|
'workstation': data.workstation,
|
||||||
|
'qty': pending_qty,
|
||||||
|
'pending_qty': pending_qty,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
dialog.fields_dict.operations.grid.refresh();
|
||||||
},
|
},
|
||||||
|
|
||||||
make_bom: function(frm) {
|
make_bom: function(frm) {
|
||||||
@@ -277,7 +262,7 @@ frappe.ui.form.on("Work Order", {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
show_progress: function(frm) {
|
show_progress_for_items: function(frm) {
|
||||||
var bars = [];
|
var bars = [];
|
||||||
var message = '';
|
var message = '';
|
||||||
var added_min = false;
|
var added_min = false;
|
||||||
@@ -311,6 +296,44 @@ frappe.ui.form.on("Work Order", {
|
|||||||
frm.dashboard.add_progress(__('Status'), bars, message);
|
frm.dashboard.add_progress(__('Status'), bars, message);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
show_progress_for_operations: function(frm) {
|
||||||
|
if (frm.doc.operations && frm.doc.operations.length) {
|
||||||
|
|
||||||
|
let progress_class = {
|
||||||
|
"Work in Progress": "progress-bar-warning",
|
||||||
|
"Completed": "progress-bar-success"
|
||||||
|
};
|
||||||
|
|
||||||
|
let bars = [];
|
||||||
|
let message = '';
|
||||||
|
let title = '';
|
||||||
|
let status_wise_oprtation_data = {};
|
||||||
|
let total_completed_qty = frm.doc.qty * frm.doc.operations.length;
|
||||||
|
|
||||||
|
frm.doc.operations.forEach(d => {
|
||||||
|
if (!status_wise_oprtation_data[d.status]) {
|
||||||
|
status_wise_oprtation_data[d.status] = [d.completed_qty, d.operation];
|
||||||
|
} else {
|
||||||
|
status_wise_oprtation_data[d.status][0] += d.completed_qty;
|
||||||
|
status_wise_oprtation_data[d.status][1] += ', ' + d.operation;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
for (let key in status_wise_oprtation_data) {
|
||||||
|
title = __("{0} Operations: {1}", [key, status_wise_oprtation_data[key][1].bold()]);
|
||||||
|
bars.push({
|
||||||
|
'title': title,
|
||||||
|
'width': status_wise_oprtation_data[key][0] / total_completed_qty * 100 + '%',
|
||||||
|
'progress_class': progress_class[key]
|
||||||
|
});
|
||||||
|
|
||||||
|
message += title + '. ';
|
||||||
|
}
|
||||||
|
|
||||||
|
frm.dashboard.add_progress(__('Status'), bars, message);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
production_item: function(frm) {
|
production_item: function(frm) {
|
||||||
if (frm.doc.production_item) {
|
if (frm.doc.production_item) {
|
||||||
frappe.call({
|
frappe.call({
|
||||||
@@ -582,6 +605,8 @@ erpnext.work_order = {
|
|||||||
description: __('Max: {0}', [max]),
|
description: __('Max: {0}', [max]),
|
||||||
default: max
|
default: max
|
||||||
}, data => {
|
}, data => {
|
||||||
|
max += (max * (frm.doc.__onload.overproduction_percentage || 0.0)) / 100;
|
||||||
|
|
||||||
if (data.qty > max) {
|
if (data.qty > max) {
|
||||||
frappe.msgprint(__('Quantity must not be more than {0}', [max]));
|
frappe.msgprint(__('Quantity must not be more than {0}', [max]));
|
||||||
reject();
|
reject();
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"actions": [],
|
||||||
"allow_import": 1,
|
"allow_import": 1,
|
||||||
"autoname": "naming_series:",
|
"autoname": "naming_series:",
|
||||||
"creation": "2013-01-10 16:34:16",
|
"creation": "2013-01-10 16:34:16",
|
||||||
@@ -468,7 +469,8 @@
|
|||||||
"idx": 1,
|
"idx": 1,
|
||||||
"image_field": "image",
|
"image_field": "image",
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"modified": "2019-08-28 12:29:35.315239",
|
"links": [],
|
||||||
|
"modified": "2019-12-04 11:20:04.695123",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Manufacturing",
|
"module": "Manufacturing",
|
||||||
"name": "Work Order",
|
"name": "Work Order",
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import frappe
|
|||||||
import json
|
import json
|
||||||
import math
|
import math
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.utils import flt, get_datetime, getdate, date_diff, cint, nowdate
|
from frappe.utils import flt, get_datetime, getdate, date_diff, cint, nowdate, get_link_to_form
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from erpnext.manufacturing.doctype.bom.bom import validate_bom_no, get_bom_items_as_dict
|
from erpnext.manufacturing.doctype.bom.bom import validate_bom_no, get_bom_items_as_dict
|
||||||
from dateutil.relativedelta import relativedelta
|
from dateutil.relativedelta import relativedelta
|
||||||
@@ -38,7 +38,7 @@ class WorkOrder(Document):
|
|||||||
ms = frappe.get_doc("Manufacturing Settings")
|
ms = frappe.get_doc("Manufacturing Settings")
|
||||||
self.set_onload("material_consumption", ms.material_consumption)
|
self.set_onload("material_consumption", ms.material_consumption)
|
||||||
self.set_onload("backflush_raw_materials_based_on", ms.backflush_raw_materials_based_on)
|
self.set_onload("backflush_raw_materials_based_on", ms.backflush_raw_materials_based_on)
|
||||||
|
self.set_onload("overproduction_percentage", ms.overproduction_percentage_for_work_order)
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.validate_production_item()
|
self.validate_production_item()
|
||||||
@@ -657,8 +657,9 @@ def make_work_order(item, qty=0, project=None):
|
|||||||
wo_doc = frappe.new_doc("Work Order")
|
wo_doc = frappe.new_doc("Work Order")
|
||||||
wo_doc.production_item = item
|
wo_doc.production_item = item
|
||||||
wo_doc.update(item_details)
|
wo_doc.update(item_details)
|
||||||
if qty > 0:
|
|
||||||
wo_doc.qty = qty
|
if flt(qty) > 0:
|
||||||
|
wo_doc.qty = flt(qty)
|
||||||
wo_doc.get_items_and_operations_from_bom()
|
wo_doc.get_items_and_operations_from_bom()
|
||||||
|
|
||||||
return wo_doc
|
return wo_doc
|
||||||
@@ -755,21 +756,41 @@ def query_sales_order(production_item):
|
|||||||
return out
|
return out
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def make_job_card(work_order, operation, workstation, qty=0):
|
def make_job_card(work_order, operations):
|
||||||
|
if isinstance(operations, string_types):
|
||||||
|
operations = json.loads(operations)
|
||||||
|
|
||||||
work_order = frappe.get_doc('Work Order', work_order)
|
work_order = frappe.get_doc('Work Order', work_order)
|
||||||
row = get_work_order_operation_data(work_order, operation, workstation)
|
for row in operations:
|
||||||
if row:
|
validate_operation_data(row)
|
||||||
return create_job_card(work_order, row, qty)
|
create_job_card(work_order, row, row.get("qty"), auto_create=True)
|
||||||
|
|
||||||
|
def validate_operation_data(row):
|
||||||
|
if row.get("qty") <= 0:
|
||||||
|
frappe.throw(_("Quantity to Manufacture can not be zero for the operation {0}")
|
||||||
|
.format(
|
||||||
|
frappe.bold(row.get("operation"))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if row.get("qty") > row.get("pending_qty"):
|
||||||
|
frappe.throw(_("For operation {0}: Quantity ({1}) can not be greter than pending quantity({2})")
|
||||||
|
.format(
|
||||||
|
frappe.bold(row.get("operation")),
|
||||||
|
frappe.bold(row.get("qty")),
|
||||||
|
frappe.bold(row.get("pending_qty"))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
def create_job_card(work_order, row, qty=0, enable_capacity_planning=False, auto_create=False):
|
def create_job_card(work_order, row, qty=0, enable_capacity_planning=False, auto_create=False):
|
||||||
doc = frappe.new_doc("Job Card")
|
doc = frappe.new_doc("Job Card")
|
||||||
doc.update({
|
doc.update({
|
||||||
'work_order': work_order.name,
|
'work_order': work_order.name,
|
||||||
'operation': row.operation,
|
'operation': row.get("operation"),
|
||||||
'workstation': row.workstation,
|
'workstation': row.get("workstation"),
|
||||||
'posting_date': nowdate(),
|
'posting_date': nowdate(),
|
||||||
'for_quantity': qty or work_order.get('qty', 0),
|
'for_quantity': qty or work_order.get('qty', 0),
|
||||||
'operation_id': row.name,
|
'operation_id': row.get("name"),
|
||||||
'bom_no': work_order.bom_no,
|
'bom_no': work_order.bom_no,
|
||||||
'project': work_order.project,
|
'project': work_order.project,
|
||||||
'company': work_order.company,
|
'company': work_order.company,
|
||||||
@@ -785,7 +806,7 @@ def create_job_card(work_order, row, qty=0, enable_capacity_planning=False, auto
|
|||||||
doc.schedule_time_logs(row)
|
doc.schedule_time_logs(row)
|
||||||
|
|
||||||
doc.insert()
|
doc.insert()
|
||||||
frappe.msgprint(_("Job card {0} created").format(doc.name))
|
frappe.msgprint(_("Job card {0} created").format(get_link_to_form("Job Card", doc.name)))
|
||||||
|
|
||||||
return doc
|
return doc
|
||||||
|
|
||||||
|
|||||||
@@ -2,11 +2,13 @@
|
|||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.views.calendar["Work Order"] = {
|
frappe.views.calendar["Work Order"] = {
|
||||||
|
fields: ["planned_start_date", "planned_end_date", "status", "produced_qty", "qty", "name", "name"],
|
||||||
field_map: {
|
field_map: {
|
||||||
"start": "planned_start_date",
|
"start": "planned_start_date",
|
||||||
"end": "planned_end_date",
|
"end": "planned_end_date",
|
||||||
"id": "name",
|
"id": "name",
|
||||||
"title": "name",
|
"title": "name",
|
||||||
|
"status": "status",
|
||||||
"allDay": "allDay",
|
"allDay": "allDay",
|
||||||
"progress": function(data) {
|
"progress": function(data) {
|
||||||
return flt(data.produced_qty) / data.qty * 100;
|
return flt(data.produced_qty) / data.qty * 100;
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ def get_data():
|
|||||||
'fieldname': 'work_order',
|
'fieldname': 'work_order',
|
||||||
'transactions': [
|
'transactions': [
|
||||||
{
|
{
|
||||||
'items': ['Pick List', 'Stock Entry', 'Job Card']
|
'label': _('Transactions'),
|
||||||
|
'items': ['Stock Entry', 'Job Card', 'Pick List']
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"actions": [],
|
||||||
"creation": "2014-10-16 14:35:41.950175",
|
"creation": "2014-10-16 14:35:41.950175",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
@@ -68,6 +69,7 @@
|
|||||||
"description": "Operation completed for how many finished goods?",
|
"description": "Operation completed for how many finished goods?",
|
||||||
"fieldname": "completed_qty",
|
"fieldname": "completed_qty",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
|
"in_list_view": 1,
|
||||||
"label": "Completed Qty",
|
"label": "Completed Qty",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
@@ -188,8 +190,9 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"modified": "2019-07-16 23:01:07.720337",
|
"links": [],
|
||||||
"modified_by": "govindsmenokee@gmail.com",
|
"modified": "2019-12-03 19:24:29.594189",
|
||||||
|
"modified_by": "Administrator",
|
||||||
"module": "Manufacturing",
|
"module": "Manufacturing",
|
||||||
"name": "Work Order Operation",
|
"name": "Work Order Operation",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
@@ -197,4 +200,4 @@
|
|||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"track_changes": 1
|
"track_changes": 1
|
||||||
}
|
}
|
||||||
@@ -13,5 +13,7 @@ def get_data():
|
|||||||
'label': _('Transaction'),
|
'label': _('Transaction'),
|
||||||
'items': ['Work Order', 'Job Card', 'Timesheet']
|
'items': ['Work Order', 'Job Card', 'Timesheet']
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
'disable_create_buttons': ['BOM', 'Routing', 'Operation',
|
||||||
|
'Work Order', 'Job Card', 'Timesheet']
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -645,7 +645,10 @@ erpnext.patches.v12_0.replace_accounting_with_accounts_in_home_settings
|
|||||||
erpnext.patches.v12_0.set_payment_entry_status
|
erpnext.patches.v12_0.set_payment_entry_status
|
||||||
erpnext.patches.v12_0.update_owner_fields_in_acc_dimension_custom_fields
|
erpnext.patches.v12_0.update_owner_fields_in_acc_dimension_custom_fields
|
||||||
erpnext.patches.v12_0.set_default_for_add_taxes_from_item_tax_template
|
erpnext.patches.v12_0.set_default_for_add_taxes_from_item_tax_template
|
||||||
|
erpnext.patches.v12_0.add_export_type_field_in_party_master
|
||||||
erpnext.patches.v12_0.remove_denied_leaves_from_leave_ledger
|
erpnext.patches.v12_0.remove_denied_leaves_from_leave_ledger
|
||||||
erpnext.patches.v12_0.update_price_or_product_discount
|
erpnext.patches.v12_0.update_price_or_product_discount
|
||||||
erpnext.patches.v12_0.set_production_capacity_in_workstation
|
erpnext.patches.v12_0.set_production_capacity_in_workstation
|
||||||
|
erpnext.patches.v12_0.set_against_blanket_order_in_sales_and_purchase_order
|
||||||
|
erpnext.patches.v12_0.set_cost_center_in_child_table_of_expense_claim
|
||||||
erpnext.patches.v12_0.set_lead_title_field
|
erpnext.patches.v12_0.set_lead_title_field
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
import frappe
|
||||||
|
from erpnext.regional.india.setup import make_custom_fields
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
|
||||||
|
company = frappe.get_all('Company', filters = {'country': 'India'})
|
||||||
|
if not company:
|
||||||
|
return
|
||||||
|
|
||||||
|
make_custom_fields()
|
||||||
|
|
||||||
|
frappe.reload_doctype('Tax Category')
|
||||||
|
frappe.reload_doctype('Sales Taxes and Charges Template')
|
||||||
|
frappe.reload_doctype('Purchase Taxes and Charges Template')
|
||||||
|
|
||||||
|
# Create tax category with inter state field checked
|
||||||
|
tax_category = frappe.db.get_value('Tax Category', {'name': 'OUT OF STATE'}, 'name')
|
||||||
|
|
||||||
|
if not tax_category:
|
||||||
|
inter_state_category = frappe.get_doc({
|
||||||
|
'doctype': 'Tax Category',
|
||||||
|
'title': 'OUT OF STATE',
|
||||||
|
'is_inter_state': 1
|
||||||
|
}).insert()
|
||||||
|
|
||||||
|
tax_category = inter_state_category.name
|
||||||
|
|
||||||
|
for doctype in ('Sales Taxes and Charges Template', 'Purchase Taxes and Charges Template'):
|
||||||
|
if not frappe.get_meta(doctype).has_field('is_inter_state'): continue
|
||||||
|
|
||||||
|
template = frappe.db.get_value(doctype, {'is_inter_state': 1, 'disabled': 0}, ['name'])
|
||||||
|
if template:
|
||||||
|
frappe.db.set_value(doctype, template, 'tax_category', tax_category)
|
||||||
|
|
||||||
|
frappe.db.sql("""
|
||||||
|
DELETE FROM `tabCustom Field`
|
||||||
|
WHERE fieldname = 'is_inter_state'
|
||||||
|
AND dt IN ('Sales Taxes and Charges Template', 'Purchase Taxes and Charges Template')
|
||||||
|
""")
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import frappe
|
||||||
|
def execute():
|
||||||
|
for doctype in ['Sales Order Item', 'Purchase Order Item']:
|
||||||
|
frappe.reload_doctype(doctype)
|
||||||
|
frappe.db.sql("""
|
||||||
|
UPDATE `tab{0}`
|
||||||
|
SET against_blanket_order = 1
|
||||||
|
WHERE ifnull(blanket_order, '') != ''
|
||||||
|
""".format(doctype))
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import frappe
|
||||||
|
def execute():
|
||||||
|
frappe.reload_doc('hr', 'doctype', 'expense_claim_detail')
|
||||||
|
frappe.db.sql("""
|
||||||
|
UPDATE `tabExpense Claim Detail` child, `tabExpense Claim` par
|
||||||
|
SET child.cost_center = par.cost_center
|
||||||
|
WHERE child.parent = par.name
|
||||||
|
""")
|
||||||
@@ -7,6 +7,8 @@ def execute():
|
|||||||
if not company:
|
if not company:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
frappe.reload_doc('accounts', 'doctype', 'Tax Category')
|
||||||
|
|
||||||
make_custom_fields()
|
make_custom_fields()
|
||||||
|
|
||||||
for doctype in ['Sales Invoice', 'Purchase Invoice']:
|
for doctype in ['Sales Invoice', 'Purchase Invoice']:
|
||||||
|
|||||||
@@ -52,7 +52,6 @@ def get_attribute_filter_data():
|
|||||||
|
|
||||||
|
|
||||||
def get_products_for_website(field_filters=None, attribute_filters=None, search=None):
|
def get_products_for_website(field_filters=None, attribute_filters=None, search=None):
|
||||||
|
|
||||||
if attribute_filters:
|
if attribute_filters:
|
||||||
item_codes = get_item_codes_by_attributes(attribute_filters)
|
item_codes = get_item_codes_by_attributes(attribute_filters)
|
||||||
items_by_attributes = get_items([['name', 'in', item_codes]])
|
items_by_attributes = get_items([['name', 'in', item_codes]])
|
||||||
@@ -302,6 +301,8 @@ def get_items(filters=None, search=None):
|
|||||||
if isinstance(filters, dict):
|
if isinstance(filters, dict):
|
||||||
filters = [['Item', fieldname, '=', value] for fieldname, value in filters.items()]
|
filters = [['Item', fieldname, '=', value] for fieldname, value in filters.items()]
|
||||||
|
|
||||||
|
enabled_items_filter = get_conditions({ 'disabled': 0 }, 'and')
|
||||||
|
|
||||||
show_in_website_condition = ''
|
show_in_website_condition = ''
|
||||||
if products_settings.hide_variants:
|
if products_settings.hide_variants:
|
||||||
show_in_website_condition = get_conditions({'show_in_website': 1 }, 'and')
|
show_in_website_condition = get_conditions({'show_in_website': 1 }, 'and')
|
||||||
@@ -337,7 +338,8 @@ def get_items(filters=None, search=None):
|
|||||||
filter_condition = get_conditions(filters, 'and')
|
filter_condition = get_conditions(filters, 'and')
|
||||||
|
|
||||||
where_conditions = ' and '.join(
|
where_conditions = ' and '.join(
|
||||||
[condition for condition in [show_in_website_condition, search_condition, filter_condition] if condition]
|
[condition for condition in [enabled_items_filter, show_in_website_condition, \
|
||||||
|
search_condition, filter_condition] if condition]
|
||||||
)
|
)
|
||||||
|
|
||||||
left_joins = []
|
left_joins = []
|
||||||
|
|||||||
@@ -4,20 +4,16 @@ frappe.ui.form.on("Project", {
|
|||||||
setup(frm) {
|
setup(frm) {
|
||||||
frm.make_methods = {
|
frm.make_methods = {
|
||||||
'Timesheet': () => {
|
'Timesheet': () => {
|
||||||
let doctype = 'Timesheet';
|
open_form(frm, "Timesheet", "Timesheet Detail", "time_logs");
|
||||||
frappe.model.with_doctype(doctype, () => {
|
},
|
||||||
let new_doc = frappe.model.get_new_doc(doctype);
|
'Purchase Order': () => {
|
||||||
|
open_form(frm, "Purchase Order", "Purchase Order Item", "items");
|
||||||
// add a new row and set the project
|
},
|
||||||
let time_log = frappe.model.get_new_doc('Timesheet Detail');
|
'Purchase Receipt': () => {
|
||||||
time_log.project = frm.doc.name;
|
open_form(frm, "Purchase Receipt", "Purchase Receipt Item", "items");
|
||||||
time_log.parent = new_doc.name;
|
},
|
||||||
time_log.parentfield = 'time_logs';
|
'Purchase Invoice': () => {
|
||||||
time_log.parenttype = 'Timesheet';
|
open_form(frm, "Purchase Invoice", "Purchase Invoice Item", "items");
|
||||||
new_doc.time_logs = [time_log];
|
|
||||||
|
|
||||||
frappe.ui.form.make_quick_entry(doctype, null, null, new_doc);
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@@ -80,7 +76,7 @@ frappe.ui.form.on("Project", {
|
|||||||
frm.events.set_status(frm, 'Cancelled');
|
frm.events.set_status(frm, 'Cancelled');
|
||||||
}, __('Set Status'));
|
}, __('Set Status'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (frappe.model.can_read("Task")) {
|
if (frappe.model.can_read("Task")) {
|
||||||
frm.add_custom_button(__("Gantt Chart"), function () {
|
frm.add_custom_button(__("Gantt Chart"), function () {
|
||||||
frappe.route_options = {
|
frappe.route_options = {
|
||||||
@@ -123,3 +119,20 @@ frappe.ui.form.on("Project", {
|
|||||||
},
|
},
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function open_form(frm, doctype, child_doctype, parentfield) {
|
||||||
|
frappe.model.with_doctype(doctype, () => {
|
||||||
|
let new_doc = frappe.model.get_new_doc(doctype);
|
||||||
|
|
||||||
|
// add a new row and set the project
|
||||||
|
let new_child_doc = frappe.model.get_new_doc(child_doctype);
|
||||||
|
new_child_doc.project = frm.doc.name;
|
||||||
|
new_child_doc.parent = new_doc.name;
|
||||||
|
new_child_doc.parentfield = parentfield;
|
||||||
|
new_child_doc.parenttype = doctype;
|
||||||
|
new_doc[parentfield] = [new_child_doc];
|
||||||
|
|
||||||
|
frappe.ui.form.make_quick_entry(doctype, null, null, new_doc);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
@@ -47,11 +47,11 @@ class Task(NestedSet):
|
|||||||
if not self.project or frappe.flags.in_test:
|
if not self.project or frappe.flags.in_test:
|
||||||
return
|
return
|
||||||
|
|
||||||
expected_end_date = getdate(frappe.db.get_value("Project", self.project, "expected_end_date"))
|
expected_end_date = frappe.db.get_value("Project", self.project, "expected_end_date")
|
||||||
|
|
||||||
if expected_end_date:
|
if expected_end_date:
|
||||||
validate_project_dates(expected_end_date, self, "exp_start_date", "exp_end_date", "Expected")
|
validate_project_dates(getdate(expected_end_date), self, "exp_start_date", "exp_end_date", "Expected")
|
||||||
validate_project_dates(expected_end_date, self, "act_start_date", "act_end_date", "Actual")
|
validate_project_dates(getdate(expected_end_date), self, "act_start_date", "act_end_date", "Actual")
|
||||||
|
|
||||||
def validate_status(self):
|
def validate_status(self):
|
||||||
if self.status!=self.get_db_value("status") and self.status == "Completed":
|
if self.status!=self.get_db_value("status") and self.status == "Completed":
|
||||||
@@ -278,4 +278,4 @@ def validate_project_dates(project_end_date, task, task_start, task_end, actual_
|
|||||||
frappe.throw(_("Task's {0} Start Date cannot be after Project's End Date.").format(actual_or_expected_date))
|
frappe.throw(_("Task's {0} Start Date cannot be after Project's End Date.").format(actual_or_expected_date))
|
||||||
|
|
||||||
if task.get(task_end) and date_diff(project_end_date, getdate(task.get(task_end))) < 0:
|
if task.get(task_end) and date_diff(project_end_date, getdate(task.get(task_end))) < 0:
|
||||||
frappe.throw(_("Task's {0} End Date cannot be after Project's End Date.").format(actual_or_expected_date))
|
frappe.throw(_("Task's {0} End Date cannot be after Project's End Date.").format(actual_or_expected_date))
|
||||||
|
|||||||
@@ -188,7 +188,8 @@ class Timesheet(Document):
|
|||||||
}, as_dict=True)
|
}, as_dict=True)
|
||||||
# check internal overlap
|
# check internal overlap
|
||||||
for time_log in self.time_logs:
|
for time_log in self.time_logs:
|
||||||
if not (time_log.from_time or time_log.to_time): continue
|
if not (time_log.from_time and time_log.to_time
|
||||||
|
and args.from_time and args.to_time): continue
|
||||||
|
|
||||||
if (fieldname != 'workstation' or args.get(fieldname) == time_log.get(fieldname)) and \
|
if (fieldname != 'workstation' or args.get(fieldname) == time_log.get(fieldname)) and \
|
||||||
args.idx != time_log.idx and ((args.from_time > time_log.from_time and args.from_time < time_log.to_time) or
|
args.idx != time_log.idx and ((args.from_time > time_log.from_time and args.from_time < time_log.to_time) or
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 7.7 KiB |
@@ -30,7 +30,7 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
|
|||||||
&& frappe.meta.has_field(this.frm.doc.doctype, "disable_rounded_total")) {
|
&& frappe.meta.has_field(this.frm.doc.doctype, "disable_rounded_total")) {
|
||||||
|
|
||||||
var df = frappe.meta.get_docfield(this.frm.doc.doctype, "disable_rounded_total");
|
var df = frappe.meta.get_docfield(this.frm.doc.doctype, "disable_rounded_total");
|
||||||
var disable = df.default || cint(frappe.sys_defaults.disable_rounded_total);
|
var disable = cint(df.default) || cint(frappe.sys_defaults.disable_rounded_total);
|
||||||
this.frm.set_value("disable_rounded_total", disable);
|
this.frm.set_value("disable_rounded_total", disable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -500,6 +500,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
|||||||
() => {
|
() => {
|
||||||
var d = locals[cdt][cdn];
|
var d = locals[cdt][cdn];
|
||||||
me.add_taxes_from_item_tax_template(d.item_tax_rate);
|
me.add_taxes_from_item_tax_template(d.item_tax_rate);
|
||||||
|
if (d.free_item_data) {
|
||||||
|
me.apply_product_discount(d.free_item_data);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
() => me.frm.script_manager.trigger("price_list_rate", cdt, cdn),
|
() => me.frm.script_manager.trigger("price_list_rate", cdt, cdn),
|
||||||
() => me.toggle_conversion_factor(item),
|
() => me.toggle_conversion_factor(item),
|
||||||
@@ -1305,6 +1308,10 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
|||||||
me.remove_pricing_rule(frappe.get_doc(d.doctype, d.name));
|
me.remove_pricing_rule(frappe.get_doc(d.doctype, d.name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (d.free_item_data) {
|
||||||
|
me.apply_product_discount(d.free_item_data);
|
||||||
|
}
|
||||||
|
|
||||||
if (d.apply_rule_on_other_items) {
|
if (d.apply_rule_on_other_items) {
|
||||||
items_rule_dict[d.name] = d;
|
items_rule_dict[d.name] = d;
|
||||||
}
|
}
|
||||||
@@ -1334,6 +1341,20 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
apply_product_discount: function(free_item_data) {
|
||||||
|
const items = this.frm.doc.items.filter(d => (d.item_code == free_item_data.item_code
|
||||||
|
&& d.is_free_item)) || [];
|
||||||
|
|
||||||
|
if (!items.length) {
|
||||||
|
let row_to_modify = frappe.model.add_child(this.frm.doc,
|
||||||
|
this.frm.doc.doctype + ' Item', 'items');
|
||||||
|
|
||||||
|
for (let key in free_item_data) {
|
||||||
|
row_to_modify[key] = free_item_data[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
apply_price_list: function(item, reset_plc_conversion) {
|
apply_price_list: function(item, reset_plc_conversion) {
|
||||||
// We need to reset plc_conversion_rate sometimes because the call to
|
// We need to reset plc_conversion_rate sometimes because the call to
|
||||||
// `erpnext.stock.get_item_details.apply_price_list` is sensitive to its value
|
// `erpnext.stock.get_item_details.apply_price_list` is sensitive to its value
|
||||||
@@ -1754,14 +1775,28 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
set_reserve_warehouse: function() {
|
||||||
|
this.autofill_warehouse("reserve_warehouse");
|
||||||
|
},
|
||||||
|
|
||||||
set_warehouse: function() {
|
set_warehouse: function() {
|
||||||
|
this.autofill_warehouse("warehouse");
|
||||||
|
},
|
||||||
|
|
||||||
|
autofill_warehouse : function (warehouse_field) {
|
||||||
|
// set warehouse in all child table rows
|
||||||
var me = this;
|
var me = this;
|
||||||
if(this.frm.doc.set_warehouse) {
|
let warehouse = (warehouse_field === "warehouse") ? me.frm.doc.set_warehouse : me.frm.doc.set_reserve_warehouse;
|
||||||
$.each(this.frm.doc.items || [], function(i, item) {
|
let child_table = (warehouse_field === "warehouse") ? me.frm.doc.items : me.frm.doc.supplied_items;
|
||||||
frappe.model.set_value(me.frm.doctype + " Item", item.name, "warehouse", me.frm.doc.set_warehouse);
|
let doctype = (warehouse_field === "warehouse") ? (me.frm.doctype + " Item") : (me.frm.doctype + " Item Supplied");
|
||||||
|
|
||||||
|
if(warehouse) {
|
||||||
|
$.each(child_table || [], function(i, item) {
|
||||||
|
frappe.model.set_value(doctype, item.name, warehouse_field, warehouse);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
coupon_code: function() {
|
coupon_code: function() {
|
||||||
var me = this;
|
var me = this;
|
||||||
frappe.run_serially([
|
frappe.run_serially([
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ erpnext.financial_statements = {
|
|||||||
"filters": get_filters(),
|
"filters": get_filters(),
|
||||||
"formatter": function(value, row, column, data, default_formatter) {
|
"formatter": function(value, row, column, data, default_formatter) {
|
||||||
if (column.fieldname=="account") {
|
if (column.fieldname=="account") {
|
||||||
value = data.account_name;
|
value = data.account_name || value;
|
||||||
|
|
||||||
column.link_onclick =
|
column.link_onclick =
|
||||||
"erpnext.financial_statements.open_general_ledger(" + JSON.stringify(data) + ")";
|
"erpnext.financial_statements.open_general_ledger(" + JSON.stringify(data) + ")";
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import NotFound from './pages/NotFound.vue';
|
|||||||
function get_route_map() {
|
function get_route_map() {
|
||||||
const read_only_routes = {
|
const read_only_routes = {
|
||||||
'marketplace/home': Home,
|
'marketplace/home': Home,
|
||||||
'marketplace/search/:keyword': Search,
|
'marketplace/search/:category/:keyword': Search,
|
||||||
'marketplace/category/:category': Category,
|
'marketplace/category/:category': Category,
|
||||||
'marketplace/item/:item': Item,
|
'marketplace/item/:item': Item,
|
||||||
'marketplace/seller/:seller': Seller,
|
'marketplace/seller/:seller': Seller,
|
||||||
|
|||||||
@@ -3,6 +3,12 @@
|
|||||||
class="marketplace-page"
|
class="marketplace-page"
|
||||||
:data-page-name="page_name"
|
:data-page-name="page_name"
|
||||||
>
|
>
|
||||||
|
<search-input
|
||||||
|
:placeholder="search_placeholder"
|
||||||
|
:on_search="set_search_route"
|
||||||
|
v-model="search_value"
|
||||||
|
/>
|
||||||
|
|
||||||
<h5>{{ page_title }}</h5>
|
<h5>{{ page_title }}</h5>
|
||||||
|
|
||||||
<item-cards-container
|
<item-cards-container
|
||||||
@@ -26,7 +32,13 @@ export default {
|
|||||||
item_id_fieldname: 'name',
|
item_id_fieldname: 'name',
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
empty_state_message: __(`No items in this category yet.`)
|
empty_state_message: __(`No items in this category yet.`),
|
||||||
|
|
||||||
|
search_value: '',
|
||||||
|
|
||||||
|
// Constants
|
||||||
|
search_placeholder: __('Search for anything ...'),
|
||||||
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -35,6 +47,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
|
this.search_value = '';
|
||||||
this.get_items();
|
this.get_items();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@@ -51,7 +64,11 @@ export default {
|
|||||||
|
|
||||||
go_to_item_details_page(hub_item_name) {
|
go_to_item_details_page(hub_item_name) {
|
||||||
frappe.set_route(`marketplace/item/${hub_item_name}`);
|
frappe.set_route(`marketplace/item/${hub_item_name}`);
|
||||||
}
|
},
|
||||||
|
|
||||||
|
set_search_route() {
|
||||||
|
frappe.set_route('marketplace', 'search', this.category, this.search_value);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||