Merge branch 'develop' into gle-opening-stock-reco-dev
This commit is contained in:
commit
9764eff1a4
@ -12,7 +12,7 @@
|
||||
"Accrued Rebates Due from Suppliers": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"Accured Income from Suppliers": {
|
||||
"Accrued Income from Suppliers": {
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"Other Debtors": {
|
||||
@ -54,7 +54,7 @@
|
||||
}
|
||||
},
|
||||
"Petty Cash": {
|
||||
"Petty Cash - Admininistration": {
|
||||
"Petty Cash - Administration": {
|
||||
"account_type": "Cash"
|
||||
},
|
||||
"Petty Cash - Others": {
|
||||
@ -85,13 +85,13 @@
|
||||
"Handling Difference in Inventory": {
|
||||
"account_type": "Stock Adjustment"
|
||||
},
|
||||
"Items Delivered to Customs on temprary Base": {}
|
||||
"Items Delivered to Customs on temporary Base": {}
|
||||
},
|
||||
"Stock in Hand": {
|
||||
"account_type": "Stock"
|
||||
}
|
||||
},
|
||||
"Perliminary and Preoperating Expenses": {
|
||||
"Preliminary and Preoperating Expenses": {
|
||||
"Preoperating Expenses": {}
|
||||
},
|
||||
"Prepayments & Deposits": {
|
||||
@ -150,16 +150,16 @@
|
||||
"account_type": "Fixed Asset"
|
||||
},
|
||||
"Leasehold Improvement": {},
|
||||
"Motor Vehicules": {
|
||||
"Motor Vehicles": {
|
||||
"account_type": "Fixed Asset"
|
||||
},
|
||||
"Work In Progrees": {},
|
||||
"Work In Progress": {},
|
||||
"account_type": "Fixed Asset"
|
||||
}
|
||||
},
|
||||
"Intangible Assets": {
|
||||
"Computer Card Renewal": {},
|
||||
"Dispoal of Outlets": {},
|
||||
"Disposal of Outlets": {},
|
||||
"Registration of Trademarks": {}
|
||||
},
|
||||
"Intercompany Accounts": {},
|
||||
@ -218,7 +218,7 @@
|
||||
},
|
||||
"MISC Charges": {
|
||||
"Other Charges": {
|
||||
"Captial Loss": {
|
||||
"Capital Loss": {
|
||||
"Disposal of Business Branch": {},
|
||||
"Loss On Fixed Assets Disposal": {},
|
||||
"Loss on Difference on Exchange": {}
|
||||
@ -253,14 +253,14 @@
|
||||
"Other Bank Charges": {}
|
||||
},
|
||||
"Communications": {
|
||||
"Courrier": {},
|
||||
"Courier": {},
|
||||
"Others - Communication": {},
|
||||
"Telephone": {},
|
||||
"Web Site Hosting Fees": {}
|
||||
},
|
||||
"Office & Various Expenses": {
|
||||
"Cleaning": {},
|
||||
"Convoyance Expenses": {},
|
||||
"Conveyance Expenses": {},
|
||||
"Gifts & Donations": {},
|
||||
"Insurance": {},
|
||||
"Kitchen and Buffet Expenses": {},
|
||||
@ -325,7 +325,7 @@
|
||||
"Current Liabilities": {
|
||||
"Accounts Payable": {
|
||||
"Payables": {
|
||||
"Advance Paybale to Suppliers": {
|
||||
"Advance Payable to Suppliers": {
|
||||
"account_type": "Payable"
|
||||
},
|
||||
"Consigned Payable": {
|
||||
|
@ -22,7 +22,5 @@ class BankGuarantee(Document):
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_vouchar_detials(column_list, doctype, docname):
|
||||
print (column_list, doctype, docname)
|
||||
return frappe.db.sql(''' select {columns} from `tab{doctype}` where name=%s'''
|
||||
.format(columns=", ".join(json.loads(column_list)), doctype=doctype), docname, as_dict=1)[0]
|
||||
|
||||
|
@ -100,6 +100,7 @@ class PurchaseInvoice(BuyingController):
|
||||
self.validate_fixed_asset()
|
||||
self.create_remarks()
|
||||
self.set_status()
|
||||
self.validate_purchase_receipt_if_update_stock()
|
||||
validate_inter_company_party(self.doctype, self.supplier, self.company, self.inter_company_invoice_reference)
|
||||
|
||||
def validate_release_date(self):
|
||||
@ -284,7 +285,7 @@ class PurchaseInvoice(BuyingController):
|
||||
|
||||
def update_status_updater_args(self):
|
||||
if cint(self.update_stock):
|
||||
self.status_updater.extend([{
|
||||
self.status_updater.append({
|
||||
'source_dt': 'Purchase Invoice Item',
|
||||
'target_dt': 'Purchase Order Item',
|
||||
'join_field': 'po_detail',
|
||||
@ -292,28 +293,29 @@ class PurchaseInvoice(BuyingController):
|
||||
'target_parent_dt': 'Purchase Order',
|
||||
'target_parent_field': 'per_received',
|
||||
'target_ref_field': 'qty',
|
||||
'source_field': 'qty',
|
||||
'source_field': 'received_qty',
|
||||
'second_source_dt': 'Purchase Receipt Item',
|
||||
'second_source_field': 'received_qty',
|
||||
'second_join_field': 'purchase_order_item',
|
||||
'percent_join_field':'purchase_order',
|
||||
# 'percent_join_field': 'prevdoc_docname',
|
||||
'overflow_type': 'receipt',
|
||||
'extra_cond': """ and exists(select name from `tabPurchase Invoice`
|
||||
where name=`tabPurchase Invoice Item`.parent and update_stock = 1)"""
|
||||
},
|
||||
{
|
||||
'source_dt': 'Purchase Invoice Item',
|
||||
'target_dt': 'Purchase Order Item',
|
||||
'join_field': 'po_detail',
|
||||
'target_field': 'returned_qty',
|
||||
'target_parent_dt': 'Purchase Order',
|
||||
# 'target_parent_field': 'per_received',
|
||||
# 'target_ref_field': 'qty',
|
||||
'source_field': '-1 * qty',
|
||||
# 'percent_join_field': 'prevdoc_docname',
|
||||
# 'overflow_type': 'receipt',
|
||||
'extra_cond': """ and exists (select name from `tabPurchase Invoice`
|
||||
where name=`tabPurchase Invoice Item`.parent and update_stock=1 and is_return=1)"""
|
||||
}
|
||||
])
|
||||
})
|
||||
if cint(self.is_return):
|
||||
self.status_updater.append({
|
||||
'source_dt': 'Purchase Invoice Item',
|
||||
'target_dt': 'Purchase Order Item',
|
||||
'join_field': 'po_detail',
|
||||
'target_field': 'returned_qty',
|
||||
'source_field': '-1 * qty',
|
||||
'second_source_dt': 'Purchase Receipt Item',
|
||||
'second_source_field': '-1 * qty',
|
||||
'second_join_field': 'purchase_order_item',
|
||||
'overflow_type': 'receipt',
|
||||
'extra_cond': """ and exists (select name from `tabPurchase Invoice`
|
||||
where name=`tabPurchase Invoice Item`.parent and update_stock=1 and is_return=1)"""
|
||||
})
|
||||
|
||||
def validate_purchase_receipt_if_update_stock(self):
|
||||
if self.update_stock:
|
||||
@ -327,13 +329,13 @@ class PurchaseInvoice(BuyingController):
|
||||
|
||||
self.check_prev_docstatus()
|
||||
self.update_status_updater_args()
|
||||
self.update_prevdoc_status()
|
||||
|
||||
frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype,
|
||||
self.company, self.base_grand_total)
|
||||
|
||||
if not self.is_return:
|
||||
self.update_against_document_in_jv()
|
||||
self.update_prevdoc_status()
|
||||
self.update_billing_status_for_zero_amount_refdoc("Purchase Order")
|
||||
self.update_billing_status_in_pr()
|
||||
|
||||
@ -763,9 +765,9 @@ class PurchaseInvoice(BuyingController):
|
||||
self.check_on_hold_or_closed_status()
|
||||
|
||||
self.update_status_updater_args()
|
||||
self.update_prevdoc_status()
|
||||
|
||||
if not self.is_return:
|
||||
self.update_prevdoc_status()
|
||||
self.update_billing_status_for_zero_amount_refdoc("Purchase Order")
|
||||
self.update_billing_status_in_pr()
|
||||
|
||||
|
@ -254,7 +254,7 @@ class SalesInvoice(SellingController):
|
||||
|
||||
def update_status_updater_args(self):
|
||||
if cint(self.update_stock):
|
||||
self.status_updater.extend([{
|
||||
self.status_updater.append({
|
||||
'source_dt':'Sales Invoice Item',
|
||||
'target_dt':'Sales Order Item',
|
||||
'target_parent_dt':'Sales Order',
|
||||
@ -272,21 +272,20 @@ class SalesInvoice(SellingController):
|
||||
'overflow_type': 'delivery',
|
||||
'extra_cond': """ and exists(select name from `tabSales Invoice`
|
||||
where name=`tabSales Invoice Item`.parent and update_stock = 1)"""
|
||||
},
|
||||
{
|
||||
'source_dt': 'Sales Invoice Item',
|
||||
'target_dt': 'Sales Order Item',
|
||||
'join_field': 'so_detail',
|
||||
'target_field': 'returned_qty',
|
||||
'target_parent_dt': 'Sales Order',
|
||||
# 'target_parent_field': 'per_delivered',
|
||||
# 'target_ref_field': 'qty',
|
||||
'source_field': '-1 * qty',
|
||||
# 'percent_join_field': 'sales_order',
|
||||
# 'overflow_type': 'delivery',
|
||||
'extra_cond': """ and exists (select name from `tabSales Invoice` where name=`tabSales Invoice Item`.parent and update_stock=1 and is_return=1)"""
|
||||
}
|
||||
])
|
||||
})
|
||||
if cint(self.is_return):
|
||||
self.status_updater.append({
|
||||
'source_dt': 'Sales Invoice Item',
|
||||
'target_dt': 'Sales Order Item',
|
||||
'join_field': 'so_detail',
|
||||
'target_field': 'returned_qty',
|
||||
'target_parent_dt': 'Sales Order',
|
||||
'source_field': '-1 * qty',
|
||||
'second_source_dt': 'Delivery Note Item',
|
||||
'second_source_field': '-1 * qty',
|
||||
'second_join_field': 'so_detail',
|
||||
'extra_cond': """ and exists (select name from `tabSales Invoice` where name=`tabSales Invoice Item`.parent and update_stock=1 and is_return=1)"""
|
||||
})
|
||||
|
||||
def check_credit_limit(self):
|
||||
from erpnext.selling.doctype.customer.customer import check_credit_limit
|
||||
@ -1166,6 +1165,9 @@ class SalesInvoice(SellingController):
|
||||
self.set_missing_values(for_validate = True)
|
||||
|
||||
def validate_inter_company_party(doctype, party, company, inter_company_reference):
|
||||
if not party:
|
||||
return
|
||||
|
||||
if doctype in ["Sales Invoice", "Sales Order"]:
|
||||
partytype, ref_partytype, internal = "Customer", "Supplier", "is_internal_customer"
|
||||
|
||||
|
@ -111,10 +111,10 @@
|
||||
<th style="width: 4%">{%= __("Age (Days)") %}</th>
|
||||
|
||||
{% if(report.report_name === "Accounts Receivable" && filters.show_sales_person_in_print) { %}
|
||||
<th style="width: 16%">{%= __("Reference") %}</th>
|
||||
<th style="width: 14%">{%= __("Reference") %}</th>
|
||||
<th style="width: 10%">{%= __("Sales Person") %}</th>
|
||||
{% } else { %}
|
||||
<th style="width: 26%">{%= __("Reference") %}</th>
|
||||
<th style="width: 24%">{%= __("Reference") %}</th>
|
||||
{% } %}
|
||||
{% if(!filters.show_pdc_in_print) { %}
|
||||
<th style="width: 20%">{%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %}</th>
|
||||
@ -127,7 +127,7 @@
|
||||
<th style="width: 10%; text-align: right">{%= __("Outstanding Amount") %}</th>
|
||||
{% if(filters.show_pdc_in_print) { %}
|
||||
{% if(report.report_name === "Accounts Receivable") { %}
|
||||
<th style="width: 10%">{%= __("Customer LPO No.") %}</th>
|
||||
<th style="width: 12%">{%= __("Customer LPO No.") %}</th>
|
||||
{% } %}
|
||||
<th style="width: 10%">{%= __("PDC/LC Ref") %}</th>
|
||||
<th style="width: 10%">{%= __("PDC/LC Amount") %}</th>
|
||||
|
@ -108,6 +108,69 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
self.assertEqual(po.get("items")[0].amount, 1400)
|
||||
self.assertEqual(get_ordered_qty(), existing_ordered_qty + 3)
|
||||
|
||||
def test_update_qty(self):
|
||||
po = create_purchase_order()
|
||||
|
||||
make_pr_against_po(po.name, 6)
|
||||
|
||||
po.load_from_db()
|
||||
self.assertEqual(po.get("items")[0].received_qty, 6)
|
||||
|
||||
# Check received_qty after make_purchase_invoice without update_stock checked
|
||||
pi1 = make_purchase_invoice(po.name)
|
||||
pi1.get("items")[0].qty = 6
|
||||
pi1.insert()
|
||||
pi1.submit()
|
||||
|
||||
po.load_from_db()
|
||||
self.assertEqual(po.get("items")[0].received_qty, 6)
|
||||
|
||||
# Check received_qty after make_purchase_invoice with update_stock checked
|
||||
pi2 = make_purchase_invoice(po.name)
|
||||
pi2.set("update_stock", 1)
|
||||
pi2.get("items")[0].qty = 3
|
||||
pi2.insert()
|
||||
pi2.submit()
|
||||
|
||||
po.load_from_db()
|
||||
self.assertEqual(po.get("items")[0].received_qty, 9)
|
||||
|
||||
def test_return_against_purchase_order(self):
|
||||
po = create_purchase_order()
|
||||
|
||||
pr = make_pr_against_po(po.name, 6)
|
||||
|
||||
po.load_from_db()
|
||||
self.assertEqual(po.get("items")[0].received_qty, 6)
|
||||
|
||||
pi2 = make_purchase_invoice(po.name)
|
||||
pi2.set("update_stock", 1)
|
||||
pi2.get("items")[0].qty = 3
|
||||
pi2.insert()
|
||||
pi2.submit()
|
||||
|
||||
po.load_from_db()
|
||||
self.assertEqual(po.get("items")[0].received_qty, 9)
|
||||
|
||||
# Make return purchase receipt, purchase invoice and check quantity
|
||||
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt \
|
||||
import make_purchase_receipt as make_purchase_receipt_return
|
||||
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice \
|
||||
import make_purchase_invoice as make_purchase_invoice_return
|
||||
|
||||
pr1 = make_purchase_receipt_return(is_return=1, return_against=pr.name, qty=-3, do_not_submit=True)
|
||||
pr1.items[0].purchase_order = po.name
|
||||
pr1.items[0].purchase_order_item = po.items[0].name
|
||||
pr1.submit()
|
||||
|
||||
pi1= make_purchase_invoice_return(is_return=1, return_against=pi2.name, qty=-1, update_stock=1, do_not_submit=True)
|
||||
pi1.items[0].purchase_order = po.name
|
||||
pi1.items[0].po_detail = po.items[0].name
|
||||
pi1.submit()
|
||||
|
||||
|
||||
po.load_from_db()
|
||||
self.assertEqual(po.get("items")[0].received_qty, 5)
|
||||
|
||||
def test_make_purchase_invoice(self):
|
||||
po = create_purchase_order(do_not_submit=True)
|
||||
@ -510,6 +573,13 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
frappe.db.set_value("Accounts Settings", "Accounts Settings",
|
||||
"unlink_advance_payment_on_cancelation_of_order", 0)
|
||||
|
||||
def make_pr_against_po(po, received_qty=0):
|
||||
pr = make_purchase_receipt(po)
|
||||
pr.get("items")[0].qty = received_qty or 5
|
||||
pr.insert()
|
||||
pr.submit()
|
||||
return pr
|
||||
|
||||
def make_subcontracted_item(item_code):
|
||||
from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
|
||||
|
||||
|
@ -0,0 +1,29 @@
|
||||
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
/* eslint-disable */
|
||||
|
||||
frappe.query_reports["Subcontracted Item To Be Received"] = {
|
||||
"filters": [
|
||||
{
|
||||
fieldname: "supplier",
|
||||
label: __("Supplier"),
|
||||
fieldtype: "Link",
|
||||
options: "Supplier",
|
||||
reqd: 1
|
||||
},
|
||||
{
|
||||
fieldname:"from_date",
|
||||
label: __("From Date"),
|
||||
fieldtype: "Date",
|
||||
default: frappe.datetime.add_months(frappe.datetime.month_start(), -1),
|
||||
reqd: 1
|
||||
},
|
||||
{
|
||||
fieldname:"to_date",
|
||||
label: __("To Date"),
|
||||
fieldtype: "Date",
|
||||
default: frappe.datetime.add_days(frappe.datetime.month_start(),-1),
|
||||
reqd: 1
|
||||
},
|
||||
]
|
||||
};
|
@ -0,0 +1,30 @@
|
||||
{
|
||||
"add_total_row": 1,
|
||||
"creation": "2019-05-03 11:25:03.685247",
|
||||
"disable_prepared_report": 0,
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"idx": 0,
|
||||
"is_standard": "Yes",
|
||||
"modified": "2019-05-03 11:25:03.685247",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Subcontracted Item To Be Received",
|
||||
"owner": "Administrator",
|
||||
"prepared_report": 0,
|
||||
"ref_doctype": "Purchase Order",
|
||||
"report_name": "Subcontracted Item To Be Received",
|
||||
"report_type": "Script Report",
|
||||
"roles": [
|
||||
{
|
||||
"role": "Stock User"
|
||||
},
|
||||
{
|
||||
"role": "Purchase Manager"
|
||||
},
|
||||
{
|
||||
"role": "Purchase User"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,104 @@
|
||||
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
|
||||
def execute(filters=None):
|
||||
if filters.from_date >= filters.to_date:
|
||||
frappe.msgprint(_("To Date must be greater than From Date"))
|
||||
|
||||
data = []
|
||||
columns = get_columns()
|
||||
get_data(data , filters)
|
||||
return columns, data
|
||||
|
||||
def get_columns():
|
||||
return [
|
||||
{
|
||||
"label": _("Purchase Order"),
|
||||
"fieldtype": "Link",
|
||||
"fieldname": "purchase_order",
|
||||
"options": "Purchase Order",
|
||||
"width": 150
|
||||
},
|
||||
{
|
||||
"label": _("Date"),
|
||||
"fieldtype": "Date",
|
||||
"fieldname": "date",
|
||||
"hidden": 1,
|
||||
"width": 150
|
||||
},
|
||||
{
|
||||
"label": _("Supplier"),
|
||||
"fieldtype": "Link",
|
||||
"fieldname": "supplier",
|
||||
"options": "Supplier",
|
||||
"width": 150
|
||||
},
|
||||
{
|
||||
"label": _("Finished Good Item Code"),
|
||||
"fieldtype": "Data",
|
||||
"fieldname": "fg_item_code",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"label": _("Item name"),
|
||||
"fieldtype": "Data",
|
||||
"fieldname": "item_name",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"label": _("Required Quantity"),
|
||||
"fieldtype": "Float",
|
||||
"fieldname": "required_qty",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"label": _("Received Quantity"),
|
||||
"fieldtype": "Float",
|
||||
"fieldname": "received_qty",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"label": _("Pending Quantity"),
|
||||
"fieldtype": "Float",
|
||||
"fieldname": "pending_qty",
|
||||
"width": 100
|
||||
}
|
||||
]
|
||||
|
||||
def get_data(data, filters):
|
||||
po = get_po(filters)
|
||||
po_name = [v.name for v in po]
|
||||
sub_items = get_purchase_order_item_supplied(po_name)
|
||||
for item in sub_items:
|
||||
for order in po:
|
||||
if order.name == item.parent and item.received_qty < item.qty:
|
||||
row ={
|
||||
'purchase_order': item.parent,
|
||||
'date': order.transaction_date,
|
||||
'supplier': order.supplier,
|
||||
'fg_item_code': item.item_code,
|
||||
'item_name': item.item_name,
|
||||
'required_qty': item.qty,
|
||||
'received_qty':item.received_qty,
|
||||
'pending_qty':item.qty - item.received_qty
|
||||
}
|
||||
data.append(row)
|
||||
|
||||
def get_po(filters):
|
||||
record_filters = [
|
||||
["is_subcontracted", "=", "Yes"],
|
||||
["supplier", "=", filters.supplier],
|
||||
["transaction_date", "<=", filters.to_date],
|
||||
["transaction_date", ">=", filters.from_date],
|
||||
["docstatus", "=", 1]
|
||||
]
|
||||
return frappe.get_all("Purchase Order", filters=record_filters, fields=["name", "transaction_date", "supplier"])
|
||||
|
||||
def get_purchase_order_item_supplied(po):
|
||||
return frappe.get_all("Purchase Order Item", filters=[
|
||||
('parent', 'IN', po)
|
||||
], fields=["parent", "item_code", "item_name", "qty", "received_qty"])
|
@ -0,0 +1,36 @@
|
||||
# Python bytecode 2.7 (62211)
|
||||
# Embedded file name: /Users/anuragmishra/frappe-develop/apps/erpnext/erpnext/buying/report/subcontracted_item_to_be_received/test_subcontracted_item_to_be_received.py
|
||||
# Compiled at: 2019-05-06 09:51:46
|
||||
# Decompiled by https://python-decompiler.com
|
||||
from __future__ import unicode_literals
|
||||
from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
|
||||
from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_receipt
|
||||
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
|
||||
from erpnext.buying.report.subcontracted_item_to_be_received.subcontracted_item_to_be_received import execute
|
||||
import frappe, unittest
|
||||
from pprint import pprint
|
||||
|
||||
class TestSubcontractedItemToBeReceived(unittest.TestCase):
|
||||
|
||||
def test_pending_and_received_qty(self):
|
||||
po = create_purchase_order(item_code='_Test FG Item', is_subcontracted='Yes')
|
||||
transfer_param = []
|
||||
make_stock_entry(item_code='_Test Item', target='_Test Warehouse 1 - _TC', qty=100, basic_rate=100)
|
||||
make_stock_entry(item_code='_Test Item Home Desktop 100', target='_Test Warehouse 1 - _TC', qty=100, basic_rate=100)
|
||||
make_purchase_receipt_against_po(po.name)
|
||||
po.reload()
|
||||
col, data = execute(filters=frappe._dict({'supplier': po.supplier,
|
||||
'from_date': frappe.utils.get_datetime(frappe.utils.add_to_date(po.transaction_date, days=-10)),
|
||||
'to_date': frappe.utils.get_datetime(frappe.utils.add_to_date(po.transaction_date, days=10))}))
|
||||
self.assertEqual(data[0]['pending_qty'], 5)
|
||||
self.assertEqual(data[0]['received_qty'], 5)
|
||||
self.assertEqual(data[0]['purchase_order'], po.name)
|
||||
self.assertEqual(data[0]['supplier'], po.supplier)
|
||||
|
||||
|
||||
def make_purchase_receipt_against_po(po, quantity=5):
|
||||
pr = make_purchase_receipt(po)
|
||||
pr.items[0].qty = quantity
|
||||
pr.supplier_warehouse = '_Test Warehouse 1 - _TC'
|
||||
pr.insert()
|
||||
pr.submit()
|
@ -0,0 +1,29 @@
|
||||
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
/* eslint-disable */
|
||||
|
||||
frappe.query_reports["Subcontracted Raw Materials To Be Transferred"] = {
|
||||
"filters": [
|
||||
{
|
||||
fieldname: "supplier",
|
||||
label: __("Supplier"),
|
||||
fieldtype: "Link",
|
||||
options: "Supplier",
|
||||
reqd: 1
|
||||
},
|
||||
{
|
||||
fieldname:"from_date",
|
||||
label: __("From Date"),
|
||||
fieldtype: "Date",
|
||||
default: frappe.datetime.add_months(frappe.datetime.month_start(), -1),
|
||||
reqd: 1
|
||||
},
|
||||
{
|
||||
fieldname:"to_date",
|
||||
label: __("To Date"),
|
||||
fieldtype: "Date",
|
||||
default: frappe.datetime.add_days(frappe.datetime.month_start(),-1),
|
||||
reqd: 1
|
||||
},
|
||||
]
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
{
|
||||
"add_total_row": 1,
|
||||
"creation": "2019-05-03 12:04:14.438345",
|
||||
"disable_prepared_report": 0,
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"idx": 0,
|
||||
"is_standard": "Yes",
|
||||
"modified": "2019-05-03 12:04:24.203721",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Subcontracted Raw Materials To Be Transferred",
|
||||
"owner": "Administrator",
|
||||
"prepared_report": 0,
|
||||
"ref_doctype": "Purchase Order",
|
||||
"report_name": "Subcontracted Raw Materials To Be Transferred",
|
||||
"report_type": "Script Report",
|
||||
"roles": [
|
||||
{
|
||||
"role": "Stock User"
|
||||
},
|
||||
{
|
||||
"role": "Purchase Manager"
|
||||
},
|
||||
{
|
||||
"role": "Purchase User"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,137 @@
|
||||
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
|
||||
def execute(filters=None):
|
||||
if filters.from_date >= filters.to_date:
|
||||
frappe.msgprint(_("To Date must be greater than From Date"))
|
||||
|
||||
data = []
|
||||
columns = get_columns()
|
||||
get_data(data , filters)
|
||||
return columns, data
|
||||
|
||||
def get_columns():
|
||||
return [
|
||||
{
|
||||
"label": _("Purchase Order"),
|
||||
"fieldtype": "Link",
|
||||
"fieldname": "purchase_order",
|
||||
"options": "Purchase Order",
|
||||
"width": 150
|
||||
},
|
||||
{
|
||||
"label": _("Date"),
|
||||
"fieldtype": "Date",
|
||||
"fieldname": "date",
|
||||
"hidden": 1,
|
||||
"width": 150
|
||||
},
|
||||
{
|
||||
"label": _("Supplier"),
|
||||
"fieldtype": "Link",
|
||||
"fieldname": "supplier",
|
||||
"options": "Supplier",
|
||||
"width": 150
|
||||
},
|
||||
{
|
||||
"label": _("Item Code"),
|
||||
"fieldtype": "Data",
|
||||
"fieldname": "rm_item_code",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"label": _("Required Quantity"),
|
||||
"fieldtype": "Float",
|
||||
"fieldname": "r_qty",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"label": _("Transferred Quantity"),
|
||||
"fieldtype": "Float",
|
||||
"fieldname": "t_qty",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"label": _("Pending Quantity"),
|
||||
"fieldtype": "Float",
|
||||
"fieldname": "p_qty",
|
||||
"width": 100
|
||||
}
|
||||
]
|
||||
|
||||
def get_data(data, filters):
|
||||
po = get_po(filters)
|
||||
po_transferred_qty_map = frappe._dict(get_transferred_quantity([v.name for v in po]))
|
||||
|
||||
sub_items = get_purchase_order_item_supplied([v.name for v in po])
|
||||
|
||||
for order in po:
|
||||
for item in sub_items:
|
||||
if order.name == item.parent and order.name in po_transferred_qty_map and \
|
||||
item.required_qty != po_transferred_qty_map.get(order.name).get(item.rm_item_code):
|
||||
transferred_qty = po_transferred_qty_map.get(order.name).get(item.rm_item_code) \
|
||||
if po_transferred_qty_map.get(order.name).get(item.rm_item_code) else 0
|
||||
row ={
|
||||
'purchase_order': item.parent,
|
||||
'date': order.transaction_date,
|
||||
'supplier': order.supplier,
|
||||
'rm_item_code': item.rm_item_code,
|
||||
'r_qty': item.required_qty,
|
||||
't_qty':transferred_qty,
|
||||
'p_qty':item.required_qty - transferred_qty
|
||||
}
|
||||
|
||||
data.append(row)
|
||||
|
||||
return(data)
|
||||
|
||||
def get_po(filters):
|
||||
record_filters = [
|
||||
["is_subcontracted", "=", "Yes"],
|
||||
["supplier", "=", filters.supplier],
|
||||
["transaction_date", "<=", filters.to_date],
|
||||
["transaction_date", ">=", filters.from_date],
|
||||
["docstatus", "=", 1]
|
||||
]
|
||||
return frappe.get_all("Purchase Order", filters=record_filters, fields=["name", "transaction_date", "supplier"])
|
||||
|
||||
def get_transferred_quantity(po_name):
|
||||
stock_entries = get_stock_entry(po_name)
|
||||
stock_entries_detail = get_stock_entry_detail([v.name for v in stock_entries])
|
||||
po_transferred_qty_map = {}
|
||||
|
||||
|
||||
for entry in stock_entries:
|
||||
for details in stock_entries_detail:
|
||||
if details.parent == entry.name:
|
||||
details["Purchase_order"] = entry.purchase_order
|
||||
if entry.purchase_order not in po_transferred_qty_map:
|
||||
po_transferred_qty_map[entry.purchase_order] = {}
|
||||
po_transferred_qty_map[entry.purchase_order][details.item_code] = details.qty
|
||||
else:
|
||||
po_transferred_qty_map[entry.purchase_order][details.item_code] = po_transferred_qty_map[entry.purchase_order].get(details.item_code, 0) + details.qty
|
||||
|
||||
return po_transferred_qty_map
|
||||
|
||||
|
||||
def get_stock_entry(po):
|
||||
return frappe.get_all("Stock Entry", filters=[
|
||||
('purchase_order', 'IN', po),
|
||||
('stock_entry_type', '=', 'Send to Subcontractor'),
|
||||
('docstatus', '=', 1)
|
||||
], fields=["name", "purchase_order"])
|
||||
|
||||
def get_stock_entry_detail(se):
|
||||
return frappe.get_all("Stock Entry Detail", filters=[
|
||||
["parent", "in", se]
|
||||
],
|
||||
fields=["parent", "item_code", "qty"])
|
||||
|
||||
def get_purchase_order_item_supplied(po):
|
||||
return frappe.get_all("Purchase Order Item Supplied", filters=[
|
||||
('parent', 'IN', po)
|
||||
], fields=['parent', 'rm_item_code', 'required_qty'])
|
@ -0,0 +1,44 @@
|
||||
# Python bytecode 2.7 (62211)
|
||||
# Embedded file name: /Users/anuragmishra/frappe-develop/apps/erpnext/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/test_subcontracted_raw_materials_to_be_transferred.py
|
||||
# Compiled at: 2019-05-06 10:24:35
|
||||
# Decompiled by https://python-decompiler.com
|
||||
from __future__ import unicode_literals
|
||||
from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
|
||||
from erpnext.buying.doctype.purchase_order.purchase_order import make_rm_stock_entry
|
||||
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
|
||||
from erpnext.buying.report.subcontracted_raw_materials_to_be_transferred.subcontracted_raw_materials_to_be_transferred import execute
|
||||
import json, frappe, unittest
|
||||
|
||||
class TestSubcontractedItemToBeReceived(unittest.TestCase):
|
||||
|
||||
def test_pending_and_received_qty(self):
|
||||
po = create_purchase_order(item_code='_Test FG Item', is_subcontracted='Yes')
|
||||
make_stock_entry(item_code='_Test Item', target='_Test Warehouse 1 - _TC', qty=100, basic_rate=100)
|
||||
make_stock_entry(item_code='_Test Item Home Desktop 100', target='_Test Warehouse 1 - _TC', qty=100, basic_rate=100)
|
||||
transfer_subcontracted_raw_materials(po.name)
|
||||
col, data = execute(filters=frappe._dict({'supplier': po.supplier,
|
||||
'from_date': frappe.utils.get_datetime(frappe.utils.add_to_date(po.transaction_date, days=-10)),
|
||||
'to_date': frappe.utils.get_datetime(frappe.utils.add_to_date(po.transaction_date, days=10))}))
|
||||
self.assertEqual(data[0]['purchase_order'], po.name)
|
||||
self.assertIn(data[0]['rm_item_code'], ['_Test Item', '_Test Item Home Desktop 100'])
|
||||
self.assertIn(data[0]['p_qty'], [9, 18])
|
||||
self.assertIn(data[0]['t_qty'], [1, 2])
|
||||
|
||||
self.assertEqual(data[1]['purchase_order'], po.name)
|
||||
self.assertIn(data[1]['rm_item_code'], ['_Test Item', '_Test Item Home Desktop 100'])
|
||||
self.assertIn(data[1]['p_qty'], [9, 18])
|
||||
self.assertIn(data[1]['t_qty'], [1, 2])
|
||||
|
||||
|
||||
def transfer_subcontracted_raw_materials(po):
|
||||
rm_item = [
|
||||
{'item_code': '_Test Item', 'rm_item_code': '_Test Item', 'item_name': '_Test Item', 'qty': 1,
|
||||
'warehouse': '_Test Warehouse - _TC', 'rate': 100, 'amount': 100, 'stock_uom': 'Nos'},
|
||||
{'item_code': '_Test Item Home Desktop 100', 'rm_item_code': '_Test Item Home Desktop 100', 'item_name': '_Test Item Home Desktop 100', 'qty': 2,
|
||||
'warehouse': '_Test Warehouse - _TC', 'rate': 100, 'amount': 200, 'stock_uom': 'Nos'}]
|
||||
rm_item_string = json.dumps(rm_item)
|
||||
se = frappe.get_doc(make_rm_stock_entry(po, rm_item_string))
|
||||
se.to_warehouse = '_Test Warehouse 1 - _TC'
|
||||
se.stock_entry_type = 'Send to Subcontractor'
|
||||
se.save()
|
||||
se.submit()
|
@ -299,6 +299,12 @@ def get_data():
|
||||
"name": "Customers Without Any Sales Transactions",
|
||||
"doctype": "Customer"
|
||||
},
|
||||
{
|
||||
"type": "report",
|
||||
"is_query_report": True,
|
||||
"name": "Sales Partners Commission",
|
||||
"doctype": "Customer"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
|
@ -21,6 +21,17 @@ frappe.ui.form.on("Opportunity", {
|
||||
frm.trigger('set_contact_link');
|
||||
},
|
||||
|
||||
onload_post_render: function(frm) {
|
||||
frm.get_field("items").grid.set_multiple_add("item_code", "qty");
|
||||
},
|
||||
|
||||
party_name: function(frm) {
|
||||
if (frm.doc.opportunity_from == "Customer") {
|
||||
frm.trigger('set_contact_link');
|
||||
erpnext.utils.get_party_details(frm);
|
||||
}
|
||||
},
|
||||
|
||||
with_items: function(frm) {
|
||||
frm.trigger('toggle_mandatory');
|
||||
},
|
||||
@ -180,4 +191,3 @@ cur_frm.cscript.lead = function(doc, cdt, cdn) {
|
||||
frm: cur_frm
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -3,13 +3,8 @@
|
||||
|
||||
frappe.ui.form.on('Additional Salary', {
|
||||
setup: function(frm) {
|
||||
frm.set_query("salary_component", function() {
|
||||
return {
|
||||
filters: {
|
||||
is_additional_component: true
|
||||
}
|
||||
};
|
||||
});
|
||||
frm.add_fetch("salary_component", "deduct_full_tax_on_selected_payroll_date", "deduct_full_tax_on_selected_payroll_date");
|
||||
|
||||
frm.set_query("employee", function() {
|
||||
return {
|
||||
filters: {
|
||||
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_events_in_timeline": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 1,
|
||||
"allow_rename": 0,
|
||||
@ -21,6 +22,7 @@
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "naming_series",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
@ -54,6 +56,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "employee",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@ -87,6 +90,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "salary_component",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@ -120,6 +124,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "amount",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
@ -153,6 +158,7 @@
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "1",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "overwrite_salary_structure_amount",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
@ -185,6 +191,40 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "deduct_full_tax_on_selected_payroll_date",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Deduct Full Tax on Selected Payroll Date",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "column_break_5",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
@ -217,6 +257,8 @@
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "",
|
||||
"description": "Date on which this component is applied",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "payroll_date",
|
||||
"fieldtype": "Date",
|
||||
"hidden": 0,
|
||||
@ -250,6 +292,7 @@
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_from": "employee.employee_name",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "employee_name",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
@ -284,6 +327,7 @@
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_from": "employee.department",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "department",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@ -317,6 +361,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@ -350,6 +395,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "salary_slip",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@ -384,6 +430,7 @@
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_from": "salary_component.type",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "type",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
@ -417,6 +464,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "amended_from",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@ -453,7 +501,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-08-21 14:44:22.921926",
|
||||
"modified": "2019-05-09 19:53:37.475839",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Additional Salary",
|
||||
|
@ -9,6 +9,12 @@ from frappe import _
|
||||
from frappe.utils import getdate, date_diff
|
||||
|
||||
class AdditionalSalary(Document):
|
||||
def before_insert(self):
|
||||
if frappe.db.exists("Additional Salary", {"employee": self.employee, "salary_component": self.salary_component,
|
||||
"amount": self.amount, "payroll_date": self.payroll_date, "company": self.company}):
|
||||
|
||||
frappe.throw(_("Additional Salary Component Exists."))
|
||||
|
||||
def validate(self):
|
||||
self.validate_dates()
|
||||
if self.amount < 0:
|
||||
@ -35,7 +41,8 @@ class AdditionalSalary(Document):
|
||||
@frappe.whitelist()
|
||||
def get_additional_salary_component(employee, start_date, end_date):
|
||||
additional_components = frappe.db.sql("""
|
||||
select salary_component, sum(amount) as amount, overwrite_salary_structure_amount from `tabAdditional Salary`
|
||||
select salary_component, sum(amount) as amount, overwrite_salary_structure_amount, deduct_full_tax_on_selected_payroll_date
|
||||
from `tabAdditional Salary`
|
||||
where employee=%(employee)s
|
||||
and docstatus = 1
|
||||
and payroll_date between %(from_date)s and %(to_date)s
|
||||
@ -48,16 +55,20 @@ def get_additional_salary_component(employee, start_date, end_date):
|
||||
}, as_dict=1)
|
||||
|
||||
additional_components_list = []
|
||||
component_fields = ["depends_on_payment_days", "salary_component_abbr", "is_tax_applicable", "variable_based_on_taxable_salary", 'type']
|
||||
for d in additional_components:
|
||||
component = frappe.get_doc("Salary Component", d.salary_component)
|
||||
struct_row = {'salary_component': d.salary_component}
|
||||
for field in ["depends_on_payment_days", "abbr", "is_tax_applicable", "variable_based_on_taxable_salary", "is_additional_component"]:
|
||||
struct_row[field] = component.get(field)
|
||||
struct_row = frappe._dict({'salary_component': d.salary_component})
|
||||
component = frappe.get_all("Salary Component", filters={'name': d.salary_component}, fields=component_fields)
|
||||
if component:
|
||||
struct_row.update(component[0])
|
||||
|
||||
additional_components_list.append({
|
||||
struct_row['deduct_full_tax_on_selected_payroll_date'] = d.deduct_full_tax_on_selected_payroll_date
|
||||
struct_row['is_additional_component'] = 1
|
||||
|
||||
additional_components_list.append(frappe._dict({
|
||||
'amount': d.amount,
|
||||
'type': component.type,
|
||||
'type': component[0].type,
|
||||
'struct_row': struct_row,
|
||||
'overwrite': d.overwrite_salary_structure_amount
|
||||
})
|
||||
'overwrite': d.overwrite_salary_structure_amount,
|
||||
}))
|
||||
return additional_components_list
|
@ -3,13 +3,18 @@
|
||||
|
||||
frappe.ui.form.on('Employee Benefit Application', {
|
||||
setup: function(frm) {
|
||||
frm.set_query("earning_component", "employee_benefits", function() {
|
||||
return {
|
||||
query : "erpnext.hr.doctype.employee_benefit_application.employee_benefit_application.get_earning_components",
|
||||
filters: {date: frm.doc.date, employee: frm.doc.employee}
|
||||
};
|
||||
});
|
||||
if(!frm.doc.employee || !frm.doc.date) {
|
||||
frappe.throw(__("Please select Employee and Date first"));
|
||||
} else {
|
||||
frm.set_query("earning_component", "employee_benefits", function() {
|
||||
return {
|
||||
query : "erpnext.hr.doctype.employee_benefit_application.employee_benefit_application.get_earning_components",
|
||||
filters: {date: frm.doc.date, employee: frm.doc.employee}
|
||||
};
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
employee: function(frm) {
|
||||
var method, args;
|
||||
if(frm.doc.employee && frm.doc.date && frm.doc.payroll_period){
|
||||
|
@ -7,7 +7,7 @@ import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import date_diff, getdate, rounded, add_days, cstr, cint, flt
|
||||
from frappe.model.document import Document
|
||||
from erpnext.hr.doctype.payroll_period.payroll_period import get_payroll_period_days
|
||||
from erpnext.hr.doctype.payroll_period.payroll_period import get_payroll_period_days, get_period_factor
|
||||
from erpnext.hr.doctype.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure
|
||||
from erpnext.hr.utils import get_sal_slip_total_benefit_given, get_holidays_for_employee, get_previous_claimed_amount
|
||||
|
||||
@ -131,7 +131,7 @@ def get_max_benefits_remaining(employee, on_date, payroll_period):
|
||||
salary_component = frappe.get_doc("Salary Component", sal_struct_row.salary_component)
|
||||
if salary_component.depends_on_payment_days == 1 and salary_component.pay_against_benefit_claim != 1:
|
||||
have_depends_on_payment_days = True
|
||||
benefit_amount = get_benefit_pro_rata_ratio_amount(sal_struct, salary_component.max_benefit_amount)
|
||||
benefit_amount = get_benefit_amount_based_on_pro_rata(sal_struct, salary_component.max_benefit_amount)
|
||||
amount_per_day = benefit_amount / payroll_period_days
|
||||
per_day_amount_total += amount_per_day
|
||||
|
||||
@ -167,16 +167,14 @@ def calculate_lwp(employee, start_date, holidays, working_days):
|
||||
lwp = cint(leave[0][1]) and (lwp + 0.5) or (lwp + 1)
|
||||
return lwp
|
||||
|
||||
def get_benefit_component_amount(employee, start_date, end_date, struct_row, sal_struct, period_length, frequency):
|
||||
payroll_period, period_factor, actual_payroll_days = get_payroll_period_days(start_date, end_date, employee)
|
||||
|
||||
def get_benefit_component_amount(employee, start_date, end_date, salary_component, sal_struct, payroll_frequency, payroll_period):
|
||||
if not payroll_period:
|
||||
frappe.msgprint(_("Start and end dates not in a valid Payroll Period, cannot calculate {0}.")
|
||||
.format(struct_row.salary_component))
|
||||
frappe.msgprint(_("Start and end dates not in a valid Payroll Period, cannot calculate {0}")
|
||||
.format(salary_component))
|
||||
return False
|
||||
|
||||
# Considering there is only one application for a year
|
||||
benefit_application_name = frappe.db.sql("""
|
||||
benefit_application = frappe.db.sql("""
|
||||
select name
|
||||
from `tabEmployee Benefit Application`
|
||||
where
|
||||
@ -185,69 +183,58 @@ def get_benefit_component_amount(employee, start_date, end_date, struct_row, sal
|
||||
and docstatus = 1
|
||||
""", {
|
||||
'employee': employee,
|
||||
'payroll_period': payroll_period
|
||||
'payroll_period': payroll_period.name
|
||||
})
|
||||
|
||||
if frappe.db.get_value("Salary Component", struct_row.salary_component, "depends_on_payment_days") != 1:
|
||||
if frequency == "Monthly" and actual_payroll_days in range(360, 370):
|
||||
period_length = 1
|
||||
period_factor = 12
|
||||
current_benefit_amount = 0.0
|
||||
component_max_benefit, depends_on_payment_days = frappe.db.get_value("Salary Component",
|
||||
salary_component, ["max_benefit_amount", "depends_on_payment_days"])
|
||||
|
||||
if period_factor:
|
||||
# If there is application for benefit then fetch the amount from the application.
|
||||
# else Split the max benefits to the pro-rata components with the ratio of their max_benefit_amount
|
||||
if benefit_application_name:
|
||||
benefit_application = frappe.get_doc("Employee Benefit Application", benefit_application_name[0][0])
|
||||
return get_benefit_amount(benefit_application, struct_row, period_factor, period_length)
|
||||
if benefit_application:
|
||||
benefit_amount = frappe.db.get_value("Employee Benefit Application Detail",
|
||||
{"parent": benefit_application[0][0], "earning_component": salary_component}, "amount")
|
||||
elif component_max_benefit:
|
||||
benefit_amount = get_benefit_amount_based_on_pro_rata(sal_struct, component_max_benefit)
|
||||
|
||||
# TODO: Check if there is benefit claim for employee then pro-rata divide the rest of amount (Late Benefit Application)
|
||||
else:
|
||||
component_max = frappe.db.get_value("Salary Component", struct_row.salary_component, "max_benefit_amount")
|
||||
if component_max:
|
||||
benefit_amount = get_benefit_pro_rata_ratio_amount(sal_struct, component_max)
|
||||
return get_amount(period_factor, benefit_amount, period_length)
|
||||
return False
|
||||
current_benefit_amount = 0
|
||||
if benefit_amount:
|
||||
total_sub_periods = get_period_factor(employee,
|
||||
start_date, end_date, payroll_frequency, payroll_period, depends_on_payment_days)[0]
|
||||
|
||||
def get_benefit_pro_rata_ratio_amount(sal_struct, component_max):
|
||||
total_pro_rata_max = 0
|
||||
current_benefit_amount = benefit_amount / total_sub_periods
|
||||
|
||||
return current_benefit_amount
|
||||
|
||||
def get_benefit_amount_based_on_pro_rata(sal_struct, component_max_benefit):
|
||||
max_benefits_total = 0
|
||||
benefit_amount = 0
|
||||
for sal_struct_row in sal_struct.get("earnings"):
|
||||
pay_against_benefit_claim, max_benefit_amount = frappe.db.get_value("Salary Component",
|
||||
sal_struct_row.salary_component, ["pay_against_benefit_claim", "max_benefit_amount"])
|
||||
if sal_struct_row.is_flexible_benefit == 1 and pay_against_benefit_claim != 1:
|
||||
total_pro_rata_max += max_benefit_amount
|
||||
if total_pro_rata_max > 0:
|
||||
benefit_amount = component_max * sal_struct.max_benefits / total_pro_rata_max
|
||||
if benefit_amount > component_max:
|
||||
benefit_amount = component_max
|
||||
for d in sal_struct.get("earnings"):
|
||||
if d.is_flexible_benefit == 1:
|
||||
component = frappe.db.get_value("Salary Component", d.salary_component, ["max_benefit_amount", "pay_against_benefit_claim"], as_dict=1)
|
||||
if not component.pay_against_benefit_claim:
|
||||
max_benefits_total += component.max_benefit_amount
|
||||
|
||||
if max_benefits_total > 0:
|
||||
benefit_amount = sal_struct.max_benefits * component.max_benefit_amount / max_benefits_total
|
||||
if benefit_amount > component_max_benefit:
|
||||
benefit_amount = component_max_benefit
|
||||
|
||||
return benefit_amount
|
||||
|
||||
def get_benefit_amount(application, struct_row, period_factor, period_length):
|
||||
amount = 0
|
||||
for employee_benefit in application.employee_benefits:
|
||||
if employee_benefit.earning_component == struct_row.salary_component:
|
||||
amount += get_amount(period_factor, employee_benefit.amount, period_length)
|
||||
return amount if amount > 0 else False
|
||||
|
||||
def get_amount(period_factor, amount, period_length):
|
||||
amount_per_day = amount / period_factor
|
||||
total_amount = amount_per_day * period_length
|
||||
return total_amount
|
||||
|
||||
def get_earning_components(doctype, txt, searchfield, start, page_len, filters):
|
||||
if len(filters) < 2:
|
||||
return {}
|
||||
employee = filters['employee']
|
||||
date = filters['date']
|
||||
salary_structure = get_assigned_salary_structure(employee, date)
|
||||
|
||||
salary_structure = get_assigned_salary_structure(filters['employee'], filters['date'])
|
||||
|
||||
if salary_structure:
|
||||
query = """select salary_component from `tabSalary Detail` where parent = '{salary_structure}'
|
||||
and is_flexible_benefit = 1
|
||||
order by name"""
|
||||
|
||||
return frappe.db.sql(query.format(**{
|
||||
"salary_structure": salary_structure
|
||||
}))
|
||||
|
||||
return {}
|
||||
return frappe.db.sql("""
|
||||
select salary_component
|
||||
from `tabSalary Detail`
|
||||
where parent = %s and is_flexible_benefit = 1
|
||||
order by name
|
||||
""", salary_structure)
|
||||
else:
|
||||
frappe.throw(_("Salary Structure not found for employee {0} and date {1}")
|
||||
.format(filters['employee'], filters['date']))
|
@ -5,9 +5,11 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import flt
|
||||
from frappe.model.document import Document
|
||||
from erpnext.hr.doctype.employee_benefit_application.employee_benefit_application import get_max_benefits
|
||||
from erpnext.hr.utils import get_payroll_period, get_previous_claimed_amount
|
||||
from erpnext.hr.utils import get_previous_claimed_amount
|
||||
from erpnext.hr.doctype.payroll_period.payroll_period import get_payroll_period
|
||||
from erpnext.hr.doctype.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure
|
||||
|
||||
class EmployeeBenefitClaim(Document):
|
||||
@ -97,31 +99,28 @@ def get_benefit_pro_rata_ratio_amount(employee, on_date, sal_struct):
|
||||
benefit_amount_total += benefit_amount
|
||||
return benefit_amount_total
|
||||
|
||||
def get_benefit_claim_amount(employee, start_date, end_date, salary_component):
|
||||
query = """select claimed_amount from `tabEmployee Benefit Claim`
|
||||
where employee=%(employee)s
|
||||
and docstatus = 1 and pay_against_benefit_claim = 1
|
||||
def get_benefit_claim_amount(employee, start_date, end_date, salary_component=None):
|
||||
query = """
|
||||
select sum(claimed_amount)
|
||||
from `tabEmployee Benefit Claim`
|
||||
where
|
||||
employee=%(employee)s
|
||||
and docstatus = 1
|
||||
and pay_against_benefit_claim = 1
|
||||
and claim_date between %(start_date)s and %(end_date)s
|
||||
"""
|
||||
if not start_date:
|
||||
query += "and claim_date <= %(end_date)s"
|
||||
else:
|
||||
query += "and (claim_date between %(start_date)s and %(end_date)s)"
|
||||
|
||||
if salary_component:
|
||||
query += "and earning_component = %(earning_component)s"
|
||||
query += " and earning_component = %(earning_component)s"
|
||||
|
||||
benefit_claim_details = frappe.db.sql(query, {
|
||||
claimed_amount = flt(frappe.db.sql(query, {
|
||||
'employee': employee,
|
||||
'start_date': start_date,
|
||||
'end_date': end_date,
|
||||
'earning_component': salary_component
|
||||
}, as_dict = True)
|
||||
if benefit_claim_details:
|
||||
claimed_amount = 0
|
||||
for claim_detail in benefit_claim_details:
|
||||
claimed_amount += claim_detail.claimed_amount
|
||||
return claimed_amount
|
||||
return False
|
||||
})[0][0])
|
||||
|
||||
return claimed_amount
|
||||
|
||||
def get_total_benefit_dispensed(employee, sal_struct, sal_slip_start_date, payroll_period):
|
||||
pro_rata_amount = 0
|
||||
@ -140,11 +139,11 @@ def get_total_benefit_dispensed(employee, sal_struct, sal_slip_start_date, payro
|
||||
else:
|
||||
pro_rata_amount = get_benefit_pro_rata_ratio_amount(employee, sal_slip_start_date, sal_struct)
|
||||
|
||||
claimed_amount += get_benefit_claim_amount(employee, payroll_period.start_date, payroll_period.end_date, False)
|
||||
claimed_amount += get_benefit_claim_amount(employee, payroll_period.start_date, payroll_period.end_date)
|
||||
|
||||
return claimed_amount + pro_rata_amount
|
||||
|
||||
def get_last_payroll_period_benefits(employee, sal_slip_start_date, sal_slip_end_date, current_flexi_amount, payroll_period, sal_struct):
|
||||
def get_last_payroll_period_benefits(employee, sal_slip_start_date, sal_slip_end_date, payroll_period, sal_struct):
|
||||
max_benefits = get_max_benefits(employee, payroll_period.end_date)
|
||||
if not max_benefits:
|
||||
max_benefits = 0
|
||||
|
@ -446,6 +446,72 @@
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "other_incomes_section",
|
||||
"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,
|
||||
"label": "Other Incomes",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "income_from_other_sources",
|
||||
"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": "Income From Other Sources",
|
||||
"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
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
@ -458,7 +524,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2019-04-25 16:38:05.847925",
|
||||
"modified": "2019-05-11 16:13:50.472670",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Employee Tax Exemption Declaration",
|
||||
|
@ -25,7 +25,8 @@ class EmployeeTaxExemptionDeclaration(Document):
|
||||
filters = {
|
||||
"employee": self.employee,
|
||||
"payroll_period": self.payroll_period,
|
||||
"name": ["!=", self.name]
|
||||
"name": ["!=", self.name],
|
||||
"docstatus": ["!=", 2]
|
||||
}
|
||||
)
|
||||
if duplicate:
|
||||
|
@ -448,6 +448,72 @@
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "other_incomes_section",
|
||||
"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,
|
||||
"label": "Other Incomes",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "income_from_other_sources",
|
||||
"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": "Income From Other Sources",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
@ -558,7 +624,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2019-04-25 17:06:36.569549",
|
||||
"modified": "2019-05-13 12:17:18.045171",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Employee Tax Exemption Proof Submission",
|
||||
|
@ -13,9 +13,11 @@ frappe.ui.form.on('HR Settings', {
|
||||
|
||||
validate: function(frm) {
|
||||
let policy = frm.doc.password_policy;
|
||||
if (policy.includes(' ') || policy.includes('--')) {
|
||||
frappe.msgprint("Password policy cannot contain spaces or simultaneous hyphens. The format will be restructured automatically");
|
||||
if (policy) {
|
||||
if (policy.includes(' ') || policy.includes('--')) {
|
||||
frappe.msgprint("Password policy cannot contain spaces or simultaneous hyphens. The format will be restructured automatically");
|
||||
}
|
||||
frm.set_value('password_policy', policy.split(new RegExp(" |-", 'g')).filter((token) => token).join('-'));
|
||||
}
|
||||
frm.set_value('password_policy', policy.split(new RegExp(" |-", 'g')).filter((token) => token).join('-'));
|
||||
}
|
||||
});
|
||||
|
@ -172,18 +172,20 @@ frappe.ui.form.on('Loan', {
|
||||
},
|
||||
|
||||
mode_of_payment: function (frm) {
|
||||
frappe.call({
|
||||
method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.get_bank_cash_account",
|
||||
args: {
|
||||
"mode_of_payment": frm.doc.mode_of_payment,
|
||||
"company": frm.doc.company
|
||||
},
|
||||
callback: function (r, rt) {
|
||||
if (r.message) {
|
||||
frm.set_value("payment_account", r.message.account);
|
||||
if (frm.doc.mode_of_payment && frm.doc.company) {
|
||||
frappe.call({
|
||||
method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.get_bank_cash_account",
|
||||
args: {
|
||||
"mode_of_payment": frm.doc.mode_of_payment,
|
||||
"company": frm.doc.company
|
||||
},
|
||||
callback: function (r, rt) {
|
||||
if (r.message) {
|
||||
frm.set_value("payment_account", r.message.account);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
loan_application: function (frm) {
|
||||
|
@ -543,14 +543,15 @@ def submit_salary_slips_for_employees(payroll_entry, salary_slips, publish_progr
|
||||
|
||||
payroll_entry.email_salary_slip(submitted_ss)
|
||||
|
||||
payroll_entry.db_set("salary_slips_submitted", 1)
|
||||
payroll_entry.notify_update()
|
||||
payroll_entry.db_set("salary_slips_submitted", 1)
|
||||
payroll_entry.notify_update()
|
||||
|
||||
if not submitted_ss and not not_submitted_ss:
|
||||
frappe.msgprint(_("No salary slip found to submit for the above selected criteria OR salary slip already submitted"))
|
||||
|
||||
if not_submitted_ss:
|
||||
frappe.msgprint(_("Could not submit some Salary Slips"))
|
||||
|
||||
def get_payroll_entries_for_jv(doctype, txt, searchfield, start, page_len, filters):
|
||||
return frappe.db.sql("""
|
||||
select name from `tabPayroll Entry`
|
||||
|
@ -5,7 +5,7 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import date_diff, getdate, formatdate, cint
|
||||
from frappe.utils import date_diff, getdate, formatdate, cint, month_diff, flt
|
||||
from frappe.model.document import Document
|
||||
from erpnext.hr.utils import get_holidays_for_employee
|
||||
|
||||
@ -48,12 +48,13 @@ class PayrollPeriod(Document):
|
||||
def get_payroll_period_days(start_date, end_date, employee):
|
||||
company = frappe.db.get_value("Employee", employee, "company")
|
||||
payroll_period = frappe.db.sql("""
|
||||
select name, start_date, end_date from `tabPayroll Period`
|
||||
where company=%(company)s
|
||||
and (
|
||||
(%(start_date)s between start_date and end_date)
|
||||
and (%(end_date)s between start_date and end_date)
|
||||
)""", {
|
||||
select name, start_date, end_date
|
||||
from `tabPayroll Period`
|
||||
where
|
||||
company=%(company)s
|
||||
and %(start_date)s between start_date and end_date
|
||||
and %(end_date)s between start_date and end_date
|
||||
""", {
|
||||
'company': company,
|
||||
'start_date': start_date,
|
||||
'end_date': end_date
|
||||
@ -67,3 +68,38 @@ def get_payroll_period_days(start_date, end_date, employee):
|
||||
working_days -= len(holidays)
|
||||
return payroll_period[0][0], working_days, actual_no_of_days
|
||||
return False, False, False
|
||||
|
||||
def get_payroll_period(from_date, to_date, company):
|
||||
payroll_period = frappe.db.sql("""
|
||||
select name, start_date, end_date
|
||||
from `tabPayroll Period`
|
||||
where start_date<=%s and end_date>= %s and company=%s
|
||||
""", (from_date, to_date, company), as_dict=1)
|
||||
|
||||
return payroll_period[0] if payroll_period else None
|
||||
|
||||
def get_period_factor(employee, start_date, end_date, payroll_frequency, payroll_period, depends_on_payment_days=0):
|
||||
# TODO if both deduct checked update the factor to make tax consistent
|
||||
period_start, period_end = payroll_period.start_date, payroll_period.end_date
|
||||
joining_date, relieving_date = frappe.db.get_value("Employee", employee, ["date_of_joining", "relieving_date"])
|
||||
|
||||
if getdate(joining_date) > getdate(period_start):
|
||||
period_start = joining_date
|
||||
if relieving_date and getdate(relieving_date) < getdate(period_end):
|
||||
period_end = relieving_date
|
||||
|
||||
total_sub_periods, remaining_sub_periods = 0.0, 0.0
|
||||
|
||||
if payroll_frequency == "Monthly" and not depends_on_payment_days:
|
||||
total_sub_periods = month_diff(payroll_period.end_date, payroll_period.start_date)
|
||||
remaining_sub_periods = month_diff(period_end, start_date)
|
||||
else:
|
||||
salary_days = date_diff(end_date, start_date) + 1
|
||||
|
||||
days_in_payroll_period = date_diff(payroll_period.end_date, payroll_period.start_date) + 1
|
||||
total_sub_periods = flt(days_in_payroll_period) / flt(salary_days)
|
||||
|
||||
remaining_days_in_payroll_period = date_diff(period_end, start_date) + 1
|
||||
remaining_sub_periods = flt(remaining_days_in_payroll_period) / flt(salary_days)
|
||||
|
||||
return total_sub_periods, remaining_sub_periods
|
@ -24,14 +24,6 @@ frappe.ui.form.on('Salary Component', {
|
||||
is_flexible_benefit: function(frm) {
|
||||
if(frm.doc.is_flexible_benefit){
|
||||
set_value_for_condition_and_formula(frm);
|
||||
frm.set_value("is_additional_component", 0);
|
||||
frm.set_value("formula", '');
|
||||
frm.set_value("amount", 0);
|
||||
}
|
||||
},
|
||||
is_additional_component: function(frm) {
|
||||
if(frm.doc.is_additional_component){
|
||||
frm.set_value("is_flexible_benefit", 0);
|
||||
frm.set_value("formula", '');
|
||||
frm.set_value("amount", 0);
|
||||
}
|
||||
|
@ -115,40 +115,6 @@
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.is_flexible_benefit != 1",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "is_additional_component",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Is Additional Component",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
@ -285,6 +251,39 @@
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "deduct_full_tax_on_selected_payroll_date",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Deduct Full Tax on Selected Payroll Date",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
@ -424,7 +423,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.type==\"Earning\" && doc.is_additional_component != 1 && doc.statistical_component!=1",
|
||||
"depends_on": "eval:doc.type==\"Earning\" && doc.statistical_component!=1",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "flexible_benefits",
|
||||
"fieldtype": "Section Break",
|
||||
@ -458,7 +457,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.is_additional_component != 1",
|
||||
"depends_on": "",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "is_flexible_benefit",
|
||||
"fieldtype": "Check",
|
||||
@ -1035,7 +1034,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2019-04-16 19:08:55.323567",
|
||||
"modified": "2019-05-13 12:55:55.424370",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Salary Component",
|
||||
|
@ -35,7 +35,6 @@
|
||||
"salary_component": "Leave Encashment",
|
||||
"type": "Earning",
|
||||
"is_payable": 1,
|
||||
"is_tax_applicable": 1,
|
||||
"is_additional_component": 1
|
||||
"is_tax_applicable": 1
|
||||
}
|
||||
]
|
@ -19,7 +19,5 @@ def create_salary_component(component_name, **args):
|
||||
"salary_component": component_name,
|
||||
"type": args.get("type") or "Earning",
|
||||
"is_payable": args.get("is_payable") or 1,
|
||||
"is_tax_applicable": args.get("is_tax_applicable") or 1,
|
||||
"is_additional_component": args.get("is_additional_component") or 1
|
||||
"is_tax_applicable": args.get("is_tax_applicable") or 1
|
||||
}).insert()
|
||||
|
@ -218,41 +218,6 @@
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "",
|
||||
"fetch_from": "salary_component.is_additional_component",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "is_additional_component",
|
||||
"fieldtype": "Check",
|
||||
"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": "Is Additional Component",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"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
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
@ -323,6 +288,39 @@
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "deduct_full_tax_on_selected_payroll_date",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Deduct Full Tax on Selected Payroll Date",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"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
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
@ -566,6 +564,41 @@
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "",
|
||||
"fetch_from": "",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "additional_amount",
|
||||
"fieldtype": "Currency",
|
||||
"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": "Additional Amount",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"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
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
@ -713,7 +746,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"modified": "2019-04-16 19:09:31.726597",
|
||||
"modified": "2019-05-11 17:33:08.508653",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Salary Detail",
|
||||
|
@ -11,7 +11,7 @@ frappe.ui.form.on("Salary Slip", {
|
||||
{fieldname: 'salary_component', columns: 6},
|
||||
{fieldname: 'amount', columns: 4}
|
||||
];
|
||||
})
|
||||
});
|
||||
|
||||
frm.fields_dict["timesheets"].grid.get_field("time_sheet").get_query = function(){
|
||||
return {
|
||||
@ -19,31 +19,39 @@ frappe.ui.form.on("Salary Slip", {
|
||||
employee: frm.doc.employee
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
frm.set_query("salary_component", "earnings", function() {
|
||||
return {
|
||||
filters: {
|
||||
type: "earning"
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
frm.set_query("salary_component", "deductions", function() {
|
||||
return {
|
||||
filters: {
|
||||
type: "deduction"
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
frm.set_query("employee", function() {
|
||||
return{
|
||||
query: "erpnext.controllers.queries.employee_query"
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
start_date: function(frm, dt, dn){
|
||||
start_date: function(frm){
|
||||
if(frm.doc.start_date){
|
||||
frm.trigger("set_end_date");
|
||||
}
|
||||
},
|
||||
|
||||
end_date: function(frm, dt, dn) {
|
||||
get_emp_and_leave_details(frm.doc, dt, dn);
|
||||
end_date: function(frm) {
|
||||
frm.events.get_emp_and_leave_details(frm);
|
||||
},
|
||||
|
||||
set_end_date: function(frm){
|
||||
@ -70,43 +78,54 @@ frappe.ui.form.on("Salary Slip", {
|
||||
|
||||
refresh: function(frm) {
|
||||
frm.trigger("toggle_fields")
|
||||
frm.trigger("toggle_reqd_fields")
|
||||
var salary_detail_fields = ["formula", "abbr", "statistical_component", "is_tax_applicable",
|
||||
"is_flexible_benefit", "variable_based_on_taxable_salary", "is_additional_component"]
|
||||
|
||||
var salary_detail_fields = ["formula", "abbr", "statistical_component", "variable_based_on_taxable_salary"];
|
||||
cur_frm.fields_dict['earnings'].grid.set_column_disp(salary_detail_fields,false);
|
||||
cur_frm.fields_dict['deductions'].grid.set_column_disp(salary_detail_fields,false);
|
||||
},
|
||||
|
||||
salary_slip_based_on_timesheet: function(frm, dt, dn) {
|
||||
salary_slip_based_on_timesheet: function(frm) {
|
||||
frm.trigger("toggle_fields");
|
||||
get_emp_and_leave_details(frm.doc, dt, dn);
|
||||
frm.events.get_emp_and_leave_details(frm);
|
||||
},
|
||||
|
||||
payroll_frequency: function(frm, dt, dn) {
|
||||
payroll_frequency: function(frm) {
|
||||
frm.trigger("toggle_fields");
|
||||
frm.set_value('end_date', '');
|
||||
},
|
||||
|
||||
employee: function(frm, dt, dn) {
|
||||
get_emp_and_leave_details(frm.doc, dt, dn);
|
||||
employee: function(frm) {
|
||||
frm.events.get_emp_and_leave_details(frm);
|
||||
},
|
||||
|
||||
leave_without_pay: function(frm){
|
||||
if (frm.doc.employee && frm.doc.start_date && frm.doc.end_date) {
|
||||
return frappe.call({
|
||||
method: 'process_salary_based_on_leave',
|
||||
doc: frm.doc,
|
||||
args: {"lwp": frm.doc.leave_without_pay},
|
||||
callback: function(r, rt) {
|
||||
frm.refresh();
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
toggle_fields: function(frm) {
|
||||
frm.toggle_display(['hourly_wages', 'timesheets'],
|
||||
cint(frm.doc.salary_slip_based_on_timesheet)==1);
|
||||
frm.toggle_display(['hourly_wages', 'timesheets'], cint(frm.doc.salary_slip_based_on_timesheet)===1);
|
||||
|
||||
frm.toggle_display(['payment_days', 'total_working_days', 'leave_without_pay'],
|
||||
frm.doc.payroll_frequency!="");
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
frappe.ui.form.on('Salary Detail', {
|
||||
earnings_remove: function(frm, dt, dn) {
|
||||
calculate_all(frm.doc, dt, dn);
|
||||
},
|
||||
deductions_remove: function(frm, dt, dn) {
|
||||
calculate_all(frm.doc, dt, dn);
|
||||
|
||||
get_emp_and_leave_details: function(frm) {
|
||||
return frappe.call({
|
||||
method: 'get_emp_and_leave_details',
|
||||
doc: frm.doc,
|
||||
callback: function(r, rt) {
|
||||
frm.refresh();
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
|
||||
@ -119,133 +138,29 @@ frappe.ui.form.on('Salary Slip Timesheet', {
|
||||
}
|
||||
});
|
||||
|
||||
// Get leave details
|
||||
//---------------------------------------------------------------------
|
||||
var get_emp_and_leave_details = function(doc, dt, dn) {
|
||||
return frappe.call({
|
||||
method: 'get_emp_and_leave_details',
|
||||
doc: locals[dt][dn],
|
||||
callback: function(r, rt) {
|
||||
cur_frm.refresh();
|
||||
calculate_all(doc, dt, dn);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
cur_frm.cscript.leave_without_pay = function(doc,dt,dn){
|
||||
if (doc.employee && doc.start_date && doc.end_date) {
|
||||
return $c_obj(doc, 'get_leave_details', {"lwp": doc.leave_without_pay}, function(r, rt) {
|
||||
var doc = locals[dt][dn];
|
||||
cur_frm.refresh();
|
||||
calculate_all(doc, dt, dn);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
var calculate_all = function(doc, dt, dn) {
|
||||
calculate_earning_total(doc, dt, dn);
|
||||
calculate_ded_total(doc, dt, dn);
|
||||
calculate_net_pay(doc, dt, dn);
|
||||
}
|
||||
|
||||
cur_frm.cscript.amount = function(doc,dt,dn){
|
||||
var child = locals[dt][dn];
|
||||
if(!doc.salary_structure){
|
||||
frappe.model.set_value(dt,dn, "default_amount", child.amount)
|
||||
}
|
||||
calculate_all(doc, dt, dn);
|
||||
}
|
||||
|
||||
cur_frm.cscript.depends_on_payment_days = function(doc,dt,dn){
|
||||
calculate_earning_total(doc, dt, dn, true);
|
||||
calculate_ded_total(doc, dt, dn, true);
|
||||
calculate_net_pay(doc, dt, dn);
|
||||
refresh_many(['amount','gross_pay', 'rounded_total', 'net_pay', 'loan_repayment']);
|
||||
};
|
||||
|
||||
// Calculate earning total
|
||||
// ------------------------------------------------------------------------
|
||||
var calculate_earning_total = function(doc, dt, dn, reset_amount) {
|
||||
|
||||
var tbl = doc.earnings || [];
|
||||
var total_earn = 0;
|
||||
for(var i = 0; i < tbl.length; i++){
|
||||
if(cint(tbl[i].depends_on_payment_days) == 1) {
|
||||
tbl[i].amount = Math.round(tbl[i].default_amount)*(flt(doc.payment_days) /
|
||||
cint(doc.total_working_days)*100)/100;
|
||||
} else if(reset_amount && tbl[i].default_amount) {
|
||||
tbl[i].amount = tbl[i].default_amount;
|
||||
}
|
||||
if(!tbl[i].do_not_include_in_total) {
|
||||
total_earn += flt(tbl[i].amount);
|
||||
|
||||
}
|
||||
}
|
||||
doc.gross_pay = total_earn;
|
||||
refresh_many(['earnings', 'amount','gross_pay']);
|
||||
|
||||
}
|
||||
|
||||
// Calculate deduction total
|
||||
// ------------------------------------------------------------------------
|
||||
var calculate_ded_total = function(doc, dt, dn, reset_amount) {
|
||||
var tbl = doc.deductions || [];
|
||||
var total_ded = 0;
|
||||
for(var i = 0; i < tbl.length; i++){
|
||||
if(cint(tbl[i].depends_on_payment_days) == 1) {
|
||||
tbl[i].amount = Math.round(tbl[i].default_amount)*(flt(doc.payment_days)/cint(doc.total_working_days)*100)/100;
|
||||
} else if(reset_amount && tbl[i].default_amount) {
|
||||
tbl[i].amount = tbl[i].default_amount;
|
||||
}
|
||||
if(!tbl[i].do_not_include_in_total) {
|
||||
total_ded += flt(tbl[i].amount);
|
||||
}
|
||||
}
|
||||
doc.total_deduction = total_ded;
|
||||
refresh_many(['deductions', 'total_deduction']);
|
||||
}
|
||||
|
||||
var calculate_net_pay = function(doc, dt, dn) {
|
||||
doc.net_pay = flt(doc.gross_pay) - flt(doc.total_deduction);
|
||||
doc.rounded_total = Math.round(doc.net_pay);
|
||||
refresh_many(['net_pay', 'rounded_total']);
|
||||
}
|
||||
|
||||
cur_frm.cscript.validate = function(doc, dt, dn) {
|
||||
calculate_all(doc, dt, dn);
|
||||
}
|
||||
|
||||
cur_frm.fields_dict.employee.get_query = function(doc,cdt,cdn) {
|
||||
return{
|
||||
query: "erpnext.controllers.queries.employee_query"
|
||||
}
|
||||
}
|
||||
|
||||
// calculate total working hours, earnings based on hourly wages and totals
|
||||
// ------------------------------------------------------------------------
|
||||
var total_work_hours = function(frm, dt, dn) {
|
||||
frm.set_value('total_working_hours', 0);
|
||||
|
||||
var total_working_hours = 0.0;
|
||||
$.each(frm.doc["timesheets"] || [], function(i, timesheet) {
|
||||
frm.doc.total_working_hours += timesheet.working_hours;
|
||||
total_working_hours += timesheet.working_hours;
|
||||
});
|
||||
frm.refresh_field('total_working_hours');
|
||||
frm.set_value('total_working_hours', total_working_hours);
|
||||
|
||||
var wages_amount = frm.doc.total_working_hours * frm.doc.hour_rate;
|
||||
|
||||
frappe.db.get_value('Salary Structure', {'name': frm.doc.salary_structure}, 'salary_component', (r) => {
|
||||
frm.set_value('gross_pay', 0);
|
||||
|
||||
var gross_pay = 0.0;
|
||||
$.each(frm.doc["earnings"], function(i, earning) {
|
||||
if (earning.salary_component == r.salary_component) {
|
||||
earning.amount = wages_amount;
|
||||
frm.refresh_fields('earnings');
|
||||
}
|
||||
frm.doc.gross_pay += earning.amount;
|
||||
gross_pay += earning.amount;
|
||||
});
|
||||
frm.set_value('gross_pay', gross_pay);
|
||||
|
||||
frm.refresh_field('gross_pay');
|
||||
calculate_net_pay(frm.doc, dt, dn);
|
||||
frm.doc.net_pay = flt(frm.doc.gross_pay) - flt(frm.doc.total_deduction);
|
||||
frm.doc.rounded_total = Math.round(frm.doc.net_pay);
|
||||
refresh_many(['net_pay', 'rounded_total']);
|
||||
});
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -45,12 +45,12 @@ class TestSalarySlip(unittest.TestCase):
|
||||
|
||||
self.assertEqual(ss.total_working_days, no_of_days[0])
|
||||
self.assertEqual(ss.payment_days, no_of_days[0])
|
||||
self.assertEqual(ss.earnings[0].amount, 25000)
|
||||
self.assertEqual(ss.earnings[0].amount, 50000)
|
||||
self.assertEqual(ss.earnings[1].amount, 3000)
|
||||
self.assertEqual(ss.deductions[0].amount, 5000)
|
||||
self.assertEqual(ss.deductions[1].amount, 5000)
|
||||
self.assertEqual(ss.gross_pay, 40500)
|
||||
self.assertEqual(ss.net_pay, 29918)
|
||||
self.assertEqual(ss.gross_pay, 78000)
|
||||
self.assertEqual(ss.net_pay, 67418.0)
|
||||
|
||||
def test_salary_slip_with_holidays_excluded(self):
|
||||
no_of_days = self.get_no_of_days()
|
||||
@ -64,13 +64,13 @@ class TestSalarySlip(unittest.TestCase):
|
||||
|
||||
self.assertEqual(ss.total_working_days, no_of_days[0] - no_of_days[1])
|
||||
self.assertEqual(ss.payment_days, no_of_days[0] - no_of_days[1])
|
||||
self.assertEqual(ss.earnings[0].amount, 25000)
|
||||
self.assertEqual(ss.earnings[0].default_amount, 25000)
|
||||
self.assertEqual(ss.earnings[0].amount, 50000)
|
||||
self.assertEqual(ss.earnings[0].default_amount, 50000)
|
||||
self.assertEqual(ss.earnings[1].amount, 3000)
|
||||
self.assertEqual(ss.deductions[0].amount, 5000)
|
||||
self.assertEqual(ss.deductions[1].amount, 5000)
|
||||
self.assertEqual(ss.gross_pay, 40500)
|
||||
self.assertEqual(ss.net_pay, 29918)
|
||||
self.assertEqual(ss.gross_pay, 78000)
|
||||
self.assertEqual(ss.net_pay, 67418.0)
|
||||
|
||||
def test_payment_days(self):
|
||||
no_of_days = self.get_no_of_days()
|
||||
@ -211,7 +211,7 @@ class TestSalarySlip(unittest.TestCase):
|
||||
tax_paid = get_tax_paid_in_period(employee)
|
||||
|
||||
# total taxable income 586000, 250000 @ 5%, 86000 @ 20% ie. 12500 + 17200
|
||||
annual_tax = 29700
|
||||
annual_tax = 113567.79
|
||||
try:
|
||||
self.assertEqual(tax_paid, annual_tax)
|
||||
except AssertionError:
|
||||
@ -250,7 +250,7 @@ class TestSalarySlip(unittest.TestCase):
|
||||
|
||||
# total taxable income 416000, 166000 @ 5% ie. 8300
|
||||
try:
|
||||
self.assertEqual(tax_paid, 8300)
|
||||
self.assertEqual(tax_paid, 88607.79)
|
||||
except AssertionError:
|
||||
print("\nSalary Slip - Tax calculation failed on following case\n", data, "\n")
|
||||
raise
|
||||
@ -265,7 +265,7 @@ class TestSalarySlip(unittest.TestCase):
|
||||
# total taxable income 566000, 250000 @ 5%, 66000 @ 20%, 12500 + 13200
|
||||
tax_paid = get_tax_paid_in_period(employee)
|
||||
try:
|
||||
self.assertEqual(tax_paid, 25700)
|
||||
self.assertEqual(tax_paid, 121211.48)
|
||||
except AssertionError:
|
||||
print("\nSalary Slip - Tax calculation failed on following case\n", data, "\n")
|
||||
raise
|
||||
@ -365,7 +365,7 @@ def make_earning_salary_component(setup=False, test_tax=False):
|
||||
"salary_component": 'Basic Salary',
|
||||
"abbr":'BS',
|
||||
"condition": 'base > 10000',
|
||||
"formula": 'base*.5',
|
||||
"formula": 'base',
|
||||
"type": "Earning",
|
||||
"amount_based_on_formula": 1
|
||||
},
|
||||
@ -386,7 +386,6 @@ def make_earning_salary_component(setup=False, test_tax=False):
|
||||
{
|
||||
"salary_component": "Leave Encashment",
|
||||
"abbr": 'LE',
|
||||
"is_additional_component": 1,
|
||||
"type": "Earning"
|
||||
}
|
||||
]
|
||||
@ -398,7 +397,8 @@ def make_earning_salary_component(setup=False, test_tax=False):
|
||||
"is_flexible_benefit": 1,
|
||||
"type": "Earning",
|
||||
"pay_against_benefit_claim": 1,
|
||||
"max_benefit_amount": 100000
|
||||
"max_benefit_amount": 100000,
|
||||
"depends_on_payment_days": 0
|
||||
},
|
||||
{
|
||||
"salary_component": "Medical Allowance",
|
||||
@ -409,9 +409,8 @@ def make_earning_salary_component(setup=False, test_tax=False):
|
||||
"max_benefit_amount": 15000
|
||||
},
|
||||
{
|
||||
"salary_component": "Perfomance Bonus",
|
||||
"salary_component": "Performance Bonus",
|
||||
"abbr": 'B',
|
||||
"is_additional_component": 1,
|
||||
"type": "Earning"
|
||||
}
|
||||
])
|
||||
@ -442,7 +441,8 @@ def make_deduction_salary_component(setup=False, test_tax=False):
|
||||
"abbr":'T',
|
||||
"formula": 'base*.1',
|
||||
"type": "Deduction",
|
||||
"amount_based_on_formula": 1
|
||||
"amount_based_on_formula": 1,
|
||||
"depends_on_payment_days": 0
|
||||
}
|
||||
]
|
||||
if not test_tax:
|
||||
@ -512,21 +512,23 @@ def create_tax_slab(payroll_period):
|
||||
{
|
||||
"from_amount": 250000,
|
||||
"to_amount": 500000,
|
||||
"percent_deduction": 5
|
||||
"percent_deduction": 5.2,
|
||||
"condition": "annual_taxable_earning > 500000"
|
||||
},
|
||||
{
|
||||
"from_amount": 500000,
|
||||
"from_amount": 500001,
|
||||
"to_amount": 1000000,
|
||||
"percent_deduction": 20
|
||||
"percent_deduction": 20.8
|
||||
},
|
||||
{
|
||||
"from_amount": 1000000,
|
||||
"percent_deduction": 30
|
||||
"from_amount": 1000001,
|
||||
"percent_deduction": 31.2
|
||||
}
|
||||
]
|
||||
payroll_period.taxable_salary_slabs = []
|
||||
for item in data:
|
||||
payroll_period.append("taxable_salary_slabs", item)
|
||||
payroll_period.standard_tax_exemption_amount = 52500
|
||||
payroll_period.save()
|
||||
|
||||
def create_salary_slips_for_payroll_period(employee, salary_structure, payroll_period, deduct_random=True):
|
||||
@ -559,7 +561,7 @@ def create_additional_salary(employee, payroll_period, amount):
|
||||
"doctype": "Additional Salary",
|
||||
"employee": employee,
|
||||
"company": erpnext.get_default_company(),
|
||||
"salary_component": "Perfomance Bonus",
|
||||
"salary_component": "Performance Bonus",
|
||||
"payroll_date": salary_date,
|
||||
"amount": amount,
|
||||
"type": "Earning"
|
||||
|
@ -147,7 +147,8 @@ frappe.ui.form.on('Salary Structure', {
|
||||
source_name: frm.doc.name,
|
||||
employee: values.employee,
|
||||
as_print: 1,
|
||||
print_format: print_format
|
||||
print_format: print_format,
|
||||
for_preview: 1
|
||||
},
|
||||
callback: function(r) {
|
||||
var new_window = window.open();
|
||||
|
@ -137,7 +137,7 @@ def get_existing_assignments(employees, salary_structure,from_date):
|
||||
return salary_structures_assignments
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_salary_slip(source_name, target_doc = None, employee = None, as_print = False, print_format = None):
|
||||
def make_salary_slip(source_name, target_doc = None, employee = None, as_print = False, print_format = None, for_preview=0):
|
||||
def postprocess(source, target):
|
||||
if employee:
|
||||
employee_details = frappe.db.get_value("Employee", employee,
|
||||
@ -147,7 +147,7 @@ def make_salary_slip(source_name, target_doc = None, employee = None, as_print =
|
||||
target.branch = employee_details.branch
|
||||
target.designation = employee_details.designation
|
||||
target.department = employee_details.department
|
||||
target.run_method('process_salary_structure')
|
||||
target.run_method('process_salary_structure', for_preview=for_preview)
|
||||
|
||||
doc = get_mapped_doc("Salary Structure", source_name, {
|
||||
"Salary Structure": {
|
||||
|
@ -39,18 +39,19 @@ class TestSalaryStructure(unittest.TestCase):
|
||||
holiday_list.save()
|
||||
|
||||
def test_amount_totals(self):
|
||||
frappe.db.set_value("HR Settings", None, "include_holidays_in_total_working_days", 0)
|
||||
sal_slip = frappe.get_value("Salary Slip", {"employee_name":"test_employee_2@salary.com"})
|
||||
if not sal_slip:
|
||||
sal_slip = make_employee_salary_slip("test_employee_2@salary.com", "Monthly", "Salary Structure Sample")
|
||||
self.assertEqual(sal_slip.get("salary_structure"), 'Salary Structure Sample')
|
||||
self.assertEqual(sal_slip.get("earnings")[0].amount, 25000)
|
||||
self.assertEqual(sal_slip.get("earnings")[0].amount, 50000)
|
||||
self.assertEqual(sal_slip.get("earnings")[1].amount, 3000)
|
||||
self.assertEqual(sal_slip.get("earnings")[2].amount, 12500)
|
||||
self.assertEqual(sal_slip.get("gross_pay"), 40500)
|
||||
self.assertEqual(sal_slip.get("earnings")[2].amount, 25000)
|
||||
self.assertEqual(sal_slip.get("gross_pay"), 78000)
|
||||
self.assertEqual(sal_slip.get("deductions")[0].amount, 5000)
|
||||
self.assertEqual(sal_slip.get("deductions")[1].amount, 5000)
|
||||
self.assertEqual(sal_slip.get("total_deduction"), 10000)
|
||||
self.assertEqual(sal_slip.get("net_pay"), 30500)
|
||||
self.assertEqual(sal_slip.get("net_pay"), 68000)
|
||||
|
||||
def test_whitespaces_in_formula_conditions_fields(self):
|
||||
salary_structure = make_salary_structure("Salary Structure Sample", "Monthly", dont_submit=True)
|
||||
|
@ -35,6 +35,8 @@ def execute(filters=None):
|
||||
for d in ded_types:
|
||||
row.append(ss_ded_map.get(ss.name, {}).get(d))
|
||||
|
||||
row.append(ss.total_loan_repayment)
|
||||
|
||||
row += [ss.total_deduction, ss.net_pay]
|
||||
|
||||
data.append(row)
|
||||
@ -67,7 +69,7 @@ def get_columns(salary_slips):
|
||||
|
||||
columns = columns + [(e + ":Currency:120") for e in salary_components[_("Earning")]] + \
|
||||
[_("Gross Pay") + ":Currency:120"] + [(d + ":Currency:120") for d in salary_components[_("Deduction")]] + \
|
||||
[_("Total Deduction") + ":Currency:120", _("Net Pay") + ":Currency:120"]
|
||||
[_("Loan Repayment") + ":Currency:120", _("Total Deduction") + ":Currency:120", _("Net Pay") + ":Currency:120"]
|
||||
|
||||
return columns, salary_components[_("Earning")], salary_components[_("Deduction")]
|
||||
|
||||
|
@ -126,9 +126,9 @@ def get_employee_fields_label():
|
||||
fields = []
|
||||
for df in frappe.get_meta("Employee").get("fields"):
|
||||
if df.fieldname in ["salutation", "user_id", "employee_number", "employment_type",
|
||||
"holiday_list", "branch", "department", "designation", "grade",
|
||||
"notice_number_of_days", "reports_to", "leave_policy", "company_email"]:
|
||||
fields.append({"value": df.fieldname, "label": df.label})
|
||||
"holiday_list", "branch", "department", "designation", "grade",
|
||||
"notice_number_of_days", "reports_to", "leave_policy", "company_email"]:
|
||||
fields.append({"value": df.fieldname, "label": df.label})
|
||||
return fields
|
||||
|
||||
@frappe.whitelist()
|
||||
@ -263,12 +263,6 @@ def get_leave_period(from_date, to_date, company):
|
||||
if leave_period:
|
||||
return leave_period
|
||||
|
||||
def get_payroll_period(from_date, to_date, company):
|
||||
payroll_period = frappe.db.sql("""select name, start_date, end_date from
|
||||
`tabPayroll Period`
|
||||
where start_date<=%s and end_date>= %s and company=%s""", (from_date, to_date, company), as_dict=1)
|
||||
return payroll_period[0] if payroll_period else None
|
||||
|
||||
def allocate_earned_leaves():
|
||||
'''Allocate earned leaves to Employees'''
|
||||
e_leave_types = frappe.get_all("Leave Type",
|
||||
|
@ -31,5 +31,9 @@ frappe.ui.form.on('Blanket Order', {
|
||||
});
|
||||
}).addClass("btn-primary");
|
||||
}
|
||||
},
|
||||
|
||||
onload_post_render: function(frm) {
|
||||
frm.get_field("items").grid.set_multiple_add("item_code", "qty");
|
||||
}
|
||||
});
|
||||
|
@ -0,0 +1,15 @@
|
||||
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
/* eslint-disable */
|
||||
|
||||
frappe.query_reports["BOM Items and Scraps"] = {
|
||||
"filters": [
|
||||
{
|
||||
fieldname: "bom",
|
||||
label: __("BOM"),
|
||||
fieldtype: "Link",
|
||||
options: "BOM",
|
||||
reqd: 1
|
||||
},
|
||||
]
|
||||
};
|
@ -0,0 +1,27 @@
|
||||
{
|
||||
"add_total_row": 0,
|
||||
"creation": "2019-05-14 12:06:14.998746",
|
||||
"disable_prepared_report": 0,
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"idx": 0,
|
||||
"is_standard": "Yes",
|
||||
"modified": "2019-05-14 12:06:14.998746",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "BOM Items and Scraps",
|
||||
"owner": "Administrator",
|
||||
"prepared_report": 0,
|
||||
"ref_doctype": "BOM",
|
||||
"report_name": "BOM Items and Scraps ",
|
||||
"report_type": "Script Report",
|
||||
"roles": [
|
||||
{
|
||||
"role": "Manufacturing Manager"
|
||||
},
|
||||
{
|
||||
"role": "Manufacturing User"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from pprint import pprint
|
||||
|
||||
def execute(filters=None):
|
||||
data = []
|
||||
columns = get_columns()
|
||||
get_data(filters, data)
|
||||
return columns, data
|
||||
|
||||
def get_data(filters, data):
|
||||
get_exploded_items(filters.bom, data)
|
||||
|
||||
def get_exploded_items(bom, data, indent=0):
|
||||
exploded_items = frappe.get_all("BOM Item",
|
||||
filters={"parent": bom},
|
||||
fields= ['qty','bom_no','qty','scrap','item_code','item_name','description','uom'])
|
||||
|
||||
for item in exploded_items:
|
||||
item["indent"] = indent
|
||||
data.append({
|
||||
'item_code': item.item_code,
|
||||
'item_name': item.item_name,
|
||||
'indent': indent,
|
||||
'bom': item.bom_no,
|
||||
'qty': item.qty,
|
||||
'uom': item.uom,
|
||||
'description': item.description,
|
||||
'scrap': item.scrap
|
||||
})
|
||||
if item.bom_no:
|
||||
get_exploded_items(item.bom_no, data, indent=indent+1)
|
||||
|
||||
def get_columns():
|
||||
return [
|
||||
{
|
||||
"label": "Item Code",
|
||||
"fieldtype": "Link",
|
||||
"fieldname": "item_code",
|
||||
"width": 300,
|
||||
"options": "Item"
|
||||
},
|
||||
{
|
||||
"label": "Item Name",
|
||||
"fieldtype": "data",
|
||||
"fieldname": "item_name",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"label": "BOM",
|
||||
"fieldtype": "Link",
|
||||
"fieldname": "bom",
|
||||
"width": 150,
|
||||
"options": "BOM"
|
||||
},
|
||||
{
|
||||
"label": "Qty",
|
||||
"fieldtype": "data",
|
||||
"fieldname": "qty",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"label": "UOM",
|
||||
"fieldtype": "data",
|
||||
"fieldname": "uom",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"label": "Standard Description",
|
||||
"fieldtype": "data",
|
||||
"fieldname": "description",
|
||||
"width": 150
|
||||
},
|
||||
{
|
||||
"label": "Scrap",
|
||||
"fieldtype": "data",
|
||||
"fieldname": "scrap",
|
||||
"width": 100
|
||||
},
|
||||
]
|
@ -70,7 +70,7 @@ def update_instructors(comp_dict):
|
||||
emp_details = frappe.get_all("Employee", fields=["name", "company"])
|
||||
|
||||
for employee in emp_details:
|
||||
records = comp_dict[employee.company]
|
||||
records = comp_dict[employee.company] if employee.company else []
|
||||
|
||||
for department in records:
|
||||
when_then.append('''
|
||||
|
@ -8,6 +8,17 @@ def execute():
|
||||
return
|
||||
frappe.reload_doc("hr", "doctype", "Employee Tax Exemption Declaration")
|
||||
frappe.reload_doc("hr", "doctype", "Employee Tax Exemption Proof Submission")
|
||||
frappe.reload_doc("hr", "doctype", "Employee Grade")
|
||||
frappe.reload_doc("hr", "doctype", "Leave Policy")
|
||||
|
||||
frappe.reload_doc("accounts", "doctype", "Bank Account")
|
||||
frappe.reload_doc("accounts", "doctype", "Tax Withholding Category")
|
||||
frappe.reload_doc("accounts", "doctype", "Allowed To Transact With")
|
||||
frappe.reload_doc("accounts", "doctype", "Finance Book")
|
||||
frappe.reload_doc("accounts", "doctype", "Loyalty Program")
|
||||
|
||||
frappe.reload_doc("stock", "doctype", "Item Barcode")
|
||||
|
||||
make_custom_fields()
|
||||
|
||||
frappe.reload_doc("accounts", "doctype", "sales_taxes_and_charges")
|
||||
|
@ -7,6 +7,9 @@ def execute():
|
||||
if not company:
|
||||
return
|
||||
|
||||
frappe.reload_doc('hr', 'doctype', 'payroll_period')
|
||||
frappe.reload_doc('hr', 'doctype', 'employee_tax_exemption_declaration_category')
|
||||
frappe.reload_doc('hr', 'doctype', 'employee_tax_exemption_proof_submission_detail')
|
||||
frappe.reload_doc('hr', 'doctype', 'employee_tax_exemption_declaration')
|
||||
frappe.reload_doc('hr', 'doctype', 'employee_tax_exemption_proof_submission')
|
||||
|
||||
|
@ -65,7 +65,7 @@ class Project(Document):
|
||||
'name': ("not in", self.deleted_task_list)
|
||||
})
|
||||
|
||||
return frappe.get_all("Task", "*", filters, order_by="exp_start_date asc")
|
||||
return frappe.get_all("Task", "*", filters, order_by="exp_start_date asc, status asc")
|
||||
|
||||
def validate(self):
|
||||
self.validate_weights()
|
||||
|
@ -196,6 +196,7 @@
|
||||
{
|
||||
"fieldname": "description",
|
||||
"fieldtype": "Text Editor",
|
||||
"in_preview": 1,
|
||||
"label": "Task Description",
|
||||
"oldfieldname": "description",
|
||||
"oldfieldtype": "Text Editor",
|
||||
@ -351,7 +352,7 @@
|
||||
"icon": "fa fa-check",
|
||||
"idx": 1,
|
||||
"max_attachments": 5,
|
||||
"modified": "2019-05-01 13:30:29.458916",
|
||||
"modified": "2019-05-16 09:51:15.599416",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Projects",
|
||||
"name": "Task",
|
||||
@ -371,6 +372,7 @@
|
||||
],
|
||||
"search_fields": "subject",
|
||||
"show_name_in_global_search": 1,
|
||||
"show_preview_popup": 1,
|
||||
"sort_order": "DESC",
|
||||
"timeline_field": "project",
|
||||
"title_field": "subject",
|
||||
|
@ -267,6 +267,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
}
|
||||
if(frappe.meta.get_docfield(this.frm.doc.doctype + " Item", "item_code")) {
|
||||
this.setup_item_selector();
|
||||
this.frm.get_field("items").grid.set_multiple_add("item_code", "qty");
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1,18 +1,18 @@
|
||||
<template>
|
||||
<section class="featured-products-section" :class='sectionType'>
|
||||
<h5 class='featured-heading' v-html="title"></h5>
|
||||
<div class="featured-products row">
|
||||
<!-- <p class='lead text-center' v-html="description"></p> -->
|
||||
<slot name="card-list-slot"></slot>
|
||||
<div class="featured-products-section py-3">
|
||||
<h5 class='featured-heading' v-html="title"></h5>
|
||||
<div class="featured-products row">
|
||||
<!-- <p class='lead text-center' v-html="description"></p> -->
|
||||
<slot name="card-list-slot"></slot>
|
||||
</div>
|
||||
<div class='mt-4 text-center'>
|
||||
<slot name="list-bottom"></slot>
|
||||
</div>
|
||||
</div>
|
||||
<div class='mt-4 text-center'>
|
||||
<slot name="list-bottom"></slot>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props:['title', 'description', 'sectionType'],
|
||||
props:['title', 'description'],
|
||||
name: "CardList",
|
||||
};
|
||||
</script>
|
||||
|
@ -8,7 +8,7 @@
|
||||
<div class='card-body'>
|
||||
<h5 class="card-title">{{ course.course_name }}</h5>
|
||||
<span class="course-list text-muted" id="getting-started">
|
||||
{{ course.course_intro }}
|
||||
{{ course.course_intro.substring(0,120) }}
|
||||
</span>
|
||||
</div>
|
||||
<div class='p-3' style="display: flex; justify-content: space-between;">
|
||||
|
@ -1,36 +1,34 @@
|
||||
<template>
|
||||
<section>
|
||||
<div class="py-5">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div>
|
||||
<h3>{{ fullName }}</h3>
|
||||
<ul>
|
||||
<li class="row">
|
||||
<div class="col-md-3 col-sm-4 pr-0 text-muted">Email:</div>
|
||||
<div class="col-md-9 col-sm-8">{{ email }}</div>
|
||||
</li>
|
||||
<li v-if="joiningDate" class="row">
|
||||
<div class="col-md-3 col-sm-4 pr-0 text-muted">Date of Joining:</div>
|
||||
<div class="col-md-9 col-sm-8">{{ joiningDate }}</div>
|
||||
</li>
|
||||
<li class="row">
|
||||
<div class="col-md-3 col-sm-4 pr-0 text-muted">Programs Enrolled:</div>
|
||||
<div class="col-md-9 col-sm-8">
|
||||
<ul v-if="enrolledPrograms">
|
||||
<li v-for="program in enrolledPrograms" :key="program">{{ program }}</li>
|
||||
</ul>
|
||||
<span v-else>None</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<a href="/update-profile" class="edit-button text-muted">Edit Profile</a>
|
||||
<div class="py-5">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div>
|
||||
<h3>{{ fullName }}</h3>
|
||||
<ul>
|
||||
<li class="row">
|
||||
<div class="col-md-3 col-sm-4 pr-0 text-muted">Email:</div>
|
||||
<div class="col-md-9 col-sm-8">{{ email }}</div>
|
||||
</li>
|
||||
<li v-if="joiningDate" class="row">
|
||||
<div class="col-md-3 col-sm-4 pr-0 text-muted">Date of Joining:</div>
|
||||
<div class="col-md-9 col-sm-8">{{ joiningDate }}</div>
|
||||
</li>
|
||||
<li class="row">
|
||||
<div class="col-md-3 col-sm-4 pr-0 text-muted">Programs Enrolled:</div>
|
||||
<div class="col-md-9 col-sm-8">
|
||||
<ul v-if="enrolledPrograms">
|
||||
<li v-for="program in enrolledPrograms" :key="program">{{ program }}</li>
|
||||
</ul>
|
||||
<span v-else>None</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<a href="/update-profile" class="edit-button text-muted">Edit Profile</a>
|
||||
</div>
|
||||
<div ></div>
|
||||
</div>
|
||||
</section>
|
||||
<div ></div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
</div>
|
||||
<div class='card-body'>
|
||||
<h5 class='card-title'>{{ program.program_name }}</h5>
|
||||
<div class="text-muted">{{ program.description.substring(0,200) }}...</div>
|
||||
<div class="text-muted">{{ program.description.substring(0,120) }}...</div>
|
||||
</div>
|
||||
</router-link>
|
||||
<div class='text-right p-3'>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
<section class='mt-2'>
|
||||
<div class='mt-2'>
|
||||
<div>
|
||||
<div class="mt-3 row">
|
||||
<div class="col-md-8">
|
||||
@ -16,8 +16,8 @@
|
||||
<youtube-player :url="contentData.url" class="mt-3"/>
|
||||
<hr>
|
||||
</div>
|
||||
</section>
|
||||
<section class="video-description-section">
|
||||
</div>
|
||||
<div class="video-description-section">
|
||||
<div>
|
||||
<div class="content" v-html="contentData.description">
|
||||
</div>
|
||||
@ -30,7 +30,7 @@
|
||||
Mistake</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
|
@ -1,6 +1,5 @@
|
||||
<template>
|
||||
<div>
|
||||
<breadcrumb/>
|
||||
<component v-bind:is="currentComponent" :content="content" :type="type">
|
||||
<ContentNavigation :nextContent="nextContent" :nextContentType="nextContentType"/>
|
||||
</component>
|
||||
@ -11,7 +10,6 @@ import Article from "../components/Article.vue"
|
||||
import Quiz from "../components/Quiz.vue"
|
||||
import Video from "../components/Video.vue"
|
||||
import ContentNavigation from "../components/ContentNavigation.vue"
|
||||
import Breadcrumb from "../components/Breadcrumb.vue"
|
||||
|
||||
export default {
|
||||
props:['program_name', 'course_name', 'topic', 'type', 'content'],
|
||||
@ -56,8 +54,7 @@ export default {
|
||||
Article,
|
||||
Video,
|
||||
Quiz,
|
||||
ContentNavigation,
|
||||
Breadcrumb
|
||||
ContentNavigation
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<div>
|
||||
<breadcrumb></breadcrumb>
|
||||
<TopSection v-bind:title="course.course_name" v-bind:description="course.description">
|
||||
<TopSection v-bind:title="course.course_name" v-bind:description="course.course_intro">
|
||||
</TopSection>
|
||||
<CardList :title="'Topics'" :description="''" :sectionType="'section-padding section-bg'">
|
||||
<CardList :title="'Topics'" :description="''">
|
||||
<TopicCard slot="card-list-slot" v-for="topic in topicData" :topic="topic" :course_name="course_name" :program_name="program_name" :key="topic.name"/>
|
||||
</CardList>
|
||||
</div>
|
||||
|
@ -3,7 +3,7 @@
|
||||
<TopSection :title="portal.title" :description="portal.description">
|
||||
<TopSectionButton/>
|
||||
</TopSection>
|
||||
<CardList :title="'Featured Programs'" :description="'Master ERPNext'" :sectionType="'section-padding section-bg'">
|
||||
<CardList :title="'Featured Programs'" :description="'Master ERPNext'">
|
||||
<ProgramCard slot="card-list-slot" v-for="item in featuredPrograms" :key="item.program.name" :program="item.program" :enrolled="item.is_enrolled"/>
|
||||
<AButton slot="list-bottom" :type="'primary'" :size="'md'" :route="'List/Program'">View All</AButton>
|
||||
</CardList>
|
||||
|
@ -3,7 +3,7 @@
|
||||
<TopSection :title="'Programs at ' + portal.title" :description="portal.description">
|
||||
<AButton v-if="isLogin" :type="'primary'" :size="'lg'" :route="{ name: 'signup'}">Sign Up</AButton>
|
||||
</TopSection>
|
||||
<CardList :title="'All Programs'" :description="''" :sectionType="'section-padding section-bg'">
|
||||
<CardList :title="'All Programs'" :description="''">
|
||||
<ProgramCard slot="card-list-slot" v-for="item in masterData" :key="item.program.name" :program="item.program" :enrolled="item.is_enrolled"/>
|
||||
</CardList>
|
||||
</div>
|
||||
@ -24,7 +24,7 @@ export default {
|
||||
CourseCard,
|
||||
ProgramCard,
|
||||
CardList,
|
||||
TopSection
|
||||
TopSection
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -2,10 +2,10 @@
|
||||
<div>
|
||||
<ProfileInfo :enrolledPrograms="enrolledPrograms"></ProfileInfo>
|
||||
<div v-if="enrolledPrograms">
|
||||
<CardList :title="'Your Progress'" :description="''" :sectionType="'section-padding section-bg'">
|
||||
<CardList :title="'Your Progress'" :description="''">
|
||||
<ProgressCard slot="card-list-slot" v-for="program in enrolledPrograms" :program="program" :key="program"/>
|
||||
</CardList>
|
||||
<CardList :title="'Quiz Attempts'" :description="''" :sectionType="'section-padding section'">
|
||||
<CardList :title="''" :description="''">
|
||||
<ScoreCard slot="card-list-slot" v-for="program in enrolledPrograms" :program="program" :key="program"/>
|
||||
</CardList>
|
||||
</div>
|
||||
|
@ -3,7 +3,7 @@
|
||||
<breadcrumb></breadcrumb>
|
||||
<TopSection v-bind:title="program.program_name" v-bind:description="program.description">
|
||||
</TopSection>
|
||||
<CardList :title="'Courses'" :description="''" :sectionType="'section-padding'">
|
||||
<CardList :title="'Courses'" :description="''">
|
||||
<CourseCard slot="card-list-slot" v-for="course in courseData" :course="course" :program_name="program_name" :key="course.name"/>
|
||||
</CardList>
|
||||
</div>
|
||||
|
@ -146,11 +146,9 @@ def calculate_annual_eligible_hra_exemption(doc):
|
||||
hra_component = frappe.get_cached_value('Company', doc.company, "hra_component")
|
||||
if not (basic_component and hra_component):
|
||||
frappe.throw(_("Please mention Basic and HRA component in Company"))
|
||||
|
||||
annual_exemption, monthly_exemption, hra_amount = 0, 0, 0
|
||||
if hra_component and basic_component:
|
||||
assignment = get_salary_assignment(doc.employee, nowdate())
|
||||
|
||||
if assignment:
|
||||
hra_component_exists = frappe.db.exists("Salary Detail", {
|
||||
"parent": assignment.salary_structure,
|
||||
@ -158,18 +156,19 @@ def calculate_annual_eligible_hra_exemption(doc):
|
||||
"parentfield": "earnings",
|
||||
"parenttype": "Salary Structure"
|
||||
})
|
||||
|
||||
if hra_component_exists:
|
||||
basic_amount, hra_amount = get_component_amt_from_salary_slip(doc.employee,
|
||||
assignment.salary_structure, basic_component, hra_component)
|
||||
if hra_amount:
|
||||
if doc.monthly_house_rent:
|
||||
annual_exemption = calculate_hra_exemption(assignment.salary_structure,
|
||||
basic_amount, hra_amount, doc.monthly_house_rent,
|
||||
doc.rented_in_metro_city)
|
||||
basic_amount, hra_amount, doc.monthly_house_rent, doc.rented_in_metro_city)
|
||||
if annual_exemption > 0:
|
||||
monthly_exemption = annual_exemption / 12
|
||||
else:
|
||||
annual_exemption = 0
|
||||
|
||||
elif doc.docstatus == 1:
|
||||
frappe.throw(_("Salary Structure must be submitted before submission of Tax Ememption Declaration"))
|
||||
|
||||
@ -180,7 +179,7 @@ def calculate_annual_eligible_hra_exemption(doc):
|
||||
})
|
||||
|
||||
def get_component_amt_from_salary_slip(employee, salary_structure, basic_component, hra_component):
|
||||
salary_slip = make_salary_slip(salary_structure, employee=employee)
|
||||
salary_slip = make_salary_slip(salary_structure, employee=employee, for_preview=1)
|
||||
basic_amt, hra_amt = 0, 0
|
||||
for earning in salary_slip.earnings:
|
||||
if earning.salary_component == basic_component:
|
||||
|
@ -79,10 +79,6 @@ frappe.ui.form.on("Sales Order", {
|
||||
if(!d.delivery_date) d.delivery_date = frm.doc.delivery_date;
|
||||
});
|
||||
refresh_field("items");
|
||||
},
|
||||
|
||||
onload_post_render: function(frm) {
|
||||
frm.get_field("items").grid.set_multiple_add("item_code", "qty");
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -124,6 +124,44 @@ class TestSalesOrder(unittest.TestCase):
|
||||
so.load_from_db()
|
||||
self.assertEqual(so.get("items")[0].delivered_qty, 9)
|
||||
|
||||
def test_return_against_sales_order(self):
|
||||
so = make_sales_order()
|
||||
|
||||
dn = create_dn_against_so(so.name, 6)
|
||||
|
||||
so.load_from_db()
|
||||
self.assertEqual(so.get("items")[0].delivered_qty, 6)
|
||||
|
||||
# Check delivered_qty after make_sales_invoice with update_stock checked
|
||||
si2 = make_sales_invoice(so.name)
|
||||
si2.set("update_stock", 1)
|
||||
si2.get("items")[0].qty = 3
|
||||
si2.insert()
|
||||
si2.submit()
|
||||
|
||||
so.load_from_db()
|
||||
|
||||
self.assertEqual(so.get("items")[0].delivered_qty, 9)
|
||||
|
||||
# Make return deliver note, sales invoice and check quantity
|
||||
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
|
||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
||||
|
||||
dn1 = create_delivery_note(is_return=1, return_against=dn.name, qty=-3, do_not_submit=True)
|
||||
dn1.items[0].against_sales_order = so.name
|
||||
dn1.items[0].so_detail = so.items[0].name
|
||||
dn1.submit()
|
||||
|
||||
si1 = create_sales_invoice(is_return=1, return_against=si2.name, qty=-1, update_stock=1, do_not_submit=True)
|
||||
si1.items[0].sales_order = so.name
|
||||
si1.items[0].so_detail = so.items[0].name
|
||||
si1.submit()
|
||||
|
||||
|
||||
so.load_from_db()
|
||||
self.assertEqual(so.get("items")[0].delivered_qty, 5)
|
||||
|
||||
|
||||
def test_reserved_qty_for_partial_delivery(self):
|
||||
make_stock_entry(target="_Test Warehouse - _TC", qty=10, rate=100)
|
||||
existing_reserved_qty = get_reserved_qty()
|
||||
|
@ -11,11 +11,6 @@ frappe.ui.form.on("Company", {
|
||||
filters: {"type": "Earning"}
|
||||
}
|
||||
});
|
||||
frm.set_query("arrear_component", function(){
|
||||
return {
|
||||
filters: {"is_additional_component": 1}
|
||||
}
|
||||
});
|
||||
|
||||
frm.set_query("parent_company", function() {
|
||||
return {
|
||||
|
@ -182,7 +182,7 @@ class TestBatch(unittest.TestCase):
|
||||
item = item_name,
|
||||
batch_id = batch_name
|
||||
)).insert(ignore_permissions=True)
|
||||
batch.submit()
|
||||
batch.save()
|
||||
|
||||
stock_entry = frappe.get_doc(dict(
|
||||
doctype = 'Stock Entry',
|
||||
|
@ -52,16 +52,20 @@ class DeliveryNote(SellingController):
|
||||
'percent_join_field': 'against_sales_invoice',
|
||||
'overflow_type': 'delivery',
|
||||
'no_tolerance': 1
|
||||
},
|
||||
{
|
||||
'source_dt': 'Delivery Note Item',
|
||||
'target_dt': 'Sales Order Item',
|
||||
'join_field': 'so_detail',
|
||||
'target_field': 'returned_qty',
|
||||
'target_parent_dt': 'Sales Order',
|
||||
'source_field': '-1 * qty',
|
||||
'extra_cond': """ and exists (select name from `tabDelivery Note` where name=`tabDelivery Note Item`.parent and is_return=1)"""
|
||||
}]
|
||||
if cint(self.is_return):
|
||||
self.status_updater.append({
|
||||
'source_dt': 'Delivery Note Item',
|
||||
'target_dt': 'Sales Order Item',
|
||||
'join_field': 'so_detail',
|
||||
'target_field': 'returned_qty',
|
||||
'target_parent_dt': 'Sales Order',
|
||||
'source_field': '-1 * qty',
|
||||
'second_source_dt': 'Sales Invoice Item',
|
||||
'second_source_field': '-1 * qty',
|
||||
'second_join_field': 'so_detail',
|
||||
'extra_cond': """ and exists (select name from `tabDelivery Note` where name=`tabDelivery Note Item`.parent and is_return=1)"""
|
||||
})
|
||||
|
||||
def before_print(self):
|
||||
def toggle_print_hide(meta, fieldname):
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -38,6 +38,10 @@ frappe.ui.form.on('Material Request', {
|
||||
};
|
||||
},
|
||||
|
||||
onload_post_render: function(frm) {
|
||||
frm.get_field("items").grid.set_multiple_add("item_code", "qty");
|
||||
},
|
||||
|
||||
refresh: function(frm) {
|
||||
frm.events.make_custom_buttons(frm);
|
||||
frm.toggle_reqd('customer', frm.doc.material_request_type=="Customer Provided");
|
||||
|
@ -368,13 +368,13 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "received_qty",
|
||||
"fieldtype": "Data",
|
||||
"fieldtype": "Float",
|
||||
"label": "Received Quantity"
|
||||
}
|
||||
],
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"modified": "2019-05-08 10:27:25.008801",
|
||||
"modified": "2019-05-16 17:00:00.056060",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Material Request Item",
|
||||
|
@ -26,10 +26,6 @@ frappe.ui.form.on("Purchase Receipt", {
|
||||
});
|
||||
},
|
||||
|
||||
onload_post_render: function(frm) {
|
||||
frm.get_field("items").grid.set_multiple_add("item_code", "qty");
|
||||
},
|
||||
|
||||
refresh: function(frm) {
|
||||
if(frm.doc.company) {
|
||||
frm.trigger("toggle_display_account_head");
|
||||
|
@ -24,14 +24,17 @@ class PurchaseReceipt(BuyingController):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(PurchaseReceipt, self).__init__(*args, **kwargs)
|
||||
self.status_updater = [{
|
||||
'source_dt': 'Purchase Receipt Item',
|
||||
'target_dt': 'Purchase Order Item',
|
||||
'join_field': 'purchase_order_item',
|
||||
'target_field': 'received_qty',
|
||||
'target_parent_dt': 'Purchase Order',
|
||||
'target_parent_field': 'per_received',
|
||||
'target_ref_field': 'qty',
|
||||
'source_field': 'qty',
|
||||
'source_dt': 'Purchase Receipt Item',
|
||||
'source_field': 'received_qty',
|
||||
'second_source_dt': 'Purchase Invoice Item',
|
||||
'second_source_field': 'received_qty',
|
||||
'second_join_field': 'po_detail',
|
||||
'percent_join_field': 'purchase_order',
|
||||
'overflow_type': 'receipt'
|
||||
},
|
||||
@ -58,6 +61,18 @@ class PurchaseReceipt(BuyingController):
|
||||
# 'overflow_type': 'receipt',
|
||||
'extra_cond': """ and exists (select name from `tabPurchase Receipt` where name=`tabPurchase Receipt Item`.parent and is_return=1)"""
|
||||
}]
|
||||
if cint(self.is_return):
|
||||
self.status_updater.append({
|
||||
'source_dt': 'Purchase Receipt Item',
|
||||
'target_dt': 'Purchase Order Item',
|
||||
'join_field': 'purchase_order_item',
|
||||
'target_field': 'returned_qty',
|
||||
'source_field': '-1 * qty',
|
||||
'second_source_dt': 'Purchase Invoice Item',
|
||||
'second_source_field': '-1 * qty',
|
||||
'second_join_field': 'po_detail',
|
||||
'extra_cond': """ and exists (select name from `tabPurchase Receipt` where name=`tabPurchase Receipt Item`.parent and is_return=1)"""
|
||||
})
|
||||
|
||||
def validate(self):
|
||||
self.validate_posting_time()
|
||||
|
@ -639,9 +639,7 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({
|
||||
}
|
||||
});
|
||||
|
||||
// if(!this.item_selector && false) {
|
||||
// this.item_selector = new erpnext.ItemSelector({frm: this.frm});
|
||||
// }
|
||||
this.frm.get_field("items").grid.set_multiple_add("item_code", "qty");
|
||||
},
|
||||
|
||||
refresh: function() {
|
||||
|
Loading…
Reference in New Issue
Block a user