Merge branch 'develop' into update-items-permission-fix

This commit is contained in:
Marica 2020-06-15 11:44:59 +05:30 committed by GitHub
commit 7dd92f15fa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 1507 additions and 491 deletions

View File

@ -14,6 +14,9 @@ frappe.treeview_settings["Account"] = {
on_change: function() {
var me = frappe.treeview_settings['Account'].treeview;
var company = me.page.fields_dict.company.get_value();
if (!company) {
frappe.throw(__("Please set a Company"));
}
frappe.call({
method: "erpnext.accounts.doctype.account.account.get_root_company",
args: {

View File

@ -319,7 +319,7 @@ class PaymentEntry(AccountsController):
invoice_payment_amount_map.setdefault(key, 0.0)
invoice_payment_amount_map[key] += reference.allocated_amount
if not invoice_paid_amount_map.get(reference.reference_name):
if not invoice_paid_amount_map.get(key):
payment_schedule = frappe.get_all('Payment Schedule', filters={'parent': reference.reference_name},
fields=['paid_amount', 'payment_amount', 'payment_term'])
for term in payment_schedule:
@ -332,12 +332,14 @@ class PaymentEntry(AccountsController):
frappe.db.sql(""" UPDATE `tabPayment Schedule` SET paid_amount = `paid_amount` - %s
WHERE parent = %s and payment_term = %s""", (amount, key[1], key[0]))
else:
outstanding = invoice_paid_amount_map.get(key)['outstanding']
outstanding = flt(invoice_paid_amount_map.get(key, {}).get('outstanding'))
if amount > outstanding:
frappe.throw(_('Cannot allocate more than {0} against payment term {1}').format(outstanding, key[0]))
frappe.db.sql(""" UPDATE `tabPayment Schedule` SET paid_amount = `paid_amount` + %s
WHERE parent = %s and payment_term = %s""", (amount, key[1], key[0]))
if amount and outstanding:
frappe.db.sql(""" UPDATE `tabPayment Schedule` SET paid_amount = `paid_amount` + %s
WHERE parent = %s and payment_term = %s""", (amount, key[1], key[0]))
def set_status(self):
if self.docstatus == 2:
@ -1091,17 +1093,20 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=
def get_reference_as_per_payment_terms(payment_schedule, dt, dn, doc, grand_total, outstanding_amount):
references = []
for payment_term in payment_schedule:
references.append({
'reference_doctype': dt,
'reference_name': dn,
'bill_no': doc.get('bill_no'),
'due_date': doc.get('due_date'),
'total_amount': grand_total,
'outstanding_amount': outstanding_amount,
'payment_term': payment_term.payment_term,
'allocated_amount': flt(payment_term.payment_amount - payment_term.paid_amount,
payment_term_outstanding = flt(payment_term.payment_amount - payment_term.paid_amount,
payment_term.precision('payment_amount'))
})
if payment_term_outstanding:
references.append({
'reference_doctype': dt,
'reference_name': dn,
'bill_no': doc.get('bill_no'),
'due_date': doc.get('due_date'),
'total_amount': grand_total,
'outstanding_amount': outstanding_amount,
'payment_term': payment_term.payment_term,
'allocated_amount': payment_term_outstanding
})
return references

View File

@ -349,9 +349,10 @@
"read_only": 1
}
],
"in_create": 1,
"is_submittable": 1,
"links": [],
"modified": "2020-05-08 10:23:02.815237",
"modified": "2020-05-29 17:38:49.392713",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Request",

View File

@ -582,14 +582,14 @@ class SalesInvoice(SellingController):
def validate_item_code(self):
for d in self.get('items'):
if not d.item_code:
if not d.item_code and self.is_opening == "No":
msgprint(_("Item Code required at Row No {0}").format(d.idx), raise_exception=True)
def validate_warehouse(self):
super(SalesInvoice, self).validate_warehouse()
for d in self.get_item_list():
if not d.warehouse and frappe.get_cached_value("Item", d.item_code, "is_stock_item"):
if not d.warehouse and d.item_code and frappe.get_cached_value("Item", d.item_code, "is_stock_item"):
frappe.throw(_("Warehouse required for stock Item {0}").format(d.item_code))
def validate_delivery_note(self):
@ -1450,11 +1450,17 @@ def get_inter_company_details(doc, doctype):
parties = frappe.db.get_all("Supplier", fields=["name"], filters={"disabled": 0, "is_internal_supplier": 1, "represents_company": doc.company})
company = frappe.get_cached_value("Customer", doc.customer, "represents_company")
if not parties:
frappe.throw(_('No Supplier found for Inter Company Transactions which represents company {0}').format(frappe.bold(doc.company)))
party = get_internal_party(parties, "Supplier", doc)
else:
parties = frappe.db.get_all("Customer", fields=["name"], filters={"disabled": 0, "is_internal_customer": 1, "represents_company": doc.company})
company = frappe.get_cached_value("Supplier", doc.supplier, "represents_company")
if not parties:
frappe.throw(_('No Customer found for Inter Company Transactions which represents company {0}').format(frappe.bold(doc.company)))
party = get_internal_party(parties, "Customer", doc)
return {

View File

@ -56,9 +56,8 @@ def get_period_list(from_fiscal_year, to_fiscal_year, period_start_date, period_
to_date = add_months(start_date, months_to_add)
start_date = to_date
if to_date == get_first_day(to_date):
# if to_date is the first day, get the last day of previous month
to_date = add_days(to_date, -1)
# Subtract one day from to_date, as it may be first day in next fiscal year or month
to_date = add_days(to_date, -1)
if to_date <= year_end_date:
# the normal case
@ -406,6 +405,7 @@ def set_gl_entries_by_account(
FROM `tabDistributed Cost Center`
WHERE cost_center IN %(cost_center)s
AND parent NOT IN %(cost_center)s
AND is_cancelled = 0
GROUP BY parent
) as DCC_allocation
WHERE company=%(company)s
@ -418,6 +418,7 @@ def set_gl_entries_by_account(
where company=%(company)s
{additional_conditions}
and posting_date <= %(to_date)s
and is_cancelled = 0
{distributed_cost_center_query}
order by account, posting_date""".format(
additional_conditions=additional_conditions,

View File

@ -265,13 +265,6 @@ def get_columns(additional_table_columns, filters):
'fieldtype': 'Currency',
'options': 'currency',
'width': 100
},
{
'fieldname': 'currency',
'label': _('Currency'),
'fieldtype': 'Currency',
'width': 80,
'hidden': 1
}
]

View File

@ -223,7 +223,7 @@ def get_columns(additional_table_columns, filters):
}
]
if filters.get('group_by') != 'Terriotory':
if filters.get('group_by') != 'Territory':
columns.extend([
{
'label': _("Territory"),
@ -304,13 +304,6 @@ def get_columns(additional_table_columns, filters):
'fieldtype': 'Currency',
'options': 'currency',
'width': 100
},
{
'fieldname': 'currency',
'label': _('Currency'),
'fieldtype': 'Currency',
'width': 80,
'hidden': 1
}
]
@ -536,6 +529,13 @@ def get_tax_accounts(item_list, columns, company_currency,
'fieldtype': 'Currency',
'options': 'currency',
'width': 100
},
{
'fieldname': 'currency',
'label': _('Currency'),
'fieldtype': 'Currency',
'width': 80,
'hidden': 1
}
]

File diff suppressed because it is too large Load Diff

View File

@ -19,7 +19,8 @@ class QualityInspectionNotSubmittedError(frappe.ValidationError): pass
class StockController(AccountsController):
def validate(self):
super(StockController, self).validate()
self.validate_inspection()
if not self.get('is_return'):
self.validate_inspection()
self.validate_serialized_batch()
self.validate_customer_provided_item()

View File

@ -3,11 +3,7 @@ from frappe import _
def get_data():
return {
'fieldname': 'prevdoc_docname',
'non_standard_fieldnames': {
'Supplier Quotation': 'opportunity',
'Quotation': 'opportunity'
},
'fieldname': 'opportunity',
'transactions': [
{
'items': ['Quotation', 'Supplier Quotation']

View File

@ -30,24 +30,32 @@
"fieldname": "text",
"fieldtype": "Small Text",
"label": "Tweet",
"mandatory_depends_on": "eval:doc.twitter ==1"
"mandatory_depends_on": "eval:doc.twitter ==1",
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "image",
"fieldtype": "Attach Image",
"label": "Image"
"label": "Image",
"show_days": 1,
"show_seconds": 1
},
{
"default": "0",
"fieldname": "twitter",
"fieldtype": "Check",
"label": "Twitter"
"label": "Twitter",
"show_days": 1,
"show_seconds": 1
},
{
"default": "0",
"fieldname": "linkedin",
"fieldtype": "Check",
"label": "LinkedIn"
"label": "LinkedIn",
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "amended_from",
@ -56,13 +64,17 @@
"no_copy": 1,
"options": "Social Media Post",
"print_hide": 1,
"read_only": 1
"read_only": 1,
"show_days": 1,
"show_seconds": 1
},
{
"depends_on": "eval:doc.twitter ==1",
"fieldname": "content",
"fieldtype": "Section Break",
"label": "Twitter"
"label": "Twitter",
"show_days": 1,
"show_seconds": 1
},
{
"allow_on_submit": 1,
@ -70,7 +82,9 @@
"fieldtype": "Select",
"label": "Post Status",
"options": "\nScheduled\nPosted\nError",
"read_only": 1
"read_only": 1,
"show_days": 1,
"show_seconds": 1
},
{
"allow_on_submit": 1,
@ -78,7 +92,9 @@
"fieldtype": "Data",
"hidden": 1,
"label": "Twitter Post Id",
"read_only": 1
"read_only": 1,
"show_days": 1,
"show_seconds": 1
},
{
"allow_on_submit": 1,
@ -86,68 +102,89 @@
"fieldtype": "Data",
"hidden": 1,
"label": "LinkedIn Post Id",
"read_only": 1
"read_only": 1,
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "campaign_name",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Campaign",
"options": "Campaign"
"options": "Campaign",
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "column_break_6",
"fieldtype": "Column Break",
"label": "Share On"
"label": "Share On",
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "column_break_14",
"fieldtype": "Column Break"
"fieldtype": "Column Break",
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "tweet_preview",
"fieldtype": "HTML"
"fieldtype": "HTML",
"show_days": 1,
"show_seconds": 1
},
{
"collapsible": 1,
"depends_on": "eval:doc.linkedin==1",
"fieldname": "linkedin_section",
"fieldtype": "Section Break",
"label": "LinkedIn"
"label": "LinkedIn",
"show_days": 1,
"show_seconds": 1
},
{
"collapsible": 1,
"fieldname": "attachments_section",
"fieldtype": "Section Break",
"label": "Attachments"
"label": "Attachments",
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "linkedin_post",
"fieldtype": "Text",
"label": "Post",
"mandatory_depends_on": "eval:doc.linkedin ==1"
"mandatory_depends_on": "eval:doc.linkedin ==1",
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "column_break_15",
"fieldtype": "Column Break"
"fieldtype": "Column Break",
"show_days": 1,
"show_seconds": 1
},
{
"allow_on_submit": 1,
"fieldname": "scheduled_time",
"fieldtype": "Datetime",
"label": "Scheduled Time",
"read_only_depends_on": "eval:doc.post_status == \"Posted\""
"read_only_depends_on": "eval:doc.post_status == \"Posted\"",
"show_days": 1,
"show_seconds": 1
}
],
"is_submittable": 1,
"links": [],
"modified": "2020-04-21 15:10:04.953713",
"modified": "2020-06-14 10:31:33.961381",
"modified_by": "Administrator",
"module": "CRM",
"name": "Social Media Post",
"owner": "Administrator",
"permissions": [
{
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
@ -157,6 +194,35 @@
"report": 1,
"role": "System Manager",
"share": 1,
"submit": 1,
"write": 1
},
{
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Sales User",
"share": 1,
"submit": 1,
"write": 1
},
{
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Sales Manager",
"share": 1,
"submit": 1,
"write": 1
}
],

View File

@ -19,11 +19,15 @@
"attendance_date",
"company",
"department",
"shift",
"attendance_request",
"amended_from",
"details_section",
"shift",
"in_time",
"out_time",
"column_break_18",
"late_entry",
"early_exit"
"early_exit",
"amended_from"
],
"fields": [
{
@ -172,13 +176,36 @@
"fieldname": "early_exit",
"fieldtype": "Check",
"label": "Early Exit"
},
{
"fieldname": "details_section",
"fieldtype": "Section Break",
"label": "Details"
},
{
"depends_on": "shift",
"fieldname": "in_time",
"fieldtype": "Datetime",
"label": "In Time",
"read_only": 1
},
{
"depends_on": "shift",
"fieldname": "out_time",
"fieldtype": "Datetime",
"label": "Out Time",
"read_only": 1
},
{
"fieldname": "column_break_18",
"fieldtype": "Column Break"
}
],
"icon": "fa fa-ok",
"idx": 1,
"is_submittable": 1,
"links": [],
"modified": "2020-04-11 11:40:14.319496",
"modified": "2020-05-29 13:51:37.177231",
"modified_by": "Administrator",
"module": "HR",
"name": "Attendance",

View File

@ -139,13 +139,13 @@ frappe.ui.form.on('Employee Advance', {
employee: function (frm) {
if (frm.doc.employee) {
return frappe.call({
method: "erpnext.hr.doctype.employee_advance.employee_advance.get_due_advance_amount",
method: "erpnext.hr.doctype.employee_advance.employee_advance.get_pending_amount",
args: {
"employee": frm.doc.employee,
"posting_date": frm.doc.posting_date
},
callback: function(r) {
frm.set_value("due_advance_amount",r.message);
frm.set_value("pending_amount",r.message);
}
});
}

View File

@ -19,7 +19,7 @@
"column_break_11",
"advance_amount",
"paid_amount",
"due_advance_amount",
"pending_amount",
"claimed_amount",
"return_amount",
"section_break_7",
@ -102,14 +102,6 @@
"options": "Company:company:default_currency",
"read_only": 1
},
{
"depends_on": "eval:cur_frm.doc.employee",
"fieldname": "due_advance_amount",
"fieldtype": "Currency",
"label": "Due Advance Amount",
"options": "Company:company:default_currency",
"read_only": 1
},
{
"fieldname": "claimed_amount",
"fieldtype": "Currency",
@ -177,11 +169,19 @@
"fieldname": "repay_unclaimed_amount_from_salary",
"fieldtype": "Check",
"label": "Repay unclaimed amount from salary"
},
{
"depends_on": "eval:cur_frm.doc.employee",
"fieldname": "pending_amount",
"fieldtype": "Currency",
"label": "Pending Amount",
"options": "Company:company:default_currency",
"read_only": 1
}
],
"is_submittable": 1,
"links": [],
"modified": "2020-03-06 15:11:33.747535",
"modified": "2020-06-12 12:42:39.833818",
"modified_by": "Administrator",
"module": "HR",
"name": "Employee Advance",

View File

@ -95,7 +95,7 @@ class EmployeeAdvance(Document):
frappe.db.set_value("Employee Advance", self.name, "status", self.status)
@frappe.whitelist()
def get_due_advance_amount(employee, posting_date):
def get_pending_amount(employee, posting_date):
employee_due_amount = frappe.get_all("Employee Advance", \
filters = {"employee":employee, "docstatus":1, "posting_date":("<=", posting_date)}, \
fields = ["advance_amount", "paid_amount"])

View File

@ -72,7 +72,7 @@ def add_log_based_on_employee_field(employee_field_value, timestamp, device_id=N
return doc
def mark_attendance_and_link_log(logs, attendance_status, attendance_date, working_hours=None, late_entry=False, early_exit=False, shift=None):
def mark_attendance_and_link_log(logs, attendance_status, attendance_date, working_hours=None, late_entry=False, early_exit=False, in_time=None, out_time=None, shift=None):
"""Creates an attendance and links the attendance to the Employee Checkin.
Note: If attendance is already present for the given date, the logs are marked as skipped and no exception is thrown.
@ -100,7 +100,9 @@ def mark_attendance_and_link_log(logs, attendance_status, attendance_date, worki
'company': employee_doc.company,
'shift': shift,
'late_entry': late_entry,
'early_exit': early_exit
'early_exit': early_exit,
'in_time': in_time,
'out_time': out_time
}
attendance = frappe.get_doc(doc_dict).insert()
attendance.submit()

View File

@ -46,6 +46,7 @@ frappe.ui.form.on("Leave Application", {
make_dashboard: function(frm) {
var leave_details;
let lwps;
if (frm.doc.employee) {
frappe.call({
method: "erpnext.hr.doctype.leave_application.leave_application.get_leave_details",
@ -61,6 +62,7 @@ frappe.ui.form.on("Leave Application", {
if (!r.exc && r.message['leave_approver']) {
frm.set_value('leave_approver', r.message['leave_approver']);
}
lwps = r.message["lwps"];
}
});
$("div").remove(".form-dashboard-section.custom");
@ -70,12 +72,17 @@ frappe.ui.form.on("Leave Application", {
})
);
frm.dashboard.show();
let allowed_leave_types = Object.keys(leave_details);
// lwps should be allowed, lwps don't have any allocation
allowed_leave_types = allowed_leave_types.concat(lwps);
frm.set_query('leave_type', function(){
return {
filters : [
['leave_type_name', 'in', Object.keys(leave_details)]
['leave_type_name', 'in', allowed_leave_types]
]
}
};
});
}
},

View File

@ -19,7 +19,6 @@ class NotAnOptionalHoliday(frappe.ValidationError): pass
from frappe.model.document import Document
class LeaveApplication(Document):
def get_feed(self):
return _("{0}: From {0} of type {1}").format(self.employee_name, self.leave_type)
@ -463,9 +462,14 @@ def get_leave_details(employee, date):
"pending_leaves": leaves_pending,
"remaining_leaves": remaining_leaves}
#is used in set query
lwps = frappe.get_list("Leave Type", filters = {"is_lwp": 1})
lwps = [lwp.name for lwp in lwps]
ret = {
'leave_allocation': leave_allocation,
'leave_approver': get_leave_approver(employee)
'leave_approver': get_leave_approver(employee),
'lwps': lwps
}
return ret

View File

@ -28,13 +28,14 @@ class ShiftType(Document):
logs = frappe.db.get_list('Employee Checkin', fields="*", filters=filters, order_by="employee,time")
for key, group in itertools.groupby(logs, key=lambda x: (x['employee'], x['shift_actual_start'])):
single_shift_logs = list(group)
attendance_status, working_hours, late_entry, early_exit = self.get_attendance(single_shift_logs)
mark_attendance_and_link_log(single_shift_logs, attendance_status, key[1].date(), working_hours, late_entry, early_exit, self.name)
attendance_status, working_hours, late_entry, early_exit, in_time, out_time = self.get_attendance(single_shift_logs)
mark_attendance_and_link_log(single_shift_logs, attendance_status, key[1].date(), working_hours, late_entry, early_exit, in_time, out_time, self.name)
for employee in self.get_assigned_employee(self.process_attendance_after, True):
self.mark_absent_for_dates_with_no_attendance(employee)
def get_attendance(self, logs):
"""Return attendance_status, working_hours for a set of logs belonging to a single shift.
"""Return attendance_status, working_hours, late_entry, early_exit, in_time, out_time
for a set of logs belonging to a single shift.
Assumtion:
1. These logs belongs to an single shift, single employee and is not in a holiday date.
2. Logs are in chronological order
@ -48,10 +49,10 @@ class ShiftType(Document):
early_exit = True
if self.working_hours_threshold_for_absent and total_working_hours < self.working_hours_threshold_for_absent:
return 'Absent', total_working_hours, late_entry, early_exit
return 'Absent', total_working_hours, late_entry, early_exit, in_time, out_time
if self.working_hours_threshold_for_half_day and total_working_hours < self.working_hours_threshold_for_half_day:
return 'Half Day', total_working_hours, late_entry, early_exit
return 'Present', total_working_hours, late_entry, early_exit
return 'Half Day', total_working_hours, late_entry, early_exit, in_time, out_time
return 'Present', total_working_hours, late_entry, early_exit, in_time, out_time
def mark_absent_for_dates_with_no_attendance(self, employee):
"""Marks Absents for the given employee on working days in this shift which have no attendance marked.

View File

@ -77,6 +77,7 @@ def create_customer(user_details):
customer = frappe.new_doc("Customer")
customer.customer_name = user_details.fullname
customer.customer_type = "Individual"
customer.flags.ignore_mandatory = True
customer.insert(ignore_permissions=True)
try:
@ -91,7 +92,11 @@ def create_customer(user_details):
"link_name": customer.name
})
contact.insert()
contact.save()
except frappe.DuplicateEntryError:
return customer.name
except Exception as e:
frappe.log_error(frappe.get_traceback(), _("Contact Creation Failed"))
pass

View File

@ -62,7 +62,10 @@ def get_member_based_on_subscription(subscription_id, email):
'subscription_id': subscription_id,
'email_id': email
}, order_by="creation desc")
return frappe.get_doc("Member", members[0]['name'])
try:
return frappe.get_doc("Member", members[0]['name'])
except:
return None
def verify_signature(data):
signature = frappe.request.headers.get('X-Razorpay-Signature')
@ -77,7 +80,7 @@ def verify_signature(data):
@frappe.whitelist(allow_guest=True)
def trigger_razorpay_subscription(*args, **kwargs):
data = frappe.request.get_data()
data = frappe.request.get_data(as_text=True)
verify_signature(data)
if isinstance(data, six.string_types):
@ -96,7 +99,10 @@ def trigger_razorpay_subscription(*args, **kwargs):
except Exception as e:
error_log = frappe.log_error(frappe.get_traceback() + '\n' + data_json , _("Membership Webhook Failed"))
notify_failure(error_log)
raise e
return False
if not member:
return False
if data.event == "subscription.activated":
member.customer_id = payment.customer_id

View File

@ -680,6 +680,8 @@ erpnext.patches.v12_0.update_appointment_reminder_scheduler_entry
erpnext.patches.v12_0.retain_permission_rules_for_video_doctype
erpnext.patches.v12_0.remove_duplicate_leave_ledger_entries #2020-05-22
erpnext.patches.v13_0.patch_to_fix_reverse_linking_in_additional_salary_encashment_and_incentive
execute:frappe.reload_doc("HR", "doctype", "Employee Advance")
erpnext.patches.v12_0.move_due_advance_amount_to_pending_amount
execute:frappe.delete_doc_if_exists("Page", "appointment-analytic")
execute:frappe.rename_doc("Desk Page", "Getting Started", "Home", force=True)
erpnext.patches.v12_0.unset_customer_supplier_based_on_type_of_item_price
@ -694,4 +696,4 @@ execute:frappe.delete_doc("Report", "Department Analytics")
execute:frappe.rename_doc("Desk Page", "Loan Management", "Loan", force=True)
erpnext.patches.v12_0.update_uom_conversion_factor
erpnext.patches.v13_0.delete_old_purchase_reports
erpnext.patches.v12_0.set_italian_import_supplier_invoice_permissions
erpnext.patches.v12_0.set_italian_import_supplier_invoice_permissions

View File

@ -0,0 +1,9 @@
# Copyright (c) 2019, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
def execute():
''' Move from due_advance_amount to pending_amount '''
frappe.db.sql(''' UPDATE `tabEmployee Advance` SET pending_amount=due_advance_amount ''')

View File

@ -7,4 +7,4 @@ def execute():
for entry in doctypes:
if frappe.db.exists('DocType', entry):
frappe.reload_doc('Healthcare', 'doctype', entry)
frappe.db.sql("update `tab{dt}` set company = '{company}' where ifnull(company, '') = ''".format(dt=entry, company=company))
frappe.db.sql("update `tab{dt}` set company = {company} where ifnull(company, '') = ''".format(dt=entry, company=frappe.db.escape(company)))

View File

@ -73,6 +73,8 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
me.frm.set_query('contact_person', erpnext.queries.contact_query);
me.frm.set_query('supplier_address', erpnext.queries.address_query);
me.frm.set_query('billing_address', erpnext.queries.company_address_query);
if(this.frm.fields_dict.supplier) {
this.frm.set_query("supplier", function() {
return{ query: "erpnext.controllers.queries.supplier_query" }});
@ -283,6 +285,11 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
"shipping_address_display", true);
},
billing_address: function() {
erpnext.utils.get_address_display(this.frm, "billing_address",
"billing_address_display", true);
},
tc_name: function() {
this.get_terms();
},

View File

@ -552,7 +552,8 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
if (show_batch_dialog)
return frappe.db.get_value("Item", item.item_code, ["has_batch_no", "has_serial_no"])
.then((r) => {
if(r.message.has_batch_no || r.message.has_serial_no) {
if (r.message &&
(r.message.has_batch_no || r.message.has_serial_no)) {
frappe.flags.hide_serial_batch_dialog = false;
}
});

View File

@ -3,16 +3,19 @@
from __future__ import unicode_literals
import frappe
import json
from frappe.model.naming import set_name_by_naming_series
from frappe import _, msgprint, throw
from frappe import _, msgprint
import frappe.defaults
from frappe.utils import flt, cint, cstr, today
from frappe.utils import flt, cint, cstr, today, get_formatted_email
from frappe.desk.reportview import build_match_conditions, get_filters_cond
from erpnext.utilities.transaction_base import TransactionBase
from erpnext.accounts.party import validate_party_accounts, get_dashboard_info, get_timeline_data # keep this
from frappe.contacts.address_and_contact import load_address_and_contact, delete_contact_and_address
from frappe.model.rename_doc import update_linked_doctypes
from frappe.model.mapper import get_mapped_doc
from frappe.utils.user import get_users_with_role
class Customer(TransactionBase):
def get_feed(self):
@ -378,10 +381,45 @@ def check_credit_limit(customer, company, ignore_outstanding_sales_order=False,
.format(customer, customer_outstanding, credit_limit))
# If not authorized person raise exception
credit_controller = frappe.db.get_value('Accounts Settings', None, 'credit_controller')
if not credit_controller or credit_controller not in frappe.get_roles():
throw(_("Please contact to the user who have Sales Master Manager {0} role")
.format(" / " + credit_controller if credit_controller else ""))
credit_controller_role = frappe.db.get_single_value('Accounts Settings', 'credit_controller')
if not credit_controller_role or credit_controller_role not in frappe.get_roles():
# form a list of emails for the credit controller users
credit_controller_users = get_users_with_role(credit_controller_role or "Sales Master Manager")
# form a list of emails and names to show to the user
credit_controller_users_list = [user for user in credit_controller_users if frappe.db.exists("Employee", {"prefered_email": user})]
credit_controller_users = [get_formatted_email(user).replace("<", "(").replace(">", ")") for user in credit_controller_users_list]
if not credit_controller_users:
frappe.throw(_("Please contact your administrator to extend the credit limits for {0}.".format(customer)))
message = """Please contact any of the following users to extend the credit limits for {0}:
<br><br><ul><li>{1}</li></ul>""".format(customer, '<li>'.join(credit_controller_users))
# if the current user does not have permissions to override credit limit,
# prompt them to send out an email to the controller users
frappe.msgprint(message,
title="Notify",
raise_exception=1,
primary_action={
'label': 'Send Email',
'server_action': 'erpnext.selling.doctype.customer.customer.send_emails',
'args': {
'customer': customer,
'customer_outstanding': customer_outstanding,
'credit_limit': credit_limit,
'credit_controller_users_list': credit_controller_users_list
}
}
)
@frappe.whitelist()
def send_emails(args):
args = json.loads(args)
subject = (_("Credit limit reached for customer {0}").format(args.get('customer')))
message = (_("Credit limit has been crossed for customer {0} ({1}/{2})")
.format(args.get('customer'), args.get('customer_outstanding'), args.get('credit_limit')))
frappe.sendmail(recipients=[args.get('credit_controller_users_list')], subject=subject, message=message)
def get_customer_outstanding(customer, company, ignore_outstanding_sales_order=False, cost_center=None):
# Outstanding based on GL Entries

View File

@ -337,21 +337,17 @@ def set_price_list_and_rate(quotation, cart_settings):
def _set_price_list(cart_settings, quotation=None):
"""Set price list based on customer or shopping cart default"""
from erpnext.accounts.party import get_default_price_list
# check if customer price list exists
party_name = quotation.get("party_name") if quotation else get_party().get("name")
selling_price_list = None
if quotation and quotation.get("party_name"):
selling_price_list = frappe.db.get_value('Customer', quotation.get("party_name"), 'default_price_list')
# else check for territory based price list
# check if default customer price list exists
if party_name:
selling_price_list = get_default_price_list(frappe.get_doc("Customer", party_name))
# check default price list in shopping cart
if not selling_price_list:
selling_price_list = cart_settings.price_list
party_name = quotation.get("party_name") if quotation else get_party().get("name")
if not selling_price_list and party_name:
selling_price_list = get_default_price_list(frappe.get_doc("Customer", party_name))
if quotation:
quotation.selling_price_list = selling_price_list

View File

@ -119,11 +119,13 @@ def get_items_with_location_and_quantity(item_doc, item_location_map):
if item_location.serial_no:
serial_nos = '\n'.join(item_location.serial_no[0: cint(stock_qty)])
auto_set_serial_no = frappe.db.get_single_value("Stock Settings", "automatically_set_serial_nos_based_on_fifo")
locations.append(frappe._dict({
'qty': qty,
'stock_qty': stock_qty,
'warehouse': item_location.warehouse,
'serial_no': serial_nos,
'serial_no': serial_nos if auto_set_serial_no else item_doc.serial_no,
'batch_no': item_location.batch_no
}))
@ -206,6 +208,7 @@ def get_available_item_locations_for_batched_item(item_code, from_warehouses, re
sle.batch_no = batch.name
and sle.`item_code`=%(item_code)s
and sle.`company` = %(company)s
and batch.disabled = 0
and IFNULL(batch.`expiry_date`, '2200-01-01') > %(today)s
{warehouse_condition}
GROUP BY
@ -471,4 +474,4 @@ def update_common_item_properties(item, location):
item.material_request = location.material_request
item.serial_no = location.serial_no
item.batch_no = location.batch_no
item.material_request_item = location.material_request_item
item.material_request_item = location.material_request_item

File diff suppressed because it is too large Load Diff

View File

@ -8,7 +8,7 @@ from frappe.utils import getdate, flt
def execute(filters=None):
if not filters: filters = {}
float_preceision = frappe.db.get_default("float_preceision")
float_precision = frappe.db.get_default("float_precision")
condition = get_condition(filters)
@ -25,7 +25,7 @@ def execute(filters=None):
data = []
for item in items:
total_outgoing = flt(consumed_item_map.get(item.name, 0)) + flt(delivered_item_map.get(item.name,0))
avg_daily_outgoing = flt(total_outgoing / diff, float_preceision)
avg_daily_outgoing = flt(total_outgoing / diff, float_precision)
reorder_level = (avg_daily_outgoing * flt(item.lead_time_days)) + flt(item.safety_stock)
data.append([item.name, item.item_name, item.item_group, item.brand, item.description,

View File

@ -180,10 +180,10 @@ def get_fifo_queue(filters, sle=None):
qty_to_pop = abs(d.actual_qty)
while qty_to_pop:
batch = fifo_queue[0] if fifo_queue else [0, None]
if 0 < batch[0] <= qty_to_pop:
if 0 < flt(batch[0]) <= qty_to_pop:
# if batch qty > 0
# not enough or exactly same qty in current batch, clear batch
qty_to_pop -= batch[0]
qty_to_pop -= flt(batch[0])
transferred_item_details[(d.voucher_no, d.name)].append(fifo_queue.pop(0))
else:
# all from current batch
@ -262,4 +262,4 @@ def get_chart_data(data, filters):
]
},
"type" : "bar"
}
}