Merge branch 'develop' into publish-item
This commit is contained in:
commit
d19bd8f0dd
21
README.md
21
README.md
@ -13,9 +13,26 @@
|
||||
|
||||
</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)
|
||||
- [Discussion Forum](https://discuss.erpnext.com/)
|
||||
|
@ -109,12 +109,13 @@ class Account(NestedSet):
|
||||
if not descendants: return
|
||||
|
||||
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',
|
||||
{"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):
|
||||
parent_acc_name_map[d["company"]] = d["name"]
|
||||
|
||||
if not parent_acc_name_map: return
|
||||
|
||||
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:
|
||||
fcontent = upfile.read()
|
||||
else:
|
||||
from frappe.utils.file_manager import get_uploaded_content
|
||||
fname, fcontent = get_uploaded_content()
|
||||
fcontent = frappe.local.uploaded_file
|
||||
fname = frappe.local.uploaded_filename
|
||||
|
||||
if frappe.safe_encode(fname).lower().endswith("csv".encode('utf-8')):
|
||||
from frappe.utils.csvutils import read_csv_content
|
||||
|
@ -29,7 +29,6 @@ class GLEntry(Document):
|
||||
self.validate_and_set_fiscal_year()
|
||||
self.pl_must_have_cost_center()
|
||||
self.validate_cost_center()
|
||||
self.validate_dimensions_for_pl_and_bs()
|
||||
|
||||
if not self.flags.from_repost:
|
||||
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):
|
||||
if not from_repost:
|
||||
self.validate_account_details(adv_adj)
|
||||
self.validate_dimensions_for_pl_and_bs()
|
||||
check_freezing_date(self.posting_date, adv_adj)
|
||||
|
||||
validate_frozen_account(self.account, adv_adj)
|
||||
|
@ -190,7 +190,6 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
|
||||
if(jvd.reference_type==="Employee Advance") {
|
||||
return {
|
||||
filters: {
|
||||
'status': ['=', 'Unpaid'],
|
||||
'docstatus': 1
|
||||
}
|
||||
};
|
||||
|
@ -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
|
||||
# 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)
|
||||
else:
|
||||
exchange_rate = 1
|
||||
|
@ -652,14 +652,16 @@ frappe.ui.form.on('Payment Entry', {
|
||||
(frm.doc.payment_type=="Receive" && frm.doc.party_type=="Student")
|
||||
) {
|
||||
if(total_positive_outstanding > total_negative_outstanding)
|
||||
frm.set_value("paid_amount",
|
||||
total_positive_outstanding - total_negative_outstanding);
|
||||
if (!frm.doc.paid_amount)
|
||||
frm.set_value("paid_amount",
|
||||
total_positive_outstanding - total_negative_outstanding);
|
||||
} else if (
|
||||
total_negative_outstanding &&
|
||||
total_positive_outstanding < total_negative_outstanding
|
||||
) {
|
||||
frm.set_value("received_amount",
|
||||
total_negative_outstanding - total_positive_outstanding);
|
||||
if (!frm.doc.received_amount)
|
||||
frm.set_value("received_amount",
|
||||
total_negative_outstanding - total_positive_outstanding);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -332,6 +332,7 @@
|
||||
"label": "Reference"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.docstatus==0",
|
||||
"fieldname": "get_outstanding_invoice",
|
||||
"fieldtype": "Button",
|
||||
"label": "Get Outstanding Invoice"
|
||||
@ -575,7 +576,7 @@
|
||||
}
|
||||
],
|
||||
"is_submittable": 1,
|
||||
"modified": "2019-11-06 12:59:43.151721",
|
||||
"modified": "2019-12-08 13:02:30.016610",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Payment Entry",
|
||||
|
@ -389,8 +389,7 @@
|
||||
"fieldname": "rate_or_discount",
|
||||
"fieldtype": "Select",
|
||||
"label": "Rate or Discount",
|
||||
"options": "\nRate\nDiscount Percentage\nDiscount Amount",
|
||||
"reqd": 1
|
||||
"options": "\nRate\nDiscount Percentage\nDiscount Amount"
|
||||
},
|
||||
{
|
||||
"default": "Grand Total",
|
||||
@ -439,19 +438,20 @@
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"depends_on": "eval:!doc.mixed_conditions",
|
||||
"depends_on": "eval:!doc.mixed_conditions && doc.apply_on != 'Transaction'",
|
||||
"fieldname": "same_item",
|
||||
"fieldtype": "Check",
|
||||
"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",
|
||||
"fieldtype": "Link",
|
||||
"label": "Free Item",
|
||||
"options": "Item"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "free_qty",
|
||||
"fieldtype": "Float",
|
||||
"label": "Qty"
|
||||
@ -554,7 +554,7 @@
|
||||
],
|
||||
"icon": "fa fa-gift",
|
||||
"idx": 1,
|
||||
"modified": "2019-10-15 12:39:40.399792",
|
||||
"modified": "2019-12-18 17:29:22.957077",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Pricing Rule",
|
||||
|
@ -47,6 +47,9 @@ class PricingRule(Document):
|
||||
if tocheck and not self.get(tocheck):
|
||||
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):
|
||||
if not self.selling and not self.buying:
|
||||
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):
|
||||
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):
|
||||
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:
|
||||
return item_details
|
||||
|
||||
if (not pricing_rule.validate_applied_rule and
|
||||
pricing_rule.price_or_product_discount == "Price"):
|
||||
apply_price_discount_pricing_rule(pricing_rule, item_details, args)
|
||||
if not pricing_rule.validate_applied_rule:
|
||||
if pricing_rule.price_or_product_discount == "Price":
|
||||
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
|
||||
|
||||
@ -293,7 +298,7 @@ def get_pricing_rule_details(args, pricing_rule):
|
||||
'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
|
||||
|
||||
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
|
||||
from frappe import throw, _
|
||||
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.stock.doctype.warehouse.warehouse import get_child_warehouses
|
||||
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
|
||||
|
||||
# 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
|
||||
|
||||
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)
|
||||
|
||||
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:
|
||||
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)
|
||||
|
||||
for field in ['additional_discount_percentage', 'discount_amount']:
|
||||
if not d.get(field): continue
|
||||
|
||||
pr_field = ('discount_percentage'
|
||||
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):
|
||||
frappe.msgprint(_("User has not applied rule on the invoice {0}")
|
||||
.format(doc.name))
|
||||
else:
|
||||
doc.set(field, d.get(pr_field))
|
||||
|
||||
doc.calculate_taxes_and_totals()
|
||||
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):
|
||||
return (item_row.get("pricing_rules").split(',')
|
||||
if item_row.get("pricing_rules") else [])
|
||||
|
||||
def apply_pricing_rule_for_free_items(doc, pricing_rule):
|
||||
if pricing_rule.get('free_item'):
|
||||
def get_product_discount_rule(pricing_rule, item_details, doc=None):
|
||||
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
|
||||
if d.item_code == (d.item_code
|
||||
if pricing_rule.get('same_item') else pricing_rule.get('free_item')) and d.is_free_item]
|
||||
if d.item_code == (pricing_rule_args.get("item_code")) and d.is_free_item]
|
||||
|
||||
if not items:
|
||||
doc.append('items', {
|
||||
'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()
|
||||
doc.append('items', pricing_rule_args)
|
||||
|
||||
def get_pricing_rule_items(pr_doc):
|
||||
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) {
|
||||
// filter on Account
|
||||
if (doc.supplier) {
|
||||
return {
|
||||
filters: {
|
||||
'account_type': 'Payable',
|
||||
'is_group': 0,
|
||||
'company': doc.company
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
filters: {
|
||||
'report_type': 'Balance Sheet',
|
||||
'is_group': 0,
|
||||
'company': doc.company
|
||||
}
|
||||
return {
|
||||
filters: {
|
||||
'account_type': 'Payable',
|
||||
'is_group': 0,
|
||||
'company': doc.company
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"actions": [],
|
||||
"allow_import": 1,
|
||||
"autoname": "naming_series:",
|
||||
"creation": "2013-05-21 16:16:39",
|
||||
@ -417,6 +418,7 @@
|
||||
"fieldname": "contact_email",
|
||||
"fieldtype": "Small Text",
|
||||
"label": "Contact Email",
|
||||
"options": "Email",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
@ -1287,7 +1289,8 @@
|
||||
"icon": "fa fa-file-text",
|
||||
"idx": 204,
|
||||
"is_submittable": 1,
|
||||
"modified": "2019-09-17 22:31:42.666601",
|
||||
"links": [],
|
||||
"modified": "2019-12-24 12:51:58.613538",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Purchase Invoice",
|
||||
|
@ -248,7 +248,7 @@ class PurchaseInvoice(BuyingController):
|
||||
def set_against_expense_account(self):
|
||||
against_accounts = []
|
||||
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)
|
||||
|
||||
self.against_expense_account = ",".join(against_accounts)
|
||||
@ -830,7 +830,11 @@ class PurchaseInvoice(BuyingController):
|
||||
)
|
||||
|
||||
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 = \
|
||||
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_rename": 1,
|
||||
"autoname": "field:title",
|
||||
"beta": 0,
|
||||
"creation": "2013-01-10 16:34:08",
|
||||
"custom": 0,
|
||||
"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.",
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "Setup",
|
||||
"editable_grid": 0,
|
||||
"field_order": [
|
||||
"title",
|
||||
"is_default",
|
||||
"disabled",
|
||||
"column_break4",
|
||||
"company",
|
||||
"tax_category",
|
||||
"section_break6",
|
||||
"taxes"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "title",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"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
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "0",
|
||||
"fieldname": "is_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
|
||||
"label": "Default"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "0",
|
||||
"fieldname": "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
|
||||
"label": "Disabled"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"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
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"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
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"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
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "taxes",
|
||||
"fieldtype": "Table",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"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
|
||||
"options": "Purchase Taxes and Charges"
|
||||
},
|
||||
{
|
||||
"fieldname": "tax_category",
|
||||
"fieldtype": "Link",
|
||||
"label": "Tax Category",
|
||||
"options": "Tax Category"
|
||||
}
|
||||
],
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"icon": "fa fa-money",
|
||||
"idx": 1,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2016-11-07 05:18:44.095798",
|
||||
"modified": "2019-11-25 13:05:26.220275",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Purchase Taxes and Charges Template",
|
||||
"owner": "wasim@webnotestech.com",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 0,
|
||||
"delete": 0,
|
||||
"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
|
||||
"role": "Purchase Manager"
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"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
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"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
|
||||
"role": "Purchase User"
|
||||
}
|
||||
],
|
||||
"quick_entry": 0,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"sort_order": "DESC",
|
||||
"track_seen": 0
|
||||
"track_changes": 1
|
||||
}
|
@ -1,3 +1,7 @@
|
||||
{% include "erpnext/regional/india/taxes.js" %}
|
||||
|
||||
erpnext.setup_auto_gst_taxation('Sales Invoice');
|
||||
|
||||
frappe.ui.form.on("Sales Invoice", {
|
||||
setup: function(frm) {
|
||||
frm.set_query('transporter', function() {
|
||||
@ -35,4 +39,5 @@ frappe.ui.form.on("Sales Invoice", {
|
||||
}, __("Make"));
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
@ -556,22 +556,11 @@ cur_frm.cscript.cost_center = function(doc, cdt, cdn) {
|
||||
}
|
||||
|
||||
cur_frm.set_query("debit_to", function(doc) {
|
||||
// filter on Account
|
||||
if (doc.customer) {
|
||||
return {
|
||||
filters: {
|
||||
'account_type': 'Receivable',
|
||||
'is_group': 0,
|
||||
'company': doc.company
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
filters: {
|
||||
'report_type': 'Balance Sheet',
|
||||
'is_group': 0,
|
||||
'company': doc.company
|
||||
}
|
||||
return {
|
||||
filters: {
|
||||
'account_type': 'Receivable',
|
||||
'is_group': 0,
|
||||
'company': doc.company
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -697,8 +686,8 @@ frappe.ui.form.on('Sales Invoice', {
|
||||
if (frm.doc.company)
|
||||
{
|
||||
frappe.call({
|
||||
method:"frappe.contacts.doctype.address.address.get_default_address",
|
||||
args:{ doctype:'Company',name:frm.doc.company},
|
||||
method:"erpnext.setup.doctype.company.company.get_default_company_address",
|
||||
args:{name:frm.doc.company, existing_address: frm.doc.company_address},
|
||||
callback: function(r){
|
||||
if (r.message){
|
||||
frm.set_value("company_address",r.message)
|
||||
|
@ -90,6 +90,7 @@ class SalesInvoice(SellingController):
|
||||
self.validate_account_for_change_amount()
|
||||
self.validate_fixed_asset()
|
||||
self.set_income_account_for_fixed_assets()
|
||||
self.validate_item_cost_centers()
|
||||
validate_inter_company_party(self.doctype, self.customer, self.company, self.inter_company_invoice_reference)
|
||||
|
||||
if cint(self.is_pos):
|
||||
@ -147,6 +148,12 @@ class SalesInvoice(SellingController):
|
||||
elif asset.status in ("Scrapped", "Cancelled", "Sold"):
|
||||
frappe.throw(_("Row #{0}: Asset {1} cannot be submitted, it is already {2}").format(d.idx, d.asset, asset.status))
|
||||
|
||||
def validate_item_cost_centers(self):
|
||||
for item in self.items:
|
||||
cost_center_company = frappe.get_cached_value("Cost Center", item.cost_center, "company")
|
||||
if cost_center_company != self.company:
|
||||
frappe.throw(_("Row #{0}: Cost Center {1} does not belong to company {2}").format(frappe.bold(item.idx), frappe.bold(item.cost_center), frappe.bold(self.company)))
|
||||
|
||||
def before_save(self):
|
||||
set_account_for_mode_of_payment(self)
|
||||
|
||||
@ -535,10 +542,8 @@ class SalesInvoice(SellingController):
|
||||
for i in dic:
|
||||
if frappe.db.get_single_value('Selling Settings', dic[i][0]) == 'Yes':
|
||||
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 is_stock_item == 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)
|
||||
if (d.item_code and not d.get(i.lower().replace(' ','_')) and not self.get(dic[i][1])):
|
||||
msgprint(_("{0} is mandatory for Item {1}").format(i,d.item_code), raise_exception=1)
|
||||
|
||||
|
||||
def validate_proj_cust(self):
|
||||
@ -953,7 +958,7 @@ class SalesInvoice(SellingController):
|
||||
)
|
||||
|
||||
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 = \
|
||||
get_round_off_account_and_cost_center(self.company)
|
||||
|
||||
|
@ -1,299 +1,119 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_import": 1,
|
||||
"allow_rename": 1,
|
||||
"autoname": "field:title",
|
||||
"beta": 0,
|
||||
"creation": "2013-01-10 16:34:09",
|
||||
"custom": 0,
|
||||
"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.",
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "Setup",
|
||||
"editable_grid": 0,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"title",
|
||||
"is_default",
|
||||
"disabled",
|
||||
"column_break_3",
|
||||
"company",
|
||||
"tax_category",
|
||||
"section_break_5",
|
||||
"taxes"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "title",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"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
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "0",
|
||||
"fieldname": "is_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
|
||||
"label": "Default"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "0",
|
||||
"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
|
||||
"label": "Disabled"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"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
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 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
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"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
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"description": "* Will be calculated in the transaction.",
|
||||
"fieldname": "taxes",
|
||||
"fieldtype": "Table",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"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
|
||||
"options": "Sales Taxes and Charges"
|
||||
},
|
||||
{
|
||||
"fieldname": "tax_category",
|
||||
"fieldtype": "Link",
|
||||
"label": "Tax Category",
|
||||
"options": "Tax Category"
|
||||
}
|
||||
],
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"icon": "fa fa-money",
|
||||
"idx": 1,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2016-11-07 05:18:41.743257",
|
||||
"modified": "2019-11-25 13:06:03.279099",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Taxes and Charges Template",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 1,
|
||||
"cancel": 0,
|
||||
"create": 0,
|
||||
"delete": 0,
|
||||
"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
|
||||
"role": "Sales User"
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"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
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"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
|
||||
}
|
||||
],
|
||||
"quick_entry": 0,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "ASC",
|
||||
"track_seen": 0
|
||||
"track_changes": 1
|
||||
}
|
@ -188,7 +188,7 @@
|
||||
}
|
||||
],
|
||||
"is_submittable": 1,
|
||||
"modified": "2019-11-07 13:31:17.999744",
|
||||
"modified": "2019-12-20 14:48:01.990600",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Share Transfer",
|
||||
@ -196,6 +196,7 @@
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 1,
|
||||
"cancel": 1,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
@ -221,6 +222,7 @@
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"cancel": 1,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
@ -230,6 +232,7 @@
|
||||
"report": 1,
|
||||
"role": "Accounts Manager",
|
||||
"share": 1,
|
||||
"submit": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
|
@ -90,8 +90,12 @@ def merge_similar_entries(gl_map):
|
||||
else:
|
||||
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
|
||||
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)
|
||||
|
||||
return merged_gl_map
|
||||
|
@ -18,6 +18,10 @@ def reconcile(bank_transaction, payment_doctype, payment_name):
|
||||
account = frappe.db.get_value("Bank Account", transaction.bank_account, "account")
|
||||
gl_entry = frappe.get_doc("GL Entry", dict(account=account, voucher_type=payment_doctype, voucher_no=payment_name))
|
||||
|
||||
if payment_doctype == "Payment Entry" and payment_entry.unallocated_amount > transaction.unallocated_amount:
|
||||
frappe.throw(_("The unallocated amount of Payment Entry {0} \
|
||||
is greater than the Bank Transaction's unallocated amount").format(payment_name))
|
||||
|
||||
if transaction.unallocated_amount == 0:
|
||||
frappe.throw(_("This bank transaction is already fully reconciled"))
|
||||
|
||||
|
@ -23,7 +23,7 @@ class DuplicatePartyAccountError(frappe.ValidationError): pass
|
||||
@frappe.whitelist()
|
||||
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,
|
||||
party_address=None, shipping_address=None, pos_profile=None):
|
||||
party_address=None, company_address=None, shipping_address=None, pos_profile=None):
|
||||
|
||||
if not party:
|
||||
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))
|
||||
return _get_party_details(party, account, party_type,
|
||||
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,
|
||||
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 = out[party_type.lower()]
|
||||
party_details = frappe._dict(set_account_and_due_date(party, account, party_type, company, posting_date, bill_date, doctype))
|
||||
party = party_details[party_type.lower()]
|
||||
|
||||
if not ignore_permissions and not frappe.has_permission(party_type, "read", party):
|
||||
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)
|
||||
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)
|
||||
set_contact_details(out, party, party_type)
|
||||
set_other_values(out, party, party_type)
|
||||
set_price_list(out, party, party_type, price_list, pos_profile)
|
||||
party_address, shipping_address = set_address_details(party_details, party, party_type, doctype, company, party_address, company_address, shipping_address)
|
||||
set_contact_details(party_details, party, party_type)
|
||||
set_other_values(party_details, party, party_type)
|
||||
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)
|
||||
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,
|
||||
billing_address=party_address, shipping_address=shipping_address)
|
||||
|
||||
if not party_details.get("taxes_and_charges"):
|
||||
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:
|
||||
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"):
|
||||
out["currency"] = currency
|
||||
if not party_details.get("currency"):
|
||||
party_details["currency"] = currency
|
||||
|
||||
# sales team
|
||||
if party_type=="Customer":
|
||||
out["sales_team"] = [{
|
||||
party_details["sales_team"] = [{
|
||||
"sales_person": d.sales_person,
|
||||
"allocated_percentage": d.allocated_percentage or None
|
||||
} for d in party.get("sales_team")]
|
||||
|
||||
# supplier tax withholding category
|
||||
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" \
|
||||
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:
|
||||
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
|
||||
out.address_display = get_address_display(out[billing_address_field])
|
||||
party_details.address_display = get_address_display(party_details[billing_address_field])
|
||||
# shipping address
|
||||
if party_type in ["Customer", "Lead"]:
|
||||
out.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_name = shipping_address or get_party_shipping_address(party_type, party.name)
|
||||
party_details.shipping_address = get_address_display(party_details["shipping_address_name"])
|
||||
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']:
|
||||
out.update(get_company_address(company))
|
||||
if out.company_address:
|
||||
out.update(get_fetch_values(doctype, 'company_address', out.company_address))
|
||||
get_regional_address_details(out, doctype, company)
|
||||
if company_address:
|
||||
party_details.update({'company_address': company_address})
|
||||
else:
|
||||
party_details.update(get_company_address(company))
|
||||
|
||||
elif doctype and doctype == "Purchase Invoice":
|
||||
out.update(get_company_address(company))
|
||||
if out.company_address:
|
||||
out["shipping_address"] = shipping_address or out["company_address"]
|
||||
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)
|
||||
if doctype and doctype in ['Delivery Note', 'Sales Invoice', 'Sales Order']:
|
||||
if party_details.company_address:
|
||||
party_details.update(get_fetch_values(doctype, 'company_address', party_details.company_address))
|
||||
get_regional_address_details(party_details, 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
|
||||
def get_regional_address_details(out, doctype, company):
|
||||
def get_regional_address_details(party_details, doctype, company):
|
||||
pass
|
||||
|
||||
def set_contact_details(out, party, party_type):
|
||||
out.contact_person = get_default_contact(party_type, party.name)
|
||||
def set_contact_details(party_details, party, party_type):
|
||||
party_details.contact_person = get_default_contact(party_type, party.name)
|
||||
|
||||
if not out.contact_person:
|
||||
out.update({
|
||||
if not party_details.contact_person:
|
||||
party_details.update({
|
||||
"contact_person": None,
|
||||
"contact_display": None,
|
||||
"contact_email": None,
|
||||
@ -125,22 +130,22 @@ def set_contact_details(out, party, party_type):
|
||||
"contact_department": None
|
||||
})
|
||||
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
|
||||
if party_type=="Customer":
|
||||
to_copy = ["customer_name", "customer_group", "territory", "language"]
|
||||
else:
|
||||
to_copy = ["supplier_name", "supplier_group", "language"]
|
||||
for f in to_copy:
|
||||
out[f] = party.get(f)
|
||||
party_details[f] = party.get(f)
|
||||
|
||||
# fields prepended with default in Customer doctype
|
||||
for f in ['currency'] \
|
||||
+ (['sales_partner', 'commission_rate'] if party_type=="Customer" else []):
|
||||
if party.get("default_" + f):
|
||||
out[f] = party.get("default_" + f)
|
||||
party_details[f] = party.get("default_" + f)
|
||||
|
||||
def get_default_price_list(party):
|
||||
"""Return default price list for party (Document object)"""
|
||||
@ -155,7 +160,7 @@ def get_default_price_list(party):
|
||||
|
||||
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 = 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
|
||||
|
||||
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):
|
||||
|
@ -171,7 +171,7 @@ class ReceivablePayableReport(object):
|
||||
row.outstanding = flt(row.invoiced - row.paid - row.credit_note, self.currency_precision)
|
||||
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
|
||||
|
||||
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):
|
||||
# customer / supplier name
|
||||
party_details = self.get_party_details(row.party)
|
||||
party_details = self.get_party_details(row.party) or {}
|
||||
row.update(party_details)
|
||||
if self.filters.get(scrub(self.filters.party_type)):
|
||||
row.currency = row.account_currency
|
||||
|
@ -4,125 +4,140 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import formatdate, getdate, flt, add_days
|
||||
from frappe.utils import formatdate, flt, add_days
|
||||
|
||||
|
||||
def execute(filters=None):
|
||||
filters.day_before_from_date = add_days(filters.from_date, -1)
|
||||
columns, data = get_columns(filters), get_data(filters)
|
||||
return columns, data
|
||||
|
||||
|
||||
def get_data(filters):
|
||||
data = []
|
||||
|
||||
asset_categories = get_asset_categories(filters)
|
||||
assets = get_assets(filters)
|
||||
asset_costs = get_asset_costs(assets, filters)
|
||||
asset_depreciations = get_accumulated_depreciations(assets, filters)
|
||||
|
||||
for asset_category in asset_categories:
|
||||
row = frappe._dict()
|
||||
row.asset_category = asset_category
|
||||
row.update(asset_costs.get(asset_category))
|
||||
# row.asset_category = asset_category
|
||||
row.update(asset_category)
|
||||
|
||||
row.cost_as_on_to_date = (flt(row.cost_as_on_from_date) + flt(row.cost_of_new_purchase)
|
||||
- flt(row.cost_of_sold_asset) - flt(row.cost_of_scrapped_asset))
|
||||
row.cost_as_on_to_date = (flt(row.cost_as_on_from_date) + flt(row.cost_of_new_purchase) -
|
||||
flt(row.cost_of_sold_asset) - flt(row.cost_of_scrapped_asset))
|
||||
|
||||
row.update(asset_depreciations.get(asset_category))
|
||||
row.update(next(asset for asset in assets if asset["asset_category"] == asset_category.get("asset_category", "")))
|
||||
row.accumulated_depreciation_as_on_to_date = (flt(row.accumulated_depreciation_as_on_from_date) +
|
||||
flt(row.depreciation_amount_during_the_period) - flt(row.depreciation_eliminated))
|
||||
flt(row.depreciation_amount_during_the_period) - flt(row.depreciation_eliminated))
|
||||
|
||||
row.net_asset_value_as_on_from_date = (flt(row.cost_as_on_from_date) -
|
||||
flt(row.accumulated_depreciation_as_on_from_date))
|
||||
flt(row.accumulated_depreciation_as_on_from_date))
|
||||
|
||||
row.net_asset_value_as_on_to_date = (flt(row.cost_as_on_to_date) -
|
||||
flt(row.accumulated_depreciation_as_on_to_date))
|
||||
flt(row.accumulated_depreciation_as_on_to_date))
|
||||
|
||||
data.append(row)
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def get_asset_categories(filters):
|
||||
return frappe.db.sql_list("""
|
||||
select distinct asset_category from `tabAsset`
|
||||
where docstatus=1 and company=%s and purchase_date <= %s
|
||||
""", (filters.company, filters.to_date))
|
||||
return frappe.db.sql("""
|
||||
SELECT asset_category,
|
||||
ifnull(sum(case when purchase_date < %(from_date)s then
|
||||
case when ifnull(disposal_date, 0) = 0 or disposal_date >= %(from_date)s then
|
||||
gross_purchase_amount
|
||||
else
|
||||
0
|
||||
end
|
||||
else
|
||||
0
|
||||
end), 0) as cost_as_on_from_date,
|
||||
ifnull(sum(case when purchase_date >= %(from_date)s then
|
||||
gross_purchase_amount
|
||||
else
|
||||
0
|
||||
end), 0) as cost_of_new_purchase,
|
||||
ifnull(sum(case when ifnull(disposal_date, 0) != 0
|
||||
and disposal_date >= %(from_date)s
|
||||
and disposal_date <= %(to_date)s then
|
||||
case when status = "Sold" then
|
||||
gross_purchase_amount
|
||||
else
|
||||
0
|
||||
end
|
||||
else
|
||||
0
|
||||
end), 0) as cost_of_sold_asset,
|
||||
ifnull(sum(case when ifnull(disposal_date, 0) != 0
|
||||
and disposal_date >= %(from_date)s
|
||||
and disposal_date <= %(to_date)s then
|
||||
case when status = "Scrapped" then
|
||||
gross_purchase_amount
|
||||
else
|
||||
0
|
||||
end
|
||||
else
|
||||
0
|
||||
end), 0) as cost_of_scrapped_asset
|
||||
from `tabAsset`
|
||||
where docstatus=1 and company=%(company)s and purchase_date <= %(to_date)s
|
||||
group by asset_category
|
||||
""", {"to_date": filters.to_date, "from_date": filters.from_date, "company": filters.company}, as_dict=1)
|
||||
|
||||
|
||||
def get_assets(filters):
|
||||
return frappe.db.sql("""
|
||||
select name, asset_category, purchase_date, gross_purchase_amount, disposal_date, status
|
||||
from `tabAsset`
|
||||
where docstatus=1 and company=%s and purchase_date <= %s""",
|
||||
(filters.company, filters.to_date), as_dict=1)
|
||||
SELECT results.asset_category,
|
||||
sum(results.accumulated_depreciation_as_on_from_date) as accumulated_depreciation_as_on_from_date,
|
||||
sum(results.depreciation_eliminated_during_the_period) as depreciation_eliminated_during_the_period,
|
||||
sum(results.depreciation_amount_during_the_period) as depreciation_amount_during_the_period
|
||||
from (SELECT a.asset_category,
|
||||
ifnull(sum(a.opening_accumulated_depreciation +
|
||||
case when ds.schedule_date < %(from_date)s and
|
||||
(ifnull(a.disposal_date, 0) = 0 or a.disposal_date >= %(from_date)s) then
|
||||
ds.depreciation_amount
|
||||
else
|
||||
0
|
||||
end), 0) as accumulated_depreciation_as_on_from_date,
|
||||
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 and a.disposal_date >= %(from_date)s
|
||||
and a.disposal_date <= %(to_date)s and ds.schedule_date <= a.disposal_date then
|
||||
ds.depreciation_amount
|
||||
else
|
||||
0
|
||||
end), 0) as depreciation_eliminated_during_the_period,
|
||||
|
||||
def get_asset_costs(assets, filters):
|
||||
asset_costs = frappe._dict()
|
||||
for d in assets:
|
||||
asset_costs.setdefault(d.asset_category, frappe._dict({
|
||||
"cost_as_on_from_date": 0,
|
||||
"cost_of_new_purchase": 0,
|
||||
"cost_of_sold_asset": 0,
|
||||
"cost_of_scrapped_asset": 0
|
||||
}))
|
||||
ifnull(sum(case when ds.schedule_date >= %(from_date)s and ds.schedule_date <= %(to_date)s
|
||||
and (ifnull(a.disposal_date, 0) = 0 or ds.schedule_date <= a.disposal_date) then
|
||||
ds.depreciation_amount
|
||||
else
|
||||
0
|
||||
end), 0) as depreciation_amount_during_the_period
|
||||
from `tabAsset` a, `tabDepreciation Schedule` ds
|
||||
where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s and a.name = ds.parent
|
||||
group by a.asset_category
|
||||
union
|
||||
SELECT a.asset_category,
|
||||
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0
|
||||
and (a.disposal_date < %(from_date)s or a.disposal_date > %(to_date)s) then
|
||||
0
|
||||
else
|
||||
a.opening_accumulated_depreciation
|
||||
end), 0) as accumulated_depreciation_as_on_from_date,
|
||||
ifnull(sum(case when a.disposal_date >= %(from_date)s and a.disposal_date <= %(to_date)s then
|
||||
a.opening_accumulated_depreciation
|
||||
else
|
||||
0
|
||||
end), 0) as depreciation_eliminated_during_the_period,
|
||||
0 as depreciation_amount_during_the_period
|
||||
from `tabAsset` a
|
||||
where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s
|
||||
and not exists(select * from `tabDepreciation Schedule` ds where a.name = ds.parent)
|
||||
group by a.asset_category) as results
|
||||
group by results.asset_category
|
||||
""", {"to_date": filters.to_date, "from_date": filters.from_date, "company": filters.company}, as_dict=1)
|
||||
|
||||
costs = asset_costs[d.asset_category]
|
||||
|
||||
if getdate(d.purchase_date) < getdate(filters.from_date):
|
||||
if not d.disposal_date or getdate(d.disposal_date) >= getdate(filters.from_date):
|
||||
costs.cost_as_on_from_date += flt(d.gross_purchase_amount)
|
||||
else:
|
||||
costs.cost_of_new_purchase += flt(d.gross_purchase_amount)
|
||||
|
||||
if d.disposal_date and getdate(d.disposal_date) >= getdate(filters.from_date) \
|
||||
and getdate(d.disposal_date) <= getdate(filters.to_date):
|
||||
if d.status == "Sold":
|
||||
costs.cost_of_sold_asset += flt(d.gross_purchase_amount)
|
||||
elif d.status == "Scrapped":
|
||||
costs.cost_of_scrapped_asset += flt(d.gross_purchase_amount)
|
||||
|
||||
return asset_costs
|
||||
|
||||
def get_accumulated_depreciations(assets, filters):
|
||||
asset_depreciations = frappe._dict()
|
||||
for d in assets:
|
||||
asset = frappe.get_doc("Asset", d.name)
|
||||
|
||||
if d.asset_category in asset_depreciations:
|
||||
asset_depreciations[d.asset_category]['accumulated_depreciation_as_on_from_date'] += asset.opening_accumulated_depreciation
|
||||
else:
|
||||
asset_depreciations.setdefault(d.asset_category, frappe._dict({
|
||||
"accumulated_depreciation_as_on_from_date": asset.opening_accumulated_depreciation,
|
||||
"depreciation_amount_during_the_period": 0,
|
||||
"depreciation_eliminated_during_the_period": 0
|
||||
}))
|
||||
|
||||
depr = asset_depreciations[d.asset_category]
|
||||
|
||||
if not asset.schedules: # if no schedule,
|
||||
if asset.disposal_date:
|
||||
# and disposal is NOT within the period, then opening accumulated depreciation not included
|
||||
if getdate(asset.disposal_date) < getdate(filters.from_date) or getdate(asset.disposal_date) > getdate(filters.to_date):
|
||||
asset_depreciations[d.asset_category]['accumulated_depreciation_as_on_from_date'] = 0
|
||||
|
||||
# if no schedule, and disposal is within period, accumulated dep is the amount eliminated
|
||||
if getdate(asset.disposal_date) >= getdate(filters.from_date) and getdate(asset.disposal_date) <= getdate(filters.to_date):
|
||||
depr.depreciation_eliminated_during_the_period += asset.opening_accumulated_depreciation
|
||||
|
||||
for schedule in asset.get("schedules"):
|
||||
if getdate(schedule.schedule_date) < getdate(filters.from_date):
|
||||
if not asset.disposal_date or getdate(asset.disposal_date) >= getdate(filters.from_date):
|
||||
depr.accumulated_depreciation_as_on_from_date += flt(schedule.depreciation_amount)
|
||||
elif getdate(schedule.schedule_date) <= getdate(filters.to_date):
|
||||
if not asset.disposal_date:
|
||||
depr.depreciation_amount_during_the_period += flt(schedule.depreciation_amount)
|
||||
else:
|
||||
if getdate(schedule.schedule_date) <= getdate(asset.disposal_date):
|
||||
depr.depreciation_amount_during_the_period += flt(schedule.depreciation_amount)
|
||||
|
||||
if asset.disposal_date and getdate(asset.disposal_date) >= getdate(filters.from_date) and getdate(asset.disposal_date) <= getdate(filters.to_date):
|
||||
if getdate(schedule.schedule_date) <= getdate(asset.disposal_date):
|
||||
depr.depreciation_eliminated_during_the_period += flt(schedule.depreciation_amount)
|
||||
|
||||
return asset_depreciations
|
||||
|
||||
def get_columns(filters):
|
||||
return [
|
||||
|
@ -46,13 +46,24 @@ frappe.query_reports["Budget Variance Report"] = {
|
||||
fieldtype: "Select",
|
||||
options: ["Cost Center", "Project"],
|
||||
default: "Cost Center",
|
||||
reqd: 1
|
||||
reqd: 1,
|
||||
on_change: function() {
|
||||
frappe.query_report.set_filter_value("budget_against_filter", []);
|
||||
frappe.query_report.refresh();
|
||||
}
|
||||
},
|
||||
{
|
||||
fieldname: "cost_center",
|
||||
label: __("Cost Center"),
|
||||
fieldtype: "Link",
|
||||
options: "Cost Center"
|
||||
fieldname:"budget_against_filter",
|
||||
label: __('Dimension Filter'),
|
||||
fieldtype: "MultiSelectList",
|
||||
get_data: function(txt) {
|
||||
if (!frappe.query_report.filters) return;
|
||||
|
||||
let budget_against = frappe.query_report.get_filter_value('budget_against');
|
||||
if (!budget_against) return;
|
||||
|
||||
return frappe.db.get_link_options(budget_against, txt);
|
||||
}
|
||||
},
|
||||
{
|
||||
fieldname:"show_cumulative",
|
||||
|
@ -12,22 +12,22 @@ from six import iteritems
|
||||
from pprint import pprint
|
||||
def execute(filters=None):
|
||||
if not filters: filters = {}
|
||||
validate_filters(filters)
|
||||
|
||||
columns = get_columns(filters)
|
||||
if filters.get("cost_center"):
|
||||
cost_centers = [filters.get("cost_center")]
|
||||
if filters.get("budget_against_filter"):
|
||||
dimensions = filters.get("budget_against_filter")
|
||||
else:
|
||||
cost_centers = get_cost_centers(filters)
|
||||
dimensions = get_cost_centers(filters)
|
||||
|
||||
period_month_ranges = get_period_month_ranges(filters["period"], filters["from_fiscal_year"])
|
||||
cam_map = get_cost_center_account_month_map(filters)
|
||||
cam_map = get_dimension_account_month_map(filters)
|
||||
|
||||
data = []
|
||||
for cost_center in cost_centers:
|
||||
cost_center_items = cam_map.get(cost_center)
|
||||
if cost_center_items:
|
||||
for account, monthwise_data in iteritems(cost_center_items):
|
||||
row = [cost_center, account]
|
||||
for dimension in dimensions:
|
||||
dimension_items = cam_map.get(dimension)
|
||||
if dimension_items:
|
||||
for account, monthwise_data in iteritems(dimension_items):
|
||||
row = [dimension, account]
|
||||
totals = [0, 0, 0]
|
||||
for year in get_fiscal_years(filters):
|
||||
last_total = 0
|
||||
@ -55,10 +55,6 @@ def execute(filters=None):
|
||||
|
||||
return columns, data
|
||||
|
||||
def validate_filters(filters):
|
||||
if filters.get("budget_against") != "Cost Center" and filters.get("cost_center"):
|
||||
frappe.throw(_("Filter based on Cost Center is only applicable if Budget Against is selected as Cost Center"))
|
||||
|
||||
def get_columns(filters):
|
||||
columns = [_(filters.get("budget_against")) + ":Link/%s:150"%(filters.get("budget_against")), _("Account") + ":Link/Account:150"]
|
||||
|
||||
@ -98,11 +94,12 @@ def get_cost_centers(filters):
|
||||
else:
|
||||
return frappe.db.sql_list("""select name from `tab{tab}`""".format(tab=filters.get("budget_against"))) #nosec
|
||||
|
||||
#Get cost center & target details
|
||||
def get_cost_center_target_details(filters):
|
||||
#Get dimension & target details
|
||||
def get_dimension_target_details(filters):
|
||||
cond = ""
|
||||
if filters.get("cost_center"):
|
||||
cond += " and b.cost_center=%s" % frappe.db.escape(filters.get("cost_center"))
|
||||
if filters.get("budget_against_filter"):
|
||||
cond += " and b.{budget_against} in (%s)".format(budget_against = \
|
||||
frappe.scrub(filters.get('budget_against'))) % ', '.join(['%s']* len(filters.get('budget_against_filter')))
|
||||
|
||||
return frappe.db.sql("""
|
||||
select b.{budget_against} as budget_against, b.monthly_distribution, ba.account, ba.budget_amount,b.fiscal_year
|
||||
@ -110,8 +107,8 @@ def get_cost_center_target_details(filters):
|
||||
where b.name=ba.parent and b.docstatus = 1 and b.fiscal_year between %s and %s
|
||||
and b.budget_against = %s and b.company=%s {cond} order by b.fiscal_year
|
||||
""".format(budget_against=filters.get("budget_against").replace(" ", "_").lower(), cond=cond),
|
||||
(filters.from_fiscal_year,filters.to_fiscal_year,filters.budget_against, filters.company), as_dict=True)
|
||||
|
||||
tuple([filters.from_fiscal_year,filters.to_fiscal_year,filters.budget_against, filters.company] + filters.get('budget_against_filter')),
|
||||
as_dict=True)
|
||||
|
||||
|
||||
#Get target distribution details of accounts of cost center
|
||||
@ -153,14 +150,14 @@ def get_actual_details(name, filters):
|
||||
|
||||
return cc_actual_details
|
||||
|
||||
def get_cost_center_account_month_map(filters):
|
||||
def get_dimension_account_month_map(filters):
|
||||
import datetime
|
||||
cost_center_target_details = get_cost_center_target_details(filters)
|
||||
dimension_target_details = get_dimension_target_details(filters)
|
||||
tdd = get_target_distribution_details(filters)
|
||||
|
||||
cam_map = {}
|
||||
|
||||
for ccd in cost_center_target_details:
|
||||
for ccd in dimension_target_details:
|
||||
actual_details = get_actual_details(ccd.budget_against, filters)
|
||||
|
||||
for month_id in range(1, 13):
|
||||
|
@ -1,5 +1,6 @@
|
||||
{%
|
||||
var report_columns = report.get_columns_for_print();
|
||||
report_columns = report_columns.filter(col => !col.hidden);
|
||||
|
||||
if (report_columns.length > 8) {
|
||||
frappe.throw(__("Too many columns. Export the report and print it using a spreadsheet application."));
|
||||
@ -15,34 +16,35 @@
|
||||
height: 37px;
|
||||
}
|
||||
</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>
|
||||
<h3 class="text-center">{%= filters.company %}</h3>
|
||||
|
||||
{% if 'cost_center' in filters %}
|
||||
<h3 class="text-center">{%= filters.cost_center %}</h3>
|
||||
{% endif %}
|
||||
|
||||
<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) { %}
|
||||
<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>
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: {%= 100 - (report_columns.length - 2) * 13 %}%"></th>
|
||||
{% for(var i=2, l=report_columns.length; i<l; i++) { %}
|
||||
<th style="width: {%= 100 - (report_columns.length - 1) * 13 %}%"></th>
|
||||
{% for (let i=1, l=report_columns.length; i<l; i++) { %}
|
||||
<th class="text-right">{%= report_columns[i].label %}</th>
|
||||
{% } %}
|
||||
</tr>
|
||||
</thead>
|
||||
<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_class = data[j].parent_account ? "" : "financial-statements-important";
|
||||
@ -52,11 +54,11 @@
|
||||
<td>
|
||||
<span style="padding-left: {%= cint(data[j].indent) * 2 %}em">{%= row.account_name %}</span>
|
||||
</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">
|
||||
{% var fieldname = report_columns[i].fieldname; %}
|
||||
{% const fieldname = report_columns[i].fieldname; %}
|
||||
{% if (!is_null(row[fieldname])) { %}
|
||||
{%= format_currency(row[fieldname], filters.presentation_currency) %}
|
||||
{%= frappe.format(row[fieldname], report_columns[i], {}, row) %}
|
||||
{% } %}
|
||||
</td>
|
||||
{% } %}
|
||||
@ -64,4 +66,6 @@
|
||||
{% } %}
|
||||
</tbody>
|
||||
</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):
|
||||
total_row = {
|
||||
"account_name": "'" + _("Total {0} ({1})").format(_(root_type), _(balance_must_be)) + "'",
|
||||
"account": "'" + _("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)),
|
||||
"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", [])))
|
||||
warehouse = list(set(invoice_cc_wh_map.get(inv.name, {}).get("warehouse", [])))
|
||||
|
||||
row = [
|
||||
inv.name, inv.posting_date, inv.customer, inv.customer_name
|
||||
]
|
||||
row = {
|
||||
'invoice': inv.name,
|
||||
'posting_date': inv.posting_date,
|
||||
'customer': inv.customer,
|
||||
'customer_name': inv.customer_name
|
||||
}
|
||||
|
||||
if 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
|
||||
base_net_total = 0
|
||||
for income_acc in income_accounts:
|
||||
income_amount = flt(invoice_income_map.get(inv.name, {}).get(income_acc))
|
||||
base_net_total += income_amount
|
||||
row.append(income_amount)
|
||||
row.update({
|
||||
frappe.scrub(income_acc): income_amount
|
||||
})
|
||||
|
||||
# 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
|
||||
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 = flt(invoice_tax_map.get(inv.name, {}).get(tax_acc), tax_amount_precision)
|
||||
total_tax += tax_amount
|
||||
row.append(tax_amount)
|
||||
row.update({
|
||||
frappe.scrub(tax_acc): tax_amount
|
||||
})
|
||||
|
||||
# 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)
|
||||
|
||||
@ -84,19 +106,118 @@ def _execute(filters, additional_table_columns=None, additional_query_columns=No
|
||||
def get_columns(invoice_list, additional_table_columns):
|
||||
"""return columns based on filters"""
|
||||
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:
|
||||
columns += additional_table_columns
|
||||
|
||||
columns +=[
|
||||
_("Customer Group") + ":Link/Customer Group:120", _("Territory") + ":Link/Territory:80",
|
||||
_("Tax Id") + "::80", _("Receivable Account") + ":Link/Account:120", _("Mode of Payment") + "::120",
|
||||
_("Project") +":Link/Project:80", _("Owner") + "::150", _("Remarks") + "::150",
|
||||
_("Sales Order") + ":Link/Sales Order:100", _("Delivery Note") + ":Link/Delivery Note:100",
|
||||
_("Cost Center") + ":Link/Cost Center:100", _("Warehouse") + ":Link/Warehouse:100",
|
||||
{
|
||||
'label': _("Custmer Group"),
|
||||
'fieldname': 'customer_group',
|
||||
'fieldtype': 'Link',
|
||||
'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",
|
||||
"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:
|
||||
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""" %
|
||||
', '.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:
|
||||
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 + \
|
||||
[_("Total Tax") + ":Currency/currency:120", _("Grand Total") + ":Currency/currency:120",
|
||||
_("Rounded Total") + ":Currency/currency:120", _("Outstanding Amount") + ":Currency/currency:120"]
|
||||
net_total_column = [{
|
||||
"label": _("Net Total"),
|
||||
"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
|
||||
|
||||
|
@ -144,6 +144,10 @@ frappe.ui.form.on('Asset', {
|
||||
frm.set_df_property('purchase_invoice', '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) {
|
||||
// if purchase receipt link is set then set PI disabled
|
||||
frm.toggle_reqd('purchase_invoice', 0);
|
||||
@ -256,6 +260,7 @@ frappe.ui.form.on('Asset', {
|
||||
},
|
||||
|
||||
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));
|
||||
},
|
||||
|
||||
|
@ -611,12 +611,18 @@ def get_asset_account(account_name, asset=None, asset_category=None, company=Non
|
||||
account = get_asset_category_account(account_name, asset=asset,
|
||||
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:
|
||||
account = frappe.get_cached_value('Company', company, account_name)
|
||||
|
||||
if not account:
|
||||
frappe.throw(_("Set {0} in asset category {1} or company {2}")
|
||||
.format(account_name.replace('_', ' ').title(), asset_category, company))
|
||||
if not asset_category:
|
||||
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
|
||||
|
||||
|
@ -110,7 +110,7 @@ class AssetMovement(Document):
|
||||
ORDER BY
|
||||
asm.transaction_date asc
|
||||
""", (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 \
|
||||
auto generated for Asset {1}').format(self.name, d.asset))
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"actions": [],
|
||||
"allow_import": 1,
|
||||
"autoname": "naming_series:",
|
||||
"creation": "2013-05-21 16:16:39",
|
||||
@ -47,6 +48,7 @@
|
||||
"ignore_pricing_rule",
|
||||
"sec_warehouse",
|
||||
"set_warehouse",
|
||||
"set_reserve_warehouse",
|
||||
"col_break_warehouse",
|
||||
"is_subcontracted",
|
||||
"supplier_warehouse",
|
||||
@ -340,6 +342,7 @@
|
||||
"fieldname": "contact_email",
|
||||
"fieldtype": "Small Text",
|
||||
"label": "Contact Email",
|
||||
"options": "Email",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
@ -1039,12 +1042,20 @@
|
||||
"fieldtype": "Link",
|
||||
"label": "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",
|
||||
"idx": 105,
|
||||
"is_submittable": 1,
|
||||
"modified": "2019-07-11 18:25:49.509343",
|
||||
"links": [],
|
||||
"modified": "2019-12-24 12:44:13.137194",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Purchase Order",
|
||||
|
3
erpnext/buying/doctype/purchase_order/regional/india.js
Normal file
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');
|
@ -118,6 +118,73 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
self.assertEqual(po.get("items")[0].amount, 1400)
|
||||
self.assertEqual(get_ordered_qty(), existing_ordered_qty + 3)
|
||||
|
||||
|
||||
def test_add_new_item_in_update_child_qty_rate(self):
|
||||
po = create_purchase_order(do_not_save=1)
|
||||
po.items[0].qty = 4
|
||||
po.save()
|
||||
po.submit()
|
||||
pr = make_pr_against_po(po.name, 2)
|
||||
|
||||
po.load_from_db()
|
||||
first_item_of_po = po.get("items")[0]
|
||||
|
||||
trans_item = json.dumps([
|
||||
{
|
||||
'item_code': first_item_of_po.item_code,
|
||||
'rate': first_item_of_po.rate,
|
||||
'qty': first_item_of_po.qty,
|
||||
'docname': first_item_of_po.name
|
||||
},
|
||||
{'item_code' : '_Test Item', 'rate' : 200, 'qty' : 7}
|
||||
])
|
||||
update_child_qty_rate('Purchase Order', trans_item, po.name)
|
||||
|
||||
po.reload()
|
||||
self.assertEquals(len(po.get('items')), 2)
|
||||
self.assertEqual(po.status, 'To Receive and Bill')
|
||||
|
||||
|
||||
def test_remove_item_in_update_child_qty_rate(self):
|
||||
po = create_purchase_order(do_not_save=1)
|
||||
po.items[0].qty = 4
|
||||
po.save()
|
||||
po.submit()
|
||||
pr = make_pr_against_po(po.name, 2)
|
||||
|
||||
po.reload()
|
||||
first_item_of_po = po.get("items")[0]
|
||||
# add an item
|
||||
trans_item = json.dumps([
|
||||
{
|
||||
'item_code': first_item_of_po.item_code,
|
||||
'rate': first_item_of_po.rate,
|
||||
'qty': first_item_of_po.qty,
|
||||
'docname': first_item_of_po.name
|
||||
},
|
||||
{'item_code' : '_Test Item', 'rate' : 200, 'qty' : 7}])
|
||||
update_child_qty_rate('Purchase Order', trans_item, po.name)
|
||||
|
||||
po.reload()
|
||||
# check if can remove received item
|
||||
trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 200, 'qty' : 7, 'docname': po.get("items")[1].name}])
|
||||
self.assertRaises(frappe.ValidationError, update_child_qty_rate, 'Purchase Order', trans_item, po.name)
|
||||
|
||||
first_item_of_po = po.get("items")[0]
|
||||
trans_item = json.dumps([
|
||||
{
|
||||
'item_code': first_item_of_po.item_code,
|
||||
'rate': first_item_of_po.rate,
|
||||
'qty': first_item_of_po.qty,
|
||||
'docname': first_item_of_po.name
|
||||
}
|
||||
])
|
||||
update_child_qty_rate('Purchase Order', trans_item, po.name)
|
||||
|
||||
po.reload()
|
||||
self.assertEquals(len(po.get('items')), 1)
|
||||
self.assertEqual(po.status, 'To Receive and Bill')
|
||||
|
||||
def test_update_qty(self):
|
||||
po = create_purchase_order()
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"actions": [],
|
||||
"autoname": "hash",
|
||||
"creation": "2013-05-24 19:29:06",
|
||||
"doctype": "DocType",
|
||||
@ -43,6 +44,7 @@
|
||||
"base_amount",
|
||||
"pricing_rules",
|
||||
"is_free_item",
|
||||
"is_fixed_asset",
|
||||
"section_break_29",
|
||||
"net_rate",
|
||||
"net_amount",
|
||||
@ -708,11 +710,20 @@
|
||||
"fieldname": "against_blanket_order",
|
||||
"fieldtype": "Check",
|
||||
"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,
|
||||
"istable": 1,
|
||||
"modified": "2019-11-19 14:10:52.865006",
|
||||
"links": [],
|
||||
"modified": "2019-12-06 13:17:12.142799",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Purchase Order Item",
|
||||
|
@ -138,7 +138,7 @@ def refresh_scorecards():
|
||||
# Check to see if any new scorecard periods are created
|
||||
if make_all_scorecards(sc.name) > 0:
|
||||
# Save the scorecard to update the score and standings
|
||||
sc.save()
|
||||
frappe.get_doc('Supplier Scorecard', sc.name).save()
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
|
@ -3,18 +3,19 @@
|
||||
"app": "ERPNext",
|
||||
"creation": "2019-11-15 14:45:32.626641",
|
||||
"docstatus": 0,
|
||||
"doctype": "Setup Wizard Slide",
|
||||
"doctype": "Onboarding Slide",
|
||||
"domains": [],
|
||||
"help_links": [
|
||||
{
|
||||
"label": "Supplier",
|
||||
"label": "Learn More",
|
||||
"video_id": "zsrrVDk6VBs"
|
||||
}
|
||||
],
|
||||
"idx": 0,
|
||||
"image_src": "/assets/erpnext/images/illustrations/supplier.png",
|
||||
"image_src": "",
|
||||
"is_completed": 0,
|
||||
"max_count": 3,
|
||||
"modified": "2019-11-26 18:26:25.498325",
|
||||
"modified": "2019-12-09 17:54:18.452038",
|
||||
"modified_by": "Administrator",
|
||||
"name": "Add A Few Suppliers",
|
||||
"owner": "Administrator",
|
||||
@ -44,6 +45,5 @@
|
||||
],
|
||||
"slide_order": 50,
|
||||
"slide_title": "Add A Few Suppliers",
|
||||
"slide_type": "Create",
|
||||
"submit_method": ""
|
||||
"slide_type": "Create"
|
||||
}
|
@ -319,8 +319,8 @@ class AccountsController(TransactionBase):
|
||||
if item.get('discount_amount'):
|
||||
item.rate = item.price_list_rate - item.discount_amount
|
||||
|
||||
elif pricing_rule_args.get('free_item'):
|
||||
apply_pricing_rule_for_free_items(self, pricing_rule_args)
|
||||
elif pricing_rule_args.get('free_item_data'):
|
||||
apply_pricing_rule_for_free_items(self, pricing_rule_args.get('free_item_data'))
|
||||
|
||||
elif pricing_rule_args.get("validate_applied_rule"):
|
||||
for pricing_rule in get_applied_pricing_rules(item):
|
||||
@ -1155,6 +1155,25 @@ def set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docna
|
||||
child_item.base_amount = 1 # Initiallize value will update in parent validation
|
||||
return child_item
|
||||
|
||||
def check_and_delete_children(parent, data):
|
||||
deleted_children = []
|
||||
updated_item_names = [d.get("docname") for d in data]
|
||||
for item in parent.items:
|
||||
if item.name not in updated_item_names:
|
||||
deleted_children.append(item)
|
||||
|
||||
for d in deleted_children:
|
||||
if parent.doctype == "Sales Order" and flt(d.delivered_qty):
|
||||
frappe.throw(_("Row #{0}: Cannot delete item {1} which has already been delivered").format(d.idx, d.item_code))
|
||||
|
||||
if parent.doctype == "Purchase Order" and flt(d.received_qty):
|
||||
frappe.throw(_("Row #{0}: Cannot delete item {1} which has already been received").format(d.idx, d.item_code))
|
||||
|
||||
if flt(d.billed_amt):
|
||||
frappe.throw(_("Row #{0}: Cannot delete item {1} which has already been billed.").format(d.idx, d.item_code))
|
||||
|
||||
d.cancel()
|
||||
d.delete()
|
||||
|
||||
@frappe.whitelist()
|
||||
def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, child_docname="items"):
|
||||
@ -1163,6 +1182,8 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
|
||||
sales_doctypes = ['Sales Order', 'Sales Invoice', 'Delivery Note', 'Quotation']
|
||||
parent = frappe.get_doc(parent_doctype, parent_doctype_name)
|
||||
|
||||
check_and_delete_children(parent, data)
|
||||
|
||||
for d in data:
|
||||
new_child_flag = False
|
||||
if not d.get("docname"):
|
||||
|
@ -265,16 +265,17 @@ class BuyingController(StockController):
|
||||
|
||||
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)
|
||||
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:
|
||||
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
|
||||
|
||||
rm_qty_to_be_consumed = transferred_qty - consumed_qty
|
||||
|
@ -311,6 +311,7 @@ def get_batch_no(doctype, txt, searchfield, start, page_len, filters):
|
||||
and sle.item_code = %(item_code)s
|
||||
and sle.warehouse = %(warehouse)s
|
||||
and (sle.batch_no like %(txt)s
|
||||
or batch.expiry_date like %(txt)s
|
||||
or batch.manufacturing_date like %(txt)s)
|
||||
and batch.docstatus < 2
|
||||
{cond}
|
||||
@ -329,6 +330,7 @@ def get_batch_no(doctype, txt, searchfield, start, page_len, filters):
|
||||
where batch.disabled = 0
|
||||
and item = %(item_code)s
|
||||
and (name like %(txt)s
|
||||
or expiry_date like %(txt)s
|
||||
or manufacturing_date like %(txt)s)
|
||||
and docstatus < 2
|
||||
{0}
|
||||
|
@ -148,13 +148,6 @@ class SellingController(StockController):
|
||||
if sales_team and total != 100.0:
|
||||
throw(_("Total allocated percentage for sales team should be 100"))
|
||||
|
||||
def validate_order_type(self):
|
||||
valid_types = ["Sales", "Maintenance", "Shopping Cart"]
|
||||
if not self.order_type:
|
||||
self.order_type = "Sales"
|
||||
elif self.order_type not in valid_types:
|
||||
throw(_("Order Type must be one of {0}").format(comma_or(valid_types)))
|
||||
|
||||
def validate_max_discount(self):
|
||||
for d in self.get("items"):
|
||||
if d.item_code:
|
||||
|
@ -11,7 +11,7 @@ from datetime import timedelta
|
||||
import frappe
|
||||
from frappe import _
|
||||
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
|
||||
|
||||
|
||||
@ -117,7 +117,7 @@ class Appointment(Document):
|
||||
if self._assign:
|
||||
return
|
||||
available_agents = _get_agents_sorted_by_asc_workload(
|
||||
self.scheduled_time.date())
|
||||
getdate(self.scheduled_time))
|
||||
for agent in available_agents:
|
||||
if(_check_agent_availability(agent, self.scheduled_time)):
|
||||
agent = agent[0]
|
||||
@ -171,7 +171,7 @@ class Appointment(Document):
|
||||
self.save(ignore_permissions=True)
|
||||
|
||||
def _get_verify_url(self):
|
||||
verify_route = '/book-appointment/verify'
|
||||
verify_route = '/book_appointment/verify'
|
||||
params = {
|
||||
'email': self.customer_email,
|
||||
'appointment': self.name
|
||||
@ -189,7 +189,7 @@ def _get_agents_sorted_by_asc_workload(date):
|
||||
assigned_to = frappe.parse_json(appointment._assign)
|
||||
if not assigned_to:
|
||||
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
|
||||
sorted_agent_list = appointment_counter.most_common()
|
||||
sorted_agent_list.reverse()
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
// License: GNU General Public License v3. See license.txt
|
||||
|
||||
frappe.provide("erpnext");
|
||||
@ -7,57 +7,54 @@ cur_frm.email_field = "email_id";
|
||||
erpnext.LeadController = frappe.ui.form.Controller.extend({
|
||||
setup: function () {
|
||||
this.frm.make_methods = {
|
||||
'Customer': this.make_customer,
|
||||
'Quotation': this.make_quotation,
|
||||
'Opportunity': this.create_opportunity
|
||||
}
|
||||
|
||||
this.frm.fields_dict.customer.get_query = function (doc, cdt, cdn) {
|
||||
return { query: "erpnext.controllers.queries.customer_query" }
|
||||
}
|
||||
'Opportunity': this.make_opportunity
|
||||
};
|
||||
|
||||
this.frm.toggle_reqd("lead_name", !this.frm.doc.organization_lead);
|
||||
},
|
||||
|
||||
onload: function () {
|
||||
if (cur_frm.fields_dict.lead_owner.df.options.match(/^User/)) {
|
||||
cur_frm.fields_dict.lead_owner.get_query = function (doc, cdt, cdn) {
|
||||
return { query: "frappe.core.doctype.user.user.user_query" }
|
||||
}
|
||||
}
|
||||
this.frm.set_query("customer", function (doc, cdt, cdn) {
|
||||
return { query: "erpnext.controllers.queries.customer_query" }
|
||||
});
|
||||
|
||||
if (cur_frm.fields_dict.contact_by.df.options.match(/^User/)) {
|
||||
cur_frm.fields_dict.contact_by.get_query = function (doc, cdt, cdn) {
|
||||
return { query: "frappe.core.doctype.user.user.user_query" }
|
||||
}
|
||||
}
|
||||
this.frm.set_query("lead_owner", function (doc, cdt, cdn) {
|
||||
return { query: "frappe.core.doctype.user.user.user_query" }
|
||||
});
|
||||
|
||||
this.frm.set_query("contact_by", function (doc, cdt, cdn) {
|
||||
return { query: "frappe.core.doctype.user.user.user_query" }
|
||||
});
|
||||
},
|
||||
|
||||
refresh: function () {
|
||||
var doc = this.frm.doc;
|
||||
let doc = this.frm.doc;
|
||||
erpnext.toggle_naming_series();
|
||||
frappe.dynamic_link = { doc: doc, fieldname: 'name', doctype: 'Lead' }
|
||||
|
||||
if(!doc.__islocal && doc.__onload && !doc.__onload.is_customer) {
|
||||
this.frm.add_custom_button(__("Customer"), this.create_customer, __('Create'));
|
||||
this.frm.add_custom_button(__("Opportunity"), this.create_opportunity, __('Create'));
|
||||
this.frm.add_custom_button(__("Quotation"), this.make_quotation, __('Create'));
|
||||
if (!this.frm.is_new() && doc.__onload && !doc.__onload.is_customer) {
|
||||
this.frm.add_custom_button(__("Customer"), this.make_customer, __("Create"));
|
||||
this.frm.add_custom_button(__("Opportunity"), this.make_opportunity, __("Create"));
|
||||
this.frm.add_custom_button(__("Quotation"), this.make_quotation, __("Create"));
|
||||
}
|
||||
|
||||
if (!this.frm.doc.__islocal) {
|
||||
frappe.contacts.render_address_and_contact(cur_frm);
|
||||
if (!this.frm.is_new()) {
|
||||
frappe.contacts.render_address_and_contact(this.frm);
|
||||
} else {
|
||||
frappe.contacts.clear_address_and_contact(cur_frm);
|
||||
frappe.contacts.clear_address_and_contact(this.frm);
|
||||
}
|
||||
},
|
||||
|
||||
create_customer: function () {
|
||||
make_customer: function () {
|
||||
frappe.model.open_mapped_doc({
|
||||
method: "erpnext.crm.doctype.lead.lead.make_customer",
|
||||
frm: cur_frm
|
||||
})
|
||||
},
|
||||
|
||||
create_opportunity: function () {
|
||||
make_opportunity: function () {
|
||||
frappe.model.open_mapped_doc({
|
||||
method: "erpnext.crm.doctype.lead.lead.make_opportunity",
|
||||
frm: cur_frm
|
||||
@ -77,7 +74,7 @@ erpnext.LeadController = frappe.ui.form.Controller.extend({
|
||||
},
|
||||
|
||||
company_name: function () {
|
||||
if (this.frm.doc.organization_lead == 1) {
|
||||
if (this.frm.doc.organization_lead && !this.frm.doc.lead_name) {
|
||||
this.frm.set_value("lead_name", this.frm.doc.company_name);
|
||||
}
|
||||
},
|
||||
@ -85,7 +82,7 @@ erpnext.LeadController = frappe.ui.form.Controller.extend({
|
||||
contact_date: function () {
|
||||
if (this.frm.doc.contact_date) {
|
||||
let d = moment(this.frm.doc.contact_date);
|
||||
d.add(1, "hours");
|
||||
d.add(1, "day");
|
||||
this.frm.set_value("ends_on", d.format(frappe.defaultDatetimeFormat));
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"actions": [],
|
||||
"allow_events_in_timeline": 1,
|
||||
"allow_import": 1,
|
||||
"autoname": "naming_series:",
|
||||
@ -16,6 +17,8 @@
|
||||
"col_break123",
|
||||
"lead_owner",
|
||||
"status",
|
||||
"salutation",
|
||||
"designation",
|
||||
"gender",
|
||||
"source",
|
||||
"customer",
|
||||
@ -28,17 +31,22 @@
|
||||
"ends_on",
|
||||
"notes_section",
|
||||
"notes",
|
||||
"contact_info",
|
||||
"address_desc",
|
||||
"address_info",
|
||||
"address_html",
|
||||
"address_title",
|
||||
"address_line1",
|
||||
"address_line2",
|
||||
"city",
|
||||
"county",
|
||||
"column_break2",
|
||||
"contact_html",
|
||||
"state",
|
||||
"country",
|
||||
"pincode",
|
||||
"contact_section",
|
||||
"phone",
|
||||
"salutation",
|
||||
"mobile_no",
|
||||
"fax",
|
||||
"website",
|
||||
"territory",
|
||||
"more_info",
|
||||
"type",
|
||||
"market_segment",
|
||||
@ -46,8 +54,11 @@
|
||||
"request_type",
|
||||
"column_break3",
|
||||
"company",
|
||||
"website",
|
||||
"territory",
|
||||
"unsubscribed",
|
||||
"blog_subscriber"
|
||||
"blog_subscriber",
|
||||
"title"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
@ -73,7 +84,6 @@
|
||||
"set_only_once": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:!doc.organization_lead",
|
||||
"fieldname": "lead_name",
|
||||
"fieldtype": "Data",
|
||||
"in_global_search": 1,
|
||||
@ -130,7 +140,13 @@
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:!doc.organization_lead",
|
||||
"depends_on": "eval: doc.__islocal",
|
||||
"fieldname": "salutation",
|
||||
"fieldtype": "Link",
|
||||
"label": "Salutation",
|
||||
"options": "Salutation"
|
||||
},
|
||||
{
|
||||
"fieldname": "gender",
|
||||
"fieldtype": "Link",
|
||||
"label": "Gender",
|
||||
@ -216,40 +232,74 @@
|
||||
"fieldtype": "Text Editor",
|
||||
"label": "Notes"
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"fieldname": "contact_info",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Address & Contact",
|
||||
"oldfieldtype": "Column Break",
|
||||
"options": "fa fa-map-marker"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.__islocal",
|
||||
"fieldname": "address_desc",
|
||||
"fieldtype": "HTML",
|
||||
"label": "Address Desc",
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "address_html",
|
||||
"fieldtype": "HTML",
|
||||
"label": "Address HTML",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval: doc.__islocal",
|
||||
"fieldname": "address_title",
|
||||
"fieldtype": "Data",
|
||||
"label": "Address Title"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval: doc.__islocal",
|
||||
"fieldname": "address_line1",
|
||||
"fieldtype": "Data",
|
||||
"label": "Address Line 1"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval: doc.__islocal",
|
||||
"fieldname": "address_line2",
|
||||
"fieldtype": "Data",
|
||||
"label": "Address Line 2"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval: doc.__islocal",
|
||||
"fieldname": "city",
|
||||
"fieldtype": "Data",
|
||||
"label": "City/Town"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval: doc.__islocal",
|
||||
"fieldname": "county",
|
||||
"fieldtype": "Data",
|
||||
"label": "County"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval: doc.__islocal",
|
||||
"fieldname": "state",
|
||||
"fieldtype": "Data",
|
||||
"label": "State"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval: doc.__islocal",
|
||||
"fieldname": "country",
|
||||
"fieldtype": "Link",
|
||||
"label": "Country",
|
||||
"options": "Country"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval: doc.__islocal",
|
||||
"fieldname": "pincode",
|
||||
"fieldtype": "Data",
|
||||
"label": "Postal Code",
|
||||
"options": "Country"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break2",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.organization_lead",
|
||||
"fieldname": "contact_html",
|
||||
"fieldtype": "HTML",
|
||||
"label": "Contact HTML",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:!doc.organization_lead",
|
||||
"depends_on": "eval: doc.__islocal",
|
||||
"fieldname": "phone",
|
||||
"fieldtype": "Data",
|
||||
"label": "Phone",
|
||||
@ -257,14 +307,7 @@
|
||||
"oldfieldtype": "Data"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:!doc.organization_lead",
|
||||
"fieldname": "salutation",
|
||||
"fieldtype": "Link",
|
||||
"label": "Salutation",
|
||||
"options": "Salutation"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:!doc.organization_lead",
|
||||
"depends_on": "eval: doc.__islocal",
|
||||
"fieldname": "mobile_no",
|
||||
"fieldtype": "Data",
|
||||
"label": "Mobile No.",
|
||||
@ -272,29 +315,13 @@
|
||||
"oldfieldtype": "Data"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:!doc.organization_lead",
|
||||
"depends_on": "eval: doc.__islocal",
|
||||
"fieldname": "fax",
|
||||
"fieldtype": "Data",
|
||||
"label": "Fax",
|
||||
"oldfieldname": "fax",
|
||||
"oldfieldtype": "Data"
|
||||
},
|
||||
{
|
||||
"fieldname": "website",
|
||||
"fieldtype": "Data",
|
||||
"label": "Website",
|
||||
"oldfieldname": "website",
|
||||
"oldfieldtype": "Data"
|
||||
},
|
||||
{
|
||||
"fieldname": "territory",
|
||||
"fieldtype": "Link",
|
||||
"label": "Territory",
|
||||
"oldfieldname": "territory",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Territory",
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"fieldname": "more_info",
|
||||
@ -350,6 +377,22 @@
|
||||
"options": "Company",
|
||||
"remember_last_selected_value": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "website",
|
||||
"fieldtype": "Data",
|
||||
"label": "Website",
|
||||
"oldfieldname": "website",
|
||||
"oldfieldtype": "Data"
|
||||
},
|
||||
{
|
||||
"fieldname": "territory",
|
||||
"fieldtype": "Link",
|
||||
"label": "Territory",
|
||||
"oldfieldname": "territory",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Territory",
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "unsubscribed",
|
||||
@ -361,12 +404,42 @@
|
||||
"fieldname": "blog_subscriber",
|
||||
"fieldtype": "Check",
|
||||
"label": "Blog Subscriber"
|
||||
},
|
||||
{
|
||||
"fieldname": "title",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"label": "Title",
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "designation",
|
||||
"fieldtype": "Link",
|
||||
"label": "Designation",
|
||||
"options": "Designation"
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"collapsible_depends_on": "eval: doc.__islocal",
|
||||
"fieldname": "address_info",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Address & Contact",
|
||||
"oldfieldtype": "Column Break",
|
||||
"options": "fa fa-map-marker"
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"collapsible_depends_on": "eval: doc.__islocal",
|
||||
"fieldname": "contact_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Contact"
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-user",
|
||||
"idx": 5,
|
||||
"image_field": "image",
|
||||
"modified": "2019-09-19 12:49:02.536647",
|
||||
"links": [],
|
||||
"modified": "2019-12-24 16:00:44.239168",
|
||||
"modified_by": "Administrator",
|
||||
"module": "CRM",
|
||||
"name": "Lead",
|
||||
@ -438,5 +511,5 @@
|
||||
"show_name_in_global_search": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"title_field": "lead_name"
|
||||
"title_field": "title"
|
||||
}
|
@ -2,18 +2,19 @@
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import (cstr, validate_email_address, cint, comma_and, has_gravatar, now, getdate, nowdate)
|
||||
from frappe.model.mapper import get_mapped_doc
|
||||
|
||||
from erpnext.controllers.selling_controller import SellingController
|
||||
from frappe.contacts.address_and_contact import load_address_and_contact
|
||||
import frappe
|
||||
from erpnext.accounts.party import set_taxes
|
||||
from erpnext.controllers.selling_controller import SellingController
|
||||
from frappe import _
|
||||
from frappe.contacts.address_and_contact import load_address_and_contact
|
||||
from frappe.email.inbox import link_communication_to_document
|
||||
from frappe.model.mapper import get_mapped_doc
|
||||
from frappe.utils import cint, comma_and, cstr, getdate, has_gravatar, nowdate, validate_email_address
|
||||
|
||||
sender_field = "email_id"
|
||||
|
||||
|
||||
class Lead(SellingController):
|
||||
def get_feed(self):
|
||||
return '{0}: {1}'.format(_(self.status), self.lead_name)
|
||||
@ -23,15 +24,23 @@ class Lead(SellingController):
|
||||
self.get("__onload").is_customer = customer
|
||||
load_address_and_contact(self)
|
||||
|
||||
def before_insert(self):
|
||||
self.address_doc = self.create_address()
|
||||
self.contact_doc = self.create_contact()
|
||||
|
||||
def after_insert(self):
|
||||
self.update_links()
|
||||
# after the address and contact are created, flush the field values
|
||||
# to avoid inconsistent reporting in case the documents are changed
|
||||
self.flush_address_and_contact_fields()
|
||||
|
||||
def validate(self):
|
||||
self.set_lead_name()
|
||||
self.set_title()
|
||||
self._prev = frappe._dict({
|
||||
"contact_date": frappe.db.get_value("Lead", self.name, "contact_date") if \
|
||||
(not cint(self.get("__islocal"))) else None,
|
||||
"ends_on": frappe.db.get_value("Lead", self.name, "ends_on") if \
|
||||
(not cint(self.get("__islocal"))) else None,
|
||||
"contact_by": frappe.db.get_value("Lead", self.name, "contact_by") if \
|
||||
(not cint(self.get("__islocal"))) else None,
|
||||
"contact_date": frappe.db.get_value("Lead", self.name, "contact_date") if (not cint(self.is_new())) else None,
|
||||
"ends_on": frappe.db.get_value("Lead", self.name, "ends_on") if (not cint(self.is_new())) else None,
|
||||
"contact_by": frappe.db.get_value("Lead", self.name, "contact_by") if (not cint(self.is_new())) else None,
|
||||
})
|
||||
|
||||
self.set_status()
|
||||
@ -39,7 +48,7 @@ class Lead(SellingController):
|
||||
|
||||
if self.email_id:
|
||||
if not self.flags.ignore_email_validation:
|
||||
validate_email_address(self.email_id, True)
|
||||
validate_email_address(self.email_id, throw=True)
|
||||
|
||||
if self.email_id == self.lead_owner:
|
||||
frappe.throw(_("Lead Owner cannot be same as the Lead"))
|
||||
@ -53,8 +62,7 @@ class Lead(SellingController):
|
||||
if self.contact_date and getdate(self.contact_date) < getdate(nowdate()):
|
||||
frappe.throw(_("Next Contact Date cannot be in the past"))
|
||||
|
||||
if self.ends_on and self.contact_date and\
|
||||
(self.ends_on < self.contact_date):
|
||||
if self.ends_on and self.contact_date and (self.ends_on < self.contact_date):
|
||||
frappe.throw(_("Ends On date cannot be before Next Contact Date."))
|
||||
|
||||
def on_update(self):
|
||||
@ -66,23 +74,21 @@ class Lead(SellingController):
|
||||
"starts_on": self.contact_date,
|
||||
"ends_on": self.ends_on or "",
|
||||
"subject": ('Contact ' + cstr(self.lead_name)),
|
||||
"description": ('Contact ' + cstr(self.lead_name)) + \
|
||||
(self.contact_by and ('. By : ' + cstr(self.contact_by)) or '')
|
||||
"description": ('Contact ' + cstr(self.lead_name)) + (self.contact_by and ('. By : ' + cstr(self.contact_by)) or '')
|
||||
}, force)
|
||||
|
||||
def check_email_id_is_unique(self):
|
||||
if self.email_id:
|
||||
# validate email is unique
|
||||
duplicate_leads = frappe.db.sql_list("""select name from tabLead
|
||||
where email_id=%s and name!=%s""", (self.email_id, self.name))
|
||||
duplicate_leads = frappe.get_all("Lead", filters={"email_id": self.email_id, "name": ["!=", self.name]})
|
||||
duplicate_leads = [lead.name for lead in duplicate_leads]
|
||||
|
||||
if duplicate_leads:
|
||||
frappe.throw(_("Email Address must be unique, already exists for {0}")
|
||||
.format(comma_and(duplicate_leads)), frappe.DuplicateEntryError)
|
||||
|
||||
def on_trash(self):
|
||||
frappe.db.sql("""update `tabIssue` set lead='' where lead=%s""",
|
||||
self.name)
|
||||
frappe.db.sql("""update `tabIssue` set lead='' where lead=%s""", self.name)
|
||||
|
||||
self.delete_events()
|
||||
|
||||
@ -115,10 +121,101 @@ class Lead(SellingController):
|
||||
|
||||
self.lead_name = self.company_name
|
||||
|
||||
def set_title(self):
|
||||
if self.organization_lead:
|
||||
self.title = self.company_name
|
||||
else:
|
||||
self.title = self.lead_name
|
||||
|
||||
def create_address(self):
|
||||
address_fields = ["address_title", "address_line1", "address_line2",
|
||||
"city", "county", "state", "country", "pincode"]
|
||||
info_fields = ["email_id", "phone", "fax"]
|
||||
|
||||
# do not create an address if no fields are available,
|
||||
# skipping country since the system auto-sets it from system defaults
|
||||
if not any([self.get(field) for field in address_fields if field != "country"]):
|
||||
return
|
||||
|
||||
address = frappe.new_doc("Address")
|
||||
address.update({addr_field: self.get(addr_field) for addr_field in address_fields})
|
||||
address.update({info_field: self.get(info_field) for info_field in info_fields})
|
||||
address.insert()
|
||||
|
||||
return address
|
||||
|
||||
def create_contact(self):
|
||||
if not self.lead_name:
|
||||
self.set_lead_name()
|
||||
|
||||
names = self.lead_name.split(" ")
|
||||
if len(names) > 1:
|
||||
first_name, last_name = names[0], " ".join(names[1:])
|
||||
else:
|
||||
first_name, last_name = self.lead_name, None
|
||||
|
||||
contact = frappe.new_doc("Contact")
|
||||
contact.update({
|
||||
"first_name": first_name,
|
||||
"last_name": last_name,
|
||||
"salutation": self.salutation,
|
||||
"gender": self.gender,
|
||||
"designation": self.designation,
|
||||
})
|
||||
|
||||
if self.email_id:
|
||||
contact.append("email_ids", {
|
||||
"email_id": self.email_id,
|
||||
"is_primary": 1
|
||||
})
|
||||
|
||||
if self.phone:
|
||||
contact.append("phone_nos", {
|
||||
"phone": self.phone,
|
||||
"is_primary": 1
|
||||
})
|
||||
|
||||
if self.mobile_no:
|
||||
contact.append("phone_nos", {
|
||||
"phone": self.mobile_no
|
||||
})
|
||||
|
||||
contact.insert()
|
||||
|
||||
return contact
|
||||
|
||||
def update_links(self):
|
||||
# update address links
|
||||
if self.address_doc:
|
||||
self.address_doc.append("links", {
|
||||
"link_doctype": "Lead",
|
||||
"link_name": self.name,
|
||||
"link_title": self.lead_name
|
||||
})
|
||||
self.address_doc.save()
|
||||
|
||||
# update contact links
|
||||
if self.contact_doc:
|
||||
self.contact_doc.append("links", {
|
||||
"link_doctype": "Lead",
|
||||
"link_name": self.name,
|
||||
"link_title": self.lead_name
|
||||
})
|
||||
self.contact_doc.save()
|
||||
|
||||
def flush_address_and_contact_fields(self):
|
||||
fields = ['address_line1', 'address_line2', 'address_title',
|
||||
'city', 'county', 'country', 'fax', 'pincode', 'state']
|
||||
|
||||
for field in fields:
|
||||
self.set(field, None)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_customer(source_name, target_doc=None):
|
||||
return _make_customer(source_name, target_doc)
|
||||
|
||||
|
||||
def _make_customer(source_name, target_doc=None, ignore_permissions=False):
|
||||
def set_missing_values(source, target):
|
||||
if source.company_name:
|
||||
@ -143,6 +240,7 @@ def _make_customer(source_name, target_doc=None, ignore_permissions=False):
|
||||
|
||||
return doclist
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_opportunity(source_name, target_doc=None):
|
||||
def set_missing_values(source, target):
|
||||
@ -164,6 +262,7 @@ def make_opportunity(source_name, target_doc=None):
|
||||
|
||||
return target_doc
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_quotation(source_name, target_doc=None):
|
||||
def set_missing_values(source, target):
|
||||
@ -205,7 +304,8 @@ def _set_missing_values(source, target):
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_lead_details(lead, posting_date=None, company=None):
|
||||
if not lead: return {}
|
||||
if not lead:
|
||||
return {}
|
||||
|
||||
from erpnext.accounts.party import set_address_details
|
||||
out = frappe._dict()
|
||||
@ -231,6 +331,7 @@ def get_lead_details(lead, posting_date=None, company=None):
|
||||
|
||||
return out
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_lead_from_communication(communication, ignore_communication_links=False):
|
||||
""" raise a issue from email """
|
||||
|
@ -22,3 +22,12 @@ class Instructor(Document):
|
||||
self.name = self.employee
|
||||
elif naming_method == 'Full Name':
|
||||
self.name = self.instructor_name
|
||||
|
||||
def validate(self):
|
||||
self.validate_duplicate_employee()
|
||||
|
||||
def validate_duplicate_employee(self):
|
||||
if self.employee and frappe.db.get_value("Instructor", {'employee': self.employee, 'name': ['!=', self.name]}, 'name'):
|
||||
frappe.throw(_("Employee ID is linked with another instructor"))
|
||||
|
||||
|
||||
|
@ -31,7 +31,7 @@ frappe.ui.form.on('Student', {
|
||||
frappe.ui.form.on('Student Guardian', {
|
||||
guardians_add: function(frm){
|
||||
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);
|
||||
$.each(doc.guardians, function(idx, val){
|
||||
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
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import getdate,today
|
||||
from frappe import _
|
||||
from frappe.desk.form.linked_with import get_linked_doctypes
|
||||
from erpnext.education.utils import check_content_completion, check_quiz_completion
|
||||
class Student(Document):
|
||||
def validate(self):
|
||||
self.title = " ".join(filter(None, [self.first_name, self.middle_name, self.last_name]))
|
||||
self.validate_dates()
|
||||
|
||||
if self.student_applicant:
|
||||
self.check_unique()
|
||||
@ -19,6 +21,13 @@ class Student(Document):
|
||||
if frappe.get_value("Student", self.name, "title") != self.title:
|
||||
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."))
|
||||
|
||||
if self.joining_date and self.date_of_leaving and getdate(self.joining_date) > getdate(self.date_of_leaving):
|
||||
frappe.throw(_("Joining Date can not be greater than Leaving Date"))
|
||||
|
||||
def update_student_name_in_linked_doctype(self):
|
||||
linked_doctypes = get_linked_doctypes("Student")
|
||||
for d in linked_doctypes:
|
||||
|
@ -122,3 +122,15 @@ frappe.ui.form.on("Student Group", {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
frappe.ui.form.on('Student Group Instructor', {
|
||||
instructors_add: function(frm){
|
||||
frm.fields_dict['instructors'].grid.get_field('instructor').get_query = function(doc){
|
||||
let instructor_list = [];
|
||||
$.each(doc.instructors, function(idx, val){
|
||||
instructor_list.push(val.instructor);
|
||||
});
|
||||
return { filters: [['Instructor', 'name', 'not in', instructor_list]] };
|
||||
};
|
||||
}
|
||||
});
|
@ -180,6 +180,7 @@ standard_portal_menu_items = [
|
||||
{"title": _("Admission"), "route": "/admissions", "reference_doctype": "Student Admission", "role": "Student"},
|
||||
{"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": _("Appointment Booking"), "route": "/book_appointment"},
|
||||
]
|
||||
|
||||
default_roles = [
|
||||
@ -246,10 +247,10 @@ doc_events = {
|
||||
"on_trash": "erpnext.regional.check_deletion_permission"
|
||||
},
|
||||
'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'): {
|
||||
'validate': 'erpnext.regional.india.utils.set_place_of_supply'
|
||||
('Sales Invoice', 'Sales Order', 'Delivery Note', 'Purchase Invoice', 'Purchase Order', 'Purchase Receipt'): {
|
||||
'validate': ['erpnext.regional.india.utils.set_place_of_supply']
|
||||
},
|
||||
"Contact": {
|
||||
"on_trash": "erpnext.support.doctype.issue.issue.update_issue",
|
||||
|
@ -5,9 +5,10 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
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 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):
|
||||
|
||||
@ -25,16 +26,14 @@ class CompensatoryLeaveRequest(Document):
|
||||
frappe.throw(_("Leave Type is madatory"))
|
||||
|
||||
def validate_attendance(self):
|
||||
query = """select attendance_date, status
|
||||
from `tabAttendance` where
|
||||
attendance_date between %(work_from_date)s and %(work_end_date)s
|
||||
and docstatus=1 and status = 'Present' and employee=%(employee)s"""
|
||||
attendance = frappe.get_all('Attendance',
|
||||
filters={
|
||||
'attendance_date': ['between', (self.work_from_date, self.work_end_date)],
|
||||
'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:
|
||||
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
|
||||
leave_period = get_leave_period(self.work_from_date, self.work_end_date, company)
|
||||
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:
|
||||
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:
|
||||
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:
|
||||
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)
|
||||
if leave_allocation:
|
||||
leave_allocation.new_leaves_allocated -= date_difference
|
||||
if leave_allocation.total_leaves_allocated - date_difference <= 0:
|
||||
leave_allocation.total_leaves_allocated = 0
|
||||
leave_allocation.submit()
|
||||
if leave_allocation.new_leaves_allocated - date_difference <= 0:
|
||||
leave_allocation.new_leaves_allocated = 0
|
||||
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("""
|
||||
select name
|
||||
from `tabLeave Allocation`
|
||||
@ -95,17 +105,18 @@ class CompensatoryLeaveRequest(Document):
|
||||
|
||||
def create_leave_allocation(self, leave_period, date_difference):
|
||||
is_carry_forward = frappe.db.get_value("Leave Type", self.leave_type, "is_carry_forward")
|
||||
allocation = frappe.new_doc("Leave Allocation")
|
||||
allocation.employee = self.employee
|
||||
allocation.employee_name = self.employee_name
|
||||
allocation.leave_type = self.leave_type
|
||||
allocation.from_date = add_days(self.work_end_date, 1)
|
||||
allocation.to_date = leave_period[0].to_date
|
||||
allocation.new_leaves_allocated = date_difference
|
||||
allocation.total_leaves_allocated = date_difference
|
||||
allocation.description = self.reason
|
||||
if is_carry_forward == 1:
|
||||
allocation.carry_forward = True
|
||||
allocation.save(ignore_permissions = True)
|
||||
allocation = frappe.get_doc(dict(
|
||||
doctype="Leave Allocation",
|
||||
employee=self.employee,
|
||||
employee_name=self.employee_name,
|
||||
leave_type=self.leave_type,
|
||||
from_date=add_days(self.work_end_date, 1),
|
||||
to_date=leave_period[0].to_date,
|
||||
carry_forward=cint(is_carry_forward),
|
||||
new_leaves_allocated=date_difference,
|
||||
total_leaves_allocated=date_difference,
|
||||
description=self.reason
|
||||
))
|
||||
allocation.insert(ignore_permissions=True)
|
||||
allocation.submit()
|
||||
return allocation
|
@ -5,37 +5,128 @@ from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
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):
|
||||
# def get_compensatory_leave_request(self):
|
||||
# return frappe.get_doc('Compensatory Leave Request', dict(
|
||||
# employee = employee,
|
||||
# work_from_date = today,
|
||||
# work_to_date = today,
|
||||
# reason = 'test'
|
||||
# )).insert()
|
||||
#
|
||||
# def test_creation_of_leave_allocation(self):
|
||||
# employee = get_employee()
|
||||
# today = get_today()
|
||||
#
|
||||
# compensatory_leave_request = self.get_compensatory_leave_request(today)
|
||||
#
|
||||
# before = get_leave_balance(employee, compensatory_leave_request.leave_type)
|
||||
#
|
||||
# compensatory_leave_request.submit()
|
||||
#
|
||||
# self.assertEqual(get_leave_balance(employee, compensatory_leave_request.leave_type), before + 1)
|
||||
#
|
||||
# def test_max_compensatory_leave(self):
|
||||
# employee = get_employee()
|
||||
# today = get_today()
|
||||
#
|
||||
# compensatory_leave_request = self.get_compensatory_leave_request()
|
||||
#
|
||||
# frappe.db.set_value('Leave Type', compensatory_leave_request.leave_type, 'max_leaves_allowed', 0)
|
||||
#
|
||||
# self.assertRaises(MaxLeavesLimitCrossed, compensatory_leave_request.submit)
|
||||
#
|
||||
# frappe.db.set_value('Leave Type', compensatory_leave_request.leave_type, 'max_leaves_allowed', 10)
|
||||
#
|
||||
class TestCompensatoryLeaveRequest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
frappe.db.sql(''' delete from `tabCompensatory Leave Request`''')
|
||||
frappe.db.sql(''' delete from `tabLeave Ledger Entry`''')
|
||||
frappe.db.sql(''' delete from `tabLeave Allocation`''')
|
||||
frappe.db.sql(''' delete from `tabAttendance` where attendance_date in {0} '''.format((today(), add_days(today(), -1)))) #nosec
|
||||
create_leave_period(add_months(today(), -3), add_months(today(), 3), "_Test Company")
|
||||
create_holiday_list()
|
||||
|
||||
employee = get_employee()
|
||||
employee.holiday_list = "_Test Compensatory Leave"
|
||||
employee.save()
|
||||
|
||||
def test_leave_balance_on_submit(self):
|
||||
''' check creation of leave allocation on submission of compensatory leave request '''
|
||||
employee = get_employee()
|
||||
mark_attendance(employee)
|
||||
compensatory_leave_request = get_compensatory_leave_request(employee.name)
|
||||
|
||||
before = get_leave_balance_on(employee.name, compensatory_leave_request.leave_type, today())
|
||||
compensatory_leave_request.submit()
|
||||
|
||||
self.assertEqual(get_leave_balance_on(employee.name, compensatory_leave_request.leave_type, add_days(today(), 1)), before + 1)
|
||||
|
||||
def test_leave_allocation_update_on_submit(self):
|
||||
employee = get_employee()
|
||||
mark_attendance(employee, date=add_days(today(), -1))
|
||||
compensatory_leave_request = get_compensatory_leave_request(employee.name, leave_date=add_days(today(), -1))
|
||||
compensatory_leave_request.submit()
|
||||
|
||||
# leave allocation creation on submit
|
||||
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
|
||||
},
|
||||
{
|
||||
"description": "You can enter any date manually",
|
||||
"fieldname": "date_of_birth",
|
||||
"fieldtype": "Date",
|
||||
"label": "Date of Birth",
|
||||
|
@ -164,6 +164,12 @@ class Employee(NestedSet):
|
||||
if self.personal_email:
|
||||
validate_email_address(self.personal_email, True)
|
||||
|
||||
def set_preferred_email(self):
|
||||
preferred_email_field = frappe.scrub(self.prefered_contact_email)
|
||||
if preferred_email_field:
|
||||
preferred_email = self.get(preferred_email_field)
|
||||
self.prefered_email = preferred_email
|
||||
|
||||
def validate_status(self):
|
||||
if self.status == 'Left':
|
||||
reports_to = frappe.db.get_all('Employee',
|
||||
|
@ -34,7 +34,7 @@ frappe.ui.form.on('Employee Advance', {
|
||||
}
|
||||
else if (
|
||||
frm.doc.docstatus === 1
|
||||
&& flt(frm.doc.claimed_amount) < flt(frm.doc.paid_amount)
|
||||
&& flt(frm.doc.claimed_amount) < flt(frm.doc.paid_amount) - flt(frm.doc.return_amount)
|
||||
&& frappe.model.can_create("Expense Claim")
|
||||
) {
|
||||
frm.add_custom_button(
|
||||
@ -45,6 +45,15 @@ frappe.ui.form.on('Employee Advance', {
|
||||
__('Create')
|
||||
);
|
||||
}
|
||||
|
||||
if (frm.doc.docstatus === 1
|
||||
&& (flt(frm.doc.claimed_amount) < flt(frm.doc.paid_amount))
|
||||
&& frappe.model.can_create("Journal Entry")) {
|
||||
|
||||
frm.add_custom_button(__("Return"), function() {
|
||||
frm.trigger('make_return_entry');
|
||||
}, __('Create'));
|
||||
}
|
||||
},
|
||||
|
||||
make_payment_entry: function(frm) {
|
||||
@ -83,6 +92,24 @@ frappe.ui.form.on('Employee Advance', {
|
||||
});
|
||||
},
|
||||
|
||||
make_return_entry: function(frm) {
|
||||
frappe.call({
|
||||
method: 'erpnext.hr.doctype.employee_advance.employee_advance.make_return_entry',
|
||||
args: {
|
||||
'employee_name': frm.doc.employee,
|
||||
'company': frm.doc.company,
|
||||
'employee_advance_name': frm.doc.name,
|
||||
'return_amount': flt(frm.doc.paid_amount - frm.doc.claimed_amount),
|
||||
'mode_of_payment': frm.doc.mode_of_payment,
|
||||
'advance_account': frm.doc.advance_account
|
||||
},
|
||||
callback: function(r) {
|
||||
const doclist = frappe.model.sync(r.message);
|
||||
frappe.set_route('Form', doclist[0].doctype, doclist[0].name);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
employee: function (frm) {
|
||||
if (frm.doc.employee) {
|
||||
return frappe.call({
|
||||
|
@ -1,707 +1,193 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_events_in_timeline": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"actions": [],
|
||||
"allow_import": 1,
|
||||
"allow_rename": 0,
|
||||
"autoname": "naming_series:",
|
||||
"beta": 0,
|
||||
"creation": "2017-10-09 14:26:29.612365",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"naming_series",
|
||||
"employee",
|
||||
"employee_name",
|
||||
"column_break_4",
|
||||
"posting_date",
|
||||
"department",
|
||||
"section_break_8",
|
||||
"purpose",
|
||||
"column_break_11",
|
||||
"advance_amount",
|
||||
"paid_amount",
|
||||
"due_advance_amount",
|
||||
"claimed_amount",
|
||||
"return_amount",
|
||||
"section_break_7",
|
||||
"status",
|
||||
"company",
|
||||
"amended_from",
|
||||
"column_break_18",
|
||||
"advance_account",
|
||||
"mode_of_payment"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "",
|
||||
"fieldname": "naming_series",
|
||||
"fieldtype": "Select",
|
||||
"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": "Series",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "HR-EAD-.YYYY.-",
|
||||
"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
|
||||
"options": "HR-EAD-.YYYY.-"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "employee",
|
||||
"fieldtype": "Link",
|
||||
"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": "Employee",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Employee",
|
||||
"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
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_from": "employee.employee_name",
|
||||
"fieldname": "employee_name",
|
||||
"fieldtype": "Read Only",
|
||||
"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": "Employee Name",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "",
|
||||
"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
|
||||
"label": "Employee Name"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_4",
|
||||
"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
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "Today",
|
||||
"fieldname": "posting_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_standard_filter": 0,
|
||||
"label": "Posting Date",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "",
|
||||
"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
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_from": "employee.department",
|
||||
"fieldname": "department",
|
||||
"fieldtype": "Link",
|
||||
"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": "Department",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Department",
|
||||
"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
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "section_break_8",
|
||||
"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
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "purpose",
|
||||
"fieldtype": "Small Text",
|
||||
"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": "Purpose",
|
||||
"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
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_11",
|
||||
"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
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "advance_amount",
|
||||
"fieldtype": "Currency",
|
||||
"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": "Advance Amount",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Company:company:default_currency",
|
||||
"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
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "paid_amount",
|
||||
"fieldtype": "Currency",
|
||||
"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": "Paid Amount",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "Company:company:default_currency",
|
||||
"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
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:cur_frm.doc.employee",
|
||||
"fieldname": "due_advance_amount",
|
||||
"fieldtype": "Currency",
|
||||
"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": "Due Advance Amount",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Company:company:default_currency",
|
||||
"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
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "claimed_amount",
|
||||
"fieldtype": "Currency",
|
||||
"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": "Claimed Amount",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "Company:company:default_currency",
|
||||
"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
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "section_break_7",
|
||||
"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
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "status",
|
||||
"fieldtype": "Select",
|
||||
"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": "Status",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "Draft\nPaid\nUnpaid\nClaimed\nCancelled",
|
||||
"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
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"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": "Company",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Company",
|
||||
"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
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "amended_from",
|
||||
"fieldtype": "Link",
|
||||
"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": "Amended From",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "Employee Advance",
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"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
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_18",
|
||||
"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
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "advance_account",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 1,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Advance Account",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "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": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "mode_of_payment",
|
||||
"fieldtype": "Link",
|
||||
"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": "Mode of Payment",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Mode of Payment",
|
||||
"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
|
||||
"options": "Mode of Payment"
|
||||
},
|
||||
{
|
||||
"fieldname": "return_amount",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Returned Amount",
|
||||
"options": "Company:company:default_currency",
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 1,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2019-01-30 11:28:15.529649",
|
||||
"links": [],
|
||||
"modified": "2019-12-15 19:04:07.044505",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Employee Advance",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 0,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Employee",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
@ -711,27 +197,17 @@
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Expense Approver",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 0,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"search_fields": "employee,employee_name",
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
"track_changes": 1
|
||||
}
|
@ -7,6 +7,7 @@ import frappe, erpnext
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import flt, nowdate
|
||||
from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account
|
||||
|
||||
class EmployeeAdvanceOverPayment(frappe.ValidationError):
|
||||
pass
|
||||
@ -53,11 +54,25 @@ class EmployeeAdvance(Document):
|
||||
and party = %s
|
||||
""", (self.name, self.employee), as_dict=1)[0].paid_amount
|
||||
|
||||
return_amount = frappe.db.sql("""
|
||||
select name, ifnull(sum(credit_in_account_currency), 0) as return_amount
|
||||
from `tabGL Entry`
|
||||
where against_voucher_type = 'Employee Advance'
|
||||
and voucher_type != 'Expense Claim'
|
||||
and against_voucher = %s
|
||||
and party_type = 'Employee'
|
||||
and party = %s
|
||||
""", (self.name, self.employee), as_dict=1)[0].return_amount
|
||||
|
||||
if flt(paid_amount) > self.advance_amount:
|
||||
frappe.throw(_("Row {0}# Paid Amount cannot be greater than requested advance amount"),
|
||||
EmployeeAdvanceOverPayment)
|
||||
|
||||
if flt(return_amount) > self.paid_amount - self.claimed_amount:
|
||||
frappe.throw(_("Return amount cannot be greater unclaimed amount"))
|
||||
|
||||
self.db_set("paid_amount", paid_amount)
|
||||
self.db_set("return_amount", return_amount)
|
||||
self.set_status()
|
||||
frappe.db.set_value("Employee Advance", self.name , "status", self.status)
|
||||
|
||||
@ -88,8 +103,6 @@ def get_due_advance_amount(employee, posting_date):
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_bank_entry(dt, dn):
|
||||
from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account
|
||||
|
||||
doc = frappe.get_doc(dt, dn)
|
||||
payment_account = get_default_bank_cash_account(doc.company, account_type="Cash",
|
||||
mode_of_payment=doc.mode_of_payment)
|
||||
@ -118,3 +131,33 @@ def make_bank_entry(dt, dn):
|
||||
})
|
||||
|
||||
return je.as_dict()
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_return_entry(employee_name, company, employee_advance_name, return_amount, mode_of_payment, advance_account):
|
||||
return_account = get_default_bank_cash_account(company, account_type='Cash', mode_of_payment = mode_of_payment)
|
||||
je = frappe.new_doc('Journal Entry')
|
||||
je.posting_date = nowdate()
|
||||
je.voucher_type = 'Bank Entry'
|
||||
je.company = company
|
||||
je.remark = 'Return against Employee Advance: ' + employee_advance_name
|
||||
|
||||
je.append('accounts', {
|
||||
'account': advance_account,
|
||||
'credit_in_account_currency': return_amount,
|
||||
'reference_type': 'Employee Advance',
|
||||
'reference_name': employee_advance_name,
|
||||
'party_type': 'Employee',
|
||||
'party': employee_name,
|
||||
'is_advance': 'Yes'
|
||||
})
|
||||
|
||||
je.append("accounts", {
|
||||
"account": return_account.account,
|
||||
"debit_in_account_currency": return_amount,
|
||||
"account_currency": return_account.account_currency,
|
||||
"account_type": return_account.account_type
|
||||
})
|
||||
|
||||
return je.as_dict()
|
||||
|
||||
|
||||
|
@ -0,0 +1,19 @@
|
||||
from __future__ import unicode_literals
|
||||
from frappe import _
|
||||
|
||||
def get_data():
|
||||
return {
|
||||
'fieldname': 'employee_advance',
|
||||
'non_standard_fieldnames': {
|
||||
'Payment Entry': 'reference_name',
|
||||
'Journal Entry': 'reference_name'
|
||||
},
|
||||
'transactions': [
|
||||
{
|
||||
'items': ['Expense Claim']
|
||||
},
|
||||
{
|
||||
'items': ['Payment Entry', 'Journal Entry']
|
||||
}
|
||||
]
|
||||
}
|
@ -7,6 +7,14 @@ frappe.ui.form.on('Employee Onboarding', {
|
||||
frm.add_fetch("employee_onboarding_template", "department", "department");
|
||||
frm.add_fetch("employee_onboarding_template", "designation", "designation");
|
||||
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) {
|
||||
|
@ -42,12 +42,6 @@ cur_frm.cscript.onload = function(doc) {
|
||||
cur_frm.set_value("posting_date", frappe.datetime.get_today());
|
||||
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) {
|
||||
@ -119,7 +113,7 @@ cur_frm.cscript.calculate_total_amount = function(doc,cdt,cdn){
|
||||
};
|
||||
|
||||
erpnext.expense_claim = {
|
||||
set_title :function(frm) {
|
||||
set_title: function(frm) {
|
||||
if (!frm.doc.task) {
|
||||
frm.set_value("title", frm.doc.employee_name);
|
||||
}
|
||||
@ -131,20 +125,20 @@ erpnext.expense_claim = {
|
||||
|
||||
frappe.ui.form.on("Expense Claim", {
|
||||
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", "default_expense_claim_payable_account", "payable_account");
|
||||
frm.set_query("employee_advance", "advances", function(doc) {
|
||||
|
||||
frm.set_query("employee_advance", "advances", function() {
|
||||
return {
|
||||
filters: [
|
||||
['docstatus', '=', 1],
|
||||
['employee', '=', doc.employee],
|
||||
['employee', '=', frm.doc.employee],
|
||||
['paid_amount', '>', 0],
|
||||
['paid_amount', '>', 'claimed_amount']
|
||||
]
|
||||
};
|
||||
});
|
||||
|
||||
frm.set_query("expense_approver", function() {
|
||||
return {
|
||||
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 {
|
||||
filters: [
|
||||
['company', '=', doc.company],
|
||||
['company', '=', frm.doc.company],
|
||||
['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) {
|
||||
@ -214,11 +243,11 @@ frappe.ui.form.on("Expense Claim", {
|
||||
|
||||
update_employee_advance_claimed_amount: function(frm) {
|
||||
let amount_to_be_allocated = frm.doc.grand_total;
|
||||
$.each(frm.doc.advances || [], function(i, advance){
|
||||
if (amount_to_be_allocated >= advance.unclaimed_amount){
|
||||
$.each(frm.doc.advances || [], function(i, advance) {
|
||||
if (amount_to_be_allocated >= advance.unclaimed_amount) {
|
||||
frm.doc.advances[i].allocated_amount = frm.doc.advances[i].unclaimed_amount;
|
||||
amount_to_be_allocated -= advance.allocated_amount;
|
||||
} else{
|
||||
} else {
|
||||
frm.doc.advances[i].allocated_amount = amount_to_be_allocated;
|
||||
amount_to_be_allocated = 0;
|
||||
}
|
||||
@ -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) {
|
||||
frm.trigger("toggle_fields");
|
||||
},
|
||||
@ -329,6 +334,10 @@ frappe.ui.form.on("Expense Claim", {
|
||||
});
|
||||
|
||||
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) {
|
||||
var child = locals[cdt][cdn];
|
||||
var doc = frm.doc;
|
||||
@ -341,6 +350,9 @@ frappe.ui.form.on("Expense Claim Detail", {
|
||||
cur_frm.cscript.calculate_total(doc,cdt,cdn);
|
||||
frm.trigger("get_taxes");
|
||||
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");
|
||||
}
|
||||
});
|
||||
|
||||
@ -412,11 +424,3 @@ frappe.ui.form.on("Expense Taxes and Charges", {
|
||||
frm.trigger("calculate_total_tax", cdt, cdn);
|
||||
}
|
||||
});
|
||||
|
||||
cur_frm.fields_dict['task'].get_query = function(doc) {
|
||||
return {
|
||||
filters:{
|
||||
'project': doc.project
|
||||
}
|
||||
};
|
||||
};
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"actions": [],
|
||||
"allow_import": 1,
|
||||
"autoname": "naming_series:",
|
||||
"creation": "2013-01-10 16:34:14",
|
||||
@ -366,7 +367,8 @@
|
||||
"icon": "fa fa-money",
|
||||
"idx": 1,
|
||||
"is_submittable": 1,
|
||||
"modified": "2019-11-08 14:13:08.964547",
|
||||
"links": [],
|
||||
"modified": "2019-12-14 23:52:05.388458",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Expense Claim",
|
||||
|
@ -43,9 +43,9 @@ class ExpenseClaim(AccountsController):
|
||||
}[cstr(self.docstatus or 0)]
|
||||
|
||||
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
|
||||
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':
|
||||
self.status = "Paid"
|
||||
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_in_account_currency": data.sanctioned_amount,
|
||||
"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):
|
||||
if not self.cost_center:
|
||||
frappe.throw(_("Cost center is required to book an expense claim"))
|
||||
for data in self.expenses:
|
||||
if not data.cost_center:
|
||||
frappe.throw(_("Cost center is required to book an expense claim"))
|
||||
|
||||
if self.is_paid:
|
||||
if not self.mode_of_payment:
|
||||
@ -321,7 +322,7 @@ def get_expense_claim_account(expense_claim_type, company):
|
||||
@frappe.whitelist()
|
||||
def get_advances(employee, advance_id=None):
|
||||
if not advance_id:
|
||||
condition = 'docstatus=1 and employee={0} and paid_amount > 0 and paid_amount > claimed_amount'.format(frappe.db.escape(employee))
|
||||
condition = 'docstatus=1 and employee={0} and paid_amount > 0 and paid_amount > claimed_amount + return_amount'.format(frappe.db.escape(employee))
|
||||
else:
|
||||
condition = 'name={0}'.format(frappe.db.escape(advance_id))
|
||||
|
||||
|
@ -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):
|
||||
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 = {
|
||||
"doctype": "Expense Claim",
|
||||
"employee": employee,
|
||||
@ -134,12 +134,15 @@ def make_expense_claim(payable_account, amount, sanctioned_amount, company, acco
|
||||
"approval_status": "Approved",
|
||||
"company": company,
|
||||
'currency': currency,
|
||||
"expenses":
|
||||
[{"expense_type": "Travel",
|
||||
"expenses": [{
|
||||
"expense_type": "Travel",
|
||||
"default_account": account,
|
||||
'currency': currency,
|
||||
"currency": currency,
|
||||
"amount": amount,
|
||||
"sanctioned_amount": sanctioned_amount}]}
|
||||
"sanctioned_amount": sanctioned_amount,
|
||||
"cost_center": cost_center
|
||||
}]
|
||||
}
|
||||
if 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",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"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": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "expense_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_standard_filter": 0,
|
||||
"label": "Expense Date",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "expense_date",
|
||||
"oldfieldtype": "Date",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 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
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "expense_type",
|
||||
"fieldtype": "Link",
|
||||
"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": "Link",
|
||||
"options": "Expense Claim Type",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"print_width": "150px",
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0,
|
||||
"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",
|
||||
"fieldname": "default_account",
|
||||
"fieldtype": "Link",
|
||||
"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",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Account",
|
||||
"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
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "section_break_4",
|
||||
"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
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_from": "",
|
||||
"fieldname": "description",
|
||||
"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_standard_filter": 0,
|
||||
"label": "Description",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "description",
|
||||
"oldfieldtype": "Small Text",
|
||||
"options": "",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "section_break_6",
|
||||
"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
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "amount",
|
||||
"fieldtype": "Currency",
|
||||
"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": "Amount",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "claim_amount",
|
||||
"oldfieldtype": "Currency",
|
||||
"options": "Company:company:default_currency",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"print_width": "150px",
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0,
|
||||
"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",
|
||||
"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
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "sanctioned_amount",
|
||||
"fieldtype": "Currency",
|
||||
"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": "Sanctioned Amount",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "sanctioned_amount",
|
||||
"oldfieldtype": "Currency",
|
||||
"options": "Company:company:default_currency",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"fieldname": "cost_center",
|
||||
"fieldtype": "Link",
|
||||
"label": "Cost Center",
|
||||
"options": "Cost Center"
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 1,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"modified": "2019-06-10 08:41:36.122565",
|
||||
"modified_by": "Administrator",
|
||||
"modified": "2019-11-22 11:57:25.110942",
|
||||
"modified_by": "jangeles@bai.ph",
|
||||
"module": "HR",
|
||||
"name": "Expense Claim Detail",
|
||||
"owner": "harshada@webnotestech.com",
|
||||
"permissions": [],
|
||||
"quick_entry": 0,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 0,
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
"sort_order": "DESC"
|
||||
}
|
@ -2,13 +2,13 @@
|
||||
// License: GNU General Public License v3. See license.txt
|
||||
|
||||
frappe.ui.form.on("Expense Claim Type", {
|
||||
refresh: function(frm){
|
||||
frm.fields_dict["accounts"].grid.get_field("default_account").get_query = function(frm, cdt, cdn){
|
||||
refresh: function(frm) {
|
||||
frm.fields_dict["accounts"].grid.get_field("default_account").get_query = function(doc, cdt, cdn) {
|
||||
var d = locals[cdt][cdn];
|
||||
return{
|
||||
return {
|
||||
filters: {
|
||||
"is_group": 0,
|
||||
"root_type": "Expense",
|
||||
"root_type": frm.doc.deferred_expense_account ? "Asset" : "Expense",
|
||||
'company': d.company
|
||||
}
|
||||
}
|
||||
|
@ -1,181 +1,72 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 1,
|
||||
"allow_rename": 1,
|
||||
"autoname": "field:expense_type",
|
||||
"beta": 0,
|
||||
"creation": "2012-03-27 14:35:55",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "Setup",
|
||||
"editable_grid": 0,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"deferred_expense_account",
|
||||
"expense_type",
|
||||
"description",
|
||||
"accounts"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "expense_type",
|
||||
"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
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "accounts",
|
||||
"fieldtype": "Table",
|
||||
"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": "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
|
||||
"options": "Expense Claim Account"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "deferred_expense_account",
|
||||
"fieldtype": "Check",
|
||||
"label": "Deferred Expense Account"
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"icon": "fa fa-flag",
|
||||
"idx": 1,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-09-18 14:13:43.770829",
|
||||
"modified_by": "Administrator",
|
||||
"modified": "2019-11-22 12:00:18.710408",
|
||||
"modified_by": "jangeles@bai.ph",
|
||||
"module": "HR",
|
||||
"name": "Expense Claim Type",
|
||||
"owner": "harshada@webnotestech.com",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 0,
|
||||
"email": 1,
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "HR Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"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
|
||||
"role": "Employee"
|
||||
}
|
||||
],
|
||||
"quick_entry": 0,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_order": "ASC",
|
||||
"track_changes": 0,
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
"sort_field": "modified",
|
||||
"sort_order": "ASC"
|
||||
}
|
@ -69,10 +69,14 @@ class LeaveAllocation(Document):
|
||||
|
||||
def validate_allocation_overlap(self):
|
||||
leave_allocation = frappe.db.sql("""
|
||||
select name from `tabLeave Allocation`
|
||||
where employee=%s and leave_type=%s and docstatus=1
|
||||
and to_date >= %s and from_date <= %s""",
|
||||
(self.employee, self.leave_type, self.from_date, self.to_date))
|
||||
SELECT
|
||||
name
|
||||
FROM `tabLeave Allocation`
|
||||
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:
|
||||
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(
|
||||
frappe.render_template('leave_application_dashboard', {
|
||||
data: leave_details
|
||||
@ -170,7 +171,7 @@ frappe.ui.form.on("Leave Application", {
|
||||
frm.set_value('to_date', '');
|
||||
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({
|
||||
method: 'erpnext.hr.doctype.leave_application.leave_application.get_number_of_leave_days',
|
||||
args: {
|
||||
@ -193,7 +194,7 @@ frappe.ui.form.on("Leave Application", {
|
||||
|
||||
set_leave_approver: function(frm) {
|
||||
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({
|
||||
method: 'erpnext.hr.doctype.leave_application.leave_application.get_leave_approver',
|
||||
args: {
|
||||
|
@ -54,9 +54,11 @@ class LeaveApplication(Document):
|
||||
self.create_leave_ledger_entry()
|
||||
self.reload()
|
||||
|
||||
def before_cancel(self):
|
||||
self.status = "Cancelled"
|
||||
|
||||
def on_cancel(self):
|
||||
self.create_leave_ledger_entry(submit=False)
|
||||
self.status = "Cancelled"
|
||||
# notify leave applier about cancellation
|
||||
self.notify_employee()
|
||||
self.cancel_attendance()
|
||||
@ -351,7 +353,7 @@ class LeaveApplication(Document):
|
||||
pass
|
||||
|
||||
def create_leave_ledger_entry(self, submit=True):
|
||||
if self.status != 'Approved':
|
||||
if self.status != 'Approved' and submit:
|
||||
return
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
else:
|
||||
elif leave_entry.transaction_type == 'Leave Application':
|
||||
if leave_entry.from_date < getdate(from_date):
|
||||
leave_entry.from_date = from_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):
|
||||
''' Returns leave entries between from_date and to_date '''
|
||||
return frappe.db.sql("""
|
||||
select employee, leave_type, from_date, to_date, leaves, transaction_type, is_carry_forward, transaction_name
|
||||
from `tabLeave Ledger Entry`
|
||||
where employee=%(employee)s and leave_type=%(leave_type)s
|
||||
and docstatus=1
|
||||
and leaves<0
|
||||
and (from_date between %(from_date)s and %(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))
|
||||
SELECT
|
||||
employee, leave_type, from_date, to_date, leaves, transaction_name, transaction_type,
|
||||
is_carry_forward, is_expired
|
||||
FROM `tabLeave Ledger Entry`
|
||||
WHERE employee=%(employee)s AND leave_type=%(leave_type)s
|
||||
AND docstatus=1 AND leaves<0
|
||||
AND (from_date between %(from_date)s AND %(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,
|
||||
"to_date": to_date,
|
||||
|
@ -301,7 +301,7 @@ class TestLeaveApplication(unittest.TestCase):
|
||||
to_date = add_days(date, 2),
|
||||
company = "_Test Company",
|
||||
docstatus = 1,
|
||||
status = "Approved"
|
||||
status = "Approved"
|
||||
))
|
||||
leave_application.submit()
|
||||
|
||||
@ -314,7 +314,7 @@ class TestLeaveApplication(unittest.TestCase):
|
||||
to_date = add_days(date, 8),
|
||||
company = "_Test Company",
|
||||
docstatus = 1,
|
||||
status = "Approved"
|
||||
status = "Approved"
|
||||
))
|
||||
self.assertRaises(frappe.ValidationError, leave_application.insert)
|
||||
|
||||
|
@ -43,10 +43,18 @@ class TestLeavePeriod(unittest.TestCase):
|
||||
leave_period.grant_leave_allocation(employee=employee_doc_name)
|
||||
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({
|
||||
"doctype": "Leave Period",
|
||||
"company": erpnext.get_default_company(),
|
||||
"company": company or erpnext.get_default_company(),
|
||||
"from_date": from_date,
|
||||
"to_date": to_date,
|
||||
"is_active": 1
|
||||
|
@ -163,7 +163,7 @@ class PayrollEntry(Document):
|
||||
"""
|
||||
cond = self.get_filter_condition()
|
||||
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
|
||||
`tabSalary Slip` t1, `tabSalary Slip Loan` eld
|
||||
where
|
||||
@ -246,6 +246,7 @@ class PayrollEntry(Document):
|
||||
accounts.append({
|
||||
"account": acc,
|
||||
"debit_in_account_currency": flt(amount, precision),
|
||||
"party_type": '',
|
||||
"cost_center": self.cost_center,
|
||||
"project": self.project
|
||||
})
|
||||
@ -257,6 +258,7 @@ class PayrollEntry(Document):
|
||||
"account": acc,
|
||||
"credit_in_account_currency": flt(amount, precision),
|
||||
"cost_center": self.cost_center,
|
||||
"party_type": '',
|
||||
"project": self.project
|
||||
})
|
||||
|
||||
@ -264,7 +266,9 @@ class PayrollEntry(Document):
|
||||
for data in loan_details:
|
||||
accounts.append({
|
||||
"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:
|
||||
@ -275,14 +279,17 @@ class PayrollEntry(Document):
|
||||
"account": data.interest_income_account,
|
||||
"credit_in_account_currency": data.interest_amount,
|
||||
"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
|
||||
accounts.append({
|
||||
"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)
|
||||
@ -546,7 +553,6 @@ def submit_salary_slips_for_employees(payroll_entry, salary_slips, publish_progr
|
||||
count += 1
|
||||
if publish_progress:
|
||||
frappe.publish_progress(count*100/len(salary_slips), title = _("Submitting Salary Slips..."))
|
||||
|
||||
if submitted_ss:
|
||||
payroll_entry.make_accrual_jv_entry()
|
||||
frappe.msgprint(_("Salary Slip submitted for period from {0} to {1}")
|
||||
|
@ -119,47 +119,52 @@ frappe.ui.form.on('Salary Structure', {
|
||||
},
|
||||
callback: function(r) {
|
||||
var employees = r.message;
|
||||
var d = new frappe.ui.Dialog({
|
||||
title: __("Preview Salary Slip"),
|
||||
fields: [
|
||||
{
|
||||
"label":__("Employee"),
|
||||
"fieldname":"employee",
|
||||
"fieldtype":"Select",
|
||||
"reqd": true,
|
||||
options: employees
|
||||
}, {
|
||||
fieldname:"fetch",
|
||||
"label":__("Show Salary Slip"),
|
||||
"fieldtype":"Button"
|
||||
}
|
||||
]
|
||||
});
|
||||
d.get_input("fetch").on("click", function() {
|
||||
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);
|
||||
}
|
||||
if(!employees) return;
|
||||
if (employees.length == 1){
|
||||
frm.events.open_salary_slip(frm, employees[0]);
|
||||
} else {
|
||||
var d = new frappe.ui.Dialog({
|
||||
title: __("Preview Salary Slip"),
|
||||
fields: [
|
||||
{
|
||||
"label":__("Employee"),
|
||||
"fieldname":"employee",
|
||||
"fieldtype":"Select",
|
||||
"reqd": true,
|
||||
options: employees
|
||||
}, {
|
||||
fieldname:"fetch",
|
||||
"label":__("Show Salary Slip"),
|
||||
"fieldtype":"Button"
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
d.show();
|
||||
d.get_input("fetch").on("click", function() {
|
||||
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>
|
||||
|
||||
<h4>{{_("Details")}}</h4>
|
||||
{{_("Event Name")}}: {{ frappe.utils.get_link_to_form(doc.doctype, doc.name) }}
|
||||
<br>{{_("Event Location")}}: {{ doc.location }}
|
||||
<br>{{_("Start Time")}}: {{ doc.start_time }}
|
||||
<br>{{_("End Time")}}: {{ doc.end_time }}
|
||||
<table class="panel-body" border="0" cellpadding="0" cellspacing="0" width="100%">
|
||||
<tr height="10"></tr>
|
||||
<tr>
|
||||
<td width="15"></td>
|
||||
<td>
|
||||
<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>
|
@ -1,5 +1,7 @@
|
||||
{
|
||||
"attach_print": 0,
|
||||
"channel": "Email",
|
||||
"condition": "",
|
||||
"creation": "2017-08-11 03:13:40.519614",
|
||||
"days_in_advance": 0,
|
||||
"docstatus": 0,
|
||||
@ -9,8 +11,8 @@
|
||||
"event": "Submit",
|
||||
"idx": 0,
|
||||
"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",
|
||||
"modified": "2017-08-13 22:49:42.338881",
|
||||
"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": "2019-11-29 15:38:31.805409",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Training Scheduled",
|
||||
|
@ -1,9 +1,44 @@
|
||||
<h3>{{_("Training Event")}}</h3>
|
||||
<p>{{ message }}</p>
|
||||
<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>
|
||||
|
||||
<h4>{{_("Details")}}</h4>
|
||||
{{_("Event Name")}}: <a href="{{ event_link }}">{{ name }}</a>
|
||||
<br>{{_("Event Location")}}: {{ location }}
|
||||
<br>{{_("Start Time")}}: {{ start_time }}
|
||||
<br>{{_("End Time")}}: {{ end_time }}
|
||||
<br>{{_("Attendance")}}: {{ attendance }}
|
||||
<table class="panel-body" border="0" cellpadding="0" cellspacing="0" width="100%">
|
||||
<tr height="10"></tr>
|
||||
<tr>
|
||||
<td width="15"></td>
|
||||
<td>
|
||||
<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:
|
||||
continue
|
||||
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):
|
||||
''' Create leave ledger entry based on the earned leave frequency '''
|
||||
allocation.new_leaves_allocated = earned_leaves
|
||||
def create_additional_leave_ledger_entry(allocation, leaves, date):
|
||||
''' Create leave ledger entry for leave types '''
|
||||
allocation.new_leaves_allocated = leaves
|
||||
allocation.from_date = date
|
||||
allocation.unused_leaves = 0
|
||||
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):
|
||||
holiday_list = get_holiday_list_for_employee(employee)
|
||||
|
||||
holidays = frappe.db.sql_list('''select holiday_date from `tabHoliday`
|
||||
where
|
||||
parent=%(holiday_list)s
|
||||
|
@ -115,6 +115,16 @@ def get_valid_items(search_value=''):
|
||||
|
||||
return valid_items
|
||||
|
||||
@frappe.whitelist()
|
||||
def update_item(ref_doc, data):
|
||||
data = json.loads(data)
|
||||
|
||||
data.update(dict(doctype='Hub Item', name=ref_doc))
|
||||
try:
|
||||
connection = get_hub_connection()
|
||||
connection.update(data)
|
||||
except Exception as e:
|
||||
frappe.log_error(message=e, title='Hub Sync Error')
|
||||
|
||||
@frappe.whitelist()
|
||||
def publish_selected_items(items_to_publish):
|
||||
|
@ -6,7 +6,6 @@ frappe.provide("erpnext.bom");
|
||||
frappe.ui.form.on("BOM", {
|
||||
setup: function(frm) {
|
||||
frm.custom_make_buttons = {
|
||||
'BOM': 'Duplicate BOM',
|
||||
'Work Order': 'Work Order',
|
||||
'Quality Inspection': 'Quality Inspection'
|
||||
};
|
||||
@ -91,10 +90,6 @@ frappe.ui.form.on("BOM", {
|
||||
}
|
||||
|
||||
if(frm.doc.docstatus!=0) {
|
||||
frm.add_custom_button(__("Duplicate BOM"), function() {
|
||||
frm.copy_doc();
|
||||
}, __("Create"));
|
||||
|
||||
frm.add_custom_button(__("Work Order"), function() {
|
||||
frm.trigger("make_work_order");
|
||||
}, __("Create"));
|
||||
|
@ -65,6 +65,7 @@ class BOM(WebsiteGenerator):
|
||||
context.parents = [{'name': 'boms', 'title': _('All BOMs') }]
|
||||
|
||||
def on_update(self):
|
||||
frappe.cache().hdel('bom_children', self.name)
|
||||
self.check_recursion()
|
||||
self.update_stock_qty()
|
||||
self.update_exploded_items()
|
||||
@ -606,6 +607,7 @@ def get_bom_items_as_dict(bom, company, qty=1, fetch_exploded=1, fetch_scrap_ite
|
||||
item.image,
|
||||
bom.project,
|
||||
item.stock_uom,
|
||||
item.item_group,
|
||||
item.allow_alternative_item,
|
||||
item_default.default_warehouse,
|
||||
item_default.expense_account as expense_account,
|
||||
|
@ -25,5 +25,5 @@ def get_data():
|
||||
}
|
||||
],
|
||||
'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', {
|
||||
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.for_quantity != frm.doc.transferred_qty) {
|
||||
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) {
|
||||
frm.add_custom_button(__("Material Transfer"), () => {
|
||||
frm.trigger("make_stock_entry");
|
||||
});
|
||||
}).addClass("btn-primary");
|
||||
}
|
||||
}
|
||||
|
||||
if (frm.doc.docstatus == 0) {
|
||||
frm.trigger("make_dashboard");
|
||||
if (frm.doc.docstatus == 0 && frm.doc.for_quantity > frm.doc.total_completed_qty
|
||||
&& (!frm.doc.items.length || frm.doc.for_quantity == frm.doc.transferred_qty)) {
|
||||
frm.trigger("prepare_timer_buttons");
|
||||
}
|
||||
},
|
||||
|
||||
if (!frm.doc.job_started) {
|
||||
frm.add_custom_button(__("Start Job"), () => {
|
||||
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.save();
|
||||
});
|
||||
} else {
|
||||
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();
|
||||
prepare_timer_buttons: function(frm) {
|
||||
frm.trigger("make_dashboard");
|
||||
if (!frm.doc.job_started) {
|
||||
frm.add_custom_button(__("Start"), () => {
|
||||
if (!frm.doc.employee) {
|
||||
frappe.prompt({fieldtype: 'Link', label: __('Employee'), options: "Employee",
|
||||
fieldname: 'employee'}, d => {
|
||||
if (d.employee) {
|
||||
frm.set_value("employee", d.employee);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
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) {
|
||||
if(frm.doc.__islocal)
|
||||
return;
|
||||
|
||||
frm.dashboard.refresh();
|
||||
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="colon">:</span>
|
||||
<span class="minutes">00</span>
|
||||
@ -58,11 +116,16 @@ frappe.ui.form.on('Job Card', {
|
||||
<span class="seconds">00</span>
|
||||
</div>`;
|
||||
|
||||
var section = frm.dashboard.add_section(timer);
|
||||
var section = frm.toolbar.page.add_inner_message(timer);
|
||||
|
||||
if (frm.doc.started_time) {
|
||||
let currentIncrement = moment(frappe.datetime.now_datetime()).diff(moment(frm.doc.started_time),"seconds");
|
||||
initialiseTimer();
|
||||
let currentIncrement = frm.doc.current_time || 0;
|
||||
if (frm.doc.started_time || frm.doc.current_time) {
|
||||
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() {
|
||||
const interval = setInterval(function() {
|
||||
@ -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) {
|
||||
frm.doc.items = [];
|
||||
frm.call({
|
||||
@ -117,5 +184,22 @@ frappe.ui.form.on('Job Card', {
|
||||
|
||||
timer: function(frm) {
|
||||
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",
|
||||
"posting_date",
|
||||
"company",
|
||||
"remarks",
|
||||
"production_section",
|
||||
"production_item",
|
||||
"item_name",
|
||||
"for_quantity",
|
||||
"wip_warehouse",
|
||||
"timing_detail",
|
||||
"column_break_12",
|
||||
"employee",
|
||||
"employee_name",
|
||||
"status",
|
||||
"project",
|
||||
"timing_detail",
|
||||
"time_logs",
|
||||
"section_break_13",
|
||||
"total_completed_qty",
|
||||
@ -28,12 +36,11 @@
|
||||
"operation_id",
|
||||
"transferred_qty",
|
||||
"requested_qty",
|
||||
"project",
|
||||
"remarks",
|
||||
"column_break_20",
|
||||
"status",
|
||||
"barcode",
|
||||
"job_started",
|
||||
"started_time",
|
||||
"current_time",
|
||||
"amended_from"
|
||||
],
|
||||
"fields": [
|
||||
@ -41,13 +48,14 @@
|
||||
"fieldname": "work_order",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 1,
|
||||
"label": "Work Order",
|
||||
"options": "Work Order",
|
||||
"read_only": 1,
|
||||
"reqd": 1,
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"fetch_from": "work_order.bom_no",
|
||||
"fieldname": "bom_no",
|
||||
"fieldtype": "Link",
|
||||
"label": "BOM No",
|
||||
@ -91,7 +99,7 @@
|
||||
"fieldname": "for_quantity",
|
||||
"fieldtype": "Float",
|
||||
"in_list_view": 1,
|
||||
"label": "For Quantity",
|
||||
"label": "Qty To Manufacture",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
@ -109,6 +117,7 @@
|
||||
{
|
||||
"fieldname": "employee",
|
||||
"fieldtype": "Link",
|
||||
"in_standard_filter": 1,
|
||||
"label": "Employee",
|
||||
"options": "Employee"
|
||||
},
|
||||
@ -198,7 +207,7 @@
|
||||
"fieldtype": "Select",
|
||||
"label": "Status",
|
||||
"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
|
||||
},
|
||||
{
|
||||
@ -236,10 +245,52 @@
|
||||
"label": "Naming Series",
|
||||
"options": "PO-JOB.#####",
|
||||
"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,
|
||||
"modified": "2019-10-30 01:49:19.606178",
|
||||
"modified": "2019-12-03 13:08:57.926201",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "Job Card",
|
||||
|
@ -194,8 +194,9 @@ class JobCard(Document):
|
||||
if self.total_completed_qty <= 0.0:
|
||||
frappe.throw(_("Total completed qty must be greater than zero"))
|
||||
|
||||
if self.total_completed_qty > self.for_quantity:
|
||||
frappe.throw(_("Total completed qty can not be greater than for quantity"))
|
||||
if self.total_completed_qty != self.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):
|
||||
if not self.work_order:
|
||||
@ -271,6 +272,8 @@ class JobCard(Document):
|
||||
self.set_status(update_status)
|
||||
|
||||
def set_status(self, update_status=False):
|
||||
if self.status == "On Hold": return
|
||||
|
||||
self.status = {
|
||||
0: "Open",
|
||||
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.calculate_rate_and_amount()
|
||||
target.set_missing_values()
|
||||
target.set_stock_entry_type()
|
||||
|
||||
doclist = get_mapped_doc("Job Card", source_name, {
|
||||
"Job Card": {
|
||||
@ -353,3 +357,45 @@ def make_stock_entry(source_name, target_doc=None):
|
||||
|
||||
def time_diff_in_minutes(string_ed_date, string_st_date):
|
||||
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
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,
|
||||
"allow_events_in_timeline": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"beta": 0,
|
||||
"creation": "2019-03-08 23:56:43.187569",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"from_time",
|
||||
"to_time",
|
||||
"column_break_2",
|
||||
"time_in_mins",
|
||||
"completed_qty"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"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
|
||||
"label": "From Time"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"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
|
||||
"label": "To Time"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"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
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"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
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"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
|
||||
"reqd": 1
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"modified": "2019-03-10 17:08:46.504910",
|
||||
"modified": "2019-12-03 12:56:02.285448",
|
||||
"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
|
||||
"track_changes": 1
|
||||
}
|
@ -71,12 +71,13 @@ frappe.ui.form.on('Production Plan', {
|
||||
}, __('Create'));
|
||||
}
|
||||
|
||||
frm.page.set_inner_btn_group_as_primary(__('Create'));
|
||||
frm.trigger("material_requirement");
|
||||
|
||||
const projected_qty_formula = ` <table class="table table-bordered" style="background-color: #f9f9f9;">
|
||||
<tr><td style="padding-left:25px">
|
||||
<div>
|
||||
<h3>
|
||||
<h3 style="text-decoration: underline;">
|
||||
<a href = "https://erpnext.com/docs/user/manual/en/stock/projected-quantity">
|
||||
${__("Projected Quantity Formula")}
|
||||
</a>
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -615,6 +615,9 @@ def get_items_for_material_requests(doc, ignore_existing_ordered_qty=None):
|
||||
|
||||
doc['mr_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')
|
||||
warehouse = doc.get('for_warehouse')
|
||||
|
||||
|
@ -6,7 +6,7 @@ def get_data():
|
||||
'fieldname': 'production_plan',
|
||||
'transactions': [
|
||||
{
|
||||
'label': _('Related'),
|
||||
'label': _('Transactions'),
|
||||
'items': ['Work Order', 'Material Request']
|
||||
},
|
||||
]
|
||||
|
@ -6,6 +6,7 @@ frappe.ui.form.on("Work Order", {
|
||||
frm.custom_make_buttons = {
|
||||
'Stock Entry': 'Start',
|
||||
'Pick List': 'Create Pick List',
|
||||
'Job Card': 'Create Job Card'
|
||||
};
|
||||
|
||||
// Set query for warehouses
|
||||
@ -131,7 +132,8 @@ frappe.ui.form.on("Work Order", {
|
||||
}
|
||||
|
||||
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
|
||||
@ -179,89 +181,72 @@ frappe.ui.form.on("Work Order", {
|
||||
|
||||
make_job_card: function(frm) {
|
||||
let qty = 0;
|
||||
const fields = [{
|
||||
fieldtype: "Link",
|
||||
fieldname: "operation",
|
||||
options: "Operation",
|
||||
label: __("Operation"),
|
||||
get_query: () => {
|
||||
const filter_workstation = frm.doc.operations.filter(d => {
|
||||
if (d.status != "Completed") {
|
||||
return d;
|
||||
}
|
||||
});
|
||||
let operations_data = [];
|
||||
|
||||
return {
|
||||
filters: {
|
||||
name: ["in", (filter_workstation || []).map(d => d.operation)]
|
||||
}
|
||||
};
|
||||
},
|
||||
reqd: true
|
||||
}, {
|
||||
fieldtype: "Link",
|
||||
fieldname: "workstation",
|
||||
options: "Workstation",
|
||||
label: __("Workstation"),
|
||||
get_query: () => {
|
||||
const operation = dialog.get_value("operation");
|
||||
const filter_workstation = frm.doc.operations.filter(d => {
|
||||
if (d.operation == operation) {
|
||||
return d;
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
filters: {
|
||||
name: ["in", (filter_workstation || []).map(d => d.workstation)]
|
||||
}
|
||||
};
|
||||
},
|
||||
onchange: () => {
|
||||
const operation = dialog.get_value("operation");
|
||||
const workstation = dialog.get_value("workstation");
|
||||
if (operation && workstation) {
|
||||
const row = frm.doc.operations.filter(d => d.operation == operation && d.workstation == workstation)[0];
|
||||
qty = frm.doc.qty - row.completed_qty;
|
||||
|
||||
if (qty > 0) {
|
||||
dialog.set_value("qty", qty);
|
||||
}
|
||||
}
|
||||
},
|
||||
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]));
|
||||
const dialog = frappe.prompt({fieldname: 'operations', fieldtype: 'Table', label: __('Operations'),
|
||||
fields: [
|
||||
{
|
||||
fieldtype:'Link',
|
||||
fieldname:'operation',
|
||||
label: __('Operation'),
|
||||
read_only:1,
|
||||
in_list_view:1
|
||||
},
|
||||
{
|
||||
fieldtype:'Link',
|
||||
fieldname:'workstation',
|
||||
label: __('Workstation'),
|
||||
read_only:1,
|
||||
in_list_view:1
|
||||
},
|
||||
{
|
||||
fieldtype:'Data',
|
||||
fieldname:'name',
|
||||
label: __('Operation Id')
|
||||
},
|
||||
{
|
||||
fieldtype:'Float',
|
||||
fieldname:'pending_qty',
|
||||
label: __('Pending Qty'),
|
||||
},
|
||||
{
|
||||
fieldtype:'Float',
|
||||
fieldname:'qty',
|
||||
label: __('Quantity to Manufacture'),
|
||||
read_only:0,
|
||||
in_list_view:1,
|
||||
},
|
||||
],
|
||||
data: operations_data,
|
||||
in_place_edit: true,
|
||||
get_data: function() {
|
||||
return operations_data;
|
||||
}
|
||||
|
||||
if (data.qty <= 0) {
|
||||
frappe.throw(__("For Quantity must be greater than zero"));
|
||||
}
|
||||
|
||||
}, function(data) {
|
||||
frappe.call({
|
||||
method: "erpnext.manufacturing.doctype.work_order.work_order.make_job_card",
|
||||
args: {
|
||||
work_order: frm.doc.name,
|
||||
operation: data.operation,
|
||||
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);
|
||||
}
|
||||
operations: data.operations,
|
||||
}
|
||||
});
|
||||
}, __("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) {
|
||||
@ -277,7 +262,7 @@ frappe.ui.form.on("Work Order", {
|
||||
});
|
||||
},
|
||||
|
||||
show_progress: function(frm) {
|
||||
show_progress_for_items: function(frm) {
|
||||
var bars = [];
|
||||
var message = '';
|
||||
var added_min = false;
|
||||
@ -311,6 +296,44 @@ frappe.ui.form.on("Work Order", {
|
||||
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) {
|
||||
if (frm.doc.production_item) {
|
||||
frappe.call({
|
||||
@ -582,6 +605,8 @@ erpnext.work_order = {
|
||||
description: __('Max: {0}', [max]),
|
||||
default: max
|
||||
}, data => {
|
||||
max += (max * (frm.doc.__onload.overproduction_percentage || 0.0)) / 100;
|
||||
|
||||
if (data.qty > max) {
|
||||
frappe.msgprint(__('Quantity must not be more than {0}', [max]));
|
||||
reject();
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"actions": [],
|
||||
"allow_import": 1,
|
||||
"autoname": "naming_series:",
|
||||
"creation": "2013-01-10 16:34:16",
|
||||
@ -468,7 +469,8 @@
|
||||
"idx": 1,
|
||||
"image_field": "image",
|
||||
"is_submittable": 1,
|
||||
"modified": "2019-08-28 12:29:35.315239",
|
||||
"links": [],
|
||||
"modified": "2019-12-04 11:20:04.695123",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "Work Order",
|
||||
|
@ -6,7 +6,7 @@ import frappe
|
||||
import json
|
||||
import math
|
||||
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 erpnext.manufacturing.doctype.bom.bom import validate_bom_no, get_bom_items_as_dict
|
||||
from dateutil.relativedelta import relativedelta
|
||||
@ -38,7 +38,7 @@ class WorkOrder(Document):
|
||||
ms = frappe.get_doc("Manufacturing Settings")
|
||||
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("overproduction_percentage", ms.overproduction_percentage_for_work_order)
|
||||
|
||||
def validate(self):
|
||||
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.production_item = item
|
||||
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()
|
||||
|
||||
return wo_doc
|
||||
@ -755,21 +756,41 @@ def query_sales_order(production_item):
|
||||
return out
|
||||
|
||||
@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)
|
||||
row = get_work_order_operation_data(work_order, operation, workstation)
|
||||
if row:
|
||||
return create_job_card(work_order, row, qty)
|
||||
for row in operations:
|
||||
validate_operation_data(row)
|
||||
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):
|
||||
doc = frappe.new_doc("Job Card")
|
||||
doc.update({
|
||||
'work_order': work_order.name,
|
||||
'operation': row.operation,
|
||||
'workstation': row.workstation,
|
||||
'operation': row.get("operation"),
|
||||
'workstation': row.get("workstation"),
|
||||
'posting_date': nowdate(),
|
||||
'for_quantity': qty or work_order.get('qty', 0),
|
||||
'operation_id': row.name,
|
||||
'operation_id': row.get("name"),
|
||||
'bom_no': work_order.bom_no,
|
||||
'project': work_order.project,
|
||||
'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.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
|
||||
|
||||
|
@ -2,11 +2,13 @@
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.views.calendar["Work Order"] = {
|
||||
fields: ["planned_start_date", "planned_end_date", "status", "produced_qty", "qty", "name", "name"],
|
||||
field_map: {
|
||||
"start": "planned_start_date",
|
||||
"end": "planned_end_date",
|
||||
"id": "name",
|
||||
"title": "name",
|
||||
"status": "status",
|
||||
"allDay": "allDay",
|
||||
"progress": function(data) {
|
||||
return flt(data.produced_qty) / data.qty * 100;
|
||||
|
@ -6,7 +6,8 @@ def get_data():
|
||||
'fieldname': 'work_order',
|
||||
'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",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
@ -68,6 +69,7 @@
|
||||
"description": "Operation completed for how many finished goods?",
|
||||
"fieldname": "completed_qty",
|
||||
"fieldtype": "Float",
|
||||
"in_list_view": 1,
|
||||
"label": "Completed Qty",
|
||||
"no_copy": 1,
|
||||
"read_only": 1
|
||||
@ -188,8 +190,9 @@
|
||||
}
|
||||
],
|
||||
"istable": 1,
|
||||
"modified": "2019-07-16 23:01:07.720337",
|
||||
"modified_by": "govindsmenokee@gmail.com",
|
||||
"links": [],
|
||||
"modified": "2019-12-03 19:24:29.594189",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "Work Order Operation",
|
||||
"owner": "Administrator",
|
||||
|
@ -13,5 +13,7 @@ def get_data():
|
||||
'label': _('Transaction'),
|
||||
'items': ['Work Order', 'Job Card', 'Timesheet']
|
||||
}
|
||||
]
|
||||
],
|
||||
'disable_create_buttons': ['BOM', 'Routing', 'Operation',
|
||||
'Work Order', 'Job Card', 'Timesheet']
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user