Merge branch 'develop' into auto-attendance-issue
This commit is contained in:
commit
c07dfbc08b
5
.github/helper/.flake8_strict
vendored
5
.github/helper/.flake8_strict
vendored
@ -65,6 +65,11 @@ ignore =
|
|||||||
E713,
|
E713,
|
||||||
E712,
|
E712,
|
||||||
|
|
||||||
|
enable-extensions =
|
||||||
|
M90
|
||||||
|
|
||||||
|
select =
|
||||||
|
M511
|
||||||
|
|
||||||
max-line-length = 200
|
max-line-length = 200
|
||||||
exclude=.github/helper/semgrep_rules,test_*.py
|
exclude=.github/helper/semgrep_rules,test_*.py
|
||||||
|
2
.github/workflows/ui-tests.yml
vendored
2
.github/workflows/ui-tests.yml
vendored
@ -104,6 +104,8 @@ jobs:
|
|||||||
|
|
||||||
- name: Build Assets
|
- name: Build Assets
|
||||||
run: cd ~/frappe-bench/ && bench build
|
run: cd ~/frappe-bench/ && bench build
|
||||||
|
env:
|
||||||
|
CI: Yes
|
||||||
|
|
||||||
- name: UI Tests
|
- name: UI Tests
|
||||||
run: cd ~/frappe-bench/ && bench --site test_site run-ui-tests erpnext --headless
|
run: cd ~/frappe-bench/ && bench --site test_site run-ui-tests erpnext --headless
|
||||||
|
@ -20,7 +20,10 @@ repos:
|
|||||||
rev: 3.9.2
|
rev: 3.9.2
|
||||||
hooks:
|
hooks:
|
||||||
- id: flake8
|
- id: flake8
|
||||||
args: ['--config', '.github/helper/.flake8_strict']
|
additional_dependencies: [
|
||||||
|
'flake8-mutable',
|
||||||
|
]
|
||||||
|
args: ['--select=M511', '--config', '.github/helper/.flake8_strict']
|
||||||
exclude: ".*setup.py$"
|
exclude: ".*setup.py$"
|
||||||
|
|
||||||
- repo: https://github.com/timothycrosley/isort
|
- repo: https://github.com/timothycrosley/isort
|
||||||
|
@ -77,6 +77,12 @@ The ERPNext code is licensed as GNU General Public License (v3) and the Document
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Learning
|
||||||
|
|
||||||
|
1. [Frappe School](https://frappe.school) - Learn Frappe Framework and ERPNext from the various courses by the maintainers or from the community.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Logo and Trademark
|
## Logo and Trademark
|
||||||
|
|
||||||
The brand name ERPNext and the logo are trademarks of Frappe Technologies Pvt. Ltd.
|
The brand name ERPNext and the logo are trademarks of Frappe Technologies Pvt. Ltd.
|
||||||
|
@ -10,6 +10,15 @@ frappe.ui.form.on('Chart of Accounts Importer', {
|
|||||||
// make company mandatory
|
// make company mandatory
|
||||||
frm.set_df_property('company', 'reqd', frm.doc.company ? 0 : 1);
|
frm.set_df_property('company', 'reqd', frm.doc.company ? 0 : 1);
|
||||||
frm.set_df_property('import_file_section', 'hidden', frm.doc.company ? 0 : 1);
|
frm.set_df_property('import_file_section', 'hidden', frm.doc.company ? 0 : 1);
|
||||||
|
|
||||||
|
if (frm.doc.import_file) {
|
||||||
|
frappe.run_serially([
|
||||||
|
() => generate_tree_preview(frm),
|
||||||
|
() => create_import_button(frm),
|
||||||
|
() => frm.set_df_property('chart_preview', 'hidden', 0)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
frm.set_df_property('chart_preview', 'hidden',
|
frm.set_df_property('chart_preview', 'hidden',
|
||||||
$(frm.fields_dict['chart_tree'].wrapper).html()!="" ? 0 : 1);
|
$(frm.fields_dict['chart_tree'].wrapper).html()!="" ? 0 : 1);
|
||||||
},
|
},
|
||||||
@ -72,13 +81,6 @@ frappe.ui.form.on('Chart of Accounts Importer', {
|
|||||||
if (!frm.doc.import_file) {
|
if (!frm.doc.import_file) {
|
||||||
frm.page.set_indicator("");
|
frm.page.set_indicator("");
|
||||||
$(frm.fields_dict['chart_tree'].wrapper).empty(); // empty wrapper on removing file
|
$(frm.fields_dict['chart_tree'].wrapper).empty(); // empty wrapper on removing file
|
||||||
} else {
|
|
||||||
frappe.run_serially([
|
|
||||||
() => validate_coa(frm),
|
|
||||||
() => generate_tree_preview(frm),
|
|
||||||
() => create_import_button(frm),
|
|
||||||
() => frm.set_df_property('chart_preview', 'hidden', 0),
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -104,26 +106,24 @@ frappe.ui.form.on('Chart of Accounts Importer', {
|
|||||||
});
|
});
|
||||||
|
|
||||||
var create_import_button = function(frm) {
|
var create_import_button = function(frm) {
|
||||||
if (frm.page.show_import_button) {
|
frm.page.set_primary_action(__("Import"), function () {
|
||||||
frm.page.set_primary_action(__("Import"), function () {
|
return frappe.call({
|
||||||
return frappe.call({
|
method: "erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.import_coa",
|
||||||
method: "erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.import_coa",
|
args: {
|
||||||
args: {
|
file_name: frm.doc.import_file,
|
||||||
file_name: frm.doc.import_file,
|
company: frm.doc.company
|
||||||
company: frm.doc.company
|
},
|
||||||
},
|
freeze: true,
|
||||||
freeze: true,
|
freeze_message: __("Creating Accounts..."),
|
||||||
freeze_message: __("Creating Accounts..."),
|
callback: function(r) {
|
||||||
callback: function(r) {
|
if (!r.exc) {
|
||||||
if (!r.exc) {
|
clearInterval(frm.page["interval"]);
|
||||||
clearInterval(frm.page["interval"]);
|
frm.page.set_indicator(__('Import Successful'), 'blue');
|
||||||
frm.page.set_indicator(__('Import Successful'), 'blue');
|
create_reset_button(frm);
|
||||||
create_reset_button(frm);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}).addClass('btn btn-primary');
|
});
|
||||||
}
|
}).addClass('btn btn-primary');
|
||||||
};
|
};
|
||||||
|
|
||||||
var create_reset_button = function(frm) {
|
var create_reset_button = function(frm) {
|
||||||
@ -137,7 +137,6 @@ var create_reset_button = function(frm) {
|
|||||||
var validate_coa = function(frm) {
|
var validate_coa = function(frm) {
|
||||||
if (frm.doc.import_file) {
|
if (frm.doc.import_file) {
|
||||||
let parent = __('All Accounts');
|
let parent = __('All Accounts');
|
||||||
|
|
||||||
return frappe.call({
|
return frappe.call({
|
||||||
'method': 'erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.get_coa',
|
'method': 'erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.get_coa',
|
||||||
'args': {
|
'args': {
|
||||||
@ -157,25 +156,23 @@ var validate_coa = function(frm) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
var generate_tree_preview = function(frm) {
|
var generate_tree_preview = function(frm) {
|
||||||
if (frm.doc.import_file) {
|
let parent = __('All Accounts');
|
||||||
let parent = __('All Accounts');
|
$(frm.fields_dict['chart_tree'].wrapper).empty(); // empty wrapper to load new data
|
||||||
$(frm.fields_dict['chart_tree'].wrapper).empty(); // empty wrapper to load new data
|
|
||||||
|
|
||||||
// generate tree structure based on the csv data
|
// generate tree structure based on the csv data
|
||||||
return new frappe.ui.Tree({
|
return new frappe.ui.Tree({
|
||||||
parent: $(frm.fields_dict['chart_tree'].wrapper),
|
parent: $(frm.fields_dict['chart_tree'].wrapper),
|
||||||
label: parent,
|
label: parent,
|
||||||
expandable: true,
|
expandable: true,
|
||||||
method: 'erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.get_coa',
|
method: 'erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.get_coa',
|
||||||
args: {
|
args: {
|
||||||
file_name: frm.doc.import_file,
|
file_name: frm.doc.import_file,
|
||||||
parent: parent,
|
parent: parent,
|
||||||
doctype: 'Chart of Accounts Importer',
|
doctype: 'Chart of Accounts Importer',
|
||||||
file_type: frm.doc.file_type
|
file_type: frm.doc.file_type
|
||||||
},
|
},
|
||||||
onclick: function(node) {
|
onclick: function(node) {
|
||||||
parent = node.value;
|
parent = node.value;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
@ -25,7 +25,9 @@ from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import
|
|||||||
|
|
||||||
|
|
||||||
class ChartofAccountsImporter(Document):
|
class ChartofAccountsImporter(Document):
|
||||||
pass
|
def validate(self):
|
||||||
|
if self.import_file:
|
||||||
|
get_coa('Chart of Accounts Importer', 'All Accounts', file_name=self.import_file, for_validate=1)
|
||||||
|
|
||||||
def validate_columns(data):
|
def validate_columns(data):
|
||||||
if not data:
|
if not data:
|
||||||
@ -34,7 +36,8 @@ def validate_columns(data):
|
|||||||
no_of_columns = max([len(d) for d in data])
|
no_of_columns = max([len(d) for d in data])
|
||||||
|
|
||||||
if no_of_columns > 7:
|
if no_of_columns > 7:
|
||||||
frappe.throw(_('More columns found than expected. Please compare the uploaded file with standard template'))
|
frappe.throw(_('More columns found than expected. Please compare the uploaded file with standard template'),
|
||||||
|
title=(_("Wrong Template")))
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def validate_company(company):
|
def validate_company(company):
|
||||||
|
@ -33,7 +33,9 @@ class TestPOSProfile(unittest.TestCase):
|
|||||||
|
|
||||||
frappe.db.sql("delete from `tabPOS Profile`")
|
frappe.db.sql("delete from `tabPOS Profile`")
|
||||||
|
|
||||||
def get_customers_list(pos_profile={}):
|
def get_customers_list(pos_profile=None):
|
||||||
|
if pos_profile is None:
|
||||||
|
pos_profile = {}
|
||||||
cond = "1=1"
|
cond = "1=1"
|
||||||
customer_groups = []
|
customer_groups = []
|
||||||
if pos_profile.get('customer_groups'):
|
if pos_profile.get('customer_groups'):
|
||||||
|
@ -398,7 +398,9 @@ def get_qty_and_rate_for_other_item(doc, pr_doc, pricing_rules):
|
|||||||
pricing_rules[0].apply_rule_on_other_items = items
|
pricing_rules[0].apply_rule_on_other_items = items
|
||||||
return pricing_rules
|
return pricing_rules
|
||||||
|
|
||||||
def get_qty_amount_data_for_cumulative(pr_doc, doc, items=[]):
|
def get_qty_amount_data_for_cumulative(pr_doc, doc, items=None):
|
||||||
|
if items is None:
|
||||||
|
items = []
|
||||||
sum_qty, sum_amt = [0, 0]
|
sum_qty, sum_amt = [0, 0]
|
||||||
doctype = doc.get('parenttype') or doc.doctype
|
doctype = doc.get('parenttype') or doc.doctype
|
||||||
|
|
||||||
|
@ -69,7 +69,9 @@ class PromotionalScheme(Document):
|
|||||||
{'promotional_scheme': self.name}):
|
{'promotional_scheme': self.name}):
|
||||||
frappe.delete_doc('Pricing Rule', rule.name)
|
frappe.delete_doc('Pricing Rule', rule.name)
|
||||||
|
|
||||||
def get_pricing_rules(doc, rules = {}):
|
def get_pricing_rules(doc, rules=None):
|
||||||
|
if rules is None:
|
||||||
|
rules = {}
|
||||||
new_doc = []
|
new_doc = []
|
||||||
for child_doc, fields in {'price_discount_slabs': price_discount_fields,
|
for child_doc, fields in {'price_discount_slabs': price_discount_fields,
|
||||||
'product_discount_slabs': product_discount_fields}.items():
|
'product_discount_slabs': product_discount_fields}.items():
|
||||||
@ -78,7 +80,9 @@ def get_pricing_rules(doc, rules = {}):
|
|||||||
|
|
||||||
return new_doc
|
return new_doc
|
||||||
|
|
||||||
def _get_pricing_rules(doc, child_doc, discount_fields, rules = {}):
|
def _get_pricing_rules(doc, child_doc, discount_fields, rules=None):
|
||||||
|
if rules is None:
|
||||||
|
rules = {}
|
||||||
new_doc = []
|
new_doc = []
|
||||||
args = get_args_for_pricing_rule(doc)
|
args = get_args_for_pricing_rule(doc)
|
||||||
applicable_for = frappe.scrub(doc.get('applicable_for'))
|
applicable_for = frappe.scrub(doc.get('applicable_for'))
|
||||||
|
@ -229,9 +229,6 @@ class SalesInvoice(SellingController):
|
|||||||
# this sequence because outstanding may get -ve
|
# this sequence because outstanding may get -ve
|
||||||
self.make_gl_entries()
|
self.make_gl_entries()
|
||||||
|
|
||||||
if self.update_stock == 1:
|
|
||||||
self.repost_future_sle_and_gle()
|
|
||||||
|
|
||||||
if self.update_stock == 1:
|
if self.update_stock == 1:
|
||||||
self.repost_future_sle_and_gle()
|
self.repost_future_sle_and_gle()
|
||||||
|
|
||||||
|
@ -2023,11 +2023,7 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
frappe.local.enable_perpetual_inventory['_Test Company 1'] = old_perpetual_inventory
|
frappe.local.enable_perpetual_inventory['_Test Company 1'] = old_perpetual_inventory
|
||||||
frappe.db.set_value("Stock Settings", None, "allow_negative_stock", old_negative_stock)
|
frappe.db.set_value("Stock Settings", None, "allow_negative_stock", old_negative_stock)
|
||||||
|
|
||||||
def test_sle_if_target_warehouse_exists_accidentally(self):
|
def test_sle_for_target_warehouse(self):
|
||||||
"""
|
|
||||||
Check if inward entry exists if Target Warehouse accidentally exists
|
|
||||||
but Customer is not an internal customer.
|
|
||||||
"""
|
|
||||||
se = make_stock_entry(
|
se = make_stock_entry(
|
||||||
item_code="138-CMS Shoe",
|
item_code="138-CMS Shoe",
|
||||||
target="Finished Goods - _TC",
|
target="Finished Goods - _TC",
|
||||||
@ -2048,9 +2044,9 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
sles = frappe.get_all("Stock Ledger Entry", filters={"voucher_no": si.name},
|
sles = frappe.get_all("Stock Ledger Entry", filters={"voucher_no": si.name},
|
||||||
fields=["name", "actual_qty"])
|
fields=["name", "actual_qty"])
|
||||||
|
|
||||||
# check if only one SLE for outward entry is created
|
# check if both SLEs are created
|
||||||
self.assertEqual(len(sles), 1)
|
self.assertEqual(len(sles), 2)
|
||||||
self.assertEqual(sles[0].actual_qty, -1)
|
self.assertEqual(sum(d.actual_qty for d in sles), 0.0)
|
||||||
|
|
||||||
# tear down
|
# tear down
|
||||||
si.cancel()
|
si.cancel()
|
||||||
|
@ -139,9 +139,9 @@ def get_account_type_based_data(company, account_type, period_list, accumulated_
|
|||||||
data["total"] = total
|
data["total"] = total
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def get_account_type_based_gl_data(company, start_date, end_date, account_type, filters={}):
|
def get_account_type_based_gl_data(company, start_date, end_date, account_type, filters=None):
|
||||||
cond = ""
|
cond = ""
|
||||||
filters = frappe._dict(filters)
|
filters = frappe._dict(filters or {})
|
||||||
|
|
||||||
if filters.include_default_book_entries:
|
if filters.include_default_book_entries:
|
||||||
company_fb = frappe.db.get_value("Company", company, 'default_finance_book')
|
company_fb = frappe.db.get_value("Company", company, 'default_finance_book')
|
||||||
|
@ -424,7 +424,7 @@ class SellingController(StockController):
|
|||||||
or (cint(self.is_return) and self.docstatus==2)):
|
or (cint(self.is_return) and self.docstatus==2)):
|
||||||
sl_entries.append(self.get_sle_for_source_warehouse(d))
|
sl_entries.append(self.get_sle_for_source_warehouse(d))
|
||||||
|
|
||||||
if d.target_warehouse and self.get("is_internal_customer"):
|
if d.target_warehouse:
|
||||||
sl_entries.append(self.get_sle_for_target_warehouse(d))
|
sl_entries.append(self.get_sle_for_target_warehouse(d))
|
||||||
|
|
||||||
if d.warehouse and ((not cint(self.is_return) and self.docstatus==2)
|
if d.warehouse and ((not cint(self.is_return) and self.docstatus==2)
|
||||||
@ -559,6 +559,12 @@ class SellingController(StockController):
|
|||||||
frappe.throw(_("Row {0}: Delivery Warehouse ({1}) and Customer Warehouse ({2}) can not be same")
|
frappe.throw(_("Row {0}: Delivery Warehouse ({1}) and Customer Warehouse ({2}) can not be same")
|
||||||
.format(d.idx, warehouse, warehouse))
|
.format(d.idx, warehouse, warehouse))
|
||||||
|
|
||||||
|
if not self.get("is_internal_customer") and any(d.get("target_warehouse") for d in items):
|
||||||
|
msg = _("Target Warehouse is set for some items but the customer is not an internal customer.")
|
||||||
|
msg += " " + _("This {} will be treated as material transfer.").format(_(self.doctype))
|
||||||
|
frappe.msgprint(msg, title="Internal Transfer", alert=True)
|
||||||
|
|
||||||
|
|
||||||
def validate_items(self):
|
def validate_items(self):
|
||||||
# validate items to see if they have is_sales_item enabled
|
# validate items to see if they have is_sales_item enabled
|
||||||
from erpnext.controllers.buying_controller import validate_item_type
|
from erpnext.controllers.buying_controller import validate_item_type
|
||||||
|
@ -51,7 +51,7 @@ erpnext.LeadController = class LeadController extends frappe.ui.form.Controller
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
add_lead_to_prospect (frm) {
|
add_lead_to_prospect () {
|
||||||
frappe.prompt([
|
frappe.prompt([
|
||||||
{
|
{
|
||||||
fieldname: 'prospect',
|
fieldname: 'prospect',
|
||||||
@ -65,7 +65,7 @@ erpnext.LeadController = class LeadController extends frappe.ui.form.Controller
|
|||||||
frappe.call({
|
frappe.call({
|
||||||
method: 'erpnext.crm.doctype.lead.lead.add_lead_to_prospect',
|
method: 'erpnext.crm.doctype.lead.lead.add_lead_to_prospect',
|
||||||
args: {
|
args: {
|
||||||
'lead': frm.doc.name,
|
'lead': cur_frm.doc.name,
|
||||||
'prospect': data.prospect
|
'prospect': data.prospect
|
||||||
},
|
},
|
||||||
callback: function(r) {
|
callback: function(r) {
|
||||||
@ -79,41 +79,41 @@ erpnext.LeadController = class LeadController extends frappe.ui.form.Controller
|
|||||||
}, __('Add Lead to Prospect'), __('Add'));
|
}, __('Add Lead to Prospect'), __('Add'));
|
||||||
}
|
}
|
||||||
|
|
||||||
make_customer (frm) {
|
make_customer () {
|
||||||
frappe.model.open_mapped_doc({
|
frappe.model.open_mapped_doc({
|
||||||
method: "erpnext.crm.doctype.lead.lead.make_customer",
|
method: "erpnext.crm.doctype.lead.lead.make_customer",
|
||||||
frm: frm
|
frm: cur_frm
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
make_opportunity (frm) {
|
make_opportunity () {
|
||||||
frappe.model.open_mapped_doc({
|
frappe.model.open_mapped_doc({
|
||||||
method: "erpnext.crm.doctype.lead.lead.make_opportunity",
|
method: "erpnext.crm.doctype.lead.lead.make_opportunity",
|
||||||
frm: frm
|
frm: cur_frm
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
make_quotation (frm) {
|
make_quotation () {
|
||||||
frappe.model.open_mapped_doc({
|
frappe.model.open_mapped_doc({
|
||||||
method: "erpnext.crm.doctype.lead.lead.make_quotation",
|
method: "erpnext.crm.doctype.lead.lead.make_quotation",
|
||||||
frm: frm
|
frm: cur_frm
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
make_prospect (frm) {
|
make_prospect () {
|
||||||
frappe.model.with_doctype("Prospect", function() {
|
frappe.model.with_doctype("Prospect", function() {
|
||||||
let prospect = frappe.model.get_new_doc("Prospect");
|
let prospect = frappe.model.get_new_doc("Prospect");
|
||||||
prospect.company_name = frm.doc.company_name;
|
prospect.company_name = cur_frm.doc.company_name;
|
||||||
prospect.no_of_employees = frm.doc.no_of_employees;
|
prospect.no_of_employees = cur_frm.doc.no_of_employees;
|
||||||
prospect.industry = frm.doc.industry;
|
prospect.industry = cur_frm.doc.industry;
|
||||||
prospect.market_segment = frm.doc.market_segment;
|
prospect.market_segment = cur_frm.doc.market_segment;
|
||||||
prospect.territory = frm.doc.territory;
|
prospect.territory = cur_frm.doc.territory;
|
||||||
prospect.fax = frm.doc.fax;
|
prospect.fax = cur_frm.doc.fax;
|
||||||
prospect.website = frm.doc.website;
|
prospect.website = cur_frm.doc.website;
|
||||||
prospect.prospect_owner = frm.doc.lead_owner;
|
prospect.prospect_owner = cur_frm.doc.lead_owner;
|
||||||
|
|
||||||
let lead_prospect_row = frappe.model.add_child(prospect, 'prospect_lead');
|
let lead_prospect_row = frappe.model.add_child(prospect, 'prospect_lead');
|
||||||
lead_prospect_row.lead = frm.doc.name;
|
lead_prospect_row.lead = cur_frm.doc.name;
|
||||||
|
|
||||||
frappe.set_route("Form", "Prospect", prospect.name);
|
frappe.set_route("Form", "Prospect", prospect.name);
|
||||||
});
|
});
|
||||||
|
@ -138,7 +138,9 @@ class Student(Document):
|
|||||||
enrollment.submit()
|
enrollment.submit()
|
||||||
return enrollment
|
return enrollment
|
||||||
|
|
||||||
def enroll_in_course(self, course_name, program_enrollment, enrollment_date=frappe.utils.datetime.datetime.now()):
|
def enroll_in_course(self, course_name, program_enrollment, enrollment_date=None):
|
||||||
|
if enrollment_date is None:
|
||||||
|
enrollment_date = frappe.utils.datetime.datetime.now()
|
||||||
try:
|
try:
|
||||||
enrollment = frappe.get_doc({
|
enrollment = frappe.get_doc({
|
||||||
"doctype": "Course Enrollment",
|
"doctype": "Course Enrollment",
|
||||||
|
@ -176,7 +176,7 @@ def generate_taxes():
|
|||||||
account = create_account(company=company_name, account_name="Output Tax CGST", account_type="Tax", parent_account=parent_account)
|
account = create_account(company=company_name, account_name="Output Tax CGST", account_type="Tax", parent_account=parent_account)
|
||||||
return {'taxes':[{
|
return {'taxes':[{
|
||||||
"account_head": account,
|
"account_head": account,
|
||||||
"rate": 0,
|
"rate": 9,
|
||||||
"description": "CGST",
|
"description": "CGST",
|
||||||
"tax_amount": 10,
|
"tax_amount": 10,
|
||||||
"total": 210
|
"total": 210
|
||||||
|
@ -56,8 +56,6 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"columns": 2,
|
"columns": 2,
|
||||||
"fetch_from": "account_head.tax_rate",
|
|
||||||
"fetch_if_empty": 1,
|
|
||||||
"fieldname": "rate",
|
"fieldname": "rate",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
@ -111,4 +109,4 @@
|
|||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "ASC",
|
"sort_order": "ASC",
|
||||||
"track_changes": 1
|
"track_changes": 1
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
# License: GNU General Public License v3. See license.txt
|
# License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
@ -94,9 +93,11 @@ def get_events(start, end, filters=None):
|
|||||||
update={"allDay": 1})
|
update={"allDay": 1})
|
||||||
|
|
||||||
|
|
||||||
def is_holiday(holiday_list, date=today()):
|
def is_holiday(holiday_list, date=None):
|
||||||
"""Returns true if the given date is a holiday in the given holiday list
|
"""Returns true if the given date is a holiday in the given holiday list
|
||||||
"""
|
"""
|
||||||
|
if date is None:
|
||||||
|
date = today()
|
||||||
if holiday_list:
|
if holiday_list:
|
||||||
return bool(frappe.get_all('Holiday List',
|
return bool(frappe.get_all('Holiday List',
|
||||||
dict(name=holiday_list, holiday_date=date)))
|
dict(name=holiday_list, holiday_date=date)))
|
||||||
|
@ -139,7 +139,7 @@ def get_shift_type_timing(shift_types):
|
|||||||
return shift_timing_map
|
return shift_timing_map
|
||||||
|
|
||||||
|
|
||||||
def get_employee_shift(employee, for_date=nowdate(), consider_default_shift=False, next_shift_direction=None):
|
def get_employee_shift(employee, for_date=None, consider_default_shift=False, next_shift_direction=None):
|
||||||
"""Returns a Shift Type for the given employee on the given date. (excluding the holidays)
|
"""Returns a Shift Type for the given employee on the given date. (excluding the holidays)
|
||||||
|
|
||||||
:param employee: Employee for which shift is required.
|
:param employee: Employee for which shift is required.
|
||||||
@ -147,6 +147,8 @@ def get_employee_shift(employee, for_date=nowdate(), consider_default_shift=Fals
|
|||||||
:param consider_default_shift: If set to true, default shift is taken when no shift assignment is found.
|
:param consider_default_shift: If set to true, default shift is taken when no shift assignment is found.
|
||||||
:param next_shift_direction: One of: None, 'forward', 'reverse'. Direction to look for next shift if shift not found on given date.
|
:param next_shift_direction: One of: None, 'forward', 'reverse'. Direction to look for next shift if shift not found on given date.
|
||||||
"""
|
"""
|
||||||
|
if for_date is None:
|
||||||
|
for_date = nowdate()
|
||||||
default_shift = frappe.db.get_value('Employee', employee, 'default_shift')
|
default_shift = frappe.db.get_value('Employee', employee, 'default_shift')
|
||||||
shift_type_name = None
|
shift_type_name = None
|
||||||
shift_assignment_details = frappe.db.get_value('Shift Assignment', {'employee':employee, 'start_date':('<=', for_date), 'docstatus': '1', 'status': "Active"}, ['shift_type', 'end_date'])
|
shift_assignment_details = frappe.db.get_value('Shift Assignment', {'employee':employee, 'start_date':('<=', for_date), 'docstatus': '1', 'status': "Active"}, ['shift_type', 'end_date'])
|
||||||
@ -200,9 +202,11 @@ def get_employee_shift(employee, for_date=nowdate(), consider_default_shift=Fals
|
|||||||
return get_shift_details(shift_type_name, for_date)
|
return get_shift_details(shift_type_name, for_date)
|
||||||
|
|
||||||
|
|
||||||
def get_employee_shift_timings(employee, for_timestamp=now_datetime(), consider_default_shift=False):
|
def get_employee_shift_timings(employee, for_timestamp=None, consider_default_shift=False):
|
||||||
"""Returns previous shift, current/upcoming shift, next_shift for the given timestamp and employee
|
"""Returns previous shift, current/upcoming shift, next_shift for the given timestamp and employee
|
||||||
"""
|
"""
|
||||||
|
if for_timestamp is None:
|
||||||
|
for_timestamp = now_datetime()
|
||||||
# write and verify a test case for midnight shift.
|
# write and verify a test case for midnight shift.
|
||||||
prev_shift = curr_shift = next_shift = None
|
prev_shift = curr_shift = next_shift = None
|
||||||
curr_shift = get_employee_shift(employee, for_timestamp.date(), consider_default_shift, 'forward')
|
curr_shift = get_employee_shift(employee, for_timestamp.date(), consider_default_shift, 'forward')
|
||||||
@ -220,7 +224,7 @@ def get_employee_shift_timings(employee, for_timestamp=now_datetime(), consider_
|
|||||||
return prev_shift, curr_shift, next_shift
|
return prev_shift, curr_shift, next_shift
|
||||||
|
|
||||||
|
|
||||||
def get_shift_details(shift_type_name, for_date=nowdate()):
|
def get_shift_details(shift_type_name, for_date=None):
|
||||||
"""Returns Shift Details which contain some additional information as described below.
|
"""Returns Shift Details which contain some additional information as described below.
|
||||||
'shift_details' contains the following keys:
|
'shift_details' contains the following keys:
|
||||||
'shift_type' - Object of DocType Shift Type,
|
'shift_type' - Object of DocType Shift Type,
|
||||||
@ -234,6 +238,8 @@ def get_shift_details(shift_type_name, for_date=nowdate()):
|
|||||||
"""
|
"""
|
||||||
if not shift_type_name:
|
if not shift_type_name:
|
||||||
return None
|
return None
|
||||||
|
if not for_date:
|
||||||
|
for_date = nowdate()
|
||||||
shift_type = frappe.get_doc('Shift Type', shift_type_name)
|
shift_type = frappe.get_doc('Shift Type', shift_type_name)
|
||||||
start_datetime = datetime.combine(for_date, datetime.min.time()) + shift_type.start_time
|
start_datetime = datetime.combine(for_date, datetime.min.time()) + shift_type.start_time
|
||||||
for_date = for_date + timedelta(days=1) if shift_type.start_time > shift_type.end_time else for_date
|
for_date = for_date + timedelta(days=1) if shift_type.start_time > shift_type.end_time else for_date
|
||||||
|
@ -155,7 +155,11 @@ def get_designation_counts(designation, company):
|
|||||||
return employee_counts
|
return employee_counts
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_active_staffing_plan_details(company, designation, from_date=getdate(nowdate()), to_date=getdate(nowdate())):
|
def get_active_staffing_plan_details(company, designation, from_date=None, to_date=None):
|
||||||
|
if from_date is None:
|
||||||
|
from_date = getdate(nowdate())
|
||||||
|
if to_date is None:
|
||||||
|
to_date = getdate(nowdate())
|
||||||
if not company or not designation:
|
if not company or not designation:
|
||||||
frappe.throw(_("Please select Company and Designation"))
|
frappe.throw(_("Please select Company and Designation"))
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ class MaintenanceSchedule(TransactionBase):
|
|||||||
"Yearly": 365
|
"Yearly": 365
|
||||||
}
|
}
|
||||||
for item in self.items:
|
for item in self.items:
|
||||||
if item.periodicity and item.start_date:
|
if item.periodicity and item.periodicity != "Random" and item.start_date:
|
||||||
if not item.end_date:
|
if not item.end_date:
|
||||||
if item.no_of_visits:
|
if item.no_of_visits:
|
||||||
item.end_date = add_days(item.start_date, item.no_of_visits * days_in_period[item.periodicity])
|
item.end_date = add_days(item.start_date, item.no_of_visits * days_in_period[item.periodicity])
|
||||||
|
@ -141,7 +141,6 @@ class TestSalarySlip(unittest.TestCase):
|
|||||||
create_salary_structure_assignment,
|
create_salary_structure_assignment,
|
||||||
)
|
)
|
||||||
|
|
||||||
no_of_days = self.get_no_of_days()
|
|
||||||
# Payroll based on attendance
|
# Payroll based on attendance
|
||||||
frappe.db.set_value("Payroll Settings", None, "payroll_based_on", "Attendance")
|
frappe.db.set_value("Payroll Settings", None, "payroll_based_on", "Attendance")
|
||||||
|
|
||||||
@ -168,9 +167,6 @@ class TestSalarySlip(unittest.TestCase):
|
|||||||
ss = make_salary_slip_for_payment_days_dependency_test("test_payment_days_based_component@salary.com", salary_structure.name)
|
ss = make_salary_slip_for_payment_days_dependency_test("test_payment_days_based_component@salary.com", salary_structure.name)
|
||||||
self.assertEqual(ss.absent_days, 1)
|
self.assertEqual(ss.absent_days, 1)
|
||||||
|
|
||||||
days_in_month = no_of_days[0]
|
|
||||||
no_of_holidays = no_of_days[1]
|
|
||||||
|
|
||||||
ss.reload()
|
ss.reload()
|
||||||
payment_days_based_comp_amount = 0
|
payment_days_based_comp_amount = 0
|
||||||
for component in ss.earnings:
|
for component in ss.earnings:
|
||||||
@ -992,13 +988,14 @@ def make_salary_structure_for_payment_days_based_component_dependency():
|
|||||||
return salary_structure_doc
|
return salary_structure_doc
|
||||||
|
|
||||||
def make_salary_slip_for_payment_days_dependency_test(employee, salary_structure):
|
def make_salary_slip_for_payment_days_dependency_test(employee, salary_structure):
|
||||||
employee = frappe.db.get_value("Employee", {
|
employee = frappe.db.get_value(
|
||||||
"user_id": employee
|
"Employee",
|
||||||
},
|
{"user_id": employee},
|
||||||
["name", "company", "employee_name"],
|
["name", "company", "employee_name"],
|
||||||
as_dict=True)
|
as_dict=True
|
||||||
|
)
|
||||||
|
|
||||||
salary_slip_name = frappe.db.get_value("Salary Slip", {"employee": frappe.db.get_value("Employee", {"user_id": employee})})
|
salary_slip_name = frappe.db.get_value("Salary Slip", {"employee": employee.name})
|
||||||
|
|
||||||
if not salary_slip_name:
|
if not salary_slip_name:
|
||||||
salary_slip = make_salary_slip(salary_structure, employee=employee.name)
|
salary_slip = make_salary_slip(salary_structure, employee=employee.name)
|
||||||
@ -1009,4 +1006,4 @@ def make_salary_slip_for_payment_days_dependency_test(employee, salary_structure
|
|||||||
else:
|
else:
|
||||||
salary_slip = frappe.get_doc("Salary Slip", salary_slip_name)
|
salary_slip = frappe.get_doc("Salary Slip", salary_slip_name)
|
||||||
|
|
||||||
return salary_slip
|
return salary_slip
|
||||||
|
@ -46,43 +46,6 @@ frappe.ui.form.on("Company", {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
change_abbreviation(frm) {
|
|
||||||
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": frm.doc.name,
|
|
||||||
"old": frm.doc.abbr,
|
|
||||||
"new": args.new_abbr
|
|
||||||
},
|
|
||||||
callback: function(r) {
|
|
||||||
if (r.exc) {
|
|
||||||
frappe.msgprint(__("There were errors."));
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
frm.set_value("abbr", args.new_abbr);
|
|
||||||
}
|
|
||||||
dialog.hide();
|
|
||||||
frm.refresh();
|
|
||||||
},
|
|
||||||
btn: this
|
|
||||||
});
|
|
||||||
});
|
|
||||||
dialog.show();
|
|
||||||
},
|
|
||||||
|
|
||||||
company_name: function(frm) {
|
company_name: function(frm) {
|
||||||
if(frm.doc.__islocal) {
|
if(frm.doc.__islocal) {
|
||||||
// add missing " " arg in split method
|
// add missing " " arg in split method
|
||||||
@ -164,10 +127,6 @@ frappe.ui.form.on("Company", {
|
|||||||
}, __('Manage'));
|
}, __('Manage'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
frm.add_custom_button(__('Change Abbreviation'), () => {
|
|
||||||
frm.trigger('change_abbreviation');
|
|
||||||
}, __('Manage'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
erpnext.company.set_chart_of_accounts_options(frm.doc);
|
erpnext.company.set_chart_of_accounts_options(frm.doc);
|
||||||
|
@ -125,7 +125,8 @@
|
|||||||
"label": "Abbr",
|
"label": "Abbr",
|
||||||
"oldfieldname": "abbr",
|
"oldfieldname": "abbr",
|
||||||
"oldfieldtype": "Data",
|
"oldfieldtype": "Data",
|
||||||
"reqd": 1
|
"reqd": 1,
|
||||||
|
"set_only_once": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"bold": 1,
|
"bold": 1,
|
||||||
@ -747,10 +748,11 @@
|
|||||||
"image_field": "company_logo",
|
"image_field": "company_logo",
|
||||||
"is_tree": 1,
|
"is_tree": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-07-12 11:27:06.353860",
|
"modified": "2021-10-04 12:09:25.833133",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Setup",
|
"module": "Setup",
|
||||||
"name": "Company",
|
"name": "Company",
|
||||||
|
"naming_rule": "By fieldname",
|
||||||
"nsm_parent_field": "parent_company",
|
"nsm_parent_field": "parent_company",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
@ -808,4 +810,4 @@
|
|||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "ASC",
|
"sort_order": "ASC",
|
||||||
"track_changes": 1
|
"track_changes": 1
|
||||||
}
|
}
|
@ -399,44 +399,6 @@ class Company(NestedSet):
|
|||||||
if not frappe.db.get_value('GL Entry', {'company': self.name}):
|
if not frappe.db.get_value('GL Entry', {'company': self.name}):
|
||||||
frappe.db.sql("delete from `tabProcess Deferred Accounting` where company=%s", self.name)
|
frappe.db.sql("delete from `tabProcess Deferred Accounting` where company=%s", self.name)
|
||||||
|
|
||||||
@frappe.whitelist()
|
|
||||||
def enqueue_replace_abbr(company, old, new):
|
|
||||||
kwargs = dict(queue="long", company=company, old=old, new=new)
|
|
||||||
frappe.enqueue('erpnext.setup.doctype.company.company.replace_abbr', **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
|
||||||
def replace_abbr(company, old, new):
|
|
||||||
new = new.strip()
|
|
||||||
if not new:
|
|
||||||
frappe.throw(_("Abbr can not be blank or space"))
|
|
||||||
|
|
||||||
frappe.only_for("System Manager")
|
|
||||||
|
|
||||||
def _rename_record(doc):
|
|
||||||
parts = doc[0].rsplit(" - ", 1)
|
|
||||||
if len(parts) == 1 or parts[1].lower() == old.lower():
|
|
||||||
frappe.rename_doc(dt, doc[0], parts[0] + " - " + new, force=True)
|
|
||||||
|
|
||||||
def _rename_records(dt):
|
|
||||||
# rename is expensive so let's be economical with memory usage
|
|
||||||
doc = (d for d in frappe.db.sql("select name from `tab%s` where company=%s" % (dt, '%s'), company))
|
|
||||||
for d in doc:
|
|
||||||
_rename_record(d)
|
|
||||||
try:
|
|
||||||
frappe.db.auto_commit_on_many_writes = 1
|
|
||||||
for dt in ["Warehouse", "Account", "Cost Center", "Department",
|
|
||||||
"Sales Taxes and Charges Template", "Purchase Taxes and Charges Template"]:
|
|
||||||
_rename_records(dt)
|
|
||||||
frappe.db.commit()
|
|
||||||
frappe.db.set_value("Company", company, "abbr", new)
|
|
||||||
|
|
||||||
except Exception:
|
|
||||||
frappe.log_error(title=_('Abbreviation Rename Error'))
|
|
||||||
finally:
|
|
||||||
frappe.db.auto_commit_on_many_writes = 0
|
|
||||||
|
|
||||||
|
|
||||||
def get_name_with_abbr(name, company):
|
def get_name_with_abbr(name, company):
|
||||||
company_abbr = frappe.get_cached_value('Company', company, "abbr")
|
company_abbr = frappe.get_cached_value('Company', company, "abbr")
|
||||||
parts = name.split(" - ")
|
parts = name.split(" - ")
|
||||||
|
@ -192,7 +192,7 @@ def get_or_create_account(company_name, account):
|
|||||||
default_root_type = 'Liability'
|
default_root_type = 'Liability'
|
||||||
root_type = account.get('root_type', default_root_type)
|
root_type = account.get('root_type', default_root_type)
|
||||||
|
|
||||||
existing_accounts = frappe.get_list('Account',
|
existing_accounts = frappe.get_all('Account',
|
||||||
filters={
|
filters={
|
||||||
'company': company_name,
|
'company': company_name,
|
||||||
'root_type': root_type
|
'root_type': root_type
|
||||||
@ -247,7 +247,7 @@ def get_or_create_tax_group(company_name, root_type):
|
|||||||
|
|
||||||
# Create a new group account named 'Duties and Taxes' or 'Tax Assets' just
|
# Create a new group account named 'Duties and Taxes' or 'Tax Assets' just
|
||||||
# below the root account
|
# below the root account
|
||||||
root_account = frappe.get_list('Account', {
|
root_account = frappe.get_all('Account', {
|
||||||
'is_group': 1,
|
'is_group': 1,
|
||||||
'root_type': root_type,
|
'root_type': root_type,
|
||||||
'company': company_name,
|
'company': company_name,
|
||||||
|
@ -185,7 +185,6 @@ class DeliveryNote(SellingController):
|
|||||||
if not d['warehouse'] and frappe.db.get_value("Item", d['item_code'], "is_stock_item") == 1:
|
if not d['warehouse'] and frappe.db.get_value("Item", d['item_code'], "is_stock_item") == 1:
|
||||||
frappe.throw(_("Warehouse required for stock Item {0}").format(d["item_code"]))
|
frappe.throw(_("Warehouse required for stock Item {0}").format(d["item_code"]))
|
||||||
|
|
||||||
|
|
||||||
def update_current_stock(self):
|
def update_current_stock(self):
|
||||||
if self.get("_action") and self._action != "update_after_submit":
|
if self.get("_action") and self._action != "update_after_submit":
|
||||||
for d in self.get('items'):
|
for d in self.get('items'):
|
||||||
|
@ -468,7 +468,7 @@
|
|||||||
"width": "100px"
|
"width": "100px"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "eval:parent.is_internal_customer",
|
"depends_on": "eval:parent.is_internal_customer || doc.target_warehouse",
|
||||||
"fieldname": "target_warehouse",
|
"fieldname": "target_warehouse",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
@ -759,7 +759,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-02-23 01:04:08.588104",
|
"modified": "2021-10-05 12:12:44.018872",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Delivery Note Item",
|
"name": "Delivery Note Item",
|
||||||
@ -767,4 +767,4 @@
|
|||||||
"permissions": [],
|
"permissions": [],
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC"
|
"sort_order": "DESC"
|
||||||
}
|
}
|
||||||
|
@ -181,6 +181,8 @@ class Item(WebsiteGenerator):
|
|||||||
"doctype": "Item Price",
|
"doctype": "Item Price",
|
||||||
"price_list": price_list,
|
"price_list": price_list,
|
||||||
"item_code": self.name,
|
"item_code": self.name,
|
||||||
|
"uom": self.stock_uom,
|
||||||
|
"brand": self.brand,
|
||||||
"currency": erpnext.get_default_currency(),
|
"currency": erpnext.get_default_currency(),
|
||||||
"price_list_rate": self.standard_rate
|
"price_list_rate": self.standard_rate
|
||||||
})
|
})
|
||||||
@ -634,9 +636,21 @@ class Item(WebsiteGenerator):
|
|||||||
_("An Item Group exists with same name, please change the item name or rename the item group"))
|
_("An Item Group exists with same name, please change the item name or rename the item group"))
|
||||||
|
|
||||||
def update_item_price(self):
|
def update_item_price(self):
|
||||||
frappe.db.sql("""update `tabItem Price` set item_name=%s,
|
frappe.db.sql("""
|
||||||
item_description=%s, brand=%s where item_code=%s""",
|
UPDATE `tabItem Price`
|
||||||
(self.item_name, self.description, self.brand, self.name))
|
SET
|
||||||
|
item_name=%(item_name)s,
|
||||||
|
item_description=%(item_description)s,
|
||||||
|
brand=%(brand)s
|
||||||
|
WHERE item_code=%(item_code)s
|
||||||
|
""",
|
||||||
|
dict(
|
||||||
|
item_name=self.item_name,
|
||||||
|
item_description=self.description,
|
||||||
|
brand=self.brand,
|
||||||
|
item_code=self.name
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
def on_trash(self):
|
def on_trash(self):
|
||||||
super(Item, self).on_trash()
|
super(Item, self).on_trash()
|
||||||
|
@ -296,7 +296,7 @@ def make_purchase_order(source_name, target_doc=None, args=None):
|
|||||||
|
|
||||||
return d.ordered_qty < d.stock_qty and child_filter
|
return d.ordered_qty < d.stock_qty and child_filter
|
||||||
|
|
||||||
doclist = get_mapped_doc("Material Request", source_name, {
|
doclist = get_mapped_doc("Material Request", source_name, {
|
||||||
"Material Request": {
|
"Material Request": {
|
||||||
"doctype": "Purchase Order",
|
"doctype": "Purchase Order",
|
||||||
"validation": {
|
"validation": {
|
||||||
@ -323,7 +323,7 @@ def make_purchase_order(source_name, target_doc=None, args=None):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def make_request_for_quotation(source_name, target_doc=None):
|
def make_request_for_quotation(source_name, target_doc=None):
|
||||||
doclist = get_mapped_doc("Material Request", source_name, {
|
doclist = get_mapped_doc("Material Request", source_name, {
|
||||||
"Material Request": {
|
"Material Request": {
|
||||||
"doctype": "Request for Quotation",
|
"doctype": "Request for Quotation",
|
||||||
"validation": {
|
"validation": {
|
||||||
|
@ -842,7 +842,8 @@ def make_stock_entry(source_name,target_doc=None):
|
|||||||
"doctype": "Stock Entry Detail",
|
"doctype": "Stock Entry Detail",
|
||||||
"field_map": {
|
"field_map": {
|
||||||
"warehouse": "s_warehouse",
|
"warehouse": "s_warehouse",
|
||||||
"parent": "reference_purchase_receipt"
|
"parent": "reference_purchase_receipt",
|
||||||
|
"batch_no": "batch_no"
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, target_doc, set_missing_values)
|
}, target_doc, set_missing_values)
|
||||||
|
@ -611,7 +611,9 @@ def get_pos_reserved_serial_nos(filters):
|
|||||||
|
|
||||||
return reserved_sr_nos
|
return reserved_sr_nos
|
||||||
|
|
||||||
def fetch_serial_numbers(filters, qty, do_not_include=[]):
|
def fetch_serial_numbers(filters, qty, do_not_include=None):
|
||||||
|
if do_not_include is None:
|
||||||
|
do_not_include = []
|
||||||
batch_join_selection = ""
|
batch_join_selection = ""
|
||||||
batch_no_condition = ""
|
batch_no_condition = ""
|
||||||
batch_nos = filters.get("batch_no")
|
batch_nos = filters.get("batch_no")
|
||||||
|
@ -382,7 +382,7 @@ def get_basic_details(args, item, overwrite_warehouse=True):
|
|||||||
|
|
||||||
return out
|
return out
|
||||||
|
|
||||||
def get_item_warehouse(item, args, overwrite_warehouse, defaults={}):
|
def get_item_warehouse(item, args, overwrite_warehouse, defaults=None):
|
||||||
if not defaults:
|
if not defaults:
|
||||||
defaults = frappe._dict({
|
defaults = frappe._dict({
|
||||||
'item_defaults' : get_item_defaults(item.name, args.company),
|
'item_defaults' : get_item_defaults(item.name, args.company),
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
from math import ceil
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
@ -149,11 +150,16 @@ def create_material_request(material_requests):
|
|||||||
conversion_factor = frappe.db.get_value("UOM Conversion Detail",
|
conversion_factor = frappe.db.get_value("UOM Conversion Detail",
|
||||||
{'parent': item.name, 'uom': uom}, 'conversion_factor') or 1.0
|
{'parent': item.name, 'uom': uom}, 'conversion_factor') or 1.0
|
||||||
|
|
||||||
|
must_be_whole_number = frappe.db.get_value("UOM", uom, "must_be_whole_number", cache=True)
|
||||||
|
qty = d.reorder_qty / conversion_factor
|
||||||
|
if must_be_whole_number:
|
||||||
|
qty = ceil(qty)
|
||||||
|
|
||||||
mr.append("items", {
|
mr.append("items", {
|
||||||
"doctype": "Material Request Item",
|
"doctype": "Material Request Item",
|
||||||
"item_code": d.item_code,
|
"item_code": d.item_code,
|
||||||
"schedule_date": add_days(nowdate(),cint(item.lead_time_days)),
|
"schedule_date": add_days(nowdate(),cint(item.lead_time_days)),
|
||||||
"qty": d.reorder_qty / conversion_factor,
|
"qty": qty,
|
||||||
"uom": uom,
|
"uom": uom,
|
||||||
"stock_uom": item.stock_uom,
|
"stock_uom": item.stock_uom,
|
||||||
"warehouse": d.warehouse,
|
"warehouse": d.warehouse,
|
||||||
|
@ -228,7 +228,7 @@ def get_time_in_timedelta(time):
|
|||||||
def set_first_response_time(communication, method):
|
def set_first_response_time(communication, method):
|
||||||
if communication.get('reference_doctype') == "Issue":
|
if communication.get('reference_doctype') == "Issue":
|
||||||
issue = get_parent_doc(communication)
|
issue = get_parent_doc(communication)
|
||||||
if is_first_response(issue):
|
if is_first_response(issue) and issue.service_level_agreement:
|
||||||
first_response_time = calculate_first_response_time(issue, get_datetime(issue.first_responded_on))
|
first_response_time = calculate_first_response_time(issue, get_datetime(issue.first_responded_on))
|
||||||
issue.db_set("first_response_time", first_response_time)
|
issue.db_set("first_response_time", first_response_time)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user