Merge branch 'develop' into auto-attendance-issue

This commit is contained in:
Jannat Patel 2021-10-07 17:22:28 +05:30 committed by GitHub
commit c07dfbc08b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 182 additions and 206 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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;
} }
}); });
}
}; };

View File

@ -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):

View File

@ -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'):

View File

@ -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

View File

@ -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'))

View File

@ -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()

View File

@ -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()

View File

@ -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')

View File

@ -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

View File

@ -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);
}); });

View File

@ -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",

View File

@ -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

View File

@ -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,

View File

@ -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)))

View File

@ -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

View File

@ -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"))

View File

@ -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])

View File

@ -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)

View File

@ -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);

View File

@ -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": [

View File

@ -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(" - ")

View File

@ -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,

View File

@ -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'):

View File

@ -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",

View File

@ -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()

View File

@ -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": {

View File

@ -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)

View File

@ -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")

View File

@ -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),

View File

@ -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,

View File

@ -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)