mirror of
https://github.com/frappe/erpnext.git
synced 2026-04-16 13:25:10 +00:00
Merge branch 'v12-pre-release' into version-12
This commit is contained in:
@@ -5,7 +5,7 @@ import frappe
|
||||
from erpnext.hooks import regional_overrides
|
||||
from frappe.utils import getdate
|
||||
|
||||
__version__ = '12.18.0'
|
||||
__version__ = '12.19.0'
|
||||
|
||||
def get_default_company(user=None):
|
||||
'''Get default company for user'''
|
||||
|
||||
@@ -21,6 +21,14 @@ frappe.ui.form.on("Bank Reconciliation", {
|
||||
};
|
||||
});
|
||||
|
||||
frm.set_query("bank_account", function() {
|
||||
return {
|
||||
"filters": {
|
||||
"is_company_account": 1
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
frm.set_value("from_date", frappe.datetime.month_start());
|
||||
frm.set_value("to_date", frappe.datetime.month_end());
|
||||
},
|
||||
|
||||
@@ -207,11 +207,11 @@ class JournalEntry(AccountsController):
|
||||
if d.reference_type=="Journal Entry":
|
||||
account_root_type = frappe.db.get_value("Account", d.account, "root_type")
|
||||
if account_root_type == "Asset" and flt(d.debit) > 0:
|
||||
frappe.throw(_("For {0}, only credit accounts can be linked against another debit entry")
|
||||
.format(d.account))
|
||||
frappe.throw(_("Row #{0}: For {1}, you can select reference document only if account gets credited")
|
||||
.format(d.idx, d.account))
|
||||
elif account_root_type == "Liability" and flt(d.credit) > 0:
|
||||
frappe.throw(_("For {0}, only debit accounts can be linked against another credit entry")
|
||||
.format(d.account))
|
||||
frappe.throw(_("Row #{0}: For {1}, you can select reference document only if account gets debited")
|
||||
.format(d.idx, d.account))
|
||||
|
||||
if d.reference_name == self.name:
|
||||
frappe.throw(_("You can not enter current voucher in 'Against Journal Entry' column"))
|
||||
|
||||
@@ -531,7 +531,8 @@
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"label": "Title",
|
||||
"print_hide": 1
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "party",
|
||||
@@ -575,7 +576,7 @@
|
||||
}
|
||||
],
|
||||
"is_submittable": 1,
|
||||
"modified": "2019-11-06 12:59:43.151721",
|
||||
"modified": "2021-03-10 13:05:16.958866",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Payment Entry",
|
||||
@@ -619,4 +620,4 @@
|
||||
"sort_order": "DESC",
|
||||
"title_field": "title",
|
||||
"track_changes": 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -441,6 +441,10 @@ class PaymentEntry(AccountsController):
|
||||
.format(total_negative_outstanding), InvalidPaymentEntry)
|
||||
|
||||
def set_title(self):
|
||||
if frappe.flags.in_import and self.title:
|
||||
# do not set title dynamically if title exists during data import.
|
||||
return
|
||||
|
||||
if self.payment_type in ("Receive", "Pay"):
|
||||
self.title = self.party
|
||||
else:
|
||||
|
||||
@@ -131,7 +131,7 @@ class PricingRule(Document):
|
||||
for d in self.items:
|
||||
max_discount = frappe.get_cached_value("Item", d.item_code, "max_discount")
|
||||
if max_discount and flt(self.discount_percentage) > flt(max_discount):
|
||||
throw(_("Max discount allowed for item: {0} is {1}%").format(self.item_code, max_discount))
|
||||
throw(_("Max discount allowed for item: {0} is {1}%").format(d.item_code, max_discount))
|
||||
|
||||
def validate_price_list_with_currency(self):
|
||||
if self.currency and self.for_price_list:
|
||||
|
||||
@@ -252,7 +252,7 @@ def make_round_off_gle(gl_map, debit_credit_diff, precision):
|
||||
|
||||
if not round_off_gle:
|
||||
for k in ["voucher_type", "voucher_no", "company",
|
||||
"posting_date", "remarks", "is_opening"]:
|
||||
"posting_date", "remarks"]:
|
||||
round_off_gle[k] = gl_map[0][k]
|
||||
|
||||
round_off_gle.update({
|
||||
@@ -264,6 +264,7 @@ def make_round_off_gle(gl_map, debit_credit_diff, precision):
|
||||
"cost_center": round_off_cost_center,
|
||||
"party_type": None,
|
||||
"party": None,
|
||||
"is_opening": "No",
|
||||
"against_voucher_type": None,
|
||||
"against_voucher": None
|
||||
})
|
||||
|
||||
@@ -224,8 +224,7 @@ def get_company_currency(filters=None):
|
||||
def calculate_values(accounts_by_name, gl_entries_by_account, companies, fiscal_year, filters):
|
||||
for entries in gl_entries_by_account.values():
|
||||
for entry in entries:
|
||||
key = entry.account_number or entry.account_name
|
||||
d = accounts_by_name.get(key)
|
||||
d = accounts_by_name.get(entry.account_name)
|
||||
if d:
|
||||
for company in companies:
|
||||
# check if posting date is within the period
|
||||
@@ -240,7 +239,8 @@ def accumulate_values_into_parents(accounts, accounts_by_name, companies):
|
||||
"""accumulate children's values in parent accounts"""
|
||||
for d in reversed(accounts):
|
||||
if d.parent_account:
|
||||
account = d.parent_account.split('-')[0].strip()
|
||||
account = d.parent_account_name
|
||||
|
||||
if not accounts_by_name.get(account):
|
||||
continue
|
||||
|
||||
@@ -251,16 +251,34 @@ def accumulate_values_into_parents(accounts, accounts_by_name, companies):
|
||||
accounts_by_name[account]["opening_balance"] = \
|
||||
accounts_by_name[account].get("opening_balance", 0.0) + d.get("opening_balance", 0.0)
|
||||
|
||||
|
||||
def get_account_heads(root_type, companies, filters):
|
||||
accounts = get_accounts(root_type, filters)
|
||||
|
||||
if not accounts:
|
||||
return None, None
|
||||
|
||||
accounts = update_parent_account_names(accounts)
|
||||
|
||||
accounts, accounts_by_name, parent_children_map = filter_accounts(accounts)
|
||||
|
||||
return accounts, accounts_by_name
|
||||
|
||||
def update_parent_account_names(accounts):
|
||||
"""Update parent_account_name in accounts list.
|
||||
|
||||
parent_name is `name` of parent account which could have other prefix
|
||||
of account_number and suffix of company abbr. This function adds key called
|
||||
`parent_account_name` which does not have such prefix/suffix.
|
||||
"""
|
||||
name_to_account_map = { d.name : d.account_name for d in accounts }
|
||||
|
||||
for account in accounts:
|
||||
if account.parent_account:
|
||||
account["parent_account_name"] = name_to_account_map[account.parent_account]
|
||||
|
||||
return accounts
|
||||
|
||||
def get_companies(filters):
|
||||
companies = {}
|
||||
all_companies = get_subsidiary_companies(filters.get('company'))
|
||||
@@ -367,9 +385,9 @@ def set_gl_entries_by_account(from_date, to_date, root_lft, root_rgt, filters, g
|
||||
convert_to_presentation_currency(gl_entries, currency_info)
|
||||
|
||||
for entry in gl_entries:
|
||||
key = entry.account_number or entry.account_name
|
||||
validate_entries(key, entry, accounts_by_name, accounts)
|
||||
gl_entries_by_account.setdefault(key, []).append(entry)
|
||||
account_name = entry.account_name
|
||||
validate_entries(account_name, entry, accounts_by_name, accounts)
|
||||
gl_entries_by_account.setdefault(account_name, []).append(entry)
|
||||
|
||||
return gl_entries_by_account
|
||||
|
||||
@@ -438,8 +456,7 @@ def filter_accounts(accounts, depth=10):
|
||||
parent_children_map = {}
|
||||
accounts_by_name = {}
|
||||
for d in accounts:
|
||||
key = d.account_number or d.account_name
|
||||
accounts_by_name[key] = d
|
||||
accounts_by_name[d.account_name] = d
|
||||
parent_children_map.setdefault(d.parent_account or None, []).append(d)
|
||||
|
||||
filtered_accounts = []
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"description": "Settings for Buying Module",
|
||||
"doctype": "DocType",
|
||||
"document_type": "Other",
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"supp_master_name",
|
||||
"supplier_group",
|
||||
@@ -92,7 +93,7 @@
|
||||
"icon": "fa fa-cog",
|
||||
"idx": 1,
|
||||
"issingle": 1,
|
||||
"modified": "2019-08-20 13:13:09.055189",
|
||||
"modified": "2021-03-02 18:16:03.947813",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Buying Settings",
|
||||
@@ -107,5 +108,8 @@
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
]
|
||||
],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
@@ -117,7 +117,7 @@ def call_mws_method(mws_method, *args, **kwargs):
|
||||
return response
|
||||
except Exception as e:
|
||||
delay = math.pow(4, x) * 125
|
||||
frappe.log_error(message=e, title=str(mws_method))
|
||||
frappe.log_error(message=e, title="Method {} failed".format(mws_method.__name__))
|
||||
time.sleep(delay)
|
||||
continue
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
{
|
||||
"actions": [],
|
||||
"creation": "2018-10-25 10:02:48.656165",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
@@ -69,8 +68,7 @@
|
||||
}
|
||||
],
|
||||
"issingle": 1,
|
||||
"links": [],
|
||||
"modified": "2020-10-29 20:24:56.916104",
|
||||
"modified": "2021-03-02 18:05:50.794105",
|
||||
"modified_by": "Administrator",
|
||||
"module": "ERPNext Integrations",
|
||||
"name": "Plaid Settings",
|
||||
@@ -88,5 +86,6 @@
|
||||
}
|
||||
],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC"
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
"creation": "2015-05-18 05:21:07.270859",
|
||||
"doctype": "DocType",
|
||||
"document_type": "System",
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"status_html",
|
||||
"enable_shopify",
|
||||
@@ -258,8 +259,8 @@
|
||||
}
|
||||
],
|
||||
"issingle": 1,
|
||||
"modified": "2020-05-28 12:32:11.384757",
|
||||
"modified_by": "umair@erpnext.com",
|
||||
"modified": "2021-03-02 18:06:00.868688",
|
||||
"modified_by": "Administrator",
|
||||
"module": "ERPNext Integrations",
|
||||
"name": "Shopify Settings",
|
||||
"owner": "Administrator",
|
||||
@@ -276,5 +277,6 @@
|
||||
}
|
||||
],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC"
|
||||
}
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
@@ -261,6 +261,9 @@ doc_events = {
|
||||
('Sales Invoice', 'Sales Order', 'Delivery Note', 'Purchase Invoice', 'Purchase Order', 'Purchase Receipt'): {
|
||||
'validate': ['erpnext.regional.india.utils.set_place_of_supply']
|
||||
},
|
||||
('Sales Invoice', 'Purchase Invoice'): {
|
||||
'validate': ['erpnext.regional.india.utils.validate_document_name']
|
||||
},
|
||||
"Contact": {
|
||||
"on_trash": "erpnext.support.doctype.issue.issue.update_issue",
|
||||
"after_insert": "erpnext.communication.doctype.call_log.call_log.set_caller_information",
|
||||
|
||||
@@ -175,7 +175,7 @@
|
||||
"idx": 1,
|
||||
"issingle": 1,
|
||||
"links": [],
|
||||
"modified": "2019-12-31 14:28:32.004121",
|
||||
"modified": "2021-02-25 13:06:37.978785",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "HR Settings",
|
||||
@@ -192,5 +192,6 @@
|
||||
}
|
||||
],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "ASC"
|
||||
"sort_order": "ASC",
|
||||
"track_changes": 1
|
||||
}
|
||||
@@ -1,16 +1,19 @@
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe, erpnext
|
||||
from frappe import _
|
||||
from frappe.utils import formatdate, format_datetime, getdate, get_datetime, nowdate, flt, cstr, add_days, today
|
||||
from frappe.model.document import Document
|
||||
from frappe.desk.form import assign_to
|
||||
import erpnext
|
||||
import frappe
|
||||
from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
|
||||
from frappe import _
|
||||
from frappe.desk.form import assign_to
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import (add_days, cstr, flt, format_datetime, formatdate,
|
||||
get_datetime, getdate, nowdate, today, unique)
|
||||
|
||||
|
||||
class DuplicateDeclarationError(frappe.ValidationError): pass
|
||||
|
||||
|
||||
class EmployeeBoardingController(Document):
|
||||
'''
|
||||
Create the project and the task for the boarding process
|
||||
@@ -48,27 +51,38 @@ class EmployeeBoardingController(Document):
|
||||
continue
|
||||
|
||||
task = frappe.get_doc({
|
||||
"doctype": "Task",
|
||||
"project": self.project,
|
||||
"subject": activity.activity_name + " : " + self.employee_name,
|
||||
"description": activity.description,
|
||||
"department": self.department,
|
||||
"company": self.company,
|
||||
"task_weight": activity.task_weight
|
||||
}).insert(ignore_permissions=True)
|
||||
"doctype": "Task",
|
||||
"project": self.project,
|
||||
"subject": activity.activity_name + " : " + self.employee_name,
|
||||
"description": activity.description,
|
||||
"department": self.department,
|
||||
"company": self.company,
|
||||
"task_weight": activity.task_weight
|
||||
}).insert(ignore_permissions=True)
|
||||
activity.db_set("task", task.name)
|
||||
|
||||
users = [activity.user] if activity.user else []
|
||||
if activity.role:
|
||||
user_list = frappe.db.sql_list('''select distinct(parent) from `tabHas Role`
|
||||
where parenttype='User' and role=%s''', activity.role)
|
||||
users = users + user_list
|
||||
user_list = frappe.db.sql_list('''
|
||||
SELECT
|
||||
DISTINCT(has_role.parent)
|
||||
FROM
|
||||
`tabHas Role` has_role
|
||||
LEFT JOIN `tabUser` user
|
||||
ON has_role.parent = user.name
|
||||
WHERE
|
||||
has_role.parenttype = 'User'
|
||||
AND user.enabled = 1
|
||||
AND has_role.role = %s
|
||||
''', activity.role)
|
||||
users = unique(users + user_list)
|
||||
|
||||
if "Administrator" in users:
|
||||
users.remove("Administrator")
|
||||
|
||||
# assign the task the users
|
||||
if users:
|
||||
self.assign_task_to_users(task, set(users))
|
||||
self.assign_task_to_users(task, users)
|
||||
|
||||
def assign_task_to_users(self, task, users):
|
||||
for user in users:
|
||||
@@ -453,4 +467,4 @@ def get_previous_claimed_amount(employee, payroll_period, non_pro_rata=False, co
|
||||
}, as_dict=True)
|
||||
if sum_of_claimed_amount and flt(sum_of_claimed_amount[0].total_amount) > 0:
|
||||
total_claimed_amount = sum_of_claimed_amount[0].total_amount
|
||||
return total_claimed_amount
|
||||
return total_claimed_amount
|
||||
|
||||
@@ -88,7 +88,7 @@ def get_bom_stock(filters):
|
||||
GROUP BY bom_item.item_code""".format(qty_field=qty_field, table=table, conditions=conditions, bom=bom), as_dict=1)
|
||||
|
||||
def get_manufacturer_records():
|
||||
details = frappe.get_list('Item Manufacturer', fields = ["manufacturer", "manufacturer_part_no, parent"])
|
||||
details = frappe.get_list('Item Manufacturer', fields = ["manufacturer", "manufacturer_part_no", "parent"])
|
||||
manufacture_details = frappe._dict()
|
||||
for detail in details:
|
||||
dic = manufacture_details.setdefault(detail.get('parent'), {})
|
||||
|
||||
@@ -75,23 +75,23 @@ frappe.ui.form.on("Project", {
|
||||
frm.add_custom_button(__('Cancelled'), () => {
|
||||
frm.events.set_status(frm, 'Cancelled');
|
||||
}, __('Set Status'));
|
||||
}
|
||||
|
||||
if (frappe.model.can_read("Task")) {
|
||||
frm.add_custom_button(__("Gantt Chart"), function () {
|
||||
frappe.route_options = {
|
||||
"project": frm.doc.name
|
||||
};
|
||||
frappe.set_route("List", "Task", "Gantt");
|
||||
});
|
||||
|
||||
frm.add_custom_button(__("Kanban Board"), () => {
|
||||
frappe.call('erpnext.projects.doctype.project.project.create_kanban_board_if_not_exists', {
|
||||
project: frm.doc.project_name
|
||||
}).then(() => {
|
||||
frappe.set_route('List', 'Task', 'Kanban', frm.doc.project_name);
|
||||
if (frappe.model.can_read("Task")) {
|
||||
frm.add_custom_button(__("Gantt Chart"), function () {
|
||||
frappe.route_options = {
|
||||
"project": frm.doc.name
|
||||
};
|
||||
frappe.set_route("List", "Task", "Gantt");
|
||||
});
|
||||
});
|
||||
|
||||
frm.add_custom_button(__("Kanban Board"), () => {
|
||||
frappe.call('erpnext.projects.doctype.project.project.create_kanban_board_if_not_exists', {
|
||||
project: frm.doc.project_name
|
||||
}).then(() => {
|
||||
frappe.set_route('List', 'Task', 'Kanban', frm.doc.project_name);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -507,6 +507,7 @@ erpnext.utils.update_child_items = function(opts) {
|
||||
}, {
|
||||
fieldtype:'Currency',
|
||||
fieldname:"rate",
|
||||
options: "currency",
|
||||
default: 0,
|
||||
read_only: 0,
|
||||
in_list_view: 1,
|
||||
|
||||
@@ -160,7 +160,7 @@ def get_item_list(invoice):
|
||||
item.update(d.as_dict())
|
||||
|
||||
item.sr_no = d.idx
|
||||
item.description = d.item_name.replace('"', '\\"')
|
||||
item.description = json.dumps(d.item_name)[1:-1]
|
||||
|
||||
item.qty = abs(item.qty)
|
||||
item.discount_amount = 0
|
||||
@@ -196,9 +196,11 @@ def update_item_taxes(invoice, item):
|
||||
item[attr] = 0
|
||||
|
||||
for t in invoice.taxes:
|
||||
# this contains item wise tax rate & tax amount (incl. discount)
|
||||
item_tax_detail = json.loads(t.item_wise_tax_detail).get(item.item_code)
|
||||
if t.account_head in gst_accounts_list:
|
||||
is_applicable = t.tax_amount and t.account_head in gst_accounts_list
|
||||
if is_applicable:
|
||||
# this contains item wise tax rate & tax amount (incl. discount)
|
||||
item_tax_detail = json.loads(t.item_wise_tax_detail).get(item.item_code)
|
||||
|
||||
item_tax_rate = item_tax_detail[0]
|
||||
# item tax amount excluding discount amount
|
||||
item_tax_amount = (item_tax_rate / 100) * item.base_net_amount
|
||||
@@ -223,7 +225,7 @@ def get_invoice_value_details(invoice):
|
||||
|
||||
if invoice.apply_discount_on == 'Net Total' and invoice.discount_amount:
|
||||
invoice_value_details.base_total = abs(invoice.base_total)
|
||||
invoice_value_details.invoice_discount_amt = invoice.base_discount_amount
|
||||
invoice_value_details.invoice_discount_amt = abs(invoice.base_discount_amount)
|
||||
else:
|
||||
invoice_value_details.base_total = abs(invoice.base_net_total)
|
||||
# since tax already considers discount amount
|
||||
|
||||
@@ -2,7 +2,7 @@ from __future__ import unicode_literals
|
||||
import frappe, re, json
|
||||
from frappe import _
|
||||
import erpnext
|
||||
from frappe.utils import cstr, flt, date_diff, nowdate, round_based_on_smallest_currency_fraction, money_in_words
|
||||
from frappe.utils import cstr, flt, date_diff, nowdate, round_based_on_smallest_currency_fraction, money_in_words, getdate
|
||||
from erpnext.regional.india import states, state_numbers
|
||||
from erpnext.controllers.taxes_and_totals import get_itemised_tax, get_itemised_taxable_amount, calculate_outstanding_amount
|
||||
from erpnext.controllers.accounts_controller import get_taxes_and_charges
|
||||
@@ -15,6 +15,13 @@ from erpnext.accounts.utils import get_account_currency
|
||||
from frappe.contacts.doctype.address.address import get_address_display
|
||||
from frappe.model.utils import get_fetch_values
|
||||
|
||||
|
||||
GST_INVOICE_NUMBER_FORMAT = re.compile(r"^[a-zA-Z0-9\-/]+$") #alphanumeric and - /
|
||||
GSTIN_FORMAT = re.compile("^[0-9]{2}[A-Z]{4}[0-9A-Z]{1}[0-9]{4}[A-Z]{1}[1-9A-Z]{1}[1-9A-Z]{1}[0-9A-Z]{1}$")
|
||||
GSTIN_UIN_FORMAT = re.compile("^[0-9]{4}[A-Z]{3}[0-9]{5}[0-9A-Z]{3}")
|
||||
PAN_NUMBER_FORMAT = re.compile("[A-Z]{5}[0-9]{4}[A-Z]{1}")
|
||||
|
||||
|
||||
def validate_gstin_for_india(doc, method):
|
||||
if hasattr(doc, 'gst_state') and doc.gst_state:
|
||||
doc.gst_state_number = state_numbers[doc.gst_state]
|
||||
@@ -38,12 +45,10 @@ def validate_gstin_for_india(doc, method):
|
||||
frappe.throw(_("Invalid GSTIN! A GSTIN must have 15 characters."))
|
||||
|
||||
if gst_category and gst_category == 'UIN Holders':
|
||||
p = re.compile("^[0-9]{4}[A-Z]{3}[0-9]{5}[0-9A-Z]{3}")
|
||||
if not p.match(doc.gstin):
|
||||
if not GSTIN_UIN_FORMAT.match(doc.gstin):
|
||||
frappe.throw(_("Invalid GSTIN! The input you've entered doesn't match the GSTIN format for UIN Holders or Non-Resident OIDAR Service Providers"))
|
||||
else:
|
||||
p = re.compile("^[0-9]{2}[A-Z]{4}[0-9A-Z]{1}[0-9]{4}[A-Z]{1}[1-9A-Z]{1}[1-9A-Z]{1}[0-9A-Z]{1}$")
|
||||
if not p.match(doc.gstin):
|
||||
if not GSTIN_FORMAT.match(doc.gstin):
|
||||
frappe.throw(_("Invalid GSTIN! The input you've entered doesn't match the format of GSTIN."))
|
||||
|
||||
validate_gstin_check_digit(doc.gstin)
|
||||
@@ -162,6 +167,20 @@ def set_transporter_address(doc, method=None):
|
||||
doc.transporter_address = transporter_address
|
||||
doc.transporter_address_display = get_address_display(transporter_address)
|
||||
|
||||
def validate_document_name(doc, method=None):
|
||||
"""Validate GST invoice number requirements."""
|
||||
country = frappe.get_cached_value("Company", doc.company, "country")
|
||||
|
||||
# Date was chosen as start of next FY to avoid irritating current users.
|
||||
if country != "India" or getdate(doc.posting_date) < getdate("2021-04-01"):
|
||||
return
|
||||
|
||||
if len(doc.name) > 16:
|
||||
frappe.throw(_("Maximum length of document number should be 16 characters as per GST rules. Please change the naming series."))
|
||||
|
||||
if not GST_INVOICE_NUMBER_FORMAT.match(doc.name):
|
||||
frappe.throw(_("Document name should only contain alphanumeric values, dash(-) and slash(/) characters as per GST rules. Please change the naming series."))
|
||||
|
||||
# don't remove this function it is used in tests
|
||||
def test_method():
|
||||
'''test function'''
|
||||
@@ -715,25 +734,12 @@ def update_grand_total_for_rcm(doc, method):
|
||||
if country != 'India':
|
||||
return
|
||||
|
||||
if not doc.total_taxes_and_charges:
|
||||
gst_tax, base_gst_tax = get_gst_tax_amount(doc)
|
||||
|
||||
if not base_gst_tax:
|
||||
return
|
||||
|
||||
if doc.reverse_charge == 'Y':
|
||||
gst_accounts = get_gst_accounts(doc.company)
|
||||
gst_account_list = gst_accounts.get('cgst_account') + gst_accounts.get('sgst_account') \
|
||||
+ gst_accounts.get('igst_account')
|
||||
|
||||
base_gst_tax = 0
|
||||
gst_tax = 0
|
||||
|
||||
for tax in doc.get('taxes'):
|
||||
if tax.category not in ("Total", "Valuation and Total"):
|
||||
continue
|
||||
|
||||
if flt(tax.base_tax_amount_after_discount_amount) and tax.account_head in gst_account_list:
|
||||
base_gst_tax += tax.base_tax_amount_after_discount_amount
|
||||
gst_tax += tax.tax_amount_after_discount_amount
|
||||
|
||||
doc.taxes_and_charges_added -= gst_tax
|
||||
doc.total_taxes_and_charges -= gst_tax
|
||||
doc.base_taxes_and_charges_added -= base_gst_tax
|
||||
@@ -765,6 +771,11 @@ def make_regional_gl_entries(gl_entries, doc):
|
||||
if country != 'India':
|
||||
return gl_entries
|
||||
|
||||
gst_tax, base_gst_tax = get_gst_tax_amount(doc)
|
||||
|
||||
if not base_gst_tax:
|
||||
return gl_entries
|
||||
|
||||
if doc.reverse_charge == 'Y':
|
||||
gst_accounts = get_gst_accounts(doc.company)
|
||||
gst_account_list = gst_accounts.get('cgst_account') + gst_accounts.get('sgst_account') \
|
||||
@@ -792,3 +803,21 @@ def make_regional_gl_entries(gl_entries, doc):
|
||||
)
|
||||
|
||||
return gl_entries
|
||||
|
||||
def get_gst_tax_amount(doc):
|
||||
gst_accounts = get_gst_accounts(doc.company)
|
||||
gst_account_list = gst_accounts.get('cgst_account', []) + gst_accounts.get('sgst_account', []) \
|
||||
+ gst_accounts.get('igst_account', [])
|
||||
|
||||
base_gst_tax = 0
|
||||
gst_tax = 0
|
||||
|
||||
for tax in doc.get('taxes'):
|
||||
if tax.category not in ("Total", "Valuation and Total"):
|
||||
continue
|
||||
|
||||
if flt(tax.base_tax_amount_after_discount_amount) and tax.account_head in gst_account_list:
|
||||
base_gst_tax += tax.base_tax_amount_after_discount_amount
|
||||
gst_tax += tax.tax_amount_after_discount_amount
|
||||
|
||||
return gst_tax, base_gst_tax
|
||||
|
||||
@@ -236,6 +236,7 @@ class Gstr1Report(object):
|
||||
self.cgst_sgst_invoices = []
|
||||
|
||||
unidentified_gst_accounts = []
|
||||
unidentified_gst_accounts_invoice = []
|
||||
for parent, account, item_wise_tax_detail, tax_amount in self.tax_details:
|
||||
if account in self.gst_accounts.cess_account:
|
||||
self.invoice_cess.setdefault(parent, tax_amount)
|
||||
@@ -251,6 +252,7 @@ class Gstr1Report(object):
|
||||
if not (cgst_or_sgst or account in self.gst_accounts.igst_account):
|
||||
if "gst" in account.lower() and account not in unidentified_gst_accounts:
|
||||
unidentified_gst_accounts.append(account)
|
||||
unidentified_gst_accounts_invoice.append(parent)
|
||||
continue
|
||||
|
||||
for item_code, tax_amounts in item_wise_tax_detail.items():
|
||||
@@ -273,7 +275,7 @@ class Gstr1Report(object):
|
||||
|
||||
# Build itemised tax for export invoices where tax table is blank
|
||||
for invoice, items in iteritems(self.invoice_items):
|
||||
if invoice not in self.items_based_on_tax_rate \
|
||||
if invoice not in self.items_based_on_tax_rate and invoice not in unidentified_gst_accounts_invoice \
|
||||
and frappe.db.get_value(self.doctype, invoice, "export_type") == "Without Payment of Tax":
|
||||
self.items_based_on_tax_rate.setdefault(invoice, {}).setdefault(0, items.keys())
|
||||
|
||||
|
||||
@@ -573,7 +573,7 @@
|
||||
"issingle": 1,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-06-25 12:56:16.332039",
|
||||
"modified": "2021-03-02 17:35:14.084342",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Selling",
|
||||
"name": "Selling Settings",
|
||||
@@ -605,7 +605,7 @@
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 0,
|
||||
"track_changes": 1,
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
}
|
||||
|
||||
@@ -26,8 +26,9 @@ def delete_company_transactions(company_name):
|
||||
tabDocField where fieldtype='Link' and options='Company'"""):
|
||||
if doctype not in ("Account", "Cost Center", "Warehouse", "Budget",
|
||||
"Party Account", "Employee", "Sales Taxes and Charges Template",
|
||||
"Purchase Taxes and Charges Template", "POS Profile", 'BOM',
|
||||
"Item default", "Customer", "Supplier"):
|
||||
"Purchase Taxes and Charges Template", "POS Profile", "BOM",
|
||||
"Company", "Bank Account", "Item Tax Template", "Mode Of Payment",
|
||||
"Item Default", "Customer", "Supplier", "GST Account"):
|
||||
delete_for_doctype(doctype, company_name)
|
||||
|
||||
# reset company values
|
||||
|
||||
@@ -17,7 +17,7 @@ frappe.ui.form.on('Global Defaults', {
|
||||
method: "frappe.client.get_list",
|
||||
args: {
|
||||
doctype: "UOM Conversion Factor",
|
||||
filters: { "category": "Length" },
|
||||
filters: { "category": __("Length") },
|
||||
fields: ["to_uom"],
|
||||
limit_page_length: 500
|
||||
},
|
||||
|
||||
@@ -161,7 +161,7 @@
|
||||
"icon": "fa fa-shopping-cart",
|
||||
"idx": 1,
|
||||
"issingle": 1,
|
||||
"modified": "2020-07-07 02:13:23.175604",
|
||||
"modified": "2021-03-02 18:05:56.721122",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Shopping Cart",
|
||||
"name": "Shopping Cart Settings",
|
||||
@@ -178,5 +178,6 @@
|
||||
}
|
||||
],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "ASC"
|
||||
"sort_order": "ASC",
|
||||
"track_changes": 1
|
||||
}
|
||||
@@ -156,7 +156,8 @@ erpnext.stock.move_item = function(item, source, target, actual_qty, rate, callb
|
||||
fieldtype: 'Float', description: __('Available {0}', [actual_qty]) },
|
||||
{fieldname: 'rate', label: __('Rate'), fieldtype: 'Currency', hidden: 1 },
|
||||
],
|
||||
})
|
||||
});
|
||||
var submitted = false;
|
||||
dialog.show();
|
||||
dialog.get_field('item_code').set_input(item);
|
||||
|
||||
@@ -180,6 +181,7 @@ erpnext.stock.move_item = function(item, source, target, actual_qty, rate, callb
|
||||
}
|
||||
|
||||
dialog.set_primary_action(__('Submit'), function() {
|
||||
if(submitted) return;
|
||||
var values = dialog.get_values();
|
||||
if(!values) {
|
||||
return;
|
||||
@@ -192,6 +194,7 @@ erpnext.stock.move_item = function(item, source, target, actual_qty, rate, callb
|
||||
frappe.msgprint(__('Source and target warehouse must be different'));
|
||||
}
|
||||
|
||||
submitted = true;
|
||||
frappe.call({
|
||||
method: 'erpnext.stock.doctype.stock_entry.stock_entry_utils.make_stock_entry',
|
||||
args: values,
|
||||
|
||||
@@ -177,7 +177,7 @@ class Item(WebsiteGenerator):
|
||||
if not self.valuation_rate and self.standard_rate:
|
||||
self.valuation_rate = self.standard_rate
|
||||
|
||||
if not self.valuation_rate:
|
||||
if not self.valuation_rate and not self.is_customer_provided_item:
|
||||
frappe.throw(_("Valuation Rate is mandatory if Opening Stock entered"))
|
||||
|
||||
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
||||
|
||||
@@ -196,7 +196,7 @@ def get_item_warehouse_map(filters, sle):
|
||||
else:
|
||||
qty_diff = flt(d.actual_qty)
|
||||
|
||||
value_diff = flt(d.stock_value) - flt(qty_dict.bal_val)
|
||||
value_diff = flt(d.stock_value_difference)
|
||||
|
||||
if d.posting_date < from_date:
|
||||
qty_dict.opening_qty += qty_diff
|
||||
|
||||
@@ -115,10 +115,17 @@ def get_serial_nos_data_after_transactions(args):
|
||||
order by posting_date, posting_time asc """, args, as_dict=1)
|
||||
|
||||
for d in data:
|
||||
if d.actual_qty > 0:
|
||||
serial_nos.extend(get_serial_nos_data(d.serial_no))
|
||||
else:
|
||||
serial_nos = list(set(serial_nos) - set(get_serial_nos_data(d.serial_no)))
|
||||
for sn in get_serial_nos_data(d.serial_no):
|
||||
if d.actual_qty > 0:
|
||||
if sn not in serial_nos:
|
||||
serial_nos.append(sn)
|
||||
else:
|
||||
serial_nos.remove(sn)
|
||||
elif d.actual_qty < 0:
|
||||
if sn in serial_nos:
|
||||
serial_nos.remove(sn)
|
||||
else:
|
||||
serial_nos.append(sn)
|
||||
|
||||
return '\n'.join(serial_nos)
|
||||
|
||||
|
||||
@@ -39,8 +39,8 @@ frappe.ui.form.on("Issue", {
|
||||
|
||||
refresh: function (frm) {
|
||||
|
||||
if (frm.doc.status !== "Closed" && frm.doc.agreement_fulfilled === "Ongoing") {
|
||||
if (frm.doc.service_level_agreement) {
|
||||
if (frm.doc.status !== "Closed") {
|
||||
if (frm.doc.service_level_agreement && frm.doc.agreement_fulfilled === "Ongoing") {
|
||||
set_time_to_resolve_and_response(frm);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user