minor changes
This commit is contained in:
commit
b95f60c6f3
@ -3,7 +3,6 @@ dist: trusty
|
|||||||
|
|
||||||
python:
|
python:
|
||||||
- "2.7"
|
- "2.7"
|
||||||
- "3.6"
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
- mysql
|
- mysql
|
||||||
@ -45,7 +44,7 @@ jobs:
|
|||||||
- stage: test
|
- stage: test
|
||||||
script:
|
script:
|
||||||
- set -e
|
- set -e
|
||||||
- bench run-tests
|
- bench run-tests --app erpnext
|
||||||
env: Server Side Test
|
env: Server Side Test
|
||||||
- # stage
|
- # stage
|
||||||
script:
|
script:
|
||||||
@ -53,5 +52,3 @@ jobs:
|
|||||||
- bench --force restore ~/frappe-bench/20171108_190013_955977f8_database.sql.gz --mariadb-root-password travis
|
- bench --force restore ~/frappe-bench/20171108_190013_955977f8_database.sql.gz --mariadb-root-password travis
|
||||||
- bench migrate
|
- bench migrate
|
||||||
env: Patch Testing
|
env: Patch Testing
|
||||||
allow_failures:
|
|
||||||
- python: "3.6"
|
|
@ -16,11 +16,11 @@ from erpnext.accounts.general_ledger import make_gl_entries, merge_similar_entri
|
|||||||
from erpnext.accounts.doctype.gl_entry.gl_entry import update_outstanding_amt
|
from erpnext.accounts.doctype.gl_entry.gl_entry import update_outstanding_amt
|
||||||
from erpnext.buying.utils import check_for_closed_status
|
from erpnext.buying.utils import check_for_closed_status
|
||||||
from erpnext.accounts.general_ledger import get_round_off_account_and_cost_center
|
from erpnext.accounts.general_ledger import get_round_off_account_and_cost_center
|
||||||
|
from erpnext.assets.doctype.asset.asset import get_asset_account
|
||||||
from frappe.model.mapper import get_mapped_doc
|
from frappe.model.mapper import get_mapped_doc
|
||||||
from six import iteritems
|
from six import iteritems
|
||||||
from erpnext.accounts.doctype.sales_invoice.sales_invoice import validate_inter_company_party, update_linked_invoice,\
|
from erpnext.accounts.doctype.sales_invoice.sales_invoice import validate_inter_company_party, update_linked_invoice,\
|
||||||
unlink_inter_company_invoice
|
unlink_inter_company_invoice
|
||||||
from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account
|
|
||||||
|
|
||||||
form_grid_templates = {
|
form_grid_templates = {
|
||||||
"items": "templates/form_grid/item_grid.html"
|
"items": "templates/form_grid/item_grid.html"
|
||||||
@ -474,17 +474,16 @@ class PurchaseInvoice(BuyingController):
|
|||||||
def get_asset_gl_entry(self, gl_entries):
|
def get_asset_gl_entry(self, gl_entries):
|
||||||
for item in self.get("items"):
|
for item in self.get("items"):
|
||||||
if item.is_fixed_asset:
|
if item.is_fixed_asset:
|
||||||
asset_accounts = self.get_company_default(["asset_received_but_not_billed",
|
eiiav_account = self.get_company_default("expenses_included_in_asset_valuation")
|
||||||
"expenses_included_in_asset_valuation", "capital_work_in_progress_account"])
|
|
||||||
|
|
||||||
asset_amount = flt(item.net_amount) + flt(item.item_tax_amount/self.conversion_rate)
|
asset_amount = flt(item.net_amount) + flt(item.item_tax_amount/self.conversion_rate)
|
||||||
base_asset_amount = flt(item.base_net_amount + item.item_tax_amount)
|
base_asset_amount = flt(item.base_net_amount + item.item_tax_amount)
|
||||||
item.expense_account = item.expense_account or asset_accounts[0]
|
item.expense_account = item.expense_account
|
||||||
|
|
||||||
if (not item.expense_account or frappe.db.get_value('Account',
|
if (not item.expense_account or frappe.db.get_value('Account',
|
||||||
item.expense_account, 'account_type') != 'Asset Received But Not Billed'):
|
item.expense_account, 'account_type') != 'Asset Received But Not Billed'):
|
||||||
frappe.throw(_("Row {0}: Expense account must be of type Asset Received But Not Billed").
|
arbnb_account = self.get_company_default("asset_received_but_not_billed")
|
||||||
format(item.idx))
|
item.expense_account = arbnb_account
|
||||||
|
|
||||||
if not self.update_stock:
|
if not self.update_stock:
|
||||||
asset_rbnb_currency = get_account_currency(item.expense_account)
|
asset_rbnb_currency = get_account_currency(item.expense_account)
|
||||||
@ -498,9 +497,9 @@ class PurchaseInvoice(BuyingController):
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
if item.item_tax_amount:
|
if item.item_tax_amount:
|
||||||
asset_eiiav_currency = get_account_currency(asset_accounts[0])
|
asset_eiiav_currency = get_account_currency(eiiav_account)
|
||||||
gl_entries.append(self.get_gl_dict({
|
gl_entries.append(self.get_gl_dict({
|
||||||
"account": asset_accounts[1],
|
"account": eiiav_account,
|
||||||
"against": self.supplier,
|
"against": self.supplier,
|
||||||
"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
|
"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
|
||||||
"cost_center": item.cost_center,
|
"cost_center": item.cost_center,
|
||||||
@ -510,8 +509,8 @@ class PurchaseInvoice(BuyingController):
|
|||||||
item.item_tax_amount / self.conversion_rate)
|
item.item_tax_amount / self.conversion_rate)
|
||||||
}))
|
}))
|
||||||
else:
|
else:
|
||||||
cwip_account = get_asset_category_account(item.asset,
|
cwip_account = get_asset_account("capital_work_in_progress_account",
|
||||||
'capital_work_in_progress_account') or asset_accounts[2]
|
item.asset, company = self.company)
|
||||||
|
|
||||||
cwip_account_currency = get_account_currency(cwip_account)
|
cwip_account_currency = get_account_currency(cwip_account)
|
||||||
gl_entries.append(self.get_gl_dict({
|
gl_entries.append(self.get_gl_dict({
|
||||||
@ -524,9 +523,9 @@ class PurchaseInvoice(BuyingController):
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
if item.item_tax_amount and not cint(erpnext.is_perpetual_inventory_enabled(self.company)):
|
if item.item_tax_amount and not cint(erpnext.is_perpetual_inventory_enabled(self.company)):
|
||||||
asset_eiiav_currency = get_account_currency(asset_accounts[1])
|
asset_eiiav_currency = get_account_currency(eiiav_account)
|
||||||
gl_entries.append(self.get_gl_dict({
|
gl_entries.append(self.get_gl_dict({
|
||||||
"account": asset_accounts[1],
|
"account": eiiav_account,
|
||||||
"against": self.supplier,
|
"against": self.supplier,
|
||||||
"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
|
"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
|
||||||
"cost_center": item.cost_center,
|
"cost_center": item.cost_center,
|
||||||
|
@ -10,7 +10,7 @@ from frappe.model.document import Document
|
|||||||
from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account
|
from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account
|
||||||
from erpnext.assets.doctype.asset.depreciation \
|
from erpnext.assets.doctype.asset.depreciation \
|
||||||
import get_disposal_account_and_cost_center, get_depreciation_accounts
|
import get_disposal_account_and_cost_center, get_depreciation_accounts
|
||||||
from erpnext.accounts.general_ledger import make_gl_entries
|
from erpnext.accounts.general_ledger import make_gl_entries, delete_gl_entries
|
||||||
from erpnext.accounts.utils import get_account_currency
|
from erpnext.accounts.utils import get_account_currency
|
||||||
from erpnext.controllers.accounts_controller import AccountsController
|
from erpnext.controllers.accounts_controller import AccountsController
|
||||||
|
|
||||||
@ -24,6 +24,8 @@ class Asset(AccountsController):
|
|||||||
self.make_depreciation_schedule()
|
self.make_depreciation_schedule()
|
||||||
self.set_accumulated_depreciation()
|
self.set_accumulated_depreciation()
|
||||||
get_depreciation_accounts(self)
|
get_depreciation_accounts(self)
|
||||||
|
else:
|
||||||
|
self.finance_books = []
|
||||||
if self.get("schedules"):
|
if self.get("schedules"):
|
||||||
self.validate_expected_value_after_useful_life()
|
self.validate_expected_value_after_useful_life()
|
||||||
|
|
||||||
@ -31,11 +33,15 @@ class Asset(AccountsController):
|
|||||||
self.validate_in_use_date()
|
self.validate_in_use_date()
|
||||||
self.set_status()
|
self.set_status()
|
||||||
self.update_stock_movement()
|
self.update_stock_movement()
|
||||||
|
if not self.booked_fixed_asset:
|
||||||
|
self.make_gl_entries()
|
||||||
|
|
||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
self.validate_cancellation()
|
self.validate_cancellation()
|
||||||
self.delete_depreciation_entries()
|
self.delete_depreciation_entries()
|
||||||
self.set_status()
|
self.set_status()
|
||||||
|
delete_gl_entries(voucher_type='Asset', voucher_no=self.name)
|
||||||
|
self.db_set('booked_fixed_asset', 0)
|
||||||
|
|
||||||
def validate_item(self):
|
def validate_item(self):
|
||||||
item = frappe.db.get_value("Item", self.item_code,
|
item = frappe.db.get_value("Item", self.item_code,
|
||||||
@ -323,12 +329,14 @@ class Asset(AccountsController):
|
|||||||
doc.submit()
|
doc.submit()
|
||||||
|
|
||||||
def make_gl_entries(self):
|
def make_gl_entries(self):
|
||||||
if self.purchase_receipt and self.purchase_receipt_amount:
|
if self.purchase_receipt and self.purchase_receipt_amount and self.available_for_use_date <= nowdate():
|
||||||
from erpnext.accounts.general_ledger import make_gl_entries
|
from erpnext.accounts.general_ledger import make_gl_entries
|
||||||
|
|
||||||
gl_entries = []
|
gl_entries = []
|
||||||
|
|
||||||
cwip_account = get_cwip_account(self.name, self.asset_category, self.company)
|
cwip_account = get_asset_account("capital_work_in_progress_account",
|
||||||
|
self.name, self.asset_category, self.company)
|
||||||
|
|
||||||
fixed_aseet_account = get_asset_category_account(self.name, 'fixed_asset_account',
|
fixed_aseet_account = get_asset_category_account(self.name, 'fixed_asset_account',
|
||||||
asset_category = self.asset_category, company = self.company)
|
asset_category = self.asset_category, company = self.company)
|
||||||
|
|
||||||
@ -469,15 +477,17 @@ def get_item_details(item_code, asset_category=None):
|
|||||||
|
|
||||||
return books
|
return books
|
||||||
|
|
||||||
def get_cwip_account(asset, asset_category=None, company=None):
|
def get_asset_account(account_name, asset=None, asset_category=None, company=None):
|
||||||
cwip_account = get_asset_category_account(asset, 'capital_work_in_progress_account',
|
account = None
|
||||||
|
if asset:
|
||||||
|
account = get_asset_category_account(asset, account_name,
|
||||||
asset_category = asset_category, company = company)
|
asset_category = asset_category, company = company)
|
||||||
|
|
||||||
if not cwip_account:
|
if not account:
|
||||||
cwip_account = frappe.db.get_value('Company', company, 'capital_work_in_progress_account')
|
account = frappe.db.get_value('Company', company, account_name)
|
||||||
|
|
||||||
if not cwip_account:
|
if not account:
|
||||||
frappe.throw(_("Set Capital Work In Progress Account in asset category {0} or company {1}")
|
frappe.throw(_("Set {0} in asset category {1} or company {2}")
|
||||||
.format(asset_category, company))
|
.format(account_name.replace('_', ' ').title(), asset_category, company))
|
||||||
|
|
||||||
return cwip_account
|
return account
|
||||||
|
@ -5,7 +5,7 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
import unittest
|
import unittest
|
||||||
from frappe.utils import cstr, nowdate, getdate, flt, add_days
|
from frappe.utils import cstr, nowdate, getdate, flt
|
||||||
from erpnext.assets.doctype.asset.depreciation import post_depreciation_entries, scrap_asset, restore_asset
|
from erpnext.assets.doctype.asset.depreciation import post_depreciation_entries, scrap_asset, restore_asset
|
||||||
from erpnext.assets.doctype.asset.asset import make_sales_invoice, make_purchase_invoice
|
from erpnext.assets.doctype.asset.asset import make_sales_invoice, make_purchase_invoice
|
||||||
|
|
||||||
@ -25,21 +25,19 @@ class TestAsset(unittest.TestCase):
|
|||||||
pi.supplier = "_Test Supplier"
|
pi.supplier = "_Test Supplier"
|
||||||
pi.insert()
|
pi.insert()
|
||||||
pi.submit()
|
pi.submit()
|
||||||
|
|
||||||
asset.load_from_db()
|
asset.load_from_db()
|
||||||
self.assertEqual(asset.supplier, "_Test Supplier")
|
self.assertEqual(asset.supplier, "_Test Supplier")
|
||||||
self.assertEqual(asset.purchase_date, getdate("2015-01-01"))
|
self.assertEqual(asset.purchase_date, getdate("2015-01-01"))
|
||||||
self.assertEqual(asset.purchase_invoice, pi.name)
|
self.assertEqual(asset.purchase_invoice, pi.name)
|
||||||
|
|
||||||
expected_gle = (
|
expected_gle = (
|
||||||
("_Test Fixed Asset - _TC", 100000.0, 0.0),
|
("Asset Received But Not Billed - _TC", 100000.0, 0.0),
|
||||||
("Creditors - _TC", 0.0, 100000.0)
|
("Creditors - _TC", 0.0, 100000.0)
|
||||||
)
|
)
|
||||||
|
|
||||||
gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
|
gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
|
||||||
where voucher_type='Purchase Invoice' and voucher_no = %s
|
where voucher_type='Purchase Invoice' and voucher_no = %s
|
||||||
order by account""", pi.name)
|
order by account""", pi.name)
|
||||||
|
|
||||||
self.assertEqual(gle, expected_gle)
|
self.assertEqual(gle, expected_gle)
|
||||||
|
|
||||||
pi.cancel()
|
pi.cancel()
|
||||||
@ -54,20 +52,21 @@ class TestAsset(unittest.TestCase):
|
|||||||
|
|
||||||
def test_schedule_for_straight_line_method(self):
|
def test_schedule_for_straight_line_method(self):
|
||||||
asset = frappe.get_doc("Asset", {"asset_name": "Macbook Pro 1"})
|
asset = frappe.get_doc("Asset", {"asset_name": "Macbook Pro 1"})
|
||||||
|
asset.calculate_depreciation = 1
|
||||||
asset.append("finance_books", {
|
asset.append("finance_books", {
|
||||||
"expected_value_after_useful_life": 10000,
|
"expected_value_after_useful_life": 10000,
|
||||||
"next_depreciation_date": "2020-12-31",
|
"next_depreciation_date": "2020-12-31",
|
||||||
"depreciation_method": "Straight Line",
|
"depreciation_method": "Straight Line",
|
||||||
"total_number_of_depreciations": 3,
|
"total_number_of_depreciations": 3,
|
||||||
"frequency_of_depreciation": 10,
|
"frequency_of_depreciation": 10,
|
||||||
"depreciation_start_date": add_days(nowdate(), 5)
|
"depreciation_start_date": "2020-06-06"
|
||||||
})
|
})
|
||||||
asset.insert()
|
asset.insert()
|
||||||
self.assertEqual(asset.status, "Draft")
|
self.assertEqual(asset.status, "Draft")
|
||||||
expected_schedules = [
|
expected_schedules = [
|
||||||
["2018-06-11", 490.20, 490.20],
|
["2020-06-06", 163.93, 163.93],
|
||||||
["2019-04-11", 49673.20, 50163.40],
|
["2021-04-06", 49836.07, 50000.0],
|
||||||
["2020-02-11", 39836.60, 90000.00]
|
["2022-02-06", 40000.0, 90000.00]
|
||||||
]
|
]
|
||||||
|
|
||||||
schedules = [[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount]
|
schedules = [[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount]
|
||||||
@ -78,6 +77,7 @@ class TestAsset(unittest.TestCase):
|
|||||||
def test_schedule_for_straight_line_method_for_existing_asset(self):
|
def test_schedule_for_straight_line_method_for_existing_asset(self):
|
||||||
asset = frappe.get_doc("Asset", {"asset_name": "Macbook Pro 1"})
|
asset = frappe.get_doc("Asset", {"asset_name": "Macbook Pro 1"})
|
||||||
asset.is_existing_asset = 1
|
asset.is_existing_asset = 1
|
||||||
|
asset.calculate_depreciation = 1
|
||||||
asset.number_of_depreciations_booked = 1
|
asset.number_of_depreciations_booked = 1
|
||||||
asset.opening_accumulated_depreciation = 40000
|
asset.opening_accumulated_depreciation = 40000
|
||||||
asset.append("finance_books", {
|
asset.append("finance_books", {
|
||||||
@ -86,14 +86,14 @@ class TestAsset(unittest.TestCase):
|
|||||||
"depreciation_method": "Straight Line",
|
"depreciation_method": "Straight Line",
|
||||||
"total_number_of_depreciations": 3,
|
"total_number_of_depreciations": 3,
|
||||||
"frequency_of_depreciation": 10,
|
"frequency_of_depreciation": 10,
|
||||||
"depreciation_start_date": add_days(nowdate(), 5)
|
"depreciation_start_date": "2020-06-06"
|
||||||
})
|
})
|
||||||
asset.insert()
|
asset.insert()
|
||||||
self.assertEqual(asset.status, "Draft")
|
self.assertEqual(asset.status, "Draft")
|
||||||
asset.save()
|
asset.save()
|
||||||
expected_schedules = [
|
expected_schedules = [
|
||||||
["2018-06-11", 588.24, 40588.24],
|
["2020-06-06", 197.37, 40197.37],
|
||||||
["2019-04-11", 49411.76, 90000.00]
|
["2021-04-06", 49802.63, 90000.00]
|
||||||
]
|
]
|
||||||
schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), d.accumulated_depreciation_amount]
|
schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), d.accumulated_depreciation_amount]
|
||||||
for d in asset.get("schedules")]
|
for d in asset.get("schedules")]
|
||||||
@ -102,22 +102,23 @@ class TestAsset(unittest.TestCase):
|
|||||||
|
|
||||||
def test_schedule_for_double_declining_method(self):
|
def test_schedule_for_double_declining_method(self):
|
||||||
asset = frappe.get_doc("Asset", {"asset_name": "Macbook Pro 1"})
|
asset = frappe.get_doc("Asset", {"asset_name": "Macbook Pro 1"})
|
||||||
|
asset.calculate_depreciation = 1
|
||||||
asset.append("finance_books", {
|
asset.append("finance_books", {
|
||||||
"expected_value_after_useful_life": 10000,
|
"expected_value_after_useful_life": 10000,
|
||||||
"next_depreciation_date": "2020-12-31",
|
"next_depreciation_date": "2020-12-31",
|
||||||
"depreciation_method": "Double Declining Balance",
|
"depreciation_method": "Double Declining Balance",
|
||||||
"total_number_of_depreciations": 3,
|
"total_number_of_depreciations": 3,
|
||||||
"frequency_of_depreciation": 10,
|
"frequency_of_depreciation": 10,
|
||||||
"depreciation_start_date": add_days(nowdate(), 5)
|
"depreciation_start_date": "2020-06-06"
|
||||||
})
|
})
|
||||||
asset.insert()
|
asset.insert()
|
||||||
self.assertEqual(asset.status, "Draft")
|
self.assertEqual(asset.status, "Draft")
|
||||||
asset.save()
|
asset.save()
|
||||||
|
|
||||||
expected_schedules = [
|
expected_schedules = [
|
||||||
["2018-06-11", 66667.0, 66667.0],
|
["2020-06-06", 66667.0, 66667.0],
|
||||||
["2019-04-11", 22222.0, 88889.0],
|
["2021-04-06", 22222.0, 88889.0],
|
||||||
["2020-02-11", 1111.0, 90000.0]
|
["2022-02-06", 1111.0, 90000.0]
|
||||||
]
|
]
|
||||||
|
|
||||||
schedules = [[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount]
|
schedules = [[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount]
|
||||||
@ -127,6 +128,7 @@ class TestAsset(unittest.TestCase):
|
|||||||
|
|
||||||
def test_schedule_for_double_declining_method_for_existing_asset(self):
|
def test_schedule_for_double_declining_method_for_existing_asset(self):
|
||||||
asset = frappe.get_doc("Asset", {"asset_name": "Macbook Pro 1"})
|
asset = frappe.get_doc("Asset", {"asset_name": "Macbook Pro 1"})
|
||||||
|
asset.calculate_depreciation = 1
|
||||||
asset.is_existing_asset = 1
|
asset.is_existing_asset = 1
|
||||||
asset.number_of_depreciations_booked = 1
|
asset.number_of_depreciations_booked = 1
|
||||||
asset.opening_accumulated_depreciation = 50000
|
asset.opening_accumulated_depreciation = 50000
|
||||||
@ -136,7 +138,7 @@ class TestAsset(unittest.TestCase):
|
|||||||
"depreciation_method": "Double Declining Balance",
|
"depreciation_method": "Double Declining Balance",
|
||||||
"total_number_of_depreciations": 3,
|
"total_number_of_depreciations": 3,
|
||||||
"frequency_of_depreciation": 10,
|
"frequency_of_depreciation": 10,
|
||||||
"depreciation_start_date": add_days(nowdate(), 5)
|
"depreciation_start_date": "2020-06-06"
|
||||||
})
|
})
|
||||||
asset.insert()
|
asset.insert()
|
||||||
self.assertEqual(asset.status, "Draft")
|
self.assertEqual(asset.status, "Draft")
|
||||||
@ -145,8 +147,8 @@ class TestAsset(unittest.TestCase):
|
|||||||
asset.save()
|
asset.save()
|
||||||
|
|
||||||
expected_schedules = [
|
expected_schedules = [
|
||||||
["2018-06-11", 33333.0, 83333.0],
|
["2020-06-06", 33333.0, 83333.0],
|
||||||
["2019-04-11", 6667.0, 90000.0]
|
["2021-04-06", 6667.0, 90000.0]
|
||||||
]
|
]
|
||||||
|
|
||||||
schedules = [[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount]
|
schedules = [[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount]
|
||||||
@ -157,6 +159,7 @@ class TestAsset(unittest.TestCase):
|
|||||||
def test_schedule_for_prorated_straight_line_method(self):
|
def test_schedule_for_prorated_straight_line_method(self):
|
||||||
set_prorated_depreciation_schedule()
|
set_prorated_depreciation_schedule()
|
||||||
asset = frappe.get_doc("Asset", {"asset_name": "Macbook Pro 1"})
|
asset = frappe.get_doc("Asset", {"asset_name": "Macbook Pro 1"})
|
||||||
|
asset.calculate_depreciation = 1
|
||||||
asset.is_existing_asset = 0
|
asset.is_existing_asset = 0
|
||||||
asset.available_for_use_date = "2020-01-30"
|
asset.available_for_use_date = "2020-01-30"
|
||||||
asset.append("finance_books", {
|
asset.append("finance_books", {
|
||||||
@ -186,6 +189,7 @@ class TestAsset(unittest.TestCase):
|
|||||||
|
|
||||||
def test_depreciation(self):
|
def test_depreciation(self):
|
||||||
asset = frappe.get_doc("Asset", {"asset_name": "Macbook Pro 1"})
|
asset = frappe.get_doc("Asset", {"asset_name": "Macbook Pro 1"})
|
||||||
|
asset.calculate_depreciation = 1
|
||||||
asset.available_for_use_date = "2020-01-30"
|
asset.available_for_use_date = "2020-01-30"
|
||||||
asset.append("finance_books", {
|
asset.append("finance_books", {
|
||||||
"expected_value_after_useful_life": 10000,
|
"expected_value_after_useful_life": 10000,
|
||||||
@ -220,6 +224,7 @@ class TestAsset(unittest.TestCase):
|
|||||||
|
|
||||||
def test_depreciation_entry_cancellation(self):
|
def test_depreciation_entry_cancellation(self):
|
||||||
asset = frappe.get_doc("Asset", {"asset_name": "Macbook Pro 1"})
|
asset = frappe.get_doc("Asset", {"asset_name": "Macbook Pro 1"})
|
||||||
|
asset.calculate_depreciation = 1
|
||||||
asset.append("finance_books", {
|
asset.append("finance_books", {
|
||||||
"expected_value_after_useful_life": 10000,
|
"expected_value_after_useful_life": 10000,
|
||||||
"depreciation_method": "Straight Line",
|
"depreciation_method": "Straight Line",
|
||||||
@ -242,15 +247,15 @@ class TestAsset(unittest.TestCase):
|
|||||||
depr_entry = asset.get("schedules")[0].journal_entry
|
depr_entry = asset.get("schedules")[0].journal_entry
|
||||||
self.assertFalse(depr_entry)
|
self.assertFalse(depr_entry)
|
||||||
|
|
||||||
|
|
||||||
def test_scrap_asset(self):
|
def test_scrap_asset(self):
|
||||||
asset = frappe.get_doc("Asset", {"asset_name": "Macbook Pro 1"})
|
asset = frappe.get_doc("Asset", {"asset_name": "Macbook Pro 1"})
|
||||||
|
asset.calculate_depreciation = 1
|
||||||
asset.append("finance_books", {
|
asset.append("finance_books", {
|
||||||
"expected_value_after_useful_life": 10000,
|
"expected_value_after_useful_life": 10000,
|
||||||
"depreciation_method": "Straight Line",
|
"depreciation_method": "Straight Line",
|
||||||
"total_number_of_depreciations": 3,
|
"total_number_of_depreciations": 3,
|
||||||
"frequency_of_depreciation": 10,
|
"frequency_of_depreciation": 10,
|
||||||
"depreciation_start_date": "2020-12-31"
|
"depreciation_start_date": "2020-06-06"
|
||||||
})
|
})
|
||||||
asset.insert()
|
asset.insert()
|
||||||
asset.submit()
|
asset.submit()
|
||||||
@ -280,6 +285,7 @@ class TestAsset(unittest.TestCase):
|
|||||||
|
|
||||||
def test_asset_sale(self):
|
def test_asset_sale(self):
|
||||||
asset = frappe.get_doc("Asset", {"asset_name": "Macbook Pro 1"})
|
asset = frappe.get_doc("Asset", {"asset_name": "Macbook Pro 1"})
|
||||||
|
asset.calculate_depreciation = 1
|
||||||
asset.append("finance_books", {
|
asset.append("finance_books", {
|
||||||
"expected_value_after_useful_life": 10000,
|
"expected_value_after_useful_life": 10000,
|
||||||
"depreciation_method": "Straight Line",
|
"depreciation_method": "Straight Line",
|
||||||
@ -320,13 +326,13 @@ class TestAsset(unittest.TestCase):
|
|||||||
|
|
||||||
def test_asset_expected_value_after_useful_life(self):
|
def test_asset_expected_value_after_useful_life(self):
|
||||||
asset = frappe.get_doc("Asset", {"asset_name": "Macbook Pro 1"})
|
asset = frappe.get_doc("Asset", {"asset_name": "Macbook Pro 1"})
|
||||||
asset.depreciation_method = "Straight Line"
|
asset.calculate_depreciation = 1
|
||||||
asset.append("finance_books", {
|
asset.append("finance_books", {
|
||||||
"expected_value_after_useful_life": 10000,
|
"expected_value_after_useful_life": 10000,
|
||||||
"depreciation_method": "Straight Line",
|
"depreciation_method": "Straight Line",
|
||||||
"total_number_of_depreciations": 3,
|
"total_number_of_depreciations": 3,
|
||||||
"frequency_of_depreciation": 10,
|
"frequency_of_depreciation": 10,
|
||||||
"depreciation_start_date": "2020-12-31"
|
"depreciation_start_date": "2020-06-06"
|
||||||
})
|
})
|
||||||
asset.insert()
|
asset.insert()
|
||||||
accumulated_depreciation_after_full_schedule = \
|
accumulated_depreciation_after_full_schedule = \
|
||||||
@ -339,7 +345,6 @@ class TestAsset(unittest.TestCase):
|
|||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
asset = frappe.get_doc("Asset", {"asset_name": "Macbook Pro 1"})
|
asset = frappe.get_doc("Asset", {"asset_name": "Macbook Pro 1"})
|
||||||
|
|
||||||
if asset.docstatus == 1 and asset.status not in ("Scrapped", "Sold", "Draft", "Cancelled"):
|
if asset.docstatus == 1 and asset.status not in ("Scrapped", "Sold", "Draft", "Cancelled"):
|
||||||
asset.cancel()
|
asset.cancel()
|
||||||
|
|
||||||
@ -367,11 +372,11 @@ def create_asset():
|
|||||||
"item_code": "Macbook Pro",
|
"item_code": "Macbook Pro",
|
||||||
"company": "_Test Company",
|
"company": "_Test Company",
|
||||||
"purchase_date": "2015-01-01",
|
"purchase_date": "2015-01-01",
|
||||||
"calculate_depreciation": 1,
|
"calculate_depreciation": 0,
|
||||||
"gross_purchase_amount": 100000,
|
"gross_purchase_amount": 100000,
|
||||||
"expected_value_after_useful_life": 10000,
|
"expected_value_after_useful_life": 10000,
|
||||||
"warehouse": "_Test Warehouse - _TC",
|
"warehouse": "_Test Warehouse - _TC",
|
||||||
"available_for_use_date": add_days(nowdate(),3),
|
"available_for_use_date": "2020-06-06",
|
||||||
"location": "Test Location",
|
"location": "Test Location",
|
||||||
"asset_owner": "Company"
|
"asset_owner": "Company"
|
||||||
})
|
})
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_in_quick_entry": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
@ -47,6 +48,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_in_quick_entry": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
@ -79,6 +81,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_in_quick_entry": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
@ -112,6 +115,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_in_quick_entry": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
@ -142,6 +146,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_in_quick_entry": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
@ -175,6 +180,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_in_quick_entry": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
@ -208,6 +214,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_in_quick_entry": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
@ -238,6 +245,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_in_quick_entry": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
@ -270,6 +278,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_in_quick_entry": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
@ -303,6 +312,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_in_quick_entry": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
@ -336,6 +346,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_in_quick_entry": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
@ -369,6 +380,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_in_quick_entry": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
@ -399,6 +411,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_in_quick_entry": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
@ -432,6 +445,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_in_quick_entry": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
@ -463,6 +477,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_in_quick_entry": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
@ -495,6 +510,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_in_quick_entry": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
@ -525,11 +541,12 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_in_quick_entry": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
"fetch_from": "task.has_certificate",
|
"fetch_from": "task.certificate_required",
|
||||||
"fieldname": "has_certificate",
|
"fieldname": "has_certificate",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -558,6 +575,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_in_quick_entry": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
@ -590,6 +608,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_in_quick_entry": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
@ -620,6 +639,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_in_quick_entry": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
@ -653,6 +673,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_in_quick_entry": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
@ -683,6 +704,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_in_quick_entry": 0,
|
||||||
"allow_on_submit": 1,
|
"allow_on_submit": 1,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
@ -714,6 +736,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_in_quick_entry": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
@ -755,7 +778,7 @@
|
|||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2018-05-25 22:43:39.866477",
|
"modified": "2018-06-09 23:45:55.492528",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Assets",
|
"module": "Assets",
|
||||||
"name": "Asset Maintenance Log",
|
"name": "Asset Maintenance Log",
|
||||||
|
@ -3,6 +3,6 @@
|
|||||||
|
|
||||||
frappe.ui.form.on('Asset Movement', {
|
frappe.ui.form.on('Asset Movement', {
|
||||||
onload: function(frm) {
|
onload: function(frm) {
|
||||||
|
//
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -277,7 +277,7 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
"fetch_from": "asset.location",
|
"fetch_from": "",
|
||||||
"fieldname": "source_location",
|
"fieldname": "source_location",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -575,7 +575,7 @@
|
|||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2018-06-06 06:21:36.607432",
|
"modified": "2018-06-11 18:42:55.381972",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Assets",
|
"module": "Assets",
|
||||||
"name": "Asset Movement",
|
"name": "Asset Movement",
|
||||||
|
@ -31,7 +31,7 @@ class AssetMovement(Document):
|
|||||||
if self.purpose in ['Transfer', 'Issue']:
|
if self.purpose in ['Transfer', 'Issue']:
|
||||||
self.source_location = frappe.db.get_value("Asset", self.asset, "location")
|
self.source_location = frappe.db.get_value("Asset", self.asset, "location")
|
||||||
|
|
||||||
if self.source_location == self.target_location:
|
if self.source_location == self.target_location and self.purpose == 'Transfer':
|
||||||
frappe.throw(_("Source and Target Location cannot be same"))
|
frappe.throw(_("Source and Target Location cannot be same"))
|
||||||
|
|
||||||
def on_submit(self):
|
def on_submit(self):
|
||||||
|
@ -339,6 +339,7 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
|
"default": "Material Transferred for Subcontract",
|
||||||
"fieldname": "backflush_raw_materials_of_subcontract_based_on",
|
"fieldname": "backflush_raw_materials_of_subcontract_based_on",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -377,7 +378,7 @@
|
|||||||
"issingle": 1,
|
"issingle": 1,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2018-05-30 16:28:46.899823",
|
"modified": "2018-06-12 03:41:41.739193",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Buying",
|
"module": "Buying",
|
||||||
"name": "Buying Settings",
|
"name": "Buying Settings",
|
||||||
|
@ -558,7 +558,8 @@ class BuyingController(StockController):
|
|||||||
'actual_qty': d.qty,
|
'actual_qty': d.qty,
|
||||||
'purchase_document_type': self.doctype,
|
'purchase_document_type': self.doctype,
|
||||||
'purchase_document_no': self.name,
|
'purchase_document_no': self.name,
|
||||||
'asset': d.asset
|
'asset': d.asset,
|
||||||
|
'location': d.asset_location
|
||||||
})
|
})
|
||||||
d.db_set('serial_no', serial_nos)
|
d.db_set('serial_no', serial_nos)
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ class StockController(AccountsController):
|
|||||||
items, warehouses = self.get_items_and_warehouses()
|
items, warehouses = self.get_items_and_warehouses()
|
||||||
update_gl_entries_after(self.posting_date, self.posting_time, warehouses, items,
|
update_gl_entries_after(self.posting_date, self.posting_time, warehouses, items,
|
||||||
warehouse_account)
|
warehouse_account)
|
||||||
elif self.doctype in ['Purchase Receipt', 'Purchase Invoice']:
|
elif self.doctype in ['Purchase Receipt', 'Purchase Invoice'] and self.docstatus == 1:
|
||||||
gl_entries = []
|
gl_entries = []
|
||||||
gl_entries = self.get_asset_gl_entry(gl_entries)
|
gl_entries = self.get_asset_gl_entry(gl_entries)
|
||||||
make_gl_entries(gl_entries, from_repost=from_repost)
|
make_gl_entries(gl_entries, from_repost=from_repost)
|
||||||
|
@ -10,7 +10,7 @@ In ERPNext, your accounting operations consists of 3 main transactions:
|
|||||||
* Purchase Invoice: Bills that your Suppliers give you for their products or services.
|
* Purchase Invoice: Bills that your Suppliers give you for their products or services.
|
||||||
* Journal Entries: For accounting entries, like payments, credit and other types.
|
* Journal Entries: For accounting entries, like payments, credit and other types.
|
||||||
|
|
||||||
<div class="embed-containers">
|
<div class="embed-container">
|
||||||
<iframe src="https://www.youtube.com/embed/5wjollWN0OA?rel=0" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen>
|
<iframe src="https://www.youtube.com/embed/5wjollWN0OA?rel=0" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen>
|
||||||
</iframe>
|
</iframe>
|
||||||
</div>
|
</div>
|
||||||
|
@ -216,7 +216,7 @@ def get_supplier(shopify_item):
|
|||||||
"doctype": "Supplier",
|
"doctype": "Supplier",
|
||||||
"supplier_name": shopify_item.get("vendor"),
|
"supplier_name": shopify_item.get("vendor"),
|
||||||
"shopify_supplier_id": shopify_item.get("vendor").lower(),
|
"shopify_supplier_id": shopify_item.get("vendor").lower(),
|
||||||
"supplier_type": get_supplier_type()
|
"supplier_group": get_supplier_group()
|
||||||
}).insert()
|
}).insert()
|
||||||
return supplier.name
|
return supplier.name
|
||||||
else:
|
else:
|
||||||
@ -224,15 +224,15 @@ def get_supplier(shopify_item):
|
|||||||
else:
|
else:
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
def get_supplier_type():
|
def get_supplier_group():
|
||||||
supplier_type = frappe.db.get_value("Supplier Type", _("Shopify Supplier"))
|
supplier_group = frappe.db.get_value("Supplier Group", _("Shopify Supplier"))
|
||||||
if not supplier_type:
|
if not supplier_group:
|
||||||
supplier_type = frappe.get_doc({
|
supplier_group = frappe.get_doc({
|
||||||
"doctype": "Supplier Type",
|
"doctype": "Supplier Group",
|
||||||
"supplier_type": _("Shopify Supplier")
|
"supplier_group_name": _("Shopify Supplier")
|
||||||
}).insert()
|
}).insert()
|
||||||
return supplier_type.name
|
return supplier_group.name
|
||||||
return supplier_type
|
return supplier_group
|
||||||
|
|
||||||
def get_item_details(shopify_item):
|
def get_item_details(shopify_item):
|
||||||
item_details = {}
|
item_details = {}
|
||||||
|
@ -33,8 +33,8 @@ class ClinicalProcedureTemplate(Document):
|
|||||||
frappe.throw("""Not permitted. Please disable the Procedure Template""")
|
frappe.throw("""Not permitted. Please disable the Procedure Template""")
|
||||||
|
|
||||||
def get_item_details(self, args=None):
|
def get_item_details(self, args=None):
|
||||||
item = frappe.db.sql("""select stock_uom, description, image, item_name,
|
item = frappe.db.sql("""select stock_uom, item_name
|
||||||
expense_account, buying_cost_center, item_group from `tabItem`
|
from `tabItem`
|
||||||
where name = %s
|
where name = %s
|
||||||
and disabled=0
|
and disabled=0
|
||||||
and (end_of_life is null or end_of_life='0000-00-00' or end_of_life > %s)""",
|
and (end_of_life is null or end_of_life='0000-00-00' or end_of_life > %s)""",
|
||||||
|
@ -25,11 +25,6 @@ class AdditionalSalary(Document):
|
|||||||
frappe.throw(_("To date can not greater than employee's relieving date"))
|
frappe.throw(_("To date can not greater than employee's relieving date"))
|
||||||
|
|
||||||
def get_amount(self, sal_start_date, sal_end_date):
|
def get_amount(self, sal_start_date, sal_end_date):
|
||||||
# If additional salary dates in between the salary slip dates
|
|
||||||
# then return complete additional salary amount
|
|
||||||
if getdate(sal_start_date) <= getdate(self.from_date) <= getdate(sal_end_date)\
|
|
||||||
and getdate(sal_end_date) >= getdate(self.to_date) >= getdate(sal_start_date):
|
|
||||||
return self.amount
|
|
||||||
start_date = getdate(sal_start_date)
|
start_date = getdate(sal_start_date)
|
||||||
end_date = getdate(sal_end_date)
|
end_date = getdate(sal_end_date)
|
||||||
total_days = date_diff(getdate(self.to_date), getdate(self.from_date)) + 1
|
total_days = date_diff(getdate(self.to_date), getdate(self.from_date)) + 1
|
||||||
@ -38,12 +33,9 @@ class AdditionalSalary(Document):
|
|||||||
start_date = getdate(self.from_date)
|
start_date = getdate(self.from_date)
|
||||||
if getdate(sal_end_date) > getdate(self.to_date):
|
if getdate(sal_end_date) > getdate(self.to_date):
|
||||||
end_date = getdate(self.to_date)
|
end_date = getdate(self.to_date)
|
||||||
no_of_days = date_diff(getdate(end_date), getdate(start_date))
|
no_of_days = date_diff(getdate(end_date), getdate(start_date)) + 1
|
||||||
return amount_per_day * no_of_days
|
return amount_per_day * no_of_days
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_additional_salary_component(employee, start_date, end_date):
|
def get_additional_salary_component(employee, start_date, end_date):
|
||||||
additional_components = frappe.db.sql("""
|
additional_components = frappe.db.sql("""
|
||||||
|
@ -7,7 +7,8 @@ import frappe
|
|||||||
from frappe.utils import getdate, validate_email_add, today, add_years
|
from frappe.utils import getdate, validate_email_add, today, add_years
|
||||||
from frappe.model.naming import set_name_by_naming_series
|
from frappe.model.naming import set_name_by_naming_series
|
||||||
from frappe import throw, _, scrub
|
from frappe import throw, _, scrub
|
||||||
import frappe.permissions
|
from frappe.permissions import add_user_permission, remove_user_permission, \
|
||||||
|
set_user_permission_if_allowed, has_permission
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from erpnext.utilities.transaction_base import delete_events
|
from erpnext.utilities.transaction_base import delete_events
|
||||||
from frappe.utils.nestedset import NestedSet
|
from frappe.utils.nestedset import NestedSet
|
||||||
@ -51,7 +52,7 @@ class Employee(NestedSet):
|
|||||||
else:
|
else:
|
||||||
existing_user_id = frappe.db.get_value("Employee", self.name, "user_id")
|
existing_user_id = frappe.db.get_value("Employee", self.name, "user_id")
|
||||||
if existing_user_id:
|
if existing_user_id:
|
||||||
frappe.permissions.remove_user_permission(
|
remove_user_permission(
|
||||||
"Employee", self.name, existing_user_id)
|
"Employee", self.name, existing_user_id)
|
||||||
|
|
||||||
def update_nsm_model(self):
|
def update_nsm_model(self):
|
||||||
@ -65,8 +66,8 @@ class Employee(NestedSet):
|
|||||||
|
|
||||||
def update_user_permissions(self):
|
def update_user_permissions(self):
|
||||||
if not self.create_user_permission: return
|
if not self.create_user_permission: return
|
||||||
frappe.permissions.add_user_permission("Employee", self.name, self.user_id)
|
add_user_permission("Employee", self.name, self.user_id)
|
||||||
frappe.permissions.set_user_permission_if_allowed("Company", self.company, self.user_id)
|
set_user_permission_if_allowed("Company", self.company, self.user_id)
|
||||||
|
|
||||||
def update_user(self):
|
def update_user(self):
|
||||||
# add employee role if missing
|
# add employee role if missing
|
||||||
@ -206,6 +207,9 @@ def validate_employee_role(doc, method):
|
|||||||
def update_user_permissions(doc, method):
|
def update_user_permissions(doc, method):
|
||||||
# called via User hook
|
# called via User hook
|
||||||
if "Employee" in [d.role for d in doc.get("roles")]:
|
if "Employee" in [d.role for d in doc.get("roles")]:
|
||||||
|
employee_name = frappe.get_value('Employee', {'user_id': doc.name}, 'name')
|
||||||
|
if has_user_permission_for_employee(doc.name, employee_name): return
|
||||||
|
if not has_permission('User Permission', ptype='write'): return
|
||||||
employee = frappe.get_doc("Employee", {"user_id": doc.name})
|
employee = frappe.get_doc("Employee", {"user_id": doc.name})
|
||||||
employee.update_user_permissions()
|
employee.update_user_permissions()
|
||||||
|
|
||||||
@ -342,3 +346,11 @@ def get_children(doctype, parent=None, company=None, is_root=False, is_tree=Fals
|
|||||||
|
|
||||||
def on_doctype_update():
|
def on_doctype_update():
|
||||||
frappe.db.add_index("Employee", ["lft", "rgt"])
|
frappe.db.add_index("Employee", ["lft", "rgt"])
|
||||||
|
|
||||||
|
def has_user_permission_for_employee(user_name, employee_name):
|
||||||
|
return frappe.db.exists({
|
||||||
|
'doctype': 'User Permission',
|
||||||
|
'user': user_name,
|
||||||
|
'allow': 'Employee',
|
||||||
|
'for_value': employee_name
|
||||||
|
})
|
||||||
|
@ -11,13 +11,43 @@ frappe.ui.form.on('Employee Benefit Application', {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
employee: function(frm) {
|
employee: function(frm) {
|
||||||
if(frm.doc.employee && frm.doc.date){
|
var method, args;
|
||||||
frappe.call({
|
if(frm.doc.employee && frm.doc.date && frm.doc.payroll_period){
|
||||||
method: "erpnext.hr.doctype.employee_benefit_application.employee_benefit_application.get_max_benefits",
|
method = "erpnext.hr.doctype.employee_benefit_application.employee_benefit_application.get_max_benefits_remaining";
|
||||||
args:{
|
args = {
|
||||||
|
employee: frm.doc.employee,
|
||||||
|
on_date: frm.doc.date,
|
||||||
|
payroll_period: frm.doc.payroll_period
|
||||||
|
};
|
||||||
|
get_max_benefits(frm, method, args);
|
||||||
|
}
|
||||||
|
else if(frm.doc.employee && frm.doc.date){
|
||||||
|
method = "erpnext.hr.doctype.employee_benefit_application.employee_benefit_application.get_max_benefits";
|
||||||
|
args = {
|
||||||
employee: frm.doc.employee,
|
employee: frm.doc.employee,
|
||||||
on_date: frm.doc.date
|
on_date: frm.doc.date
|
||||||
|
};
|
||||||
|
get_max_benefits(frm, method, args);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
payroll_period: function(frm) {
|
||||||
|
var method, args;
|
||||||
|
if(frm.doc.employee && frm.doc.date && frm.doc.payroll_period){
|
||||||
|
method = "erpnext.hr.doctype.employee_benefit_application.employee_benefit_application.get_max_benefits_remaining";
|
||||||
|
args = {
|
||||||
|
employee: frm.doc.employee,
|
||||||
|
on_date: frm.doc.date,
|
||||||
|
payroll_period: frm.doc.payroll_period
|
||||||
|
};
|
||||||
|
get_max_benefits(frm, method, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var get_max_benefits=function(frm, method, args) {
|
||||||
|
frappe.call({
|
||||||
|
method: method,
|
||||||
|
args: args,
|
||||||
callback: function (data) {
|
callback: function (data) {
|
||||||
if(!data.exc){
|
if(!data.exc){
|
||||||
if(data.message){
|
if(data.message){
|
||||||
@ -26,9 +56,7 @@ frappe.ui.form.on('Employee Benefit Application', {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
frappe.ui.form.on("Employee Benefit Application Detail",{
|
frappe.ui.form.on("Employee Benefit Application Detail",{
|
||||||
amount: function(frm) {
|
amount: function(frm) {
|
||||||
|
@ -5,10 +5,11 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.utils import date_diff, getdate
|
from frappe.utils import date_diff, getdate, rounded
|
||||||
from frappe.model.document import Document
|
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
|
||||||
from erpnext.hr.doctype.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure
|
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
|
||||||
|
|
||||||
class EmployeeBenefitApplication(Document):
|
class EmployeeBenefitApplication(Document):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
@ -41,9 +42,10 @@ class EmployeeBenefitApplication(Document):
|
|||||||
pro_rata_amount += max_benefit_amount
|
pro_rata_amount += max_benefit_amount
|
||||||
else:
|
else:
|
||||||
non_pro_rata_amount += max_benefit_amount
|
non_pro_rata_amount += max_benefit_amount
|
||||||
|
|
||||||
if pro_rata_amount == 0 and non_pro_rata_amount == 0:
|
if pro_rata_amount == 0 and non_pro_rata_amount == 0:
|
||||||
frappe.throw(_("Please add the remainig benefits {0} to any of the existing component").format(self.remainig_benefits))
|
frappe.throw(_("Please add the remainig benefits {0} to any of the existing component").format(self.remainig_benefits))
|
||||||
elif non_pro_rata_amount > 0 and non_pro_rata_amount < self.remainig_benefits:
|
elif non_pro_rata_amount > 0 and non_pro_rata_amount < rounded(self.remainig_benefits):
|
||||||
frappe.throw(_("You can claim only an amount of {0}, the rest amount {1} should be in the application \
|
frappe.throw(_("You can claim only an amount of {0}, the rest amount {1} should be in the application \
|
||||||
as pro-rata component").format(non_pro_rata_amount, self.remainig_benefits - non_pro_rata_amount))
|
as pro-rata component").format(non_pro_rata_amount, self.remainig_benefits - non_pro_rata_amount))
|
||||||
elif non_pro_rata_amount == 0:
|
elif non_pro_rata_amount == 0:
|
||||||
@ -65,7 +67,9 @@ class EmployeeBenefitApplication(Document):
|
|||||||
for employee_benefit in self.employee_benefits:
|
for employee_benefit in self.employee_benefits:
|
||||||
if employee_benefit.earning_component == earning_component_name:
|
if employee_benefit.earning_component == earning_component_name:
|
||||||
benefit_amount += employee_benefit.amount
|
benefit_amount += employee_benefit.amount
|
||||||
if benefit_amount > max_benefit_amount:
|
prev_sal_slip_flexi_amount = get_sal_slip_total_benefit_given(self.employee, frappe.get_doc("Payroll Period", self.payroll_period), earning_component_name)
|
||||||
|
benefit_amount += prev_sal_slip_flexi_amount
|
||||||
|
if rounded(benefit_amount, 2) > max_benefit_amount:
|
||||||
frappe.throw(_("Maximum benefit amount of component {0} exceeds {1}").format(earning_component_name, max_benefit_amount))
|
frappe.throw(_("Maximum benefit amount of component {0} exceeds {1}").format(earning_component_name, max_benefit_amount))
|
||||||
|
|
||||||
def validate_duplicate_on_payroll_period(self):
|
def validate_duplicate_on_payroll_period(self):
|
||||||
@ -87,10 +91,17 @@ def get_max_benefits(employee, on_date):
|
|||||||
max_benefits = frappe.db.get_value("Salary Structure", sal_struct, "max_benefits")
|
max_benefits = frappe.db.get_value("Salary Structure", sal_struct, "max_benefits")
|
||||||
if max_benefits > 0:
|
if max_benefits > 0:
|
||||||
return max_benefits
|
return max_benefits
|
||||||
else:
|
return False
|
||||||
frappe.throw(_("Employee {0} has no max benefits in salary structure {1}").format(employee, sal_struct[0][0]))
|
|
||||||
else:
|
@frappe.whitelist()
|
||||||
frappe.throw(_("Employee {0} has no salary structure assigned").format(employee))
|
def get_max_benefits_remaining(employee, on_date, payroll_period):
|
||||||
|
max_benefits = get_max_benefits(employee, on_date)
|
||||||
|
if max_benefits and max_benefits > 0:
|
||||||
|
payroll_period_obj = frappe.get_doc("Payroll Period", payroll_period)
|
||||||
|
# Get all salary slip flexi amount in the payroll period
|
||||||
|
prev_sal_slip_flexi_total = get_sal_slip_total_benefit_given(employee, payroll_period_obj)
|
||||||
|
return max_benefits - prev_sal_slip_flexi_total
|
||||||
|
return max_benefits
|
||||||
|
|
||||||
def get_benefit_component_amount(employee, start_date, end_date, struct_row, sal_struct):
|
def get_benefit_component_amount(employee, start_date, end_date, struct_row, sal_struct):
|
||||||
# Considering there is only one application for an year
|
# Considering there is only one application for an year
|
||||||
|
@ -13,6 +13,8 @@ from erpnext.hr.doctype.salary_structure_assignment.salary_structure_assignment
|
|||||||
class EmployeeBenefitClaim(Document):
|
class EmployeeBenefitClaim(Document):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
max_benefits = get_max_benefits(self.employee, self.claim_date)
|
max_benefits = get_max_benefits(self.employee, self.claim_date)
|
||||||
|
if not max_benefits or max_benefits <= 0:
|
||||||
|
frappe.throw(_("Employee {0} has no maximum benefit amount").format(self.employee))
|
||||||
payroll_period = get_payroll_period(self.claim_date, self.claim_date, frappe.db.get_value("Employee", self.employee, "company"))
|
payroll_period = get_payroll_period(self.claim_date, self.claim_date, frappe.db.get_value("Employee", self.employee, "company"))
|
||||||
self.validate_max_benefit_for_component(payroll_period)
|
self.validate_max_benefit_for_component(payroll_period)
|
||||||
self.validate_max_benefit_for_sal_struct(max_benefits)
|
self.validate_max_benefit_for_sal_struct(max_benefits)
|
||||||
|
@ -8,7 +8,7 @@ import unittest
|
|||||||
from erpnext.hr.doctype.salary_structure.test_salary_structure import make_employee
|
from erpnext.hr.doctype.salary_structure.test_salary_structure import make_employee
|
||||||
|
|
||||||
class TestEmployeeTaxExemptionDeclaration(unittest.TestCase):
|
class TestEmployeeTaxExemptionDeclaration(unittest.TestCase):
|
||||||
def setup(self):
|
def setUp(self):
|
||||||
make_employee("employee@taxexepmtion.com")
|
make_employee("employee@taxexepmtion.com")
|
||||||
make_employee("employee1@taxexepmtion.com")
|
make_employee("employee1@taxexepmtion.com")
|
||||||
create_payroll_period()
|
create_payroll_period()
|
||||||
@ -19,7 +19,7 @@ class TestEmployeeTaxExemptionDeclaration(unittest.TestCase):
|
|||||||
declaration = frappe.get_doc({
|
declaration = frappe.get_doc({
|
||||||
"doctype": "Employee Tax Exemption Declaration",
|
"doctype": "Employee Tax Exemption Declaration",
|
||||||
"employee": frappe.get_value("Employee", {"user_id":"employee@taxexepmtion.com"}, "name"),
|
"employee": frappe.get_value("Employee", {"user_id":"employee@taxexepmtion.com"}, "name"),
|
||||||
"payroll_period": "Test Payroll Period",
|
"payroll_period": "_Test Payroll Period",
|
||||||
"declarations": [dict(exemption_sub_category = "_Test Sub Category",
|
"declarations": [dict(exemption_sub_category = "_Test Sub Category",
|
||||||
exemption_category = "_Test Category",
|
exemption_category = "_Test Category",
|
||||||
amount = 150000)]
|
amount = 150000)]
|
||||||
@ -27,7 +27,7 @@ class TestEmployeeTaxExemptionDeclaration(unittest.TestCase):
|
|||||||
self.assertRaises(frappe.ValidationError, declaration.save)
|
self.assertRaises(frappe.ValidationError, declaration.save)
|
||||||
declaration = frappe.get_doc({
|
declaration = frappe.get_doc({
|
||||||
"doctype": "Employee Tax Exemption Declaration",
|
"doctype": "Employee Tax Exemption Declaration",
|
||||||
"payroll_period": "Test Payroll Period",
|
"payroll_period": "_Test Payroll Period",
|
||||||
"employee": frappe.get_value("Employee", {"user_id":"employee@taxexepmtion.com"}, "name"),
|
"employee": frappe.get_value("Employee", {"user_id":"employee@taxexepmtion.com"}, "name"),
|
||||||
"declarations": [dict(exemption_sub_category = "_Test Sub Category",
|
"declarations": [dict(exemption_sub_category = "_Test Sub Category",
|
||||||
exemption_category = "_Test Category",
|
exemption_category = "_Test Category",
|
||||||
@ -39,7 +39,8 @@ class TestEmployeeTaxExemptionDeclaration(unittest.TestCase):
|
|||||||
declaration = frappe.get_doc({
|
declaration = frappe.get_doc({
|
||||||
"doctype": "Employee Tax Exemption Declaration",
|
"doctype": "Employee Tax Exemption Declaration",
|
||||||
"employee": frappe.get_value("Employee", {"user_id":"employee@taxexepmtion.com"}, "name"),
|
"employee": frappe.get_value("Employee", {"user_id":"employee@taxexepmtion.com"}, "name"),
|
||||||
"payroll_period": "Test Payroll Period",
|
"company": "_Test Company",
|
||||||
|
"payroll_period": "_Test Payroll Period",
|
||||||
"declarations": [dict(exemption_sub_category = "_Test Sub Category",
|
"declarations": [dict(exemption_sub_category = "_Test Sub Category",
|
||||||
exemption_category = "_Test Category",
|
exemption_category = "_Test Category",
|
||||||
amount = 100000),
|
amount = 100000),
|
||||||
@ -54,7 +55,8 @@ class TestEmployeeTaxExemptionDeclaration(unittest.TestCase):
|
|||||||
declaration = frappe.get_doc({
|
declaration = frappe.get_doc({
|
||||||
"doctype": "Employee Tax Exemption Declaration",
|
"doctype": "Employee Tax Exemption Declaration",
|
||||||
"employee": frappe.get_value("Employee", {"user_id":"employee@taxexepmtion.com"}, "name"),
|
"employee": frappe.get_value("Employee", {"user_id":"employee@taxexepmtion.com"}, "name"),
|
||||||
"payroll_period": "Test Payroll Period",
|
"company": "_Test Company",
|
||||||
|
"payroll_period": "_Test Payroll Period",
|
||||||
"declarations": [dict(exemption_sub_category = "_Test Sub Category",
|
"declarations": [dict(exemption_sub_category = "_Test Sub Category",
|
||||||
exemption_category = "_Test Category",
|
exemption_category = "_Test Category",
|
||||||
amount = 100000),
|
amount = 100000),
|
||||||
@ -62,17 +64,18 @@ class TestEmployeeTaxExemptionDeclaration(unittest.TestCase):
|
|||||||
exemption_category = "_Test Category",
|
exemption_category = "_Test Category",
|
||||||
amount = 50000),
|
amount = 50000),
|
||||||
]
|
]
|
||||||
})
|
}).insert()
|
||||||
self.assertTrue(declaration.submit)
|
self.assertTrue(declaration.submit)
|
||||||
duplicate_declaration = frappe.get_doc({
|
duplicate_declaration = frappe.get_doc({
|
||||||
"doctype": "Employee Tax Exemption Declaration",
|
"doctype": "Employee Tax Exemption Declaration",
|
||||||
"employee": frappe.get_value("Employee", {"user_id":"employee@taxexepmtion.com"}, "name"),
|
"employee": frappe.get_value("Employee", {"user_id":"employee@taxexepmtion.com"}, "name"),
|
||||||
"payroll_period": "Test Payroll Period",
|
"company": "_Test Company",
|
||||||
|
"payroll_period": "_Test Payroll Period",
|
||||||
"declarations": [dict(exemption_sub_category = "_Test Sub Category",
|
"declarations": [dict(exemption_sub_category = "_Test Sub Category",
|
||||||
exemption_category = "_Test Category",
|
exemption_category = "_Test Category",
|
||||||
amount = 100000)
|
amount = 100000)
|
||||||
]
|
]
|
||||||
})
|
}).insert()
|
||||||
self.assertRaises(frappe.DocstatusTransitionError, duplicate_declaration.submit)
|
self.assertRaises(frappe.DocstatusTransitionError, duplicate_declaration.submit)
|
||||||
duplicate_declaration.employee = frappe.get_value("Employee", {"user_id":"employee1@taxexepmtion.com"}, "name")
|
duplicate_declaration.employee = frappe.get_value("Employee", {"user_id":"employee1@taxexepmtion.com"}, "name")
|
||||||
self.assertTrue(duplicate_declaration.submit)
|
self.assertTrue(duplicate_declaration.submit)
|
||||||
@ -93,20 +96,22 @@ def create_exemption_category():
|
|||||||
category = frappe.get_doc({
|
category = frappe.get_doc({
|
||||||
"doctype": "Employee Tax Exemption Category",
|
"doctype": "Employee Tax Exemption Category",
|
||||||
"name": "_Test Category",
|
"name": "_Test Category",
|
||||||
"deduction_component": "_Test Tax",
|
"deduction_component": "Income Tax",
|
||||||
"max_amount": 100000
|
"max_amount": 100000
|
||||||
}).insert()
|
}).insert()
|
||||||
if not frappe.db.exists("Employee Tax Exemption Sub Category", "_Test Category"):
|
if not frappe.db.exists("Employee Tax Exemption Sub Category", "_Test Sub Category"):
|
||||||
frappe.get_doc({
|
frappe.get_doc({
|
||||||
"doctype": "Employee Tax Exemption Sub Category",
|
"doctype": "Employee Tax Exemption Sub Category",
|
||||||
"name": "_Test Sub Category",
|
"name": "_Test Sub Category",
|
||||||
"exemption_category": "_Test Category",
|
"exemption_category": "_Test Category",
|
||||||
"max_amount": 100000
|
"max_amount": 100000,
|
||||||
|
"is_active": 1
|
||||||
}).insert()
|
}).insert()
|
||||||
if not frappe.db.exists("Employee Tax Exemption Sub Category", "_Test Category"):
|
if not frappe.db.exists("Employee Tax Exemption Sub Category", "_Test1 Sub Category"):
|
||||||
frappe.get_doc({
|
frappe.get_doc({
|
||||||
"doctype": "Employee Tax Exemption Sub Category",
|
"doctype": "Employee Tax Exemption Sub Category",
|
||||||
"name": "_Test1 Sub Category",
|
"name": "_Test1 Sub Category",
|
||||||
"exemption_category": "_Test Category",
|
"exemption_category": "_Test Category",
|
||||||
"max_amount": 50000
|
"max_amount": 50000,
|
||||||
|
"is_active": 1
|
||||||
}).insert()
|
}).insert()
|
||||||
|
@ -54,9 +54,12 @@ class TestLeaveApplication(unittest.TestCase):
|
|||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
frappe.set_user("Administrator")
|
frappe.set_user("Administrator")
|
||||||
|
|
||||||
|
def _clear_roles(self):
|
||||||
|
frappe.db.sql("""delete from `tabHas Role` where parent in
|
||||||
|
("test@example.com", "test1@example.com", "test2@example.com")""")
|
||||||
|
|
||||||
def _clear_applications(self):
|
def _clear_applications(self):
|
||||||
frappe.db.sql("""delete from `tabLeave Application`""")
|
frappe.db.sql("""delete from `tabLeave Application`""")
|
||||||
frappe.db.sql("""delete from `tabDepartment Approver` where parentfield = 'Leave Approver'""")
|
|
||||||
|
|
||||||
def get_application(self, doc):
|
def get_application(self, doc):
|
||||||
application = frappe.copy_doc(doc)
|
application = frappe.copy_doc(doc)
|
||||||
@ -65,6 +68,10 @@ class TestLeaveApplication(unittest.TestCase):
|
|||||||
return application
|
return application
|
||||||
|
|
||||||
def test_block_list(self):
|
def test_block_list(self):
|
||||||
|
self._clear_roles()
|
||||||
|
|
||||||
|
from frappe.utils.user import add_role
|
||||||
|
add_role("test@example.com", "HR User")
|
||||||
clear_user_permissions_for_doctype("Employee")
|
clear_user_permissions_for_doctype("Employee")
|
||||||
|
|
||||||
frappe.db.set_value("Department", "_Test Department - _TC",
|
frappe.db.set_value("Department", "_Test Department - _TC",
|
||||||
@ -86,8 +93,11 @@ class TestLeaveApplication(unittest.TestCase):
|
|||||||
self.assertTrue(application.insert())
|
self.assertTrue(application.insert())
|
||||||
|
|
||||||
def test_overlap(self):
|
def test_overlap(self):
|
||||||
|
self._clear_roles()
|
||||||
self._clear_applications()
|
self._clear_applications()
|
||||||
|
|
||||||
|
from frappe.utils.user import add_role
|
||||||
|
add_role("test@example.com", "Employee")
|
||||||
frappe.set_user("test@example.com")
|
frappe.set_user("test@example.com")
|
||||||
|
|
||||||
make_allocation_record()
|
make_allocation_record()
|
||||||
@ -99,8 +109,11 @@ class TestLeaveApplication(unittest.TestCase):
|
|||||||
self.assertRaises(OverlapError, application.insert)
|
self.assertRaises(OverlapError, application.insert)
|
||||||
|
|
||||||
def test_overlap_with_half_day_1(self):
|
def test_overlap_with_half_day_1(self):
|
||||||
|
self._clear_roles()
|
||||||
self._clear_applications()
|
self._clear_applications()
|
||||||
|
|
||||||
|
from frappe.utils.user import add_role
|
||||||
|
add_role("test@example.com", "Employee")
|
||||||
frappe.set_user("test@example.com")
|
frappe.set_user("test@example.com")
|
||||||
|
|
||||||
make_allocation_record()
|
make_allocation_record()
|
||||||
@ -129,8 +142,12 @@ class TestLeaveApplication(unittest.TestCase):
|
|||||||
self.assertRaises(OverlapError, application.insert)
|
self.assertRaises(OverlapError, application.insert)
|
||||||
|
|
||||||
def test_overlap_with_half_day_2(self):
|
def test_overlap_with_half_day_2(self):
|
||||||
|
self._clear_roles()
|
||||||
self._clear_applications()
|
self._clear_applications()
|
||||||
|
|
||||||
|
from frappe.utils.user import add_role
|
||||||
|
add_role("test@example.com", "Employee")
|
||||||
|
|
||||||
frappe.set_user("test@example.com")
|
frappe.set_user("test@example.com")
|
||||||
|
|
||||||
make_allocation_record()
|
make_allocation_record()
|
||||||
@ -147,8 +164,12 @@ class TestLeaveApplication(unittest.TestCase):
|
|||||||
self.assertRaises(OverlapError, application.insert)
|
self.assertRaises(OverlapError, application.insert)
|
||||||
|
|
||||||
def test_overlap_with_half_day_3(self):
|
def test_overlap_with_half_day_3(self):
|
||||||
|
self._clear_roles()
|
||||||
self._clear_applications()
|
self._clear_applications()
|
||||||
|
|
||||||
|
from frappe.utils.user import add_role
|
||||||
|
add_role("test@example.com", "Employee")
|
||||||
|
|
||||||
frappe.set_user("test@example.com")
|
frappe.set_user("test@example.com")
|
||||||
|
|
||||||
make_allocation_record()
|
make_allocation_record()
|
||||||
@ -176,7 +197,6 @@ class TestLeaveApplication(unittest.TestCase):
|
|||||||
application.half_day_date = "2013-01-05"
|
application.half_day_date = "2013-01-05"
|
||||||
application.insert()
|
application.insert()
|
||||||
|
|
||||||
|
|
||||||
def test_optional_leave(self):
|
def test_optional_leave(self):
|
||||||
leave_period = get_leave_period()
|
leave_period = get_leave_period()
|
||||||
today = nowdate()
|
today = nowdate()
|
||||||
@ -228,6 +248,7 @@ class TestLeaveApplication(unittest.TestCase):
|
|||||||
# check leave balance is reduced
|
# check leave balance is reduced
|
||||||
self.assertEqual(get_leave_balance_on(employee.name, leave_type, today), 9)
|
self.assertEqual(get_leave_balance_on(employee.name, leave_type, today), 9)
|
||||||
|
|
||||||
|
|
||||||
def test_leaves_allowed(self):
|
def test_leaves_allowed(self):
|
||||||
employee = get_employee()
|
employee = get_employee()
|
||||||
leave_period = get_leave_period()
|
leave_period = get_leave_period()
|
||||||
@ -348,7 +369,7 @@ class TestLeaveApplication(unittest.TestCase):
|
|||||||
employee = get_employee()
|
employee = get_employee()
|
||||||
leave_type = 'Test Earned Leave Type'
|
leave_type = 'Test Earned Leave Type'
|
||||||
if not frappe.db.exists('Leave Type', leave_type):
|
if not frappe.db.exists('Leave Type', leave_type):
|
||||||
leave_type = frappe.get_doc(dict(
|
leave_type_doc = frappe.get_doc(dict(
|
||||||
leave_type_name = leave_type,
|
leave_type_name = leave_type,
|
||||||
doctype = 'Leave Type',
|
doctype = 'Leave Type',
|
||||||
is_earned_leave = 1,
|
is_earned_leave = 1,
|
||||||
@ -428,4 +449,3 @@ def allocate_leaves(employee, leave_period, leave_type, new_leaves_allocated, el
|
|||||||
}).insert()
|
}).insert()
|
||||||
|
|
||||||
allocate_leave.submit()
|
allocate_leave.submit()
|
||||||
|
|
||||||
|
@ -107,7 +107,10 @@ class SalarySlip(TransactionBase):
|
|||||||
'depends_on_lwp' : struct_row.depends_on_lwp,
|
'depends_on_lwp' : struct_row.depends_on_lwp,
|
||||||
'salary_component' : struct_row.salary_component,
|
'salary_component' : struct_row.salary_component,
|
||||||
'abbr' : struct_row.abbr,
|
'abbr' : struct_row.abbr,
|
||||||
'do_not_include_in_total' : struct_row.do_not_include_in_total
|
'do_not_include_in_total' : struct_row.do_not_include_in_total,
|
||||||
|
'is_tax_applicable': struct_row.is_tax_applicable,
|
||||||
|
'is_flexible_benefit': struct_row.is_flexible_benefit,
|
||||||
|
'variable_based_on_taxable_salary': struct_row.variable_based_on_taxable_salary
|
||||||
})
|
})
|
||||||
else:
|
else:
|
||||||
component_row.amount = amount
|
component_row.amount = amount
|
||||||
@ -447,7 +450,7 @@ class SalarySlip(TransactionBase):
|
|||||||
else:
|
else:
|
||||||
self.set_status()
|
self.set_status()
|
||||||
self.update_status(self.name)
|
self.update_status(self.name)
|
||||||
if(frappe.db.get_single_value("HR Settings", "email_salary_slip_to_employee")) and not frappe.flags.via_payroll_entry:
|
if (frappe.db.get_single_value("HR Settings", "email_salary_slip_to_employee")) and not frappe.flags.via_payroll_entry:
|
||||||
self.email_salary_slip()
|
self.email_salary_slip()
|
||||||
|
|
||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
@ -466,7 +469,10 @@ class SalarySlip(TransactionBase):
|
|||||||
"reference_doctype": self.doctype,
|
"reference_doctype": self.doctype,
|
||||||
"reference_name": self.name
|
"reference_name": self.name
|
||||||
}
|
}
|
||||||
|
if not frappe.flags.in_test:
|
||||||
enqueue(method=frappe.sendmail, queue='short', timeout=300, async=True, **email_args)
|
enqueue(method=frappe.sendmail, queue='short', timeout=300, async=True, **email_args)
|
||||||
|
else:
|
||||||
|
frappe.sendmail(**email_args)
|
||||||
else:
|
else:
|
||||||
msgprint(_("{0}: Employee email not found, hence email not sent").format(self.employee_name))
|
msgprint(_("{0}: Employee email not found, hence email not sent").format(self.employee_name))
|
||||||
|
|
||||||
@ -495,25 +501,35 @@ class SalarySlip(TransactionBase):
|
|||||||
return status
|
return status
|
||||||
|
|
||||||
def calculate_variable_based_on_taxable_salary(self, tax_component):
|
def calculate_variable_based_on_taxable_salary(self, tax_component):
|
||||||
# TODO case both checked - restrict to and make this mandatory on final period of payroll?
|
|
||||||
# case only deduct_tax_for_unsubmitted_tax_exemption_proof checked not handled, calculate_variable_tax called
|
|
||||||
payroll_period = get_payroll_period(self.start_date, self.end_date, self.company)
|
payroll_period = get_payroll_period(self.start_date, self.end_date, self.company)
|
||||||
if not payroll_period:
|
if not payroll_period:
|
||||||
frappe.msgprint(_("Start and end dates not in a valid Payroll Period, \
|
frappe.msgprint(_("Start and end dates not in a valid Payroll Period, \
|
||||||
cannot calculate {0}.").format(tax_component))
|
cannot calculate {0}.").format(tax_component))
|
||||||
return False, False
|
return False, False
|
||||||
if self.deduct_tax_for_unclaimed_employee_benefits and not self.deduct_tax_for_unsubmitted_tax_exemption_proof:
|
if payroll_period.end_date <= getdate(self.end_date):
|
||||||
total_taxable_benefit = self.calculate_unclaimed_benefit_amount(payroll_period)
|
if not self.deduct_tax_for_unsubmitted_tax_exemption_proof \
|
||||||
total_taxable_benefit += self.get_taxable_earnings(only_flexi=True)
|
or not self.deduct_tax_for_unclaimed_employee_benefits:
|
||||||
return self.calculate_variable_tax(tax_component, payroll_period, benefit_amount=total_taxable_benefit)
|
frappe.throw(_("You have to Deduct Tax for Unsubmitted Tax Exemption Proof \
|
||||||
elif self.deduct_tax_for_unclaimed_employee_benefits and self.deduct_tax_for_unsubmitted_tax_exemption_proof:
|
and Unclaimed Employee Benefits in the last Salary Slip of Payroll Period"))
|
||||||
return self.calculate_tax_for_payroll_period(tax_component, payroll_period)
|
|
||||||
else:
|
else:
|
||||||
return self.calculate_variable_tax(tax_component, payroll_period)
|
return self.calculate_tax_for_payroll_period(tax_component, payroll_period)
|
||||||
|
|
||||||
def calculate_variable_tax(self, tax_component, payroll_period, benefit_amount=0):
|
benefit_amount_to_tax = 0
|
||||||
|
if self.deduct_tax_for_unclaimed_employee_benefits:
|
||||||
|
# get all untaxed benefits till date, pass amount to be taxed by later methods
|
||||||
|
benefit_amount_to_tax = self.calculate_unclaimed_taxable_benefit(payroll_period)
|
||||||
|
# flexi's excluded from monthly tax, add flexis in this slip to total_taxable_benefit
|
||||||
|
benefit_amount_to_tax += self.get_taxable_earnings(only_flexi=True)
|
||||||
|
if self.deduct_tax_for_unsubmitted_tax_exemption_proof:
|
||||||
|
# calc tax to be paid for the period till date considering prorata taxes paid and proofs submitted
|
||||||
|
return self.calculate_unclaimed_taxable_earning(payroll_period, tax_component, benefit_amount_to_tax)
|
||||||
|
|
||||||
|
# calc prorata tax to be applied
|
||||||
|
return self.calculate_variable_tax(tax_component, payroll_period, benefit_amount_to_tax=benefit_amount_to_tax)
|
||||||
|
|
||||||
|
def calculate_variable_tax(self, tax_component, payroll_period, benefit_amount_to_tax=0):
|
||||||
total_taxable_earning = self.get_taxable_earnings()
|
total_taxable_earning = self.get_taxable_earnings()
|
||||||
period_factor = self.get_period_factor(payroll_period.start_date, payroll_period.end_date)
|
period_factor = self.get_period_factor(payroll_period.start_date, payroll_period.end_date, self.start_date, self.end_date)
|
||||||
annual_earning = total_taxable_earning * period_factor
|
annual_earning = total_taxable_earning * period_factor
|
||||||
|
|
||||||
# Calculate total exemption declaration
|
# Calculate total exemption declaration
|
||||||
@ -525,18 +541,7 @@ class SalarySlip(TransactionBase):
|
|||||||
"total_exemption_amount")
|
"total_exemption_amount")
|
||||||
annual_taxable_earning = annual_earning - exemption_amount
|
annual_taxable_earning = annual_earning - exemption_amount
|
||||||
|
|
||||||
# Get tax calc by period
|
return self.calculate_tax(payroll_period, tax_component, annual_taxable_earning, period_factor, 0, benefit_amount_to_tax)
|
||||||
annual_tax = self.calculate_tax(payroll_period.name, annual_taxable_earning)
|
|
||||||
|
|
||||||
# Calc prorata tax
|
|
||||||
pro_rata_tax = annual_tax / period_factor
|
|
||||||
struct_row = self.get_salary_slip_row(tax_component)
|
|
||||||
|
|
||||||
# find the annual tax diff caused by benefit, add to pro_rata_tax
|
|
||||||
if benefit_amount > 0:
|
|
||||||
annual_tax_with_benefit = self.calculate_tax(payroll_period.name, annual_taxable_earning + benefit_amount)
|
|
||||||
pro_rata_tax += annual_tax_with_benefit - annual_tax
|
|
||||||
return struct_row, pro_rata_tax
|
|
||||||
|
|
||||||
def calculate_tax_for_payroll_period(self, tax_component, payroll_period):
|
def calculate_tax_for_payroll_period(self, tax_component, payroll_period):
|
||||||
# get total taxable income, total tax paid in payroll period
|
# get total taxable income, total tax paid in payroll period
|
||||||
@ -556,18 +561,15 @@ class SalarySlip(TransactionBase):
|
|||||||
if sum_benefit_claim and sum_benefit_claim[0][0]:
|
if sum_benefit_claim and sum_benefit_claim[0][0]:
|
||||||
total_benefit_claim = sum_benefit_claim[0][0]
|
total_benefit_claim = sum_benefit_claim[0][0]
|
||||||
total_taxable_earning = taxable_income - total_tax_exemption_proof - total_benefit_claim
|
total_taxable_earning = taxable_income - total_tax_exemption_proof - total_benefit_claim
|
||||||
|
|
||||||
# add taxable earnings of current salary_slip, include flexi
|
# add taxable earnings of current salary_slip, include flexi
|
||||||
total_taxable_earning += self.get_taxable_earnings(include_flexi=1)
|
total_taxable_earning += self.get_taxable_earnings(include_flexi=1)
|
||||||
# calc annual tax by tax slab
|
return self.calculate_tax(payroll_period, tax_component, total_taxable_earning, 1, tax_paid, 0)
|
||||||
annual_tax = self.calculate_tax(payroll_period.name, total_taxable_earning)
|
|
||||||
# get balance amount to tax, even if -ve add to deduction
|
|
||||||
pay_slip_tax = annual_tax - tax_paid
|
|
||||||
struct_row = self.get_salary_slip_row(tax_component)
|
|
||||||
return struct_row, pay_slip_tax
|
|
||||||
|
|
||||||
def calculate_unclaimed_benefit_amount(self, payroll_period):
|
def calculate_unclaimed_taxable_benefit(self, payroll_period):
|
||||||
total_benefit = 0
|
total_benefit = 0
|
||||||
start_date = payroll_period.start_date
|
start_date = payroll_period.start_date
|
||||||
|
|
||||||
# if tax for unclaimed benefit deducted earlier set the start date
|
# if tax for unclaimed benefit deducted earlier set the start date
|
||||||
last_deducted = frappe.db.sql("""select end_date from `tabSalary Slip` where
|
last_deducted = frappe.db.sql("""select end_date from `tabSalary Slip` where
|
||||||
deduct_tax_for_unclaimed_employee_benefits=1 and docstatus=1 and
|
deduct_tax_for_unclaimed_employee_benefits=1 and docstatus=1 and
|
||||||
@ -576,6 +578,8 @@ class SalarySlip(TransactionBase):
|
|||||||
self.employee, payroll_period.start_date, payroll_period.end_date))
|
self.employee, payroll_period.start_date, payroll_period.end_date))
|
||||||
if last_deducted and last_deducted[0][0]:
|
if last_deducted and last_deducted[0][0]:
|
||||||
start_date = getdate(last_deducted[0][0])
|
start_date = getdate(last_deducted[0][0])
|
||||||
|
|
||||||
|
# get total sum of benefits paid
|
||||||
sum_benefit = frappe.db.sql("""select sum(sd.amount) from `tabSalary Detail` sd join
|
sum_benefit = frappe.db.sql("""select sum(sd.amount) from `tabSalary Detail` sd join
|
||||||
`tabSalary Slip` ss on sd.parent=ss.name where sd.parentfield='earnings'
|
`tabSalary Slip` ss on sd.parent=ss.name where sd.parentfield='earnings'
|
||||||
and sd.is_tax_applicable=1 and is_flexible_benefit=1 and ss.docstatus=1
|
and sd.is_tax_applicable=1 and is_flexible_benefit=1 and ss.docstatus=1
|
||||||
@ -584,6 +588,8 @@ class SalarySlip(TransactionBase):
|
|||||||
start_date, payroll_period.end_date))
|
start_date, payroll_period.end_date))
|
||||||
if sum_benefit and sum_benefit[0][0]:
|
if sum_benefit and sum_benefit[0][0]:
|
||||||
total_benefit = sum_benefit[0][0]
|
total_benefit = sum_benefit[0][0]
|
||||||
|
|
||||||
|
# get total benefits claimed
|
||||||
total_benefit_claim = 0
|
total_benefit_claim = 0
|
||||||
sum_benefit_claim = frappe.db.sql("""select sum(claimed_amount) from
|
sum_benefit_claim = frappe.db.sql("""select sum(claimed_amount) from
|
||||||
`tabEmployee Benefit Claim` where docstatus=1 and employee='{0}' and claim_date
|
`tabEmployee Benefit Claim` where docstatus=1 and employee='{0}' and claim_date
|
||||||
@ -592,28 +598,103 @@ class SalarySlip(TransactionBase):
|
|||||||
total_benefit_claim = sum_benefit_claim[0][0]
|
total_benefit_claim = sum_benefit_claim[0][0]
|
||||||
return total_benefit - total_benefit_claim
|
return total_benefit - total_benefit_claim
|
||||||
|
|
||||||
|
def calculate_unclaimed_taxable_earning(self, payroll_period, tax_component, benefit_amount_to_tax):
|
||||||
|
total_taxable_earning, total_tax_paid = 0, 0
|
||||||
|
start_date = payroll_period.start_date
|
||||||
|
|
||||||
|
# if tax deducted earlier set the start date
|
||||||
|
last_deducted = frappe.db.sql("""select end_date from `tabSalary Slip` where
|
||||||
|
deduct_tax_for_unsubmitted_tax_exemption_proof=1 and docstatus=1 and
|
||||||
|
employee='{0}' and start_date between '{1}' and '{2}' and end_date
|
||||||
|
between '{1}' and '{2}' order by end_date desc limit 1""".format(
|
||||||
|
self.employee, payroll_period.start_date, self.start_date))
|
||||||
|
if last_deducted and last_deducted[0][0]:
|
||||||
|
start_date = getdate(last_deducted[0][0])
|
||||||
|
|
||||||
|
# calc total taxable amount in period
|
||||||
|
sum_taxable_earning = frappe.db.sql("""select sum(sd.amount) from `tabSalary Detail` sd join
|
||||||
|
`tabSalary Slip` ss on sd.parent=ss.name where sd.parentfield='earnings'
|
||||||
|
and sd.is_tax_applicable=1 and is_flexible_benefit=0 and ss.docstatus=1
|
||||||
|
and ss.employee='{0}' and ss.start_date between '{1}' and '{2}' and
|
||||||
|
ss.end_date between '{1}' and '{2}'""".format(self.employee,
|
||||||
|
start_date, self.start_date))
|
||||||
|
if sum_taxable_earning and sum_taxable_earning[0][0]:
|
||||||
|
total_taxable_earning = sum_taxable_earning[0][0]
|
||||||
|
|
||||||
|
# add taxable earning in this salary slip
|
||||||
|
total_taxable_earning += self.get_taxable_earnings()
|
||||||
|
|
||||||
|
# find total_tax_paid from salary slip where benefit is not taxed
|
||||||
|
sum_tax_paid = frappe.db.sql("""select sum(sd.amount) from `tabSalary Detail` sd join
|
||||||
|
`tabSalary Slip` ss on sd.parent=ss.name where sd.parentfield='deductions'
|
||||||
|
and sd.salary_component='{3}' and sd.variable_based_on_taxable_salary=1 and ss.docstatus=1
|
||||||
|
and ss.employee='{0}' and ss.deduct_tax_for_unclaimed_employee_benefits=0
|
||||||
|
and ss.start_date between '{1}' and '{2}' and ss.end_date between '{1}' and
|
||||||
|
'{2}'""".format(self.employee, start_date, self.start_date, tax_component))
|
||||||
|
if sum_tax_paid and sum_tax_paid[0][0]:
|
||||||
|
total_tax_paid = sum_tax_paid[0][0]
|
||||||
|
|
||||||
|
# get benefit taxed salary slips
|
||||||
|
benefit_taxed_ss = frappe.db.sql("""select name from `tabSalary Slip` where
|
||||||
|
deduct_tax_for_unsubmitted_tax_exemption_proof=0 and
|
||||||
|
deduct_tax_for_unclaimed_employee_benefits=1 and docstatus=1 and employee='{0}'
|
||||||
|
and start_date between '{1}' and '{2}' and end_date between '{1}'
|
||||||
|
and '{2}'""".format(self.employee, start_date, self.start_date))
|
||||||
|
# add pro_rata_tax of all salary slips where benefit tax added up
|
||||||
|
if benefit_taxed_ss and benefit_taxed_ss[0]:
|
||||||
|
for salary_slip in benefit_taxed_ss[0]:
|
||||||
|
ss_obj = frappe.get_doc("Salary Slip", salary_slip)
|
||||||
|
struct_row, pro_rata_tax = ss_obj.calculate_variable_tax(tax_component, payroll_period)
|
||||||
|
if pro_rata_tax:
|
||||||
|
total_tax_paid += pro_rata_tax
|
||||||
|
total_exemption_amount = 0
|
||||||
|
|
||||||
|
# add up total Proof Submission
|
||||||
|
sum_exemption = frappe.db.sql("""select sum(total_amount) from
|
||||||
|
`tabEmployee Tax Exemption Proof Submission` where docstatus=1 and employee='{0}' and
|
||||||
|
payroll_period='{1}' and processed_in_payroll=0""".format(self.employee, payroll_period.name))
|
||||||
|
if sum_exemption and sum_exemption[0][0]:
|
||||||
|
total_exemption_amount = sum_exemption[0][0]
|
||||||
|
total_taxable_earning -= total_exemption_amount
|
||||||
|
|
||||||
|
# recalc annual tax slab by start date and end date
|
||||||
|
period_factor = self.get_period_factor(payroll_period.start_date, payroll_period.end_date, start_date, self.end_date)
|
||||||
|
annual_taxable_earning = total_taxable_earning * period_factor
|
||||||
|
return self.calculate_tax(payroll_period, tax_component, annual_taxable_earning, period_factor, total_tax_paid, benefit_amount_to_tax)
|
||||||
|
|
||||||
def get_taxable_earnings(self, include_flexi=0, only_flexi=0):
|
def get_taxable_earnings(self, include_flexi=0, only_flexi=0):
|
||||||
# TODO remove this, iterate in self.earnings. map_doc fails to copy field values from Salary Structure to Slary Slip
|
taxable_earning = 0
|
||||||
tax_applicable_components = []
|
for earning in self.earnings:
|
||||||
for earning in self._salary_structure_doc.earnings:
|
|
||||||
if only_flexi:
|
if only_flexi:
|
||||||
if earning.is_tax_applicable and earning.is_flexible_benefit:
|
if earning.is_tax_applicable and earning.is_flexible_benefit:
|
||||||
tax_applicable_components.append(earning.salary_component)
|
taxable_earning += earning.amount
|
||||||
continue
|
continue
|
||||||
if include_flexi:
|
if include_flexi:
|
||||||
if earning.is_tax_applicable or (earning.is_tax_applicable and earning.is_flexible_benefit):
|
if earning.is_tax_applicable or (earning.is_tax_applicable and earning.is_flexible_benefit):
|
||||||
tax_applicable_components.append(earning.salary_component)
|
taxable_earning += earning.amount
|
||||||
else:
|
else:
|
||||||
if earning.is_tax_applicable and not earning.is_flexible_benefit:
|
if earning.is_tax_applicable and not earning.is_flexible_benefit:
|
||||||
tax_applicable_components.append(earning.salary_component)
|
|
||||||
|
|
||||||
taxable_earning = 0
|
|
||||||
for earning in self.earnings:
|
|
||||||
if earning.salary_component in tax_applicable_components:
|
|
||||||
taxable_earning += earning.amount
|
taxable_earning += earning.amount
|
||||||
return taxable_earning
|
return taxable_earning
|
||||||
|
|
||||||
def calculate_tax(self, payroll_period, annual_earning):
|
def calculate_tax(self, payroll_period, tax_component, annual_taxable_earning, period_factor, tax_paid=0, benefit_amount_to_tax=0):
|
||||||
|
# Get tax calc by period
|
||||||
|
annual_tax = self.calculate_tax_by_tax_slab(payroll_period.name, annual_taxable_earning)
|
||||||
|
|
||||||
|
# Calc prorata tax
|
||||||
|
tax_amount = annual_tax / period_factor
|
||||||
|
if tax_paid:
|
||||||
|
tax_amount -= tax_paid
|
||||||
|
|
||||||
|
# find the annual tax diff caused by benefit_amount_to_tax, add to tax_amount
|
||||||
|
if benefit_amount_to_tax > 0:
|
||||||
|
annual_tax_with_benefit_amt = self.calculate_tax_by_tax_slab(payroll_period.name, annual_taxable_earning + benefit_amount_to_tax)
|
||||||
|
tax_amount += annual_tax_with_benefit_amt - annual_tax
|
||||||
|
struct_row = self.get_salary_slip_row(tax_component)
|
||||||
|
return struct_row, tax_amount
|
||||||
|
|
||||||
|
def calculate_tax_by_tax_slab(self, payroll_period, annual_earning):
|
||||||
|
# TODO consider condition in tax slab
|
||||||
payroll_period_obj = frappe.get_doc("Payroll Period", payroll_period)
|
payroll_period_obj = frappe.get_doc("Payroll Period", payroll_period)
|
||||||
taxable_amount = 0
|
taxable_amount = 0
|
||||||
for slab in payroll_period_obj.taxable_salary_slabs:
|
for slab in payroll_period_obj.taxable_salary_slabs:
|
||||||
@ -623,13 +704,17 @@ class SalarySlip(TransactionBase):
|
|||||||
taxable_amount += (slab.to_amount - slab.from_amount) * slab.percent_deduction * .01
|
taxable_amount += (slab.to_amount - slab.from_amount) * slab.percent_deduction * .01
|
||||||
return taxable_amount
|
return taxable_amount
|
||||||
|
|
||||||
def get_period_factor(self, start_date, end_date):
|
def get_period_factor(self, period_start, period_end, start_date=None, end_date=None):
|
||||||
# period length is hard coded to keep tax calc consistent
|
# TODO make this configurable? - use hard coded period length to keep tax calc consistent
|
||||||
frequency_days = {"Daily": 1, "Weekly": 7, "Fortnightly": 15, "Monthly": 30, "Bimonthly": 60}
|
frequency_days = {"Daily": 1, "Weekly": 7, "Fortnightly": 15, "Monthly": 30, "Bimonthly": 60}
|
||||||
payroll_days = date_diff(end_date, start_date) + 1
|
payroll_days = date_diff(period_end, period_start) + 1
|
||||||
|
if start_date and end_date:
|
||||||
|
salary_days = date_diff(end_date, start_date) +1
|
||||||
|
return flt(payroll_days)/flt(salary_days)
|
||||||
return flt(payroll_days)/frequency_days[self.payroll_frequency]
|
return flt(payroll_days)/frequency_days[self.payroll_frequency]
|
||||||
|
|
||||||
def get_tax_detail_till_date(self, payroll_period, tax_component):
|
def get_tax_detail_till_date(self, payroll_period, tax_component):
|
||||||
|
# find total taxable income, total tax paid by employee in payroll period
|
||||||
total_taxable_income = 0
|
total_taxable_income = 0
|
||||||
total_tax_paid = 0
|
total_tax_paid = 0
|
||||||
sum_income = frappe.db.sql("""select sum(sd.amount) from `tabSalary Detail` sd join
|
sum_income = frappe.db.sql("""select sum(sd.amount) from `tabSalary Detail` sd join
|
||||||
@ -658,6 +743,9 @@ class SalarySlip(TransactionBase):
|
|||||||
struct_row['salary_component'] = component.name
|
struct_row['salary_component'] = component.name
|
||||||
struct_row['abbr'] = component.salary_component_abbr
|
struct_row['abbr'] = component.salary_component_abbr
|
||||||
struct_row['do_not_include_in_total'] = component.do_not_include_in_total
|
struct_row['do_not_include_in_total'] = component.do_not_include_in_total
|
||||||
|
struct_row['is_tax_applicable'] = component.is_tax_applicable
|
||||||
|
struct_row['is_flexible_benefit'] = component.is_flexible_benefit
|
||||||
|
struct_row['variable_based_on_taxable_salary'] = component.variable_based_on_taxable_salary
|
||||||
return struct_row
|
return struct_row
|
||||||
|
|
||||||
def unlink_ref_doc_from_salary_slip(ref_no):
|
def unlink_ref_doc_from_salary_slip(ref_no):
|
||||||
|
@ -279,7 +279,7 @@ def make_salary_structure(sal_struct, payroll_frequency, employee):
|
|||||||
"payroll_frequency": payroll_frequency,
|
"payroll_frequency": payroll_frequency,
|
||||||
"payment_account": get_random("Account")
|
"payment_account": get_random("Account")
|
||||||
}).insert()
|
}).insert()
|
||||||
create_salary_structure_assignment(employee, salary_structure)
|
create_salary_structure_assignment(employee, salary_structure.name)
|
||||||
|
|
||||||
elif not frappe.db.get_value("Salary Structure Assignment",{'salary_structure':sal_struct, 'employee':employee},'name'):
|
elif not frappe.db.get_value("Salary Structure Assignment",{'salary_structure':sal_struct, 'employee':employee},'name'):
|
||||||
create_salary_structure_assignment(employee, sal_struct)
|
create_salary_structure_assignment(employee, sal_struct)
|
||||||
|
@ -350,3 +350,31 @@ def get_annual_component_pay(frequency, amount):
|
|||||||
return amount * 12
|
return amount * 12
|
||||||
elif frequency == "Bimonthly":
|
elif frequency == "Bimonthly":
|
||||||
return amount * 6
|
return amount * 6
|
||||||
|
|
||||||
|
def get_sal_slip_total_benefit_given(employee, payroll_period, component=False):
|
||||||
|
total_given_benefit_amount = 0
|
||||||
|
query = """
|
||||||
|
select sum(sd.amount) as 'total_amount'
|
||||||
|
from `tabSalary Slip` ss, `tabSalary Detail` sd
|
||||||
|
where ss.employee=%(employee)s
|
||||||
|
and ss.docstatus = 1 and ss.name = sd.parent
|
||||||
|
and sd.is_flexible_benefit = 1 and sd.parentfield = "earnings"
|
||||||
|
and sd.parenttype = "Salary Slip"
|
||||||
|
and (ss.start_date between %(start_date)s and %(end_date)s
|
||||||
|
or ss.end_date between %(start_date)s and %(end_date)s
|
||||||
|
or (ss.start_date < %(start_date)s and ss.end_date > %(end_date)s))
|
||||||
|
"""
|
||||||
|
|
||||||
|
if component:
|
||||||
|
query += "and sd.salary_component = %(component)s"
|
||||||
|
|
||||||
|
sum_of_given_benefit = frappe.db.sql(query, {
|
||||||
|
'employee': employee,
|
||||||
|
'start_date': payroll_period.start_date,
|
||||||
|
'end_date': payroll_period.end_date,
|
||||||
|
'component': component
|
||||||
|
}, as_dict=True)
|
||||||
|
|
||||||
|
if sum_of_given_benefit and sum_of_given_benefit[0].total_amount > 0:
|
||||||
|
total_given_benefit_amount = sum_of_given_benefit[0].total_amount
|
||||||
|
return total_given_benefit_amount
|
||||||
|
@ -554,12 +554,15 @@ class WorkOrder(Document):
|
|||||||
consumed_qty = frappe.db.sql('''select sum(qty)
|
consumed_qty = frappe.db.sql('''select sum(qty)
|
||||||
from `tabStock Entry` entry, `tabStock Entry Detail` detail
|
from `tabStock Entry` entry, `tabStock Entry Detail` detail
|
||||||
where
|
where
|
||||||
entry.work_order = %s
|
entry.work_order = %(name)s
|
||||||
and (entry.purpose = "Material Consumption for Manufacture"
|
and (entry.purpose = "Material Consumption for Manufacture"
|
||||||
or entry.purpose = "Manufacture")
|
or entry.purpose = "Manufacture")
|
||||||
and entry.docstatus = 1
|
and entry.docstatus = 1
|
||||||
and detail.parent = entry.name
|
and detail.parent = entry.name
|
||||||
and detail.item_code = %s''', (self.name, d.item_code))[0][0]
|
and (detail.item_code = %(item)s or detail.original_item = %(item)s)''', {
|
||||||
|
'name': self.name,
|
||||||
|
'item': d.item_code
|
||||||
|
})[0][0]
|
||||||
|
|
||||||
d.db_set('consumed_qty', flt(consumed_qty), update_modified = False)
|
d.db_set('consumed_qty', flt(consumed_qty), update_modified = False)
|
||||||
|
|
||||||
|
@ -547,4 +547,5 @@ erpnext.patches.v10_0.update_status_in_purchase_receipt
|
|||||||
erpnext.patches.v11_0.inter_state_field_for_gst
|
erpnext.patches.v11_0.inter_state_field_for_gst
|
||||||
erpnext.patches.v11_0.rename_members_with_naming_series #04-06-2018
|
erpnext.patches.v11_0.rename_members_with_naming_series #04-06-2018
|
||||||
erpnext.patches.v11_0.set_update_field_and_value_in_workflow_state
|
erpnext.patches.v11_0.set_update_field_and_value_in_workflow_state
|
||||||
|
erpnext.patches.v11_0.update_total_qty_field
|
||||||
erpnext.patches.v11_0.update_sales_partner_type
|
erpnext.patches.v11_0.update_sales_partner_type
|
@ -14,14 +14,22 @@ def execute():
|
|||||||
"Purchase Order", "Purchase Invoice", "Purchase Receipt", "Quotation", "Supplier Quotation"]
|
"Purchase Order", "Purchase Invoice", "Purchase Receipt", "Quotation", "Supplier Quotation"]
|
||||||
|
|
||||||
for doctype in doctypes:
|
for doctype in doctypes:
|
||||||
|
total_qty = frappe.db.sql('''
|
||||||
|
SELECT
|
||||||
|
parent, SUM(qty) as qty
|
||||||
|
FROM
|
||||||
|
`tab%s Item`
|
||||||
|
GROUP BY parent
|
||||||
|
''' % (doctype), as_dict = True)
|
||||||
|
|
||||||
|
when_then = []
|
||||||
|
for d in total_qty:
|
||||||
|
when_then.append("""
|
||||||
|
when dt.name = '{0}' then {1}
|
||||||
|
""".format(frappe.db.escape(d.get("parent")), d.get("qty")))
|
||||||
|
|
||||||
|
if when_then:
|
||||||
frappe.db.sql('''
|
frappe.db.sql('''
|
||||||
UPDATE
|
UPDATE
|
||||||
`tab%s` dt SET dt.total_qty =
|
`tab%s` dt SET dt.total_qty = CASE %s END
|
||||||
(
|
''' % (doctype, " ".join(when_then)))
|
||||||
SELECT SUM(dt_item.qty)
|
|
||||||
FROM
|
|
||||||
`tab%s Item` dt_item
|
|
||||||
WHERE
|
|
||||||
dt_item.parent=dt.name
|
|
||||||
)
|
|
||||||
''' % (doctype, doctype))
|
|
@ -152,8 +152,6 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
|||||||
onload: function() {
|
onload: function() {
|
||||||
var me = this;
|
var me = this;
|
||||||
|
|
||||||
this.setup_quality_inspection();
|
|
||||||
|
|
||||||
if(this.frm.doc.__islocal) {
|
if(this.frm.doc.__islocal) {
|
||||||
var currency = frappe.defaults.get_user_default("currency");
|
var currency = frappe.defaults.get_user_default("currency");
|
||||||
|
|
||||||
@ -230,6 +228,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
|||||||
erpnext.hide_company();
|
erpnext.hide_company();
|
||||||
this.set_dynamic_labels();
|
this.set_dynamic_labels();
|
||||||
this.setup_sms();
|
this.setup_sms();
|
||||||
|
this.setup_quality_inspection();
|
||||||
},
|
},
|
||||||
|
|
||||||
apply_default_taxes: function() {
|
apply_default_taxes: function() {
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('Sales Partner Type', {
|
frappe.ui.form.on('Sales Partner Type', {
|
||||||
refresh: function(frm) {
|
refresh: function() {
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
# For license information, please see license.txt
|
# For license information, please see license.txt
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
class SalesPartnerType(Document):
|
class SalesPartnerType(Document):
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
# See license.txt
|
# See license.txt
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import frappe
|
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
class TestSalesPartnerType(unittest.TestCase):
|
class TestSalesPartnerType(unittest.TestCase):
|
||||||
|
@ -1223,7 +1223,7 @@
|
|||||||
"collapsible": 1,
|
"collapsible": 1,
|
||||||
"collapsible_depends_on": "eval:doc.has_batch_no || doc.has_serial_no || doc.is_fixed_asset",
|
"collapsible_depends_on": "eval:doc.has_batch_no || doc.has_serial_no || doc.is_fixed_asset",
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
"depends_on": "is_stock_item",
|
"depends_on": "eval:doc.is_stock_item || doc.is_fixed_asset",
|
||||||
"fieldname": "serial_nos_and_batches",
|
"fieldname": "serial_nos_and_batches",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -3918,7 +3918,7 @@
|
|||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"max_attachments": 1,
|
"max_attachments": 1,
|
||||||
"modified": "2018-05-28 14:18:03.234070",
|
"modified": "2018-06-11 17:11:55.458770",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Item",
|
"name": "Item",
|
||||||
|
@ -13,8 +13,8 @@ from erpnext.controllers.buying_controller import BuyingController
|
|||||||
from erpnext.accounts.utils import get_account_currency
|
from erpnext.accounts.utils import get_account_currency
|
||||||
from frappe.desk.notifications import clear_doctype_notifications
|
from frappe.desk.notifications import clear_doctype_notifications
|
||||||
from erpnext.buying.utils import check_for_closed_status
|
from erpnext.buying.utils import check_for_closed_status
|
||||||
|
from erpnext.assets.doctype.asset.asset import get_asset_account
|
||||||
from six import iteritems
|
from six import iteritems
|
||||||
from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account
|
|
||||||
|
|
||||||
form_grid_templates = {
|
form_grid_templates = {
|
||||||
"items": "templates/form_grid/item_grid.html"
|
"items": "templates/form_grid/item_grid.html"
|
||||||
@ -314,12 +314,11 @@ class PurchaseReceipt(BuyingController):
|
|||||||
def get_asset_gl_entry(self, gl_entries):
|
def get_asset_gl_entry(self, gl_entries):
|
||||||
for d in self.get("items"):
|
for d in self.get("items"):
|
||||||
if d.is_fixed_asset:
|
if d.is_fixed_asset:
|
||||||
asset_accounts = self.get_company_default(["capital_work_in_progress_account",
|
arbnb_account = self.get_company_default("asset_received_but_not_billed")
|
||||||
"asset_received_but_not_billed"])
|
|
||||||
|
|
||||||
# CWIP entry
|
# CWIP entry
|
||||||
cwip_account = get_asset_category_account(d.asset,
|
cwip_account = get_asset_account("capital_work_in_progress_account", d.asset,
|
||||||
'capital_work_in_progress_account') or asset_accounts[0]
|
company = self.company)
|
||||||
|
|
||||||
asset_amount = flt(d.net_amount) + flt(d.item_tax_amount/self.conversion_rate)
|
asset_amount = flt(d.net_amount) + flt(d.item_tax_amount/self.conversion_rate)
|
||||||
base_asset_amount = flt(d.base_net_amount + d.item_tax_amount)
|
base_asset_amount = flt(d.base_net_amount + d.item_tax_amount)
|
||||||
@ -327,7 +326,7 @@ class PurchaseReceipt(BuyingController):
|
|||||||
cwip_account_currency = get_account_currency(cwip_account)
|
cwip_account_currency = get_account_currency(cwip_account)
|
||||||
gl_entries.append(self.get_gl_dict({
|
gl_entries.append(self.get_gl_dict({
|
||||||
"account": cwip_account,
|
"account": cwip_account,
|
||||||
"against": asset_accounts[1],
|
"against": arbnb_account,
|
||||||
"cost_center": d.cost_center,
|
"cost_center": d.cost_center,
|
||||||
"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
|
"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
|
||||||
"debit": base_asset_amount,
|
"debit": base_asset_amount,
|
||||||
@ -336,10 +335,10 @@ class PurchaseReceipt(BuyingController):
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
# Asset received but not billed
|
# Asset received but not billed
|
||||||
asset_rbnb_currency = get_account_currency(asset_accounts[1])
|
asset_rbnb_currency = get_account_currency(arbnb_account)
|
||||||
gl_entries.append(self.get_gl_dict({
|
gl_entries.append(self.get_gl_dict({
|
||||||
"account": asset_accounts[1],
|
"account": arbnb_account,
|
||||||
"against": asset_accounts[0],
|
"against": cwip_account,
|
||||||
"cost_center": d.cost_center,
|
"cost_center": d.cost_center,
|
||||||
"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
|
"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
|
||||||
"credit": base_asset_amount,
|
"credit": base_asset_amount,
|
||||||
|
@ -92,6 +92,7 @@ class TestPurchaseReceipt(unittest.TestCase):
|
|||||||
def test_subcontracting(self):
|
def test_subcontracting(self):
|
||||||
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
|
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
|
||||||
|
|
||||||
|
frappe.db.set_value("Buying Settings", None, "backflush_raw_materials_of_subcontract_based_on", "BOM")
|
||||||
make_stock_entry(item_code="_Test Item", target="_Test Warehouse 1 - _TC", qty=100, basic_rate=100)
|
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",
|
make_stock_entry(item_code="_Test Item Home Desktop 100", target="_Test Warehouse 1 - _TC",
|
||||||
qty=100, basic_rate=100)
|
qty=100, basic_rate=100)
|
||||||
@ -314,9 +315,10 @@ class TestPurchaseReceipt(unittest.TestCase):
|
|||||||
|
|
||||||
asset_category = doc.name
|
asset_category = doc.name
|
||||||
|
|
||||||
asset_item = make_item(asset_item, {'is_stock_item':0,
|
item_data = make_item(asset_item, {'is_stock_item':0,
|
||||||
'stock_uom': 'Box', 'is_fixed_asset': 1, 'has_serial_no': 1,
|
'stock_uom': 'Box', 'is_fixed_asset': 1, 'has_serial_no': 1,
|
||||||
'asset_category': asset_category, 'serial_no_series': 'ABC.###'})
|
'asset_category': asset_category, 'serial_no_series': 'ABC.###'})
|
||||||
|
asset_item = item_data.item_code
|
||||||
|
|
||||||
if not frappe.db.exists('Location', 'Test Location'):
|
if not frappe.db.exists('Location', 'Test Location'):
|
||||||
frappe.get_doc({
|
frappe.get_doc({
|
||||||
@ -334,7 +336,6 @@ class TestPurchaseReceipt(unittest.TestCase):
|
|||||||
serial_nos = frappe.get_all('Serial No', {'asset': asset}, 'name') or []
|
serial_nos = frappe.get_all('Serial No', {'asset': asset}, 'name') or []
|
||||||
self.assertEquals(len(serial_nos), 0)
|
self.assertEquals(len(serial_nos), 0)
|
||||||
frappe.db.sql("delete from `tabLocation")
|
frappe.db.sql("delete from `tabLocation")
|
||||||
frappe.db.sql("delete from `tabAsset Category`")
|
|
||||||
frappe.db.sql("delete from `tabAsset`")
|
frappe.db.sql("delete from `tabAsset`")
|
||||||
|
|
||||||
def get_gl_entries(voucher_type, voucher_no):
|
def get_gl_entries(voucher_type, voucher_no):
|
||||||
|
@ -306,6 +306,7 @@ def auto_make_serial_nos(args):
|
|||||||
sr.via_stock_ledger = True
|
sr.via_stock_ledger = True
|
||||||
sr.item_code = args.get('item_code')
|
sr.item_code = args.get('item_code')
|
||||||
sr.warehouse = args.get('warehouse') if args.get('actual_qty', 0) > 0 else None
|
sr.warehouse = args.get('warehouse') if args.get('actual_qty', 0) > 0 else None
|
||||||
|
sr.location = args.get('location')
|
||||||
sr.save(ignore_permissions=True)
|
sr.save(ignore_permissions=True)
|
||||||
elif args.get('actual_qty', 0) > 0:
|
elif args.get('actual_qty', 0) > 0:
|
||||||
make_serial_no(serial_no, args)
|
make_serial_no(serial_no, args)
|
||||||
@ -330,6 +331,7 @@ def make_serial_no(serial_no, args):
|
|||||||
sr.company = args.get('company')
|
sr.company = args.get('company')
|
||||||
sr.via_stock_ledger = args.get('via_stock_ledger') or True
|
sr.via_stock_ledger = args.get('via_stock_ledger') or True
|
||||||
sr.asset = args.get('asset')
|
sr.asset = args.get('asset')
|
||||||
|
sr.location = args.get('location')
|
||||||
|
|
||||||
if args.get('purchase_document_type'):
|
if args.get('purchase_document_type'):
|
||||||
sr.purchase_document_type = args.get('purchase_document_type')
|
sr.purchase_document_type = args.get('purchase_document_type')
|
||||||
|
@ -59,7 +59,6 @@ frappe.ui.form.on('Stock Entry', {
|
|||||||
});
|
});
|
||||||
|
|
||||||
frm.add_fetch("bom_no", "inspection_required", "inspection_required");
|
frm.add_fetch("bom_no", "inspection_required", "inspection_required");
|
||||||
frm.trigger("setup_quality_inspection");
|
|
||||||
},
|
},
|
||||||
|
|
||||||
setup_quality_inspection: function(frm) {
|
setup_quality_inspection: function(frm) {
|
||||||
@ -189,6 +188,8 @@ frappe.ui.form.on('Stock Entry', {
|
|||||||
frm.trigger("make_retention_stock_entry");
|
frm.trigger("make_retention_stock_entry");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
frm.trigger("setup_quality_inspection");
|
||||||
},
|
},
|
||||||
|
|
||||||
purpose: function(frm) {
|
purpose: function(frm) {
|
||||||
|
@ -832,7 +832,7 @@ class StockEntry(StockController):
|
|||||||
def get_transfered_raw_materials(self):
|
def get_transfered_raw_materials(self):
|
||||||
transferred_materials = frappe.db.sql("""
|
transferred_materials = frappe.db.sql("""
|
||||||
select
|
select
|
||||||
item_name, item_code, sum(qty) as qty, sed.t_warehouse as warehouse,
|
item_name, original_item, item_code, sum(qty) as qty, sed.t_warehouse as warehouse,
|
||||||
description, stock_uom, expense_account, cost_center
|
description, stock_uom, expense_account, cost_center
|
||||||
from `tabStock Entry` se,`tabStock Entry Detail` sed
|
from `tabStock Entry` se,`tabStock Entry Detail` sed
|
||||||
where
|
where
|
||||||
@ -866,8 +866,9 @@ class StockEntry(StockController):
|
|||||||
|
|
||||||
for item in transferred_materials:
|
for item in transferred_materials:
|
||||||
qty= item.qty
|
qty= item.qty
|
||||||
|
item_code = item.original_item or item.item_code
|
||||||
req_items = frappe.get_all('Work Order Item',
|
req_items = frappe.get_all('Work Order Item',
|
||||||
filters={'parent': self.work_order, 'item_code': item.item_code},
|
filters={'parent': self.work_order, 'item_code': item_code},
|
||||||
fields=["required_qty", "consumed_qty"]
|
fields=["required_qty", "consumed_qty"]
|
||||||
)
|
)
|
||||||
req_qty = flt(req_items[0].required_qty)
|
req_qty = flt(req_items[0].required_qty)
|
||||||
@ -912,6 +913,7 @@ class StockEntry(StockController):
|
|||||||
"stock_uom": item.stock_uom,
|
"stock_uom": item.stock_uom,
|
||||||
"expense_account": item.expense_account,
|
"expense_account": item.expense_account,
|
||||||
"cost_center": item.buying_cost_center,
|
"cost_center": item.buying_cost_center,
|
||||||
|
"original_item": item.original_item
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -986,6 +988,7 @@ class StockEntry(StockController):
|
|||||||
se_child.cost_center = item_dict[d].get("cost_center") or cost_center
|
se_child.cost_center = item_dict[d].get("cost_center") or cost_center
|
||||||
se_child.allow_alternative_item = item_dict[d].get("allow_alternative_item", 0)
|
se_child.allow_alternative_item = item_dict[d].get("allow_alternative_item", 0)
|
||||||
se_child.subcontracted_item = item_dict[d].get("main_item_code")
|
se_child.subcontracted_item = item_dict[d].get("main_item_code")
|
||||||
|
se_child.original_item = item_dict[d].get("original_item")
|
||||||
|
|
||||||
if item_dict[d].get("idx"):
|
if item_dict[d].get("idx"):
|
||||||
se_child.idx = item_dict[d].get("idx")
|
se_child.idx = item_dict[d].get("idx")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user