Merge branch 'version-13-hotfix' into tax_template_mapping

This commit is contained in:
Deepesh Garg
2021-12-02 17:18:22 +05:30
committed by GitHub
36 changed files with 370 additions and 338 deletions

View File

@@ -8,6 +8,7 @@ on:
jobs:
main:
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- name: Checkout Actions
uses: actions/checkout@v2

View File

@@ -6,6 +6,7 @@ on:
jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: 'Setup Environment'

View File

@@ -11,6 +11,7 @@ on:
jobs:
test:
runs-on: ubuntu-18.04
timeout-minutes: 60
name: Patch Test

View File

@@ -15,6 +15,7 @@ on:
jobs:
test:
runs-on: ubuntu-18.04
timeout-minutes: 60
strategy:
fail-fast: false

View File

@@ -9,6 +9,7 @@ on:
jobs:
test:
runs-on: ubuntu-18.04
timeout-minutes: 60
strategy:
fail-fast: false

View File

@@ -5,10 +5,10 @@
import unittest
import frappe
from frappe.utils import now_datetime
from erpnext.accounts.doctype.fiscal_year.fiscal_year import FiscalYearIncorrectDate
test_records = frappe.get_test_records('Fiscal Year')
test_ignore = ["Company"]
class TestFiscalYear(unittest.TestCase):
@@ -25,3 +25,29 @@ class TestFiscalYear(unittest.TestCase):
})
self.assertRaises(FiscalYearIncorrectDate, fy.insert)
def test_record_generator():
test_records = [
{
"doctype": "Fiscal Year",
"year": "_Test Short Fiscal Year 2011",
"is_short_year": 1,
"year_end_date": "2011-04-01",
"year_start_date": "2011-12-31"
}
]
start = 2012
end = now_datetime().year + 5
for year in range(start, end):
test_records.append({
"doctype": "Fiscal Year",
"year": f"_Test Fiscal Year {year}",
"year_start_date": f"{year}-01-01",
"year_end_date": f"{year}-12-31"
})
return test_records
test_records = test_record_generator()

View File

@@ -1,69 +0,0 @@
[
{
"doctype": "Fiscal Year",
"year": "_Test Short Fiscal Year 2011",
"is_short_year": 1,
"year_end_date": "2011-04-01",
"year_start_date": "2011-12-31"
},
{
"doctype": "Fiscal Year",
"year": "_Test Fiscal Year 2012",
"year_end_date": "2012-12-31",
"year_start_date": "2012-01-01"
},
{
"doctype": "Fiscal Year",
"year": "_Test Fiscal Year 2013",
"year_end_date": "2013-12-31",
"year_start_date": "2013-01-01"
},
{
"doctype": "Fiscal Year",
"year": "_Test Fiscal Year 2014",
"year_end_date": "2014-12-31",
"year_start_date": "2014-01-01"
},
{
"doctype": "Fiscal Year",
"year": "_Test Fiscal Year 2015",
"year_end_date": "2015-12-31",
"year_start_date": "2015-01-01"
},
{
"doctype": "Fiscal Year",
"year": "_Test Fiscal Year 2016",
"year_end_date": "2016-12-31",
"year_start_date": "2016-01-01"
},
{
"doctype": "Fiscal Year",
"year": "_Test Fiscal Year 2017",
"year_end_date": "2017-12-31",
"year_start_date": "2017-01-01"
},
{
"doctype": "Fiscal Year",
"year": "_Test Fiscal Year 2018",
"year_end_date": "2018-12-31",
"year_start_date": "2018-01-01"
},
{
"doctype": "Fiscal Year",
"year": "_Test Fiscal Year 2019",
"year_end_date": "2019-12-31",
"year_start_date": "2019-01-01"
},
{
"doctype": "Fiscal Year",
"year": "_Test Fiscal Year 2020",
"year_end_date": "2020-12-31",
"year_start_date": "2020-01-01"
},
{
"doctype": "Fiscal Year",
"year": "_Test Fiscal Year 2021",
"year_end_date": "2021-12-31",
"year_start_date": "2021-01-01"
}
]

View File

@@ -3,22 +3,20 @@
{% include "erpnext/public/js/controllers/accounts.js" %}
frappe.ui.form.on("POS Profile", "onload", function(frm) {
frm.set_query("selling_price_list", function() {
return { filters: { selling: 1 } };
});
frm.set_query("tc_name", function() {
return { filters: { selling: 1 } };
});
erpnext.queries.setup_queries(frm, "Warehouse", function() {
return erpnext.queries.warehouse(frm.doc);
});
});
frappe.ui.form.on('POS Profile', {
setup: function(frm) {
frm.set_query("selling_price_list", function() {
return { filters: { selling: 1 } };
});
frm.set_query("tc_name", function() {
return { filters: { selling: 1 } };
});
erpnext.queries.setup_queries(frm, "Warehouse", function() {
return erpnext.queries.warehouse(frm.doc);
});
frm.set_query("print_format", function() {
return {
filters: [
@@ -27,10 +25,16 @@ frappe.ui.form.on('POS Profile', {
};
});
frm.set_query("account_for_change_amount", function() {
frm.set_query("account_for_change_amount", function(doc) {
if (!doc.company) {
frappe.throw(__('Please set Company'));
}
return {
filters: {
account_type: ['in', ["Cash", "Bank"]]
account_type: ['in', ["Cash", "Bank"]],
is_group: 0,
company: doc.company
}
};
});
@@ -45,7 +49,7 @@ frappe.ui.form.on('POS Profile', {
});
frm.set_query('company_address', function(doc) {
if(!doc.company) {
if (!doc.company) {
frappe.throw(__('Please set Company'));
}
@@ -58,11 +62,79 @@ frappe.ui.form.on('POS Profile', {
};
});
frm.set_query('income_account', function(doc) {
if (!doc.company) {
frappe.throw(__('Please set Company'));
}
return {
filters: {
'is_group': 0,
'company': doc.company,
'account_type': "Income Account"
}
};
});
frm.set_query('cost_center', function(doc) {
if (!doc.company) {
frappe.throw(__('Please set Company'));
}
return {
filters: {
'company': doc.company,
'is_group': 0
}
};
});
frm.set_query('expense_account', function(doc) {
if (!doc.company) {
frappe.throw(__('Please set Company'));
}
return {
filters: {
"report_type": "Profit and Loss",
"company": doc.company,
"is_group": 0
}
};
});
frm.set_query("select_print_heading", function() {
return {
filters: [
['Print Heading', 'docstatus', '!=', 2]
]
};
});
frm.set_query("write_off_account", function(doc) {
return {
filters: {
'report_type': 'Profit and Loss',
'is_group': 0,
'company': doc.company
}
};
});
frm.set_query("write_off_cost_center", function(doc) {
return {
filters: {
'is_group': 0,
'company': doc.company
}
};
});
erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype);
},
refresh: function(frm) {
if(frm.doc.company) {
if (frm.doc.company) {
frm.trigger("toggle_display_account_head");
}
},
@@ -76,71 +148,4 @@ frappe.ui.form.on('POS Profile', {
frm.toggle_display('expense_account',
erpnext.is_perpetual_inventory_enabled(frm.doc.company));
}
})
// Income Account
// --------------------------------
cur_frm.fields_dict['income_account'].get_query = function(doc,cdt,cdn) {
return{
filters:{
'is_group': 0,
'company': doc.company,
'account_type': "Income Account"
}
};
};
// Cost Center
// -----------------------------
cur_frm.fields_dict['cost_center'].get_query = function(doc,cdt,cdn) {
return{
filters:{
'company': doc.company,
'is_group': 0
}
};
};
// Expense Account
// -----------------------------
cur_frm.fields_dict["expense_account"].get_query = function(doc) {
return {
filters: {
"report_type": "Profit and Loss",
"company": doc.company,
"is_group": 0
}
};
};
// ------------------ Get Print Heading ------------------------------------
cur_frm.fields_dict['select_print_heading'].get_query = function(doc, cdt, cdn) {
return{
filters:[
['Print Heading', 'docstatus', '!=', 2]
]
};
};
cur_frm.fields_dict.write_off_account.get_query = function(doc) {
return{
filters:{
'report_type': 'Profit and Loss',
'is_group': 0,
'company': doc.company
}
};
};
// Write off cost center
// -----------------------
cur_frm.fields_dict.write_off_cost_center.get_query = function(doc) {
return{
filters:{
'is_group': 0,
'company': doc.company
}
};
};
});

View File

@@ -517,15 +517,6 @@ cur_frm.fields_dict.write_off_cost_center.get_query = function(doc) {
}
}
// project name
//--------------------------
cur_frm.fields_dict['project'].get_query = function(doc, cdt, cdn) {
return{
query: "erpnext.controllers.queries.get_project_name",
filters: {'customer': doc.customer}
}
}
// Income Account in Details Table
// --------------------------------
cur_frm.set_query("income_account", "items", function(doc) {

View File

@@ -92,6 +92,11 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
"label": __("Include Default Book Entries"),
"fieldtype": "Check",
"default": 1
},
{
"fieldname": "show_zero_values",
"label": __("Show zero values"),
"fieldtype": "Check"
}
],
"formatter": function(value, row, column, data, default_formatter) {

View File

@@ -22,7 +22,11 @@ from erpnext.accounts.report.cash_flow.cash_flow import (
get_cash_flow_accounts,
)
from erpnext.accounts.report.cash_flow.cash_flow import get_report_summary as get_cash_flow_summary
from erpnext.accounts.report.financial_statements import get_fiscal_year_data, sort_accounts
from erpnext.accounts.report.financial_statements import (
filter_out_zero_value_rows,
get_fiscal_year_data,
sort_accounts,
)
from erpnext.accounts.report.profit_and_loss_statement.profit_and_loss_statement import (
get_chart_data as get_pl_chart_data,
)
@@ -265,7 +269,7 @@ def get_columns(companies, filters):
return columns
def get_data(companies, root_type, balance_must_be, fiscal_year, filters=None, ignore_closing_entries=False):
accounts, accounts_by_name = get_account_heads(root_type,
accounts, accounts_by_name, parent_children_map = get_account_heads(root_type,
companies, filters)
if not accounts: return []
@@ -294,6 +298,8 @@ def get_data(companies, root_type, balance_must_be, fiscal_year, filters=None, i
out = prepare_data(accounts, start_date, end_date, balance_must_be, companies, company_currency, filters)
out = filter_out_zero_value_rows(out, parent_children_map, show_zero_values=filters.get("show_zero_values"))
if out:
add_total_row(out, root_type, balance_must_be, companies, company_currency)
@@ -370,7 +376,7 @@ def get_account_heads(root_type, companies, filters):
accounts, accounts_by_name, parent_children_map = filter_accounts(accounts)
return accounts, accounts_by_name
return accounts, accounts_by_name, parent_children_map
def update_parent_account_names(accounts):
"""Update parent_account_name in accounts list.

View File

@@ -80,20 +80,20 @@ frappe.ui.form.on('Asset', {
if (frm.doc.docstatus==1) {
if (in_list(["Submitted", "Partially Depreciated", "Fully Depreciated"], frm.doc.status)) {
frm.add_custom_button("Transfer Asset", function() {
frm.add_custom_button(__("Transfer Asset"), function() {
erpnext.asset.transfer_asset(frm);
}, __("Manage"));
frm.add_custom_button("Scrap Asset", function() {
frm.add_custom_button(__("Scrap Asset"), function() {
erpnext.asset.scrap_asset(frm);
}, __("Manage"));
frm.add_custom_button("Sell Asset", function() {
frm.add_custom_button(__("Sell Asset"), function() {
frm.trigger("make_sales_invoice");
}, __("Manage"));
} else if (frm.doc.status=='Scrapped') {
frm.add_custom_button("Restore Asset", function() {
frm.add_custom_button(__("Restore Asset"), function() {
erpnext.asset.restore_asset(frm);
}, __("Manage"));
}
@@ -121,7 +121,7 @@ frappe.ui.form.on('Asset', {
}
if (frm.doc.purchase_receipt || !frm.doc.is_existing_asset) {
frm.add_custom_button("View General Ledger", function() {
frm.add_custom_button(__("View General Ledger"), function() {
frappe.route_options = {
"voucher_no": frm.doc.name,
"from_date": frm.doc.available_for_use_date,

View File

@@ -60,6 +60,10 @@ frappe.ui.form.on('Asset Repair', {
if (frm.doc.repair_status == "Completed") {
frm.set_value('completion_date', frappe.datetime.now_datetime());
}
},
stock_items_on_form_rendered() {
erpnext.setup_serial_or_batch_no();
}
});

View File

@@ -118,9 +118,10 @@ class AssetRepair(AccountsController):
for stock_item in self.get('stock_items'):
stock_entry.append('items', {
"s_warehouse": self.warehouse,
"item_code": stock_item.item,
"item_code": stock_item.item_code,
"qty": stock_item.consumed_quantity,
"basic_rate": stock_item.valuation_rate
"basic_rate": stock_item.valuation_rate,
"serial_no": stock_item.serial_no
})
stock_entry.insert()

View File

@@ -11,12 +11,15 @@ from erpnext.assets.doctype.asset.test_asset import (
create_asset_data,
set_depreciation_settings_in_company,
)
from erpnext.stock.doctype.item.test_item import create_item
class TestAssetRepair(unittest.TestCase):
def setUp(self):
@classmethod
def setUpClass(cls):
set_depreciation_settings_in_company()
create_asset_data()
create_item("_Test Stock Item")
frappe.db.sql("delete from `tabTax Rule`")
def test_update_status(self):
@@ -70,9 +73,28 @@ class TestAssetRepair(unittest.TestCase):
self.assertEqual(stock_entry.stock_entry_type, "Material Issue")
self.assertEqual(stock_entry.items[0].s_warehouse, asset_repair.warehouse)
self.assertEqual(stock_entry.items[0].item_code, asset_repair.stock_items[0].item)
self.assertEqual(stock_entry.items[0].item_code, asset_repair.stock_items[0].item_code)
self.assertEqual(stock_entry.items[0].qty, asset_repair.stock_items[0].consumed_quantity)
def test_serialized_item_consumption(self):
from erpnext.stock.doctype.serial_no.serial_no import SerialNoRequiredError
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
stock_entry = make_serialized_item()
serial_nos = stock_entry.get("items")[0].serial_no
serial_no = serial_nos.split("\n")[0]
# should not raise any error
create_asset_repair(stock_consumption = 1, item_code = stock_entry.get("items")[0].item_code,
warehouse = "_Test Warehouse - _TC", serial_no = serial_no, submit = 1)
# should raise error
asset_repair = create_asset_repair(stock_consumption = 1, warehouse = "_Test Warehouse - _TC",
item_code = stock_entry.get("items")[0].item_code)
asset_repair.repair_status = "Completed"
self.assertRaises(SerialNoRequiredError, asset_repair.submit)
def test_increase_in_asset_value_due_to_stock_consumption(self):
asset = create_asset(calculate_depreciation = 1, submit=1)
initial_asset_value = get_asset_value(asset)
@@ -137,11 +159,12 @@ def create_asset_repair(**args):
if args.stock_consumption:
asset_repair.stock_consumption = 1
asset_repair.warehouse = create_warehouse("Test Warehouse", company = asset.company)
asset_repair.warehouse = args.warehouse or create_warehouse("Test Warehouse", company = asset.company)
asset_repair.append("stock_items", {
"item": args.item or args.item_code or "_Test Item",
"item_code": args.item_code or "_Test Stock Item",
"valuation_rate": args.rate if args.get("rate") is not None else 100,
"consumed_quantity": args.qty or 1
"consumed_quantity": args.qty or 1,
"serial_no": args.serial_no
})
asset_repair.insert(ignore_if_duplicate=True)
@@ -158,7 +181,7 @@ def create_asset_repair(**args):
})
stock_entry.append('items', {
"t_warehouse": asset_repair.warehouse,
"item_code": asset_repair.stock_items[0].item,
"item_code": asset_repair.stock_items[0].item_code,
"qty": asset_repair.stock_items[0].consumed_quantity
})
stock_entry.submit()

View File

@@ -5,19 +5,13 @@
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"item",
"item_code",
"valuation_rate",
"consumed_quantity",
"total_value"
"total_value",
"serial_no"
],
"fields": [
{
"fieldname": "item",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Item",
"options": "Item"
},
{
"fetch_from": "item.valuation_rate",
"fieldname": "valuation_rate",
@@ -38,12 +32,24 @@
"in_list_view": 1,
"label": "Total Value",
"read_only": 1
},
{
"fieldname": "serial_no",
"fieldtype": "Small Text",
"label": "Serial No"
},
{
"fieldname": "item_code",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Item",
"options": "Item"
}
],
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2021-05-12 03:19:55.006300",
"modified": "2021-11-11 18:23:00.492483",
"modified_by": "Administrator",
"module": "Assets",
"name": "Asset Repair Consumed Item",

View File

@@ -539,6 +539,10 @@ def get_filtered_dimensions(doctype, txt, searchfield, start, page_len, filters)
dimension_filters = get_dimension_filter_map()
dimension_filters = dimension_filters.get((filters.get('dimension'),filters.get('account')))
query_filters = []
or_filters = []
fields = ['name']
searchfields = frappe.get_meta(doctype).get_search_fields()
meta = frappe.get_meta(doctype)
if meta.is_tree:
@@ -550,8 +554,9 @@ def get_filtered_dimensions(doctype, txt, searchfield, start, page_len, filters)
if meta.has_field('company'):
query_filters.append(['company', '=', filters.get('company')])
if txt:
query_filters.append([searchfield, 'LIKE', "%%%s%%" % txt])
for field in searchfields:
or_filters.append([field, 'LIKE', "%%%s%%" % txt])
fields.append(field)
if dimension_filters:
if dimension_filters['allow_or_restrict'] == 'Allow':
@@ -566,10 +571,9 @@ def get_filtered_dimensions(doctype, txt, searchfield, start, page_len, filters)
query_filters.append(['name', query_selector, dimensions])
output = frappe.get_list(doctype, filters=query_filters)
result = [d.name for d in output]
output = frappe.get_list(doctype, fields=fields, filters=query_filters, or_filters=or_filters, as_list=1)
return [(d,) for d in set(result)]
return [tuple(d) for d in set(output)]
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs

View File

@@ -256,20 +256,18 @@ doc_events = {
"validate": "erpnext.regional.india.utils.validate_tax_category"
},
"Sales Invoice": {
"after_insert": "erpnext.regional.saudi_arabia.utils.create_qr_code",
"on_submit": [
"erpnext.regional.create_transaction_log",
"erpnext.regional.italy.utils.sales_invoice_on_submit",
"erpnext.regional.saudi_arabia.utils.create_qr_code",
"erpnext.erpnext_integrations.taxjar_integration.create_transaction"
],
"on_cancel": [
"erpnext.regional.italy.utils.sales_invoice_on_cancel",
"erpnext.erpnext_integrations.taxjar_integration.delete_transaction"
],
"on_trash": [
"erpnext.regional.check_deletion_permission",
"erpnext.erpnext_integrations.taxjar_integration.delete_transaction",
"erpnext.regional.saudi_arabia.utils.delete_qr_code_file"
],
"on_trash": "erpnext.regional.check_deletion_permission",
"validate": [
"erpnext.regional.india.utils.validate_document_name",
"erpnext.regional.india.utils.update_taxable_values"

View File

@@ -96,15 +96,8 @@ class Employee(NestedSet):
'user': self.user_id
})
if employee_user_permission_exists: return
employee_user_permission_exists = frappe.db.exists('User Permission', {
'allow': 'Employee',
'for_value': self.name,
'user': self.user_id
})
if employee_user_permission_exists: return
if employee_user_permission_exists:
return
add_user_permission("Employee", self.name, self.user_id)
set_user_permission_if_allowed("Company", self.company, self.user_id)

View File

@@ -2,7 +2,6 @@
# See license.txt
import unittest
from datetime import date
import frappe
from frappe.utils import add_days, getdate
@@ -12,16 +11,14 @@ from erpnext.hr.doctype.employee.test_employee import make_employee
class TestEmployeeTransfer(unittest.TestCase):
def setUp(self):
make_employee("employee2@transfers.com")
make_employee("employee3@transfers.com")
create_company()
create_employee()
create_employee_transfer()
def tearDown(self):
frappe.db.rollback()
def test_submit_before_transfer_date(self):
make_employee("employee2@transfers.com")
transfer_obj = frappe.get_doc({
"doctype": "Employee Transfer",
"employee": frappe.get_value("Employee", {"user_id":"employee2@transfers.com"}, "name"),
@@ -43,6 +40,8 @@ class TestEmployeeTransfer(unittest.TestCase):
self.assertEqual(transfer.docstatus, 1)
def test_new_employee_creation(self):
make_employee("employee3@transfers.com")
transfer = frappe.get_doc({
"doctype": "Employee Transfer",
"employee": frappe.get_value("Employee", {"user_id":"employee3@transfers.com"}, "name"),
@@ -63,60 +62,51 @@ class TestEmployeeTransfer(unittest.TestCase):
self.assertEqual(frappe.get_value("Employee", transfer.employee, "status"), "Left")
def test_employee_history(self):
name = frappe.get_value("Employee", {"first_name": "John", "company": "Test Company"}, "name")
doc = frappe.get_doc("Employee",name)
employee = make_employee("employee4@transfers.com",
company="Test Company",
date_of_birth=getdate("30-09-1980"),
date_of_joining=getdate("01-10-2021"),
department="Accounts - TC",
designation="Accountant"
)
transfer = create_employee_transfer(employee)
count = 0
department = ["Accounts - TC", "Management - TC"]
designation = ["Accountant", "Manager"]
dt = [getdate("01-10-2021"), date.today()]
dt = [getdate("01-10-2021"), getdate()]
for data in doc.internal_work_history:
employee = frappe.get_doc("Employee", employee)
for data in employee.internal_work_history:
self.assertEqual(data.department, department[count])
self.assertEqual(data.designation, designation[count])
self.assertEqual(data.from_date, dt[count])
count = count + 1
data = frappe.db.get_list("Employee Transfer", filters={"employee":name}, fields=["*"])
doc = frappe.get_doc("Employee Transfer", data[0]["name"])
doc.cancel()
employee_doc = frappe.get_doc("Employee",name)
transfer.cancel()
employee.reload()
for data in employee_doc.internal_work_history:
for data in employee.internal_work_history:
self.assertEqual(data.designation, designation[0])
self.assertEqual(data.department, department[0])
self.assertEqual(data.from_date, dt[0])
def create_employee():
doc = frappe.get_doc({
"doctype": "Employee",
"first_name": "John",
"company": "Test Company",
"gender": "Male",
"date_of_birth": getdate("30-09-1980"),
"date_of_joining": getdate("01-10-2021"),
"department": "Accounts - TC",
"designation": "Accountant"
})
doc.save()
def create_company():
exists = frappe.db.exists("Company", "Test Company")
if not exists:
doc = frappe.get_doc({
"doctype": "Company",
"company_name": "Test Company",
"default_currency": "INR",
"country": "India"
})
if not frappe.db.exists("Company", "Test Company"):
frappe.get_doc({
"doctype": "Company",
"company_name": "Test Company",
"default_currency": "INR",
"country": "India"
}).insert()
doc.save()
def create_employee_transfer():
def create_employee_transfer(employee):
doc = frappe.get_doc({
"doctype": "Employee Transfer",
"employee": frappe.get_value("Employee", {"first_name": "John", "company": "Test Company"}, "name"),
"transfer_date": date.today(),
"employee": employee,
"transfer_date": getdate(),
"transfer_details": [
{
"property": "Designation",
@@ -134,4 +124,6 @@ def create_employee_transfer():
})
doc.save()
doc.submit()
doc.submit()
return doc

View File

@@ -19,8 +19,8 @@ class ShiftAssignment(Document):
validate_active_employee(self.employee)
self.validate_overlapping_dates()
if self.end_date and self.end_date <= self.start_date:
frappe.throw(_("End Date must not be lesser than Start Date"))
if self.end_date:
self.validate_from_to_dates('start_date', 'end_date')
def validate_overlapping_dates(self):
if not self.name:

View File

@@ -178,8 +178,9 @@
},
{
"fieldname": "batch_size",
"fieldtype": "Int",
"label": "Batch Size"
"fieldtype": "Float",
"label": "Batch Size",
"read_only": 1
},
{
"fieldname": "sequence_id",
@@ -200,7 +201,7 @@
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2021-11-24 04:52:54.295168",
"modified": "2021-11-29 16:37:18.824489",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Work Order Operation",

View File

@@ -2,7 +2,7 @@
import unittest
import frappe
from frappe.utils import add_days, getdate, nowdate
from frappe.utils import add_days, getdate
from erpnext.hr.doctype.employee.test_employee import make_employee
from erpnext.projects.doctype.timesheet.test_timesheet import (
@@ -14,21 +14,26 @@ from erpnext.projects.report.project_profitability.project_profitability import
class TestProjectProfitability(unittest.TestCase):
def setUp(self):
frappe.db.sql('delete from `tabTimesheet`')
emp = make_employee('test_employee_9@salary.com', company='_Test Company')
if not frappe.db.exists('Salary Component', 'Timesheet Component'):
frappe.get_doc({'doctype': 'Salary Component', 'salary_component': 'Timesheet Component'}).insert()
make_salary_structure_for_timesheet(emp, company='_Test Company')
self.timesheet = make_timesheet(emp, simulate = True, is_billable=1)
date = getdate()
self.timesheet = make_timesheet(emp, is_billable=1)
self.salary_slip = make_salary_slip(self.timesheet.name)
holidays = self.salary_slip.get_holidays_for_employee(nowdate(), nowdate())
holidays = self.salary_slip.get_holidays_for_employee(date, date)
if holidays:
frappe.db.set_value('Payroll Settings', None, 'include_holidays_in_total_working_days', 1)
self.salary_slip.submit()
self.sales_invoice = make_sales_invoice(self.timesheet.name, '_Test Item', '_Test Customer')
self.sales_invoice.due_date = nowdate()
self.sales_invoice.due_date = date
self.sales_invoice.submit()
frappe.db.set_value('HR Settings', None, 'standard_working_hours', 8)
@@ -64,6 +69,4 @@ class TestProjectProfitability(unittest.TestCase):
self.assertEqual(fractional_cost, row.fractional_cost)
def tearDown(self):
frappe.get_doc("Sales Invoice", self.sales_invoice.name).cancel()
frappe.get_doc("Salary Slip", self.salary_slip.name).cancel()
frappe.get_doc("Timesheet", self.timesheet.name).cancel()
frappe.db.rollback()

View File

@@ -1094,7 +1094,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
$.each(this.frm.doc.taxes || [], function(i, d) {
if(d.charge_type == "Actual") {
frappe.model.set_value(d.doctype, d.name, "tax_amount",
flt(d.tax_amount) / flt(exchange_rate));
flt(d.base_tax_amount) / flt(exchange_rate));
}
});
},

View File

@@ -495,6 +495,11 @@
font-size: var(--text-md);
}
> .item-qty-total-container {
@extend .net-total-container;
padding: 5px 0px 0px 0px;
}
> .taxes-container {
display: none;
flex-direction: column;

View File

@@ -560,17 +560,17 @@ def get_item_list(data, doc, hsn_wise=False):
}
item_data_attrs = ['sgstRate', 'cgstRate', 'igstRate', 'cessRate', 'cessNonAdvol']
hsn_wise_charges, hsn_taxable_amount = get_itemised_tax_breakup_data(doc, account_wise=True, hsn_wise=hsn_wise)
for hsn_code, taxable_amount in hsn_taxable_amount.items():
for item_or_hsn, taxable_amount in hsn_taxable_amount.items():
item_data = frappe._dict()
if not hsn_code:
if not item_or_hsn:
frappe.throw(_('GST HSN Code does not exist for one or more items'))
item_data.hsnCode = int(hsn_code)
item_data.hsnCode = int(item_or_hsn) if hsn_wise else item_or_hsn
item_data.taxableAmount = taxable_amount
item_data.qtyUnit = ""
for attr in item_data_attrs:
item_data[attr] = 0
for account, tax_detail in hsn_wise_charges.get(hsn_code, {}).items():
for account, tax_detail in hsn_wise_charges.get(item_or_hsn, {}).items():
account_type = gst_accounts.get(account, '')
for tax_acc, attrs in tax_map.items():
if account_type == tax_acc:

File diff suppressed because one or more lines are too long

View File

@@ -1,7 +1,10 @@
import io
import os
from base64 import b64encode
import frappe
from frappe import _
from frappe.utils.data import add_to_date, get_time, getdate
from pyqrcode import create as qr_create
from erpnext import get_region
@@ -28,24 +31,74 @@ def create_qr_code(doc, method):
for field in meta.get_image_fields():
if field.fieldname == 'qr_code':
from urllib.parse import urlencode
''' TLV conversion for
1. Seller's Name
2. VAT Number
3. Time Stamp
4. Invoice Amount
5. VAT Amount
'''
tlv_array = []
# Sellers Name
# Creating public url to print format
default_print_format = frappe.db.get_value('Property Setter', dict(property='default_print_format', doc_type=doc.doctype), "value")
seller_name = frappe.db.get_value(
'Company',
doc.company,
'company_name_in_arabic')
# System Language
language = frappe.get_system_settings('language')
if not seller_name:
frappe.throw(_('Arabic name missing for {} in the company document').format(doc.company))
params = urlencode({
'format': default_print_format or 'Standard',
'_lang': language,
'key': doc.get_signature()
})
tag = bytes([1]).hex()
length = bytes([len(seller_name.encode('utf-8'))]).hex()
value = seller_name.encode('utf-8').hex()
tlv_array.append(''.join([tag, length, value]))
# VAT Number
tax_id = frappe.db.get_value('Company', doc.company, 'tax_id')
if not tax_id:
frappe.throw(_('Tax ID missing for {} in the company document').format(doc.company))
tag = bytes([2]).hex()
length = bytes([len(tax_id)]).hex()
value = tax_id.encode('utf-8').hex()
tlv_array.append(''.join([tag, length, value]))
# Time Stamp
posting_date = getdate(doc.posting_date)
time = get_time(doc.posting_time)
seconds = time.hour * 60 * 60 + time.minute * 60 + time.second
time_stamp = add_to_date(posting_date, seconds=seconds)
time_stamp = time_stamp.strftime('%Y-%m-%dT%H:%M:%SZ')
tag = bytes([3]).hex()
length = bytes([len(time_stamp)]).hex()
value = time_stamp.encode('utf-8').hex()
tlv_array.append(''.join([tag, length, value]))
# Invoice Amount
invoice_amount = str(doc.total)
tag = bytes([4]).hex()
length = bytes([len(invoice_amount)]).hex()
value = invoice_amount.encode('utf-8').hex()
tlv_array.append(''.join([tag, length, value]))
# VAT Amount
vat_amount = str(doc.total_taxes_and_charges)
tag = bytes([5]).hex()
length = bytes([len(vat_amount)]).hex()
value = vat_amount.encode('utf-8').hex()
tlv_array.append(''.join([tag, length, value]))
# Joining bytes into one
tlv_buff = ''.join(tlv_array)
# base64 conversion for QR Code
base64_string = b64encode(bytes.fromhex(tlv_buff)).decode()
# creating qr code for the url
url = f"{ frappe.utils.get_url() }/{ doc.doctype }/{ doc.name }?{ params }"
qr_image = io.BytesIO()
url = qr_create(url, error='L')
url = qr_create(base64_string, error='L')
url.png(qr_image, scale=2, quiet_zone=1)
# making file

View File

@@ -952,8 +952,7 @@
"idx": 82,
"is_submittable": 1,
"links": [],
"max_attachments": 1,
"modified": "2021-08-27 20:10:07.864951",
"modified": "2021-11-30 01:33:21.106073",
"modified_by": "Administrator",
"module": "Selling",
"name": "Quotation",

View File

@@ -100,6 +100,10 @@ erpnext.PointOfSale.ItemCart = class {
`<div class="add-discount-wrapper">
${this.get_discount_icon()} ${__('Add Discount')}
</div>
<div class="item-qty-total-container">
<div class="item-qty-total-label">${__('Total Items')}</div>
<div class="item-qty-total-value">0.00</div>
</div>
<div class="net-total-container">
<div class="net-total-label">${__("Net Total")}</div>
<div class="net-total-value">0.00</div>
@@ -142,6 +146,7 @@ erpnext.PointOfSale.ItemCart = class {
this.$numpad_section.prepend(
`<div class="numpad-totals">
<span class="numpad-item-qty-total"></span>
<span class="numpad-net-total"></span>
<span class="numpad-grand-total"></span>
</div>`
@@ -470,6 +475,7 @@ erpnext.PointOfSale.ItemCart = class {
if (!frm) frm = this.events.get_frm();
this.render_net_total(frm.doc.net_total);
this.render_total_item_qty(frm.doc.items);
const grand_total = cint(frappe.sys_defaults.disable_rounded_total) ? frm.doc.grand_total : frm.doc.rounded_total;
this.render_grand_total(grand_total);
@@ -487,6 +493,21 @@ erpnext.PointOfSale.ItemCart = class {
);
}
render_total_item_qty(items) {
var total_item_qty = 0;
items.map((item) => {
total_item_qty = total_item_qty + item.qty;
});
this.$totals_section.find('.item-qty-total-container').html(
`<div>${__('Total Quantity')}</div><div>${total_item_qty}</div>`
);
this.$numpad_section.find('.numpad-item-qty-total').html(
`<div>${__('Total Quantity')}: <span>${total_item_qty}</span></div>`
);
}
render_grand_total(value) {
const currency = this.events.get_frm().doc.currency;
this.$totals_section.find('.grand-total-container').html(

View File

@@ -204,43 +204,6 @@ erpnext.company.set_chart_of_accounts_options = function(doc) {
}
}
cur_frm.cscript.change_abbr = function() {
var dialog = new frappe.ui.Dialog({
title: "Replace Abbr",
fields: [
{"fieldtype": "Data", "label": "New Abbreviation", "fieldname": "new_abbr",
"reqd": 1 },
{"fieldtype": "Button", "label": "Update", "fieldname": "update"},
]
});
dialog.fields_dict.update.$input.click(function() {
var args = dialog.get_values();
if(!args) return;
frappe.show_alert(__("Update in progress. It might take a while."));
return frappe.call({
method: "erpnext.setup.doctype.company.company.enqueue_replace_abbr",
args: {
"company": cur_frm.doc.name,
"old": cur_frm.doc.abbr,
"new": args.new_abbr
},
callback: function(r) {
if(r.exc) {
frappe.msgprint(__("There were errors."));
return;
} else {
cur_frm.set_value("abbr", args.new_abbr);
}
dialog.hide();
cur_frm.refresh();
},
btn: this
})
});
dialog.show();
}
erpnext.company.setup_queries = function(frm) {
$.each([
["default_bank_account", {"account_type": "Bank"}],

View File

@@ -12,7 +12,6 @@
"details",
"company_name",
"abbr",
"change_abbr",
"is_group",
"cb0",
"domain",
@@ -128,12 +127,6 @@
"reqd": 1,
"set_only_once": 1
},
{
"depends_on": "eval:!doc.__islocal && in_list(frappe.user_roles, \"System Manager\")",
"fieldname": "change_abbr",
"fieldtype": "Button",
"label": "Change Abbreviation"
},
{
"bold": 1,
"default": "0",
@@ -749,7 +742,7 @@
"image_field": "company_logo",
"is_tree": 1,
"links": [],
"modified": "2021-05-12 16:51:08.187233",
"modified": "2021-12-02 14:52:08.187233",
"modified_by": "Administrator",
"module": "Setup",
"name": "Company",

View File

@@ -949,8 +949,7 @@
"image_field": "image",
"index_web_pages_for_search": 1,
"links": [],
"max_attachments": 1,
"modified": "2021-09-10 12:23:07.277077",
"modified": "2021-11-30 01:33:06.572442",
"modified_by": "Administrator",
"module": "Stock",
"name": "Item",

View File

@@ -54,9 +54,11 @@ class RepostItemValuation(Document):
@frappe.whitelist()
def restart_reposting(self):
self.set_status('Queued')
frappe.enqueue(repost, timeout=1800, queue='long',
job_name='repost_sle', now=True, doc=self)
self.set_status('Queued', write=False)
self.current_index = 0
self.distinct_item_and_warehouse = None
self.items_to_be_repost = None
self.db_update()
def deduplicate_similar_repost(self):
""" Deduplicate similar reposts based on item-warehouse-posting combination."""

View File

@@ -1,4 +1,5 @@
{
"actions": [],
"autoname": "naming_series:",
"creation": "2013-03-28 10:35:31",
"description": "This tool helps you to update or fix the quantity and valuation of stock in the system. It is typically used to synchronise the system values and what actually exists in your warehouses.",
@@ -153,11 +154,12 @@
"icon": "fa fa-upload-alt",
"idx": 1,
"is_submittable": 1,
"max_attachments": 1,
"modified": "2020-04-08 17:02:47.196206",
"links": [],
"modified": "2021-11-30 01:33:51.437194",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock Reconciliation",
"naming_rule": "By \"Naming Series\" field",
"owner": "Administrator",
"permissions": [
{

View File

@@ -300,7 +300,7 @@ def get_basic_details(args, item, overwrite_warehouse=True):
"warehouse": warehouse,
"income_account": get_default_income_account(args, item_defaults, item_group_defaults, brand_defaults),
"expense_account": expense_account or get_default_expense_account(args, item_defaults, item_group_defaults, brand_defaults) ,
"discount_account": None or get_default_discount_account(args, item_defaults),
"discount_account": get_default_discount_account(args, item_defaults),
"cost_center": get_default_cost_center(args, item_defaults, item_group_defaults, brand_defaults),
'has_serial_no': item.has_serial_no,
'has_batch_no': item.has_batch_no,
@@ -318,6 +318,7 @@ def get_basic_details(args, item, overwrite_warehouse=True):
"net_rate": 0.0,
"net_amount": 0.0,
"discount_percentage": 0.0,
"discount_amount": 0.0,
"supplier": get_default_supplier(args, item_defaults, item_group_defaults, brand_defaults),
"update_stock": args.get("update_stock") if args.get('doctype') in ['Sales Invoice', 'Purchase Invoice'] else 0,
"delivered_by_supplier": item.delivered_by_supplier if args.get("doctype") in ["Sales Order", "Sales Invoice"] else 0,