Merge branch 'version-13-hotfix' of https://github.com/frappe/erpnext into rcm_tax_template_fetch_issue
This commit is contained in:
@@ -295,8 +295,15 @@ class PurchaseInvoice(BuyingController):
|
||||
item.expense_account = stock_not_billed_account
|
||||
|
||||
elif item.is_fixed_asset and not is_cwip_accounting_enabled(asset_category):
|
||||
item.expense_account = get_asset_category_account('fixed_asset_account', item=item.item_code,
|
||||
asset_category_account = get_asset_category_account('fixed_asset_account', item=item.item_code,
|
||||
company = self.company)
|
||||
if not asset_category_account:
|
||||
form_link = get_link_to_form('Asset Category', asset_category)
|
||||
throw(
|
||||
_("Please set Fixed Asset Account in {} against {}.").format(form_link, self.company),
|
||||
title=_("Missing Account")
|
||||
)
|
||||
item.expense_account = asset_category_account
|
||||
elif item.is_fixed_asset and item.pr_detail:
|
||||
item.expense_account = asset_received_but_not_billed
|
||||
elif not item.expense_account and for_validate:
|
||||
|
||||
@@ -24,6 +24,7 @@ from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
|
||||
get_accounting_dimensions,
|
||||
)
|
||||
from erpnext.accounts.doctype.subscription_plan.subscription_plan import get_plan_rate
|
||||
from erpnext.accounts.party import get_party_account_currency
|
||||
|
||||
|
||||
class Subscription(Document):
|
||||
@@ -356,6 +357,9 @@ class Subscription(Document):
|
||||
if frappe.db.get_value('Supplier', self.party, 'tax_withholding_category'):
|
||||
invoice.apply_tds = 1
|
||||
|
||||
### Add party currency to invoice
|
||||
invoice.currency = get_party_account_currency(self.party_type, self.party, self.company)
|
||||
|
||||
## Add dimensions in invoice for subscription:
|
||||
accounting_dimensions = get_accounting_dimensions()
|
||||
|
||||
|
||||
@@ -60,15 +60,38 @@ def create_plan():
|
||||
plan.billing_interval_count = 3
|
||||
plan.insert()
|
||||
|
||||
if not frappe.db.exists('Subscription Plan', '_Test Plan Multicurrency'):
|
||||
plan = frappe.new_doc('Subscription Plan')
|
||||
plan.plan_name = '_Test Plan Multicurrency'
|
||||
plan.item = '_Test Non Stock Item'
|
||||
plan.price_determination = "Fixed Rate"
|
||||
plan.cost = 50
|
||||
plan.currency = 'USD'
|
||||
plan.billing_interval = 'Month'
|
||||
plan.billing_interval_count = 1
|
||||
plan.insert()
|
||||
|
||||
def create_parties():
|
||||
if not frappe.db.exists('Supplier', '_Test Supplier'):
|
||||
supplier = frappe.new_doc('Supplier')
|
||||
supplier.supplier_name = '_Test Supplier'
|
||||
supplier.supplier_group = 'All Supplier Groups'
|
||||
supplier.insert()
|
||||
|
||||
if not frappe.db.exists('Customer', '_Test Subscription Customer'):
|
||||
customer = frappe.new_doc('Customer')
|
||||
customer.customer_name = '_Test Subscription Customer'
|
||||
customer.billing_currency = 'USD'
|
||||
customer.append('accounts', {
|
||||
'company': '_Test Company',
|
||||
'account': '_Test Receivable USD - _TC'
|
||||
})
|
||||
customer.insert()
|
||||
|
||||
class TestSubscription(unittest.TestCase):
|
||||
def setUp(self):
|
||||
create_plan()
|
||||
create_parties()
|
||||
|
||||
def test_create_subscription_with_trial_with_correct_period(self):
|
||||
subscription = frappe.new_doc('Subscription')
|
||||
@@ -637,3 +660,22 @@ class TestSubscription(unittest.TestCase):
|
||||
|
||||
subscription.process()
|
||||
self.assertEqual(len(subscription.invoices), 1)
|
||||
|
||||
def test_multicurrency_subscription(self):
|
||||
subscription = frappe.new_doc('Subscription')
|
||||
subscription.party_type = 'Customer'
|
||||
subscription.party = '_Test Subscription Customer'
|
||||
subscription.generate_invoice_at_period_start = 1
|
||||
subscription.company = '_Test Company'
|
||||
# select subscription start date as '2018-01-15'
|
||||
subscription.start_date = '2018-01-01'
|
||||
subscription.append('plans', {'plan': '_Test Plan Multicurrency', 'qty': 1})
|
||||
subscription.save()
|
||||
|
||||
subscription.process()
|
||||
self.assertEqual(len(subscription.invoices), 1)
|
||||
self.assertEqual(subscription.status, 'Unpaid')
|
||||
|
||||
# Check the currency of the created invoice
|
||||
currency = frappe.db.get_value('Sales Invoice', subscription.invoices[0].invoice, 'currency')
|
||||
self.assertEqual(currency, 'USD')
|
||||
@@ -545,7 +545,9 @@ class ReceivablePayableReport(object):
|
||||
|
||||
def set_ageing(self, row):
|
||||
if self.filters.ageing_based_on == "Due Date":
|
||||
entry_date = row.due_date
|
||||
# use posting date as a fallback for advances posted via journal and payment entry
|
||||
# when ageing viewed by due date
|
||||
entry_date = row.due_date or row.posting_date
|
||||
elif self.filters.ageing_based_on == "Supplier Invoice Date":
|
||||
entry_date = row.bill_date
|
||||
else:
|
||||
|
||||
@@ -36,12 +36,16 @@ def get_result(filters, tds_docs, tds_accounts, tax_category_map):
|
||||
posting_date = entry.posting_date
|
||||
voucher_type = entry.voucher_type
|
||||
|
||||
if not tax_withholding_category:
|
||||
tax_withholding_category = supplier_map.get(supplier, {}).get('tax_withholding_category')
|
||||
rate = tax_rate_map.get(tax_withholding_category)
|
||||
|
||||
if entry.account in tds_accounts:
|
||||
tds_deducted += (entry.credit - entry.debit)
|
||||
|
||||
total_amount_credited += (entry.credit - entry.debit)
|
||||
|
||||
if rate and tds_deducted:
|
||||
if tds_deducted:
|
||||
row = {
|
||||
'pan' if frappe.db.has_column('Supplier', 'pan') else 'tax_id': supplier_map.get(supplier, {}).get('pan'),
|
||||
'supplier': supplier_map.get(supplier, {}).get('name')
|
||||
@@ -67,7 +71,7 @@ def get_result(filters, tds_docs, tds_accounts, tax_category_map):
|
||||
|
||||
def get_supplier_pan_map():
|
||||
supplier_map = frappe._dict()
|
||||
suppliers = frappe.db.get_all('Supplier', fields=['name', 'pan', 'supplier_type', 'supplier_name'])
|
||||
suppliers = frappe.db.get_all('Supplier', fields=['name', 'pan', 'supplier_type', 'supplier_name', 'tax_withholding_category'])
|
||||
|
||||
for d in suppliers:
|
||||
supplier_map[d.name] = d
|
||||
|
||||
@@ -110,7 +110,7 @@ frappe.ui.form.on('Asset', {
|
||||
|
||||
if (frm.doc.status != 'Fully Depreciated') {
|
||||
frm.add_custom_button(__("Adjust Asset Value"), function() {
|
||||
frm.trigger("create_asset_adjustment");
|
||||
frm.trigger("create_asset_value_adjustment");
|
||||
}, __("Manage"));
|
||||
}
|
||||
|
||||
@@ -322,14 +322,14 @@ frappe.ui.form.on('Asset', {
|
||||
});
|
||||
},
|
||||
|
||||
create_asset_adjustment: function(frm) {
|
||||
create_asset_value_adjustment: function(frm) {
|
||||
frappe.call({
|
||||
args: {
|
||||
"asset": frm.doc.name,
|
||||
"asset_category": frm.doc.asset_category,
|
||||
"company": frm.doc.company
|
||||
},
|
||||
method: "erpnext.assets.doctype.asset.asset.create_asset_adjustment",
|
||||
method: "erpnext.assets.doctype.asset.asset.create_asset_value_adjustment",
|
||||
freeze: 1,
|
||||
callback: function(r) {
|
||||
var doclist = frappe.model.sync(r.message);
|
||||
|
||||
@@ -470,7 +470,6 @@ class Asset(AccountsController):
|
||||
|
||||
asset_value_after_full_schedule = flt(
|
||||
flt(self.gross_purchase_amount) -
|
||||
flt(self.opening_accumulated_depreciation) -
|
||||
flt(accumulated_depreciation_after_full_schedule), self.precision('gross_purchase_amount'))
|
||||
|
||||
if (row.expected_value_after_useful_life and
|
||||
@@ -732,14 +731,14 @@ def create_asset_repair(asset, asset_name):
|
||||
return asset_repair
|
||||
|
||||
@frappe.whitelist()
|
||||
def create_asset_adjustment(asset, asset_category, company):
|
||||
asset_maintenance = frappe.get_doc("Asset Value Adjustment")
|
||||
asset_maintenance.update({
|
||||
def create_asset_value_adjustment(asset, asset_category, company):
|
||||
asset_value_adjustment = frappe.new_doc("Asset Value Adjustment")
|
||||
asset_value_adjustment.update({
|
||||
"asset": asset,
|
||||
"company": company,
|
||||
"asset_category": asset_category
|
||||
})
|
||||
return asset_maintenance
|
||||
return asset_value_adjustment
|
||||
|
||||
@frappe.whitelist()
|
||||
def transfer_asset(args):
|
||||
|
||||
@@ -124,6 +124,14 @@ frappe.ui.form.on("Request for Quotation",{
|
||||
dialog.show()
|
||||
},
|
||||
|
||||
schedule_date(frm) {
|
||||
if(frm.doc.schedule_date){
|
||||
frm.doc.items.forEach((item) => {
|
||||
item.schedule_date = frm.doc.schedule_date;
|
||||
})
|
||||
}
|
||||
refresh_field("items");
|
||||
},
|
||||
preview: (frm) => {
|
||||
let dialog = new frappe.ui.Dialog({
|
||||
title: __('Preview Email'),
|
||||
@@ -184,7 +192,13 @@ frappe.ui.form.on("Request for Quotation",{
|
||||
dialog.show();
|
||||
}
|
||||
})
|
||||
|
||||
frappe.ui.form.on("Request for Quotation Item", {
|
||||
items_add(frm, cdt, cdn) {
|
||||
if (frm.doc.schedule_date) {
|
||||
frappe.model.set_value(cdt, cdn, 'schedule_date', frm.doc.schedule_date);
|
||||
}
|
||||
}
|
||||
});
|
||||
frappe.ui.form.on("Request for Quotation Supplier",{
|
||||
supplier: function(frm, cdt, cdn) {
|
||||
var d = locals[cdt][cdn]
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
"vendor",
|
||||
"column_break1",
|
||||
"transaction_date",
|
||||
"schedule_date",
|
||||
"status",
|
||||
"amended_from",
|
||||
"suppliers_section",
|
||||
@@ -246,16 +247,22 @@
|
||||
"fieldname": "sec_break_email_2",
|
||||
"fieldtype": "Section Break",
|
||||
"hide_border": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "schedule_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Required Date"
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-shopping-cart",
|
||||
"index_web_pages_for_search": 1,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-11-05 22:04:29.017134",
|
||||
"modified": "2021-11-24 17:47:49.909000",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Request for Quotation",
|
||||
"naming_rule": "By \"Naming Series\" field",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
|
||||
@@ -115,8 +115,7 @@
|
||||
],
|
||||
"issingle": 1,
|
||||
"links": [],
|
||||
"migration_hash": "8ca1ea3309ed28547b19da8e6e27e96f",
|
||||
"modified": "2021-11-30 11:17:24.647979",
|
||||
"modified": "2021-11-30 12:17:24.647979",
|
||||
"modified_by": "Administrator",
|
||||
"module": "ERPNext Integrations",
|
||||
"name": "TaxJar Settings",
|
||||
|
||||
@@ -56,9 +56,14 @@ class TestMaintenanceSchedule(unittest.TestCase):
|
||||
|
||||
ms.submit()
|
||||
s_id = ms.get_pending_data(data_type = "id", item_name = i.item_name, s_date = expected_dates[1])
|
||||
test = make_maintenance_visit(source_name = ms.name, item_name = "_Test Item", s_id = s_id)
|
||||
|
||||
# Check if item is mapped in visit.
|
||||
test_map_visit = make_maintenance_visit(source_name = ms.name, item_name = "_Test Item", s_id = s_id)
|
||||
self.assertEqual(len(test_map_visit.purposes), 1)
|
||||
self.assertEqual(test_map_visit.purposes[0].item_name, "_Test Item")
|
||||
|
||||
visit = frappe.new_doc('Maintenance Visit')
|
||||
visit = test
|
||||
visit = test_map_visit
|
||||
visit.maintenance_schedule = ms.name
|
||||
visit.maintenance_schedule_detail = s_id
|
||||
visit.completion_status = "Partially Completed"
|
||||
|
||||
@@ -47,7 +47,7 @@ frappe.ui.form.on('Maintenance Visit', {
|
||||
frm.set_value({ status: 'Draft' });
|
||||
}
|
||||
if (frm.doc.__islocal) {
|
||||
frm.clear_table("purposes");
|
||||
frm.doc.maintenance_type == 'Unscheduled' && frm.clear_table("purposes");
|
||||
frm.set_value({ mntc_date: frappe.datetime.get_today() });
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
|
||||
import unittest
|
||||
|
||||
import frappe
|
||||
from frappe.utils import add_months, today
|
||||
|
||||
from erpnext import get_company_currency
|
||||
from erpnext.tests.utils import ERPNextTestCase
|
||||
|
||||
from .blanket_order import make_order
|
||||
|
||||
|
||||
class TestBlanketOrder(unittest.TestCase):
|
||||
class TestBlanketOrder(ERPNextTestCase):
|
||||
def setUp(self):
|
||||
frappe.flags.args = frappe._dict()
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
|
||||
import unittest
|
||||
from collections import deque
|
||||
from functools import partial
|
||||
|
||||
@@ -18,10 +17,11 @@ from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import
|
||||
create_stock_reconciliation,
|
||||
)
|
||||
from erpnext.tests.test_subcontracting import set_backflush_based_on
|
||||
from erpnext.tests.utils import ERPNextTestCase
|
||||
|
||||
test_records = frappe.get_test_records('BOM')
|
||||
|
||||
class TestBOM(unittest.TestCase):
|
||||
class TestBOM(ERPNextTestCase):
|
||||
def setUp(self):
|
||||
if not frappe.get_value('Item', '_Test Item'):
|
||||
make_test_records('Item')
|
||||
|
||||
@@ -1,19 +1,16 @@
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
|
||||
|
||||
import unittest
|
||||
|
||||
import frappe
|
||||
|
||||
from erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool import update_cost
|
||||
from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
|
||||
from erpnext.stock.doctype.item.test_item import create_item
|
||||
from erpnext.tests.utils import ERPNextTestCase
|
||||
|
||||
test_records = frappe.get_test_records('BOM')
|
||||
|
||||
class TestBOMUpdateTool(unittest.TestCase):
|
||||
class TestBOMUpdateTool(ERPNextTestCase):
|
||||
def test_replace_bom(self):
|
||||
current_bom = "BOM-_Test Item Home Desktop Manufactured-001"
|
||||
|
||||
|
||||
@@ -76,6 +76,15 @@ frappe.ui.form.on('Job Card', {
|
||||
frm.trigger("prepare_timer_buttons");
|
||||
}
|
||||
frm.trigger("setup_quality_inspection");
|
||||
|
||||
if (frm.doc.work_order) {
|
||||
frappe.db.get_value('Work Order', frm.doc.work_order,
|
||||
'transfer_material_against').then((r) => {
|
||||
if (r.message.transfer_material_against == 'Work Order') {
|
||||
frm.set_df_property('items', 'hidden', 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
setup_quality_inspection: function(frm) {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
import unittest
|
||||
|
||||
import frappe
|
||||
from frappe.utils import random_string
|
||||
@@ -12,9 +11,10 @@ from erpnext.manufacturing.doctype.job_card.job_card import (
|
||||
from erpnext.manufacturing.doctype.work_order.test_work_order import make_wo_order_test_record
|
||||
from erpnext.manufacturing.doctype.workstation.test_workstation import make_workstation
|
||||
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
||||
from erpnext.tests.utils import ERPNextTestCase
|
||||
|
||||
|
||||
class TestJobCard(unittest.TestCase):
|
||||
class TestJobCard(ERPNextTestCase):
|
||||
def setUp(self):
|
||||
make_bom_for_jc_tests()
|
||||
|
||||
@@ -329,4 +329,4 @@ def make_bom_for_jc_tests():
|
||||
bom.rm_cost_as_per = "Valuation Rate"
|
||||
bom.items[0].uom = "_Test UOM 1"
|
||||
bom.items[0].conversion_factor = 5
|
||||
bom.insert()
|
||||
bom.insert()
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
|
||||
import unittest
|
||||
|
||||
import frappe
|
||||
from frappe.utils import add_to_date, flt, now_datetime, nowdate
|
||||
|
||||
@@ -17,9 +14,10 @@ from erpnext.stock.doctype.item.test_item import create_item
|
||||
from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import (
|
||||
create_stock_reconciliation,
|
||||
)
|
||||
from erpnext.tests.utils import ERPNextTestCase
|
||||
|
||||
|
||||
class TestProductionPlan(unittest.TestCase):
|
||||
class TestProductionPlan(ERPNextTestCase):
|
||||
def setUp(self):
|
||||
for item in ['Test Production Item 1', 'Subassembly Item 1',
|
||||
'Raw Material Item 1', 'Raw Material Item 2']:
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
|
||||
import unittest
|
||||
|
||||
import frappe
|
||||
from frappe.test_runner import make_test_records
|
||||
|
||||
from erpnext.manufacturing.doctype.job_card.job_card import OperationSequenceError
|
||||
from erpnext.manufacturing.doctype.work_order.test_work_order import make_wo_order_test_record
|
||||
from erpnext.stock.doctype.item.test_item import make_item
|
||||
from erpnext.tests.utils import ERPNextTestCase
|
||||
|
||||
|
||||
class TestRouting(unittest.TestCase):
|
||||
class TestRouting(ERPNextTestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.item_code = "Test Routing Item - A"
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
import unittest
|
||||
|
||||
import frappe
|
||||
from frappe.utils import add_months, cint, flt, now, today
|
||||
@@ -21,9 +20,10 @@ from erpnext.stock.doctype.item.test_item import create_item, make_item
|
||||
from erpnext.stock.doctype.stock_entry import test_stock_entry
|
||||
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
|
||||
from erpnext.stock.utils import get_bin
|
||||
from erpnext.tests.utils import ERPNextTestCase, timeout
|
||||
|
||||
|
||||
class TestWorkOrder(unittest.TestCase):
|
||||
class TestWorkOrder(ERPNextTestCase):
|
||||
def setUp(self):
|
||||
self.warehouse = '_Test Warehouse 2 - _TC'
|
||||
self.item = '_Test Item'
|
||||
@@ -91,7 +91,7 @@ class TestWorkOrder(unittest.TestCase):
|
||||
|
||||
def test_reserved_qty_for_partial_completion(self):
|
||||
item = "_Test Item"
|
||||
warehouse = create_warehouse("Test Warehouse for reserved_qty - _TC")
|
||||
warehouse = "_Test Warehouse - _TC"
|
||||
|
||||
bin1_at_start = get_bin(item, warehouse)
|
||||
|
||||
@@ -376,6 +376,7 @@ class TestWorkOrder(unittest.TestCase):
|
||||
self.assertEqual(len(ste.additional_costs), 1)
|
||||
self.assertEqual(ste.total_additional_costs, 1000)
|
||||
|
||||
@timeout(seconds=60)
|
||||
def test_job_card(self):
|
||||
stock_entries = []
|
||||
bom = frappe.get_doc('BOM', {
|
||||
@@ -769,6 +770,7 @@ class TestWorkOrder(unittest.TestCase):
|
||||
total_pl_qty
|
||||
)
|
||||
|
||||
@timeout(seconds=60)
|
||||
def test_job_card_scrap_item(self):
|
||||
items = ['Test FG Item for Scrap Item Test', 'Test RM Item 1 for Scrap Item Test',
|
||||
'Test RM Item 2 for Scrap Item Test']
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors
|
||||
# See license.txt
|
||||
|
||||
import unittest
|
||||
|
||||
import frappe
|
||||
from frappe.test_runner import make_test_records
|
||||
|
||||
@@ -13,12 +10,13 @@ from erpnext.manufacturing.doctype.workstation.workstation import (
|
||||
WorkstationHolidayError,
|
||||
check_if_within_operating_hours,
|
||||
)
|
||||
from erpnext.tests.utils import ERPNextTestCase
|
||||
|
||||
test_dependencies = ["Warehouse"]
|
||||
test_records = frappe.get_test_records('Workstation')
|
||||
make_test_records('Workstation')
|
||||
|
||||
class TestWorkstation(unittest.TestCase):
|
||||
class TestWorkstation(ERPNextTestCase):
|
||||
def test_validate_timings(self):
|
||||
check_if_within_operating_hours("_Test Workstation 1", "Operation 1", "2013-02-02 11:00:00", "2013-02-02 19:00:00")
|
||||
check_if_within_operating_hours("_Test Workstation 1", "Operation 1", "2013-02-02 10:00:00", "2013-02-02 20:00:00")
|
||||
|
||||
@@ -178,6 +178,7 @@ erpnext.patches.v12_0.set_updated_purpose_in_pick_list
|
||||
erpnext.patches.v12_0.set_default_payroll_based_on
|
||||
erpnext.patches.v12_0.repost_stock_ledger_entries_for_target_warehouse
|
||||
erpnext.patches.v12_0.update_end_date_and_status_in_email_campaign
|
||||
erpnext.patches.v13_0.validate_options_for_data_field
|
||||
erpnext.patches.v13_0.move_tax_slabs_from_payroll_period_to_income_tax_slab #123
|
||||
erpnext.patches.v12_0.fix_quotation_expired_status
|
||||
erpnext.patches.v12_0.update_appointment_reminder_scheduler_entry
|
||||
@@ -311,7 +312,6 @@ execute:frappe.reload_doc("erpnext_integrations", "doctype", "TaxJar Settings")
|
||||
execute:frappe.reload_doc("erpnext_integrations", "doctype", "Product Tax Category")
|
||||
erpnext.patches.v13_0.custom_fields_for_taxjar_integration #08-11-2021
|
||||
erpnext.patches.v13_0.set_operation_time_based_on_operating_cost
|
||||
erpnext.patches.v13_0.validate_options_for_data_field
|
||||
erpnext.patches.v13_0.create_website_items #30-09-2021
|
||||
erpnext.patches.v13_0.populate_e_commerce_settings
|
||||
erpnext.patches.v13_0.make_homepage_products_website_items
|
||||
@@ -336,5 +336,5 @@ erpnext.patches.v13_0.item_naming_series_not_mandatory
|
||||
erpnext.patches.v13_0.update_category_in_ltds_certificate
|
||||
erpnext.patches.v13_0.create_ksa_vat_custom_fields
|
||||
erpnext.patches.v13_0.rename_ksa_qr_field
|
||||
erpnext.patches.v13_0.disable_ksa_print_format_for_others
|
||||
erpnext.patches.v13_0.update_tax_category_for_rcm
|
||||
erpnext.patches.v13_0.disable_ksa_print_format_for_others # 16-12-2021
|
||||
erpnext.patches.v13_0.update_tax_category_for_rcm
|
||||
|
||||
@@ -3,10 +3,13 @@
|
||||
|
||||
import frappe
|
||||
|
||||
from erpnext.regional.saudi_arabia.setup import add_print_formats
|
||||
|
||||
|
||||
def execute():
|
||||
company = frappe.get_all('Company', filters = {'country': 'Saudi Arabia'})
|
||||
if company:
|
||||
add_print_formats()
|
||||
return
|
||||
|
||||
if frappe.db.exists('DocType', 'Print Format'):
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
import frappe
|
||||
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
|
||||
from frappe.model.utils.rename_field import rename_field
|
||||
|
||||
|
||||
@@ -12,5 +13,20 @@ def execute():
|
||||
|
||||
if frappe.db.exists('DocType', 'Sales Invoice'):
|
||||
frappe.reload_doc('accounts', 'doctype', 'sales_invoice', force=True)
|
||||
|
||||
# rename_field method assumes that the field already exists or the doc is synced
|
||||
if not frappe.db.has_column('Sales Invoice', 'ksa_einv_qr'):
|
||||
create_custom_fields({
|
||||
'Sales Invoice': [
|
||||
dict(
|
||||
fieldname='ksa_einv_qr',
|
||||
label='KSA E-Invoicing QR',
|
||||
fieldtype='Attach Image',
|
||||
read_only=1, no_copy=1, hidden=1
|
||||
)
|
||||
]
|
||||
})
|
||||
|
||||
if frappe.db.has_column('Sales Invoice', 'qr_code'):
|
||||
rename_field('Sales Invoice', 'qr_code', 'ksa_einv_qr')
|
||||
frappe.delete_doc_if_exists("Custom Field", "Sales Invoice-qr_code")
|
||||
|
||||
@@ -944,10 +944,12 @@ class SalarySlip(TransactionBase):
|
||||
|
||||
def get_amount_based_on_payment_days(self, row, joining_date, relieving_date):
|
||||
amount, additional_amount = row.amount, row.additional_amount
|
||||
timesheet_component = frappe.db.get_value("Salary Structure", self.salary_structure, "salary_component")
|
||||
|
||||
if (self.salary_structure and
|
||||
cint(row.depends_on_payment_days) and cint(self.total_working_days)
|
||||
and not (row.additional_salary and row.default_amount) # to identify overwritten additional salary
|
||||
and (not self.salary_slip_based_on_timesheet or
|
||||
and (row.salary_component != timesheet_component or
|
||||
getdate(self.start_date) < joining_date or
|
||||
(relieving_date and getdate(self.end_date) > relieving_date)
|
||||
)):
|
||||
@@ -956,7 +958,7 @@ class SalarySlip(TransactionBase):
|
||||
amount = flt((flt(row.default_amount) * flt(self.payment_days)
|
||||
/ cint(self.total_working_days)), row.precision("amount")) + additional_amount
|
||||
|
||||
elif not self.payment_days and not self.salary_slip_based_on_timesheet and cint(row.depends_on_payment_days):
|
||||
elif not self.payment_days and row.salary_component != timesheet_component and cint(row.depends_on_payment_days):
|
||||
amount, additional_amount = 0, 0
|
||||
elif not row.amount:
|
||||
amount = flt(row.default_amount) + flt(row.additional_amount)
|
||||
|
||||
@@ -134,6 +134,57 @@ class TestSalarySlip(unittest.TestCase):
|
||||
|
||||
frappe.db.set_value("Payroll Settings", None, "payroll_based_on", "Leave")
|
||||
|
||||
def test_payment_days_in_salary_slip_based_on_timesheet(self):
|
||||
from erpnext.hr.doctype.attendance.attendance import mark_attendance
|
||||
from erpnext.projects.doctype.timesheet.test_timesheet import (
|
||||
make_salary_structure_for_timesheet,
|
||||
make_timesheet,
|
||||
)
|
||||
from erpnext.projects.doctype.timesheet.timesheet import (
|
||||
make_salary_slip as make_salary_slip_for_timesheet,
|
||||
)
|
||||
|
||||
# Payroll based on attendance
|
||||
frappe.db.set_value("Payroll Settings", None, "payroll_based_on", "Attendance")
|
||||
|
||||
emp = make_employee("test_employee_timesheet@salary.com", company="_Test Company")
|
||||
frappe.db.set_value("Employee", emp, {"relieving_date": None, "status": "Active"})
|
||||
|
||||
# mark attendance
|
||||
month_start_date = get_first_day(nowdate())
|
||||
month_end_date = get_last_day(nowdate())
|
||||
|
||||
first_sunday = frappe.db.sql("""
|
||||
select holiday_date from `tabHoliday`
|
||||
where parent = 'Salary Slip Test Holiday List'
|
||||
and holiday_date between %s and %s
|
||||
order by holiday_date
|
||||
""", (month_start_date, month_end_date))[0][0]
|
||||
|
||||
mark_attendance(emp, add_days(first_sunday, 1), 'Absent', ignore_validate=True) # counted as absent
|
||||
|
||||
# salary structure based on timesheet
|
||||
make_salary_structure_for_timesheet(emp)
|
||||
timesheet = make_timesheet(emp, simulate=True, is_billable=1)
|
||||
salary_slip = make_salary_slip_for_timesheet(timesheet.name)
|
||||
salary_slip.start_date = month_start_date
|
||||
salary_slip.end_date = month_end_date
|
||||
salary_slip.save()
|
||||
salary_slip.submit()
|
||||
|
||||
no_of_days = self.get_no_of_days()
|
||||
days_in_month = no_of_days[0]
|
||||
no_of_holidays = no_of_days[1]
|
||||
|
||||
self.assertEqual(salary_slip.payment_days, days_in_month - no_of_holidays - 1)
|
||||
|
||||
# gross pay calculation based on attendance (payment days)
|
||||
gross_pay = 78100 - ((78000 / (days_in_month - no_of_holidays)) * flt(salary_slip.leave_without_pay + salary_slip.absent_days))
|
||||
|
||||
self.assertEqual(salary_slip.gross_pay, flt(gross_pay, 2))
|
||||
|
||||
frappe.db.set_value("Payroll Settings", None, "payroll_based_on", "Leave")
|
||||
|
||||
def test_component_amount_dependent_on_another_payment_days_based_component(self):
|
||||
from erpnext.hr.doctype.attendance.attendance import mark_attendance
|
||||
from erpnext.payroll.doctype.salary_structure.test_salary_structure import (
|
||||
|
||||
@@ -34,10 +34,6 @@ class TestTimesheet(unittest.TestCase):
|
||||
for dt in ["Salary Slip", "Salary Structure", "Salary Structure Assignment", "Timesheet"]:
|
||||
frappe.db.sql("delete from `tab%s`" % dt)
|
||||
|
||||
if not frappe.db.exists("Salary Component", "Timesheet Component"):
|
||||
frappe.get_doc({"doctype": "Salary Component", "salary_component": "Timesheet Component"}).insert()
|
||||
|
||||
|
||||
def test_timesheet_billing_amount(self):
|
||||
emp = make_employee("test_employee_6@salary.com")
|
||||
|
||||
@@ -160,6 +156,9 @@ def make_salary_structure_for_timesheet(employee, company=None):
|
||||
salary_structure_name = "Timesheet Salary Structure Test"
|
||||
frequency = "Monthly"
|
||||
|
||||
if not frappe.db.exists("Salary Component", "Timesheet Component"):
|
||||
frappe.get_doc({"doctype": "Salary Component", "salary_component": "Timesheet Component"}).insert()
|
||||
|
||||
salary_structure = make_salary_structure(salary_structure_name, frequency, company=company, dont_submit=True)
|
||||
salary_structure.salary_component = "Timesheet Component"
|
||||
salary_structure.salary_slip_based_on_timesheet = 1
|
||||
|
||||
@@ -115,9 +115,11 @@ def get_items(filters):
|
||||
|
||||
items = frappe.db.sql("""
|
||||
select
|
||||
`tabSales Invoice Item`.name, `tabSales Invoice Item`.base_price_list_rate,
|
||||
`tabSales Invoice Item`.gst_hsn_code, `tabSales Invoice Item`.stock_qty,
|
||||
`tabSales Invoice Item`.stock_uom, `tabSales Invoice Item`.base_net_amount,
|
||||
`tabSales Invoice Item`.gst_hsn_code,
|
||||
`tabSales Invoice Item`.stock_uom,
|
||||
sum(`tabSales Invoice Item`.stock_qty) as stock_qty,
|
||||
sum(`tabSales Invoice Item`.base_net_amount) as base_net_amount,
|
||||
sum(`tabSales Invoice Item`.base_price_list_rate) as base_price_list_rate,
|
||||
`tabSales Invoice Item`.parent, `tabSales Invoice Item`.item_code,
|
||||
`tabGST HSN Code`.description
|
||||
from `tabSales Invoice`, `tabSales Invoice Item`, `tabGST HSN Code`
|
||||
@@ -125,6 +127,8 @@ def get_items(filters):
|
||||
and `tabSales Invoice`.docstatus = 1
|
||||
and `tabSales Invoice Item`.gst_hsn_code is not NULL
|
||||
and `tabSales Invoice Item`.gst_hsn_code = `tabGST HSN Code`.name %s %s
|
||||
group by
|
||||
`tabSales Invoice Item`.parent, `tabSales Invoice Item`.item_code
|
||||
|
||||
""" % (conditions, match_conditions), filters, as_dict=1)
|
||||
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
import frappe
|
||||
|
||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
||||
from erpnext.regional.doctype.gstr_3b_report.test_gstr_3b_report import (
|
||||
make_company as setup_company,
|
||||
)
|
||||
from erpnext.regional.doctype.gstr_3b_report.test_gstr_3b_report import (
|
||||
make_customers as setup_customers,
|
||||
)
|
||||
from erpnext.regional.doctype.gstr_3b_report.test_gstr_3b_report import (
|
||||
set_account_heads as setup_gst_settings,
|
||||
)
|
||||
from erpnext.regional.report.hsn_wise_summary_of_outward_supplies.hsn_wise_summary_of_outward_supplies import (
|
||||
execute as run_report,
|
||||
)
|
||||
from erpnext.stock.doctype.item.test_item import make_item
|
||||
|
||||
|
||||
class TestHSNWiseSummaryReport(TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
setup_company()
|
||||
setup_customers()
|
||||
setup_gst_settings()
|
||||
make_item("Golf Car", properties={ "gst_hsn_code": "999900" })
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
frappe.db.rollback()
|
||||
|
||||
def test_hsn_summary_for_invoice_with_duplicate_items(self):
|
||||
si = create_sales_invoice(
|
||||
company="_Test Company GST",
|
||||
customer = "_Test GST Customer",
|
||||
currency = "INR",
|
||||
warehouse = "Finished Goods - _GST",
|
||||
debit_to = "Debtors - _GST",
|
||||
income_account = "Sales - _GST",
|
||||
expense_account = "Cost of Goods Sold - _GST",
|
||||
cost_center = "Main - _GST",
|
||||
do_not_save=1
|
||||
)
|
||||
|
||||
si.items = []
|
||||
si.append("items", {
|
||||
"item_code": "Golf Car",
|
||||
"gst_hsn_code": "999900",
|
||||
"qty": "1",
|
||||
"rate": "120",
|
||||
"cost_center": "Main - _GST"
|
||||
})
|
||||
si.append("items", {
|
||||
"item_code": "Golf Car",
|
||||
"gst_hsn_code": "999900",
|
||||
"qty": "1",
|
||||
"rate": "140",
|
||||
"cost_center": "Main - _GST"
|
||||
})
|
||||
si.append("taxes", {
|
||||
"charge_type": "On Net Total",
|
||||
"account_head": "Output Tax IGST - _GST",
|
||||
"cost_center": "Main - _GST",
|
||||
"description": "IGST @ 18.0",
|
||||
"rate": 18
|
||||
})
|
||||
si.posting_date = "2020-11-17"
|
||||
si.submit()
|
||||
si.reload()
|
||||
|
||||
[columns, data] = run_report(filters=frappe._dict({
|
||||
"company": "_Test Company GST",
|
||||
"gst_hsn_code": "999900",
|
||||
"company_gstin": si.company_gstin,
|
||||
"from_date": si.posting_date,
|
||||
"to_date": si.posting_date
|
||||
}))
|
||||
|
||||
filtered_rows = list(filter(lambda row: row['gst_hsn_code'] == "999900", data))
|
||||
self.assertTrue(filtered_rows)
|
||||
|
||||
hsn_row = filtered_rows[0]
|
||||
self.assertEquals(hsn_row['stock_qty'], 2.0)
|
||||
self.assertEquals(hsn_row['total_amount'], 306.8)
|
||||
@@ -2,8 +2,6 @@
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
|
||||
import unittest
|
||||
|
||||
import frappe
|
||||
from frappe.test_runner import make_test_records
|
||||
from frappe.utils import flt
|
||||
@@ -11,7 +9,7 @@ from frappe.utils import flt
|
||||
from erpnext.accounts.party import get_due_date
|
||||
from erpnext.exceptions import PartyDisabled, PartyFrozen
|
||||
from erpnext.selling.doctype.customer.customer import get_credit_limit, get_customer_outstanding
|
||||
from erpnext.tests.utils import create_test_contact_and_address
|
||||
from erpnext.tests.utils import ERPNextTestCase, create_test_contact_and_address
|
||||
|
||||
test_ignore = ["Price List"]
|
||||
test_dependencies = ['Payment Term', 'Payment Terms Template']
|
||||
@@ -20,7 +18,7 @@ test_records = frappe.get_test_records('Customer')
|
||||
from six import iteritems
|
||||
|
||||
|
||||
class TestCustomer(unittest.TestCase):
|
||||
class TestCustomer(ERPNextTestCase):
|
||||
def setUp(self):
|
||||
if not frappe.get_value('Item', '_Test Item'):
|
||||
make_test_records('Item')
|
||||
|
||||
@@ -6,6 +6,7 @@ import unittest
|
||||
import frappe
|
||||
|
||||
from erpnext.controllers.queries import item_query
|
||||
from erpnext.tests.utils import ERPNextTestCase
|
||||
|
||||
test_dependencies = ['Item', 'Customer', 'Supplier']
|
||||
|
||||
@@ -17,7 +18,7 @@ def create_party_specific_item(**args):
|
||||
psi.based_on_value = args.get('based_on_value')
|
||||
psi.insert()
|
||||
|
||||
class TestPartySpecificItem(unittest.TestCase):
|
||||
class TestPartySpecificItem(ERPNextTestCase):
|
||||
def setUp(self):
|
||||
self.customer = frappe.get_last_doc("Customer")
|
||||
self.supplier = frappe.get_last_doc("Supplier")
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
import unittest
|
||||
|
||||
import frappe
|
||||
from frappe.utils import add_days, add_months, flt, getdate, nowdate
|
||||
|
||||
from erpnext.tests.utils import ERPNextTestCase
|
||||
|
||||
test_dependencies = ["Product Bundle"]
|
||||
|
||||
|
||||
class TestQuotation(unittest.TestCase):
|
||||
class TestQuotation(ERPNextTestCase):
|
||||
def test_make_quotation_without_terms(self):
|
||||
quotation = make_quotation(do_not_save=1)
|
||||
self.assertFalse(quotation.get('payment_schedule'))
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
import json
|
||||
import unittest
|
||||
|
||||
import frappe
|
||||
import frappe.permissions
|
||||
@@ -22,12 +21,14 @@ from erpnext.selling.doctype.sales_order.sales_order import (
|
||||
)
|
||||
from erpnext.stock.doctype.item.test_item import make_item
|
||||
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
||||
from erpnext.tests.utils import ERPNextTestCase
|
||||
|
||||
|
||||
class TestSalesOrder(unittest.TestCase):
|
||||
class TestSalesOrder(ERPNextTestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.unlink_setting = int(frappe.db.get_value("Accounts Settings", "Accounts Settings",
|
||||
"unlink_advance_payment_on_cancelation_of_order"))
|
||||
|
||||
@@ -36,6 +37,7 @@ class TestSalesOrder(unittest.TestCase):
|
||||
# reset config to previous state
|
||||
frappe.db.set_value("Accounts Settings", "Accounts Settings",
|
||||
"unlink_advance_payment_on_cancelation_of_order", cls.unlink_setting)
|
||||
super().tearDownClass()
|
||||
|
||||
def tearDown(self):
|
||||
frappe.set_user("Administrator")
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
# For license information, please see license.txt
|
||||
|
||||
|
||||
import unittest
|
||||
|
||||
from frappe.utils import add_months, nowdate
|
||||
|
||||
from erpnext.selling.doctype.sales_order.sales_order import make_material_request
|
||||
@@ -11,9 +9,10 @@ from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_orde
|
||||
from erpnext.selling.report.pending_so_items_for_purchase_request.pending_so_items_for_purchase_request import (
|
||||
execute,
|
||||
)
|
||||
from erpnext.tests.utils import ERPNextTestCase
|
||||
|
||||
|
||||
class TestPendingSOItemsForPurchaseRequest(unittest.TestCase):
|
||||
class TestPendingSOItemsForPurchaseRequest(ERPNextTestCase):
|
||||
def test_result_for_partial_material_request(self):
|
||||
so = make_sales_order()
|
||||
mr=make_material_request(so.name)
|
||||
|
||||
@@ -2,15 +2,14 @@
|
||||
# For license information, please see license.txt
|
||||
|
||||
|
||||
import unittest
|
||||
|
||||
import frappe
|
||||
|
||||
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
|
||||
from erpnext.selling.report.sales_analytics.sales_analytics import execute
|
||||
from erpnext.tests.utils import ERPNextTestCase
|
||||
|
||||
|
||||
class TestAnalytics(unittest.TestCase):
|
||||
class TestAnalytics(ERPNextTestCase):
|
||||
def test_sales_analytics(self):
|
||||
frappe.db.sql("delete from `tabSales Order` where company='_Test Company 2'")
|
||||
|
||||
|
||||
@@ -33,10 +33,10 @@ class Bin(Document):
|
||||
in open work orders'''
|
||||
self.reserved_qty_for_production = frappe.db.sql('''
|
||||
SELECT
|
||||
CASE WHEN ifnull(skip_transfer, 0) = 0 THEN
|
||||
SUM(item.required_qty - item.transferred_qty)
|
||||
SUM(CASE WHEN ifnull(skip_transfer, 0) = 0 THEN
|
||||
item.required_qty - item.transferred_qty
|
||||
ELSE
|
||||
SUM(item.required_qty - item.consumed_qty)
|
||||
item.required_qty - item.consumed_qty END)
|
||||
END
|
||||
FROM `tabWork Order` pro, `tabWork Order Item` item
|
||||
WHERE
|
||||
|
||||
@@ -359,8 +359,7 @@
|
||||
"fieldname": "valuation_method",
|
||||
"fieldtype": "Select",
|
||||
"label": "Valuation Method",
|
||||
"options": "\nFIFO\nMoving Average",
|
||||
"set_only_once": 1
|
||||
"options": "\nFIFO\nMoving Average"
|
||||
},
|
||||
{
|
||||
"depends_on": "is_stock_item",
|
||||
@@ -956,7 +955,7 @@
|
||||
"image_field": "image",
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2021-12-03 08:32:03.869294",
|
||||
"modified": "2021-12-14 04:13:16.857534",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Item",
|
||||
|
||||
@@ -1,451 +1,140 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"autoname": "hash",
|
||||
"beta": 0,
|
||||
"creation": "2013-04-08 13:10:16",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "Document",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"actions": [],
|
||||
"autoname": "hash",
|
||||
"creation": "2013-04-08 13:10:16",
|
||||
"doctype": "DocType",
|
||||
"document_type": "Document",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"item_code",
|
||||
"column_break_2",
|
||||
"item_name",
|
||||
"batch_no",
|
||||
"desc_section",
|
||||
"description",
|
||||
"quantity_section",
|
||||
"qty",
|
||||
"net_weight",
|
||||
"column_break_10",
|
||||
"stock_uom",
|
||||
"weight_uom",
|
||||
"page_break",
|
||||
"dn_detail"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "item_code",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 1,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Item Code",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Item",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"print_width": "100px",
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0,
|
||||
"fieldname": "item_code",
|
||||
"fieldtype": "Link",
|
||||
"in_global_search": 1,
|
||||
"in_list_view": 1,
|
||||
"label": "Item Code",
|
||||
"options": "Item",
|
||||
"print_width": "100px",
|
||||
"reqd": 1,
|
||||
"width": "100px"
|
||||
},
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_2",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "column_break_2",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "item_name",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Item Name",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "item_code.item_name",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"print_width": "200px",
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0,
|
||||
"fetch_from": "item_code.item_name",
|
||||
"fieldname": "item_name",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "Item Name",
|
||||
"print_width": "200px",
|
||||
"read_only": 1,
|
||||
"width": "200px"
|
||||
},
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "batch_no",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Batch No",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Batch",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "batch_no",
|
||||
"fieldtype": "Link",
|
||||
"label": "Batch No",
|
||||
"options": "Batch"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 1,
|
||||
"columns": 0,
|
||||
"fieldname": "desc_section",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Description",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"collapsible": 1,
|
||||
"fieldname": "desc_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Description"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "description",
|
||||
"fieldtype": "Text Editor",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Description",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "description",
|
||||
"fieldtype": "Text Editor",
|
||||
"label": "Description"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "quantity_section",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Quantity",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "quantity_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Quantity"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "qty",
|
||||
"fieldtype": "Float",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Quantity",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"print_width": "100px",
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0,
|
||||
"fieldname": "qty",
|
||||
"fieldtype": "Float",
|
||||
"in_list_view": 1,
|
||||
"label": "Quantity",
|
||||
"print_width": "100px",
|
||||
"reqd": 1,
|
||||
"width": "100px"
|
||||
},
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "net_weight",
|
||||
"fieldtype": "Float",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Net Weight",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"print_width": "100px",
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0,
|
||||
"fieldname": "net_weight",
|
||||
"fieldtype": "Float",
|
||||
"in_list_view": 1,
|
||||
"label": "Net Weight",
|
||||
"print_width": "100px",
|
||||
"width": "100px"
|
||||
},
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_10",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "column_break_10",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "stock_uom",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "UOM",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "UOM",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"print_width": "100px",
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0,
|
||||
"fieldname": "stock_uom",
|
||||
"fieldtype": "Link",
|
||||
"label": "UOM",
|
||||
"options": "UOM",
|
||||
"print_width": "100px",
|
||||
"read_only": 1,
|
||||
"width": "100px"
|
||||
},
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "weight_uom",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Weight UOM",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "UOM",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"print_width": "100px",
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0,
|
||||
"fieldname": "weight_uom",
|
||||
"fieldtype": "Link",
|
||||
"label": "Weight UOM",
|
||||
"options": "UOM",
|
||||
"print_width": "100px",
|
||||
"width": "100px"
|
||||
},
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "page_break",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Page Break",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"allow_on_submit": 1,
|
||||
"default": "0",
|
||||
"fieldname": "page_break",
|
||||
"fieldtype": "Check",
|
||||
"in_list_view": 1,
|
||||
"label": "Page Break"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "dn_detail",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "DN Detail",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
"fieldname": "dn_detail",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"in_list_view": 1,
|
||||
"label": "DN Detail"
|
||||
}
|
||||
],
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 1,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-06-01 07:21:58.220980",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Packing Slip Item",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 0,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"track_changes": 1,
|
||||
"track_seen": 0
|
||||
],
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2021-12-14 01:22:00.715935",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Packing Slip Item",
|
||||
"naming_rule": "Random",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
@@ -48,6 +48,7 @@ def get_item_info(filters):
|
||||
conditions = [get_item_group_condition(filters.get("item_group"))]
|
||||
if filters.get("brand"):
|
||||
conditions.append("item.brand=%(brand)s")
|
||||
conditions.append("is_stock_item = 1")
|
||||
|
||||
return frappe.db.sql("""select name, item_name, description, brand, item_group,
|
||||
safety_stock, lead_time_days from `tabItem` item where {}"""
|
||||
|
||||
@@ -168,7 +168,7 @@ def get_stock_ledger_entries(filters, items):
|
||||
sle.company, sle.voucher_type, sle.qty_after_transaction, sle.stock_value_difference,
|
||||
sle.item_code as name, sle.voucher_no, sle.stock_value, sle.batch_no
|
||||
from
|
||||
`tabStock Ledger Entry` sle force index (posting_sort_index)
|
||||
`tabStock Ledger Entry` sle
|
||||
where sle.docstatus < 2 %s %s
|
||||
and is_cancelled = 0
|
||||
order by sle.posting_date, sle.posting_time, sle.creation, sle.actual_qty""" % #nosec
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
/* eslint-disable */
|
||||
|
||||
const DIFFERNCE_FIELD_NAMES = [
|
||||
"difference_in_qty",
|
||||
"fifo_qty_diff",
|
||||
"fifo_value_diff",
|
||||
"fifo_valuation_diff",
|
||||
"valuation_diff",
|
||||
"fifo_difference_diff"
|
||||
];
|
||||
|
||||
frappe.query_reports["Stock Ledger Invariant Check"] = {
|
||||
"filters": [
|
||||
{
|
||||
"fieldname": "item_code",
|
||||
"fieldtype": "Link",
|
||||
"label": "Item",
|
||||
"mandatory": 1,
|
||||
"options": "Item",
|
||||
get_query: function() {
|
||||
return {
|
||||
filters: {is_stock_item: 1, has_serial_no: 0}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"fieldname": "warehouse",
|
||||
"fieldtype": "Link",
|
||||
"label": "Warehouse",
|
||||
"mandatory": 1,
|
||||
"options": "Warehouse",
|
||||
}
|
||||
],
|
||||
formatter (value, row, column, data, default_formatter) {
|
||||
value = default_formatter(value, row, column, data);
|
||||
if (DIFFERNCE_FIELD_NAMES.includes(column.fieldname) && Math.abs(data[column.fieldname]) > 0.001) {
|
||||
value = "<span style='color:red'>" + value + "</span>";
|
||||
}
|
||||
return value;
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"add_total_row": 0,
|
||||
"columns": [],
|
||||
"creation": "2021-12-16 06:31:23.290916",
|
||||
"disable_prepared_report": 0,
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"filters": [],
|
||||
"idx": 0,
|
||||
"is_standard": "Yes",
|
||||
"modified": "2021-12-16 09:55:58.341764",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Stock Ledger Invariant Check",
|
||||
"owner": "Administrator",
|
||||
"prepared_report": 0,
|
||||
"ref_doctype": "Stock Ledger Entry",
|
||||
"report_name": "Stock Ledger Invariant Check",
|
||||
"report_type": "Script Report",
|
||||
"roles": [
|
||||
{
|
||||
"role": "System Manager"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,236 @@
|
||||
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# License: GNU GPL v3. See LICENSE
|
||||
|
||||
import json
|
||||
|
||||
import frappe
|
||||
|
||||
SLE_FIELDS = (
|
||||
"name",
|
||||
"posting_date",
|
||||
"posting_time",
|
||||
"creation",
|
||||
"voucher_type",
|
||||
"voucher_no",
|
||||
"actual_qty",
|
||||
"qty_after_transaction",
|
||||
"incoming_rate",
|
||||
"outgoing_rate",
|
||||
"stock_queue",
|
||||
"batch_no",
|
||||
"stock_value",
|
||||
"stock_value_difference",
|
||||
"valuation_rate",
|
||||
)
|
||||
|
||||
|
||||
def execute(filters=None):
|
||||
columns = get_columns()
|
||||
data = get_data(filters)
|
||||
return columns, data
|
||||
|
||||
|
||||
def get_data(filters):
|
||||
sles = get_stock_ledger_entries(filters)
|
||||
return add_invariant_check_fields(sles)
|
||||
|
||||
|
||||
def get_stock_ledger_entries(filters):
|
||||
return frappe.get_all(
|
||||
"Stock Ledger Entry",
|
||||
fields=SLE_FIELDS,
|
||||
filters={
|
||||
"item_code": filters.item_code,
|
||||
"warehouse": filters.warehouse,
|
||||
"is_cancelled": 0
|
||||
},
|
||||
order_by="timestamp(posting_date, posting_time), creation",
|
||||
)
|
||||
|
||||
|
||||
def add_invariant_check_fields(sles):
|
||||
balance_qty = 0.0
|
||||
for idx, sle in enumerate(sles):
|
||||
queue = json.loads(sle.stock_queue)
|
||||
|
||||
fifo_qty = 0.0
|
||||
fifo_value = 0.0
|
||||
for qty, rate in queue:
|
||||
fifo_qty += qty
|
||||
fifo_value += qty * rate
|
||||
|
||||
balance_qty += sle.actual_qty
|
||||
if sle.voucher_type == "Stock Reconciliation" and not sle.batch_no:
|
||||
balance_qty = sle.qty_after_transaction
|
||||
|
||||
sle.fifo_queue_qty = fifo_qty
|
||||
sle.fifo_stock_value = fifo_value
|
||||
sle.fifo_valuation_rate = fifo_value / fifo_qty if fifo_qty else None
|
||||
sle.balance_value_by_qty = (
|
||||
sle.stock_value / sle.qty_after_transaction if sle.qty_after_transaction else None
|
||||
)
|
||||
sle.expected_qty_after_transaction = balance_qty
|
||||
|
||||
# set difference fields
|
||||
sle.difference_in_qty = sle.qty_after_transaction - sle.expected_qty_after_transaction
|
||||
sle.fifo_qty_diff = sle.qty_after_transaction - fifo_qty
|
||||
sle.fifo_value_diff = sle.stock_value - fifo_value
|
||||
sle.fifo_valuation_diff = (
|
||||
sle.valuation_rate - sle.fifo_valuation_rate if sle.fifo_valuation_rate else None
|
||||
)
|
||||
sle.valuation_diff = (
|
||||
sle.valuation_rate - sle.balance_value_by_qty if sle.balance_value_by_qty else None
|
||||
)
|
||||
|
||||
if idx > 0:
|
||||
sle.fifo_stock_diff = sle.fifo_stock_value - sles[idx - 1].fifo_stock_value
|
||||
sle.fifo_difference_diff = sle.fifo_stock_diff - sle.stock_value_difference
|
||||
|
||||
return sles
|
||||
|
||||
|
||||
def get_columns():
|
||||
return [
|
||||
{
|
||||
"fieldname": "name",
|
||||
"fieldtype": "Link",
|
||||
"label": "Stock Ledger Entry",
|
||||
"options": "Stock Ledger Entry",
|
||||
},
|
||||
{
|
||||
"fieldname": "posting_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Posting Date",
|
||||
},
|
||||
{
|
||||
"fieldname": "posting_time",
|
||||
"fieldtype": "Time",
|
||||
"label": "Posting Time",
|
||||
},
|
||||
{
|
||||
"fieldname": "creation",
|
||||
"fieldtype": "Datetime",
|
||||
"label": "Creation",
|
||||
},
|
||||
{
|
||||
"fieldname": "voucher_type",
|
||||
"fieldtype": "Link",
|
||||
"label": "Voucher Type",
|
||||
"options": "DocType",
|
||||
},
|
||||
{
|
||||
"fieldname": "voucher_no",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"label": "Voucher No",
|
||||
"options": "voucher_type",
|
||||
},
|
||||
{
|
||||
"fieldname": "batch_no",
|
||||
"fieldtype": "Link",
|
||||
"label": "Batch",
|
||||
"options": "Batch",
|
||||
},
|
||||
{
|
||||
"fieldname": "actual_qty",
|
||||
"fieldtype": "Float",
|
||||
"label": "Qty Change",
|
||||
},
|
||||
{
|
||||
"fieldname": "incoming_rate",
|
||||
"fieldtype": "Float",
|
||||
"label": "Incoming Rate",
|
||||
},
|
||||
{
|
||||
"fieldname": "outgoing_rate",
|
||||
"fieldtype": "Float",
|
||||
"label": "Outgoing Rate",
|
||||
},
|
||||
{
|
||||
"fieldname": "qty_after_transaction",
|
||||
"fieldtype": "Float",
|
||||
"label": "(A) Qty After Transaction",
|
||||
},
|
||||
{
|
||||
"fieldname": "expected_qty_after_transaction",
|
||||
"fieldtype": "Float",
|
||||
"label": "(B) Expected Qty After Transaction",
|
||||
},
|
||||
{
|
||||
"fieldname": "difference_in_qty",
|
||||
"fieldtype": "Float",
|
||||
"label": "A - B",
|
||||
},
|
||||
{
|
||||
"fieldname": "stock_queue",
|
||||
"fieldtype": "Data",
|
||||
"label": "FIFO Queue",
|
||||
},
|
||||
|
||||
{
|
||||
"fieldname": "fifo_queue_qty",
|
||||
"fieldtype": "Float",
|
||||
"label": "(C) Total qty in queue",
|
||||
},
|
||||
{
|
||||
"fieldname": "fifo_qty_diff",
|
||||
"fieldtype": "Float",
|
||||
"label": "A - C",
|
||||
},
|
||||
{
|
||||
"fieldname": "stock_value",
|
||||
"fieldtype": "Float",
|
||||
"label": "(D) Balance Stock Value",
|
||||
},
|
||||
{
|
||||
"fieldname": "fifo_stock_value",
|
||||
"fieldtype": "Float",
|
||||
"label": "(E) Balance Stock Value in Queue",
|
||||
},
|
||||
{
|
||||
"fieldname": "fifo_value_diff",
|
||||
"fieldtype": "Float",
|
||||
"label": "D - E",
|
||||
},
|
||||
|
||||
{
|
||||
"fieldname": "stock_value_difference",
|
||||
"fieldtype": "Float",
|
||||
"label": "(F) Stock Value Difference",
|
||||
},
|
||||
{
|
||||
"fieldname": "fifo_stock_diff",
|
||||
"fieldtype": "Float",
|
||||
"label": "(G) Stock Value difference (FIFO queue)",
|
||||
},
|
||||
{
|
||||
"fieldname": "fifo_difference_diff",
|
||||
"fieldtype": "Float",
|
||||
"label": "F - G",
|
||||
},
|
||||
{
|
||||
"fieldname": "valuation_rate",
|
||||
"fieldtype": "Float",
|
||||
"label": "(H) Valuation Rate",
|
||||
},
|
||||
{
|
||||
"fieldname": "fifo_valuation_rate",
|
||||
"fieldtype": "Float",
|
||||
"label": "(I) Valuation Rate as per FIFO",
|
||||
},
|
||||
|
||||
{
|
||||
"fieldname": "fifo_valuation_diff",
|
||||
"fieldtype": "Float",
|
||||
"label": "H - I",
|
||||
},
|
||||
{
|
||||
"fieldname": "balance_value_by_qty",
|
||||
"fieldtype": "Float",
|
||||
"label": "(J) Valuation = Value (D) ÷ Qty (A)",
|
||||
},
|
||||
{
|
||||
"fieldname": "valuation_diff",
|
||||
"fieldtype": "Float",
|
||||
"label": "H - J",
|
||||
},
|
||||
]
|
||||
@@ -41,6 +41,12 @@ REPORT_FILTER_TEST_CASES: List[Tuple[ReportName, ReportFilters]] = [
|
||||
("Total Stock Summary", {"group_by": "warehouse",}),
|
||||
("Batch Item Expiry Status", {}),
|
||||
("Stock Ageing", {"range1": 30, "range2": 60, "range3": 90, "_optional": True}),
|
||||
("Stock Ledger Invariant Check",
|
||||
{
|
||||
"warehouse": "_Test Warehouse - _TC",
|
||||
"item": "_Test Item"
|
||||
}
|
||||
),
|
||||
]
|
||||
|
||||
OPTIONAL_FILTERS = {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
import copy
|
||||
import signal
|
||||
import unittest
|
||||
from contextlib import contextmanager
|
||||
from typing import Any, Dict, NewType, Optional
|
||||
@@ -135,3 +136,23 @@ def execute_script_report(
|
||||
report_execute_fn(filter_with_optional_param)
|
||||
|
||||
return report_data
|
||||
|
||||
|
||||
def timeout(seconds=30, error_message="Test timed out."):
|
||||
""" Timeout decorator to ensure a test doesn't run for too long.
|
||||
|
||||
adapted from https://stackoverflow.com/a/2282656"""
|
||||
def decorator(func):
|
||||
def _handle_timeout(signum, frame):
|
||||
raise Exception(error_message)
|
||||
|
||||
def wrapper(*args, **kwargs):
|
||||
signal.signal(signal.SIGALRM, _handle_timeout)
|
||||
signal.alarm(seconds)
|
||||
try:
|
||||
result = func(*args, **kwargs)
|
||||
finally:
|
||||
signal.alarm(0)
|
||||
return result
|
||||
return wrapper
|
||||
return decorator
|
||||
|
||||
Reference in New Issue
Block a user