Merge branch 'develop' of https://github.com/frappe/erpnext into develop
@ -332,6 +332,7 @@
|
|||||||
"label": "Reference"
|
"label": "Reference"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"depends_on": "eval:doc.docstatus==0",
|
||||||
"fieldname": "get_outstanding_invoice",
|
"fieldname": "get_outstanding_invoice",
|
||||||
"fieldtype": "Button",
|
"fieldtype": "Button",
|
||||||
"label": "Get Outstanding Invoice"
|
"label": "Get Outstanding Invoice"
|
||||||
@ -575,7 +576,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"modified": "2019-11-06 12:59:43.151721",
|
"modified": "2019-12-08 13:02:30.016610",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Payment Entry",
|
"name": "Payment Entry",
|
||||||
|
@ -830,7 +830,11 @@ class PurchaseInvoice(BuyingController):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def make_gle_for_rounding_adjustment(self, gl_entries):
|
def make_gle_for_rounding_adjustment(self, gl_entries):
|
||||||
if self.rounding_adjustment:
|
# if rounding adjustment in small and conversion rate is also small then
|
||||||
|
# base_rounding_adjustment may become zero due to small precision
|
||||||
|
# eg: rounding_adjustment = 0.01 and exchange rate = 0.05 and precision of base_rounding_adjustment is 2
|
||||||
|
# then base_rounding_adjustment becomes zero and error is thrown in GL Entry
|
||||||
|
if self.rounding_adjustment and self.base_rounding_adjustment:
|
||||||
round_off_account, round_off_cost_center = \
|
round_off_account, round_off_cost_center = \
|
||||||
get_round_off_account_and_cost_center(self.company)
|
get_round_off_account_and_cost_center(self.company)
|
||||||
|
|
||||||
|
@ -953,7 +953,7 @@ class SalesInvoice(SellingController):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def make_gle_for_rounding_adjustment(self, gl_entries):
|
def make_gle_for_rounding_adjustment(self, gl_entries):
|
||||||
if flt(self.rounding_adjustment, self.precision("rounding_adjustment")):
|
if flt(self.rounding_adjustment, self.precision("rounding_adjustment")) and self.base_rounding_adjustment:
|
||||||
round_off_account, round_off_cost_center = \
|
round_off_account, round_off_cost_center = \
|
||||||
get_round_off_account_and_cost_center(self.company)
|
get_round_off_account_and_cost_center(self.company)
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"actions": [],
|
||||||
"autoname": "hash",
|
"autoname": "hash",
|
||||||
"creation": "2013-05-24 19:29:06",
|
"creation": "2013-05-24 19:29:06",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
@ -43,6 +44,7 @@
|
|||||||
"base_amount",
|
"base_amount",
|
||||||
"pricing_rules",
|
"pricing_rules",
|
||||||
"is_free_item",
|
"is_free_item",
|
||||||
|
"is_fixed_asset",
|
||||||
"section_break_29",
|
"section_break_29",
|
||||||
"net_rate",
|
"net_rate",
|
||||||
"net_amount",
|
"net_amount",
|
||||||
@ -708,11 +710,20 @@
|
|||||||
"fieldname": "against_blanket_order",
|
"fieldname": "against_blanket_order",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Against Blanket Order"
|
"label": "Against Blanket Order"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fetch_from": "item_code.is_fixed_asset",
|
||||||
|
"fieldname": "is_fixed_asset",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Is Fixed Asset",
|
||||||
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"modified": "2019-11-19 14:10:52.865006",
|
"links": [],
|
||||||
|
"modified": "2019-12-06 13:17:12.142799",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Buying",
|
"module": "Buying",
|
||||||
"name": "Purchase Order Item",
|
"name": "Purchase Order Item",
|
||||||
|
@ -3,18 +3,18 @@
|
|||||||
"app": "ERPNext",
|
"app": "ERPNext",
|
||||||
"creation": "2019-11-15 14:45:32.626641",
|
"creation": "2019-11-15 14:45:32.626641",
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "Setup Wizard Slide",
|
"doctype": "Onboarding Slide",
|
||||||
"domains": [],
|
"domains": [],
|
||||||
"help_links": [
|
"help_links": [
|
||||||
{
|
{
|
||||||
"label": "Supplier",
|
"label": "Learn More",
|
||||||
"video_id": "zsrrVDk6VBs"
|
"video_id": "zsrrVDk6VBs"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
"image_src": "/assets/erpnext/images/illustrations/supplier.png",
|
"image_src": "/assets/erpnext/images/illustrations/supplier-onboard.png",
|
||||||
"max_count": 3,
|
"max_count": 3,
|
||||||
"modified": "2019-11-26 18:26:25.498325",
|
"modified": "2019-12-03 22:53:50.552445",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"name": "Add A Few Suppliers",
|
"name": "Add A Few Suppliers",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
@ -44,6 +44,5 @@
|
|||||||
],
|
],
|
||||||
"slide_order": 50,
|
"slide_order": 50,
|
||||||
"slide_title": "Add A Few Suppliers",
|
"slide_title": "Add A Few Suppliers",
|
||||||
"slide_type": "Create",
|
"slide_type": "Create"
|
||||||
"submit_method": ""
|
|
||||||
}
|
}
|
@ -5,9 +5,10 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.utils import date_diff, add_days, getdate
|
from frappe.utils import date_diff, add_days, getdate, cint
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from erpnext.hr.utils import validate_dates, validate_overlap, get_leave_period, get_holidays_for_employee
|
from erpnext.hr.utils import validate_dates, validate_overlap, get_leave_period, \
|
||||||
|
get_holidays_for_employee, create_additional_leave_ledger_entry
|
||||||
|
|
||||||
class CompensatoryLeaveRequest(Document):
|
class CompensatoryLeaveRequest(Document):
|
||||||
|
|
||||||
@ -25,16 +26,14 @@ class CompensatoryLeaveRequest(Document):
|
|||||||
frappe.throw(_("Leave Type is madatory"))
|
frappe.throw(_("Leave Type is madatory"))
|
||||||
|
|
||||||
def validate_attendance(self):
|
def validate_attendance(self):
|
||||||
query = """select attendance_date, status
|
attendance = frappe.get_all('Attendance',
|
||||||
from `tabAttendance` where
|
filters={
|
||||||
attendance_date between %(work_from_date)s and %(work_end_date)s
|
'attendance_date': ['between', (self.work_from_date, self.work_end_date)],
|
||||||
and docstatus=1 and status = 'Present' and employee=%(employee)s"""
|
'status': 'Present',
|
||||||
|
'docstatus': 1,
|
||||||
|
'employee': self.employee
|
||||||
|
}, fields=['attendance_date', 'status'])
|
||||||
|
|
||||||
attendance = frappe.db.sql(query, {
|
|
||||||
"work_from_date": self.work_from_date,
|
|
||||||
"work_end_date": self.work_end_date,
|
|
||||||
"employee": self.employee
|
|
||||||
}, as_dict=True)
|
|
||||||
if len(attendance) < date_diff(self.work_end_date, self.work_from_date) + 1:
|
if len(attendance) < date_diff(self.work_end_date, self.work_from_date) + 1:
|
||||||
frappe.throw(_("You are not present all day(s) between compensatory leave request days"))
|
frappe.throw(_("You are not present all day(s) between compensatory leave request days"))
|
||||||
|
|
||||||
@ -50,13 +49,19 @@ class CompensatoryLeaveRequest(Document):
|
|||||||
date_difference -= 0.5
|
date_difference -= 0.5
|
||||||
leave_period = get_leave_period(self.work_from_date, self.work_end_date, company)
|
leave_period = get_leave_period(self.work_from_date, self.work_end_date, company)
|
||||||
if leave_period:
|
if leave_period:
|
||||||
leave_allocation = self.exists_allocation_for_period(leave_period)
|
leave_allocation = self.get_existing_allocation_for_period(leave_period)
|
||||||
if leave_allocation:
|
if leave_allocation:
|
||||||
leave_allocation.new_leaves_allocated += date_difference
|
leave_allocation.new_leaves_allocated += date_difference
|
||||||
leave_allocation.submit()
|
leave_allocation.validate()
|
||||||
|
leave_allocation.db_set("new_leaves_allocated", leave_allocation.total_leaves_allocated)
|
||||||
|
leave_allocation.db_set("total_leaves_allocated", leave_allocation.total_leaves_allocated)
|
||||||
|
|
||||||
|
# generate additional ledger entry for the new compensatory leaves off
|
||||||
|
create_additional_leave_ledger_entry(leave_allocation, date_difference, add_days(self.work_end_date, 1))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
leave_allocation = self.create_leave_allocation(leave_period, date_difference)
|
leave_allocation = self.create_leave_allocation(leave_period, date_difference)
|
||||||
self.db_set("leave_allocation", leave_allocation.name)
|
self.leave_allocation=leave_allocation.name
|
||||||
else:
|
else:
|
||||||
frappe.throw(_("There is no leave period in between {0} and {1}").format(self.work_from_date, self.work_end_date))
|
frappe.throw(_("There is no leave period in between {0} and {1}").format(self.work_from_date, self.work_end_date))
|
||||||
|
|
||||||
@ -68,11 +73,16 @@ class CompensatoryLeaveRequest(Document):
|
|||||||
leave_allocation = frappe.get_doc("Leave Allocation", self.leave_allocation)
|
leave_allocation = frappe.get_doc("Leave Allocation", self.leave_allocation)
|
||||||
if leave_allocation:
|
if leave_allocation:
|
||||||
leave_allocation.new_leaves_allocated -= date_difference
|
leave_allocation.new_leaves_allocated -= date_difference
|
||||||
if leave_allocation.total_leaves_allocated - date_difference <= 0:
|
if leave_allocation.new_leaves_allocated - date_difference <= 0:
|
||||||
leave_allocation.total_leaves_allocated = 0
|
leave_allocation.new_leaves_allocated = 0
|
||||||
leave_allocation.submit()
|
leave_allocation.validate()
|
||||||
|
leave_allocation.db_set("new_leaves_allocated", leave_allocation.total_leaves_allocated)
|
||||||
|
leave_allocation.db_set("total_leaves_allocated", leave_allocation.total_leaves_allocated)
|
||||||
|
|
||||||
def exists_allocation_for_period(self, leave_period):
|
# create reverse entry on cancelation
|
||||||
|
create_additional_leave_ledger_entry(leave_allocation, date_difference * -1, add_days(self.work_end_date, 1))
|
||||||
|
|
||||||
|
def get_existing_allocation_for_period(self, leave_period):
|
||||||
leave_allocation = frappe.db.sql("""
|
leave_allocation = frappe.db.sql("""
|
||||||
select name
|
select name
|
||||||
from `tabLeave Allocation`
|
from `tabLeave Allocation`
|
||||||
@ -95,17 +105,18 @@ class CompensatoryLeaveRequest(Document):
|
|||||||
|
|
||||||
def create_leave_allocation(self, leave_period, date_difference):
|
def create_leave_allocation(self, leave_period, date_difference):
|
||||||
is_carry_forward = frappe.db.get_value("Leave Type", self.leave_type, "is_carry_forward")
|
is_carry_forward = frappe.db.get_value("Leave Type", self.leave_type, "is_carry_forward")
|
||||||
allocation = frappe.new_doc("Leave Allocation")
|
allocation = frappe.get_doc(dict(
|
||||||
allocation.employee = self.employee
|
doctype="Leave Allocation",
|
||||||
allocation.employee_name = self.employee_name
|
employee=self.employee,
|
||||||
allocation.leave_type = self.leave_type
|
employee_name=self.employee_name,
|
||||||
allocation.from_date = add_days(self.work_end_date, 1)
|
leave_type=self.leave_type,
|
||||||
allocation.to_date = leave_period[0].to_date
|
from_date=add_days(self.work_end_date, 1),
|
||||||
allocation.new_leaves_allocated = date_difference
|
to_date=leave_period[0].to_date,
|
||||||
allocation.total_leaves_allocated = date_difference
|
carry_forward=cint(is_carry_forward),
|
||||||
allocation.description = self.reason
|
new_leaves_allocated=date_difference,
|
||||||
if is_carry_forward == 1:
|
total_leaves_allocated=date_difference,
|
||||||
allocation.carry_forward = True
|
description=self.reason
|
||||||
allocation.save(ignore_permissions = True)
|
))
|
||||||
|
allocation.insert(ignore_permissions=True)
|
||||||
allocation.submit()
|
allocation.submit()
|
||||||
return allocation
|
return allocation
|
@ -5,37 +5,128 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
import unittest
|
import unittest
|
||||||
|
from frappe.utils import today, add_months, add_days
|
||||||
|
from erpnext.hr.doctype.attendance_request.test_attendance_request import get_employee
|
||||||
|
from erpnext.hr.doctype.leave_period.test_leave_period import create_leave_period
|
||||||
|
from erpnext.hr.doctype.leave_application.leave_application import get_leave_balance_on
|
||||||
|
|
||||||
# class TestCompensatoryLeaveRequest(unittest.TestCase):
|
class TestCompensatoryLeaveRequest(unittest.TestCase):
|
||||||
# def get_compensatory_leave_request(self):
|
def setUp(self):
|
||||||
# return frappe.get_doc('Compensatory Leave Request', dict(
|
frappe.db.sql(''' delete from `tabCompensatory Leave Request`''')
|
||||||
# employee = employee,
|
frappe.db.sql(''' delete from `tabLeave Ledger Entry`''')
|
||||||
# work_from_date = today,
|
frappe.db.sql(''' delete from `tabLeave Allocation`''')
|
||||||
# work_to_date = today,
|
frappe.db.sql(''' delete from `tabAttendance` where attendance_date in {0} '''.format((today(), add_days(today(), -1)))) #nosec
|
||||||
# reason = 'test'
|
create_leave_period(add_months(today(), -3), add_months(today(), 3), "_Test Company")
|
||||||
# )).insert()
|
create_holiday_list()
|
||||||
#
|
|
||||||
# def test_creation_of_leave_allocation(self):
|
employee = get_employee()
|
||||||
# employee = get_employee()
|
employee.holiday_list = "_Test Compensatory Leave"
|
||||||
# today = get_today()
|
employee.save()
|
||||||
#
|
|
||||||
# compensatory_leave_request = self.get_compensatory_leave_request(today)
|
def test_leave_balance_on_submit(self):
|
||||||
#
|
''' check creation of leave allocation on submission of compensatory leave request '''
|
||||||
# before = get_leave_balance(employee, compensatory_leave_request.leave_type)
|
employee = get_employee()
|
||||||
#
|
mark_attendance(employee)
|
||||||
# compensatory_leave_request.submit()
|
compensatory_leave_request = get_compensatory_leave_request(employee.name)
|
||||||
#
|
|
||||||
# self.assertEqual(get_leave_balance(employee, compensatory_leave_request.leave_type), before + 1)
|
before = get_leave_balance_on(employee.name, compensatory_leave_request.leave_type, today())
|
||||||
#
|
compensatory_leave_request.submit()
|
||||||
# def test_max_compensatory_leave(self):
|
|
||||||
# employee = get_employee()
|
self.assertEqual(get_leave_balance_on(employee.name, compensatory_leave_request.leave_type, add_days(today(), 1)), before + 1)
|
||||||
# today = get_today()
|
|
||||||
#
|
def test_leave_allocation_update_on_submit(self):
|
||||||
# compensatory_leave_request = self.get_compensatory_leave_request()
|
employee = get_employee()
|
||||||
#
|
mark_attendance(employee, date=add_days(today(), -1))
|
||||||
# frappe.db.set_value('Leave Type', compensatory_leave_request.leave_type, 'max_leaves_allowed', 0)
|
compensatory_leave_request = get_compensatory_leave_request(employee.name, leave_date=add_days(today(), -1))
|
||||||
#
|
compensatory_leave_request.submit()
|
||||||
# self.assertRaises(MaxLeavesLimitCrossed, compensatory_leave_request.submit)
|
|
||||||
#
|
# leave allocation creation on submit
|
||||||
# frappe.db.set_value('Leave Type', compensatory_leave_request.leave_type, 'max_leaves_allowed', 10)
|
leaves_allocated = frappe.db.get_value('Leave Allocation', {
|
||||||
#
|
'name': compensatory_leave_request.leave_allocation
|
||||||
|
}, ['total_leaves_allocated'])
|
||||||
|
self.assertEqual(leaves_allocated, 1)
|
||||||
|
|
||||||
|
mark_attendance(employee)
|
||||||
|
compensatory_leave_request = get_compensatory_leave_request(employee.name)
|
||||||
|
compensatory_leave_request.submit()
|
||||||
|
|
||||||
|
# leave allocation updates on submission of second compensatory leave request
|
||||||
|
leaves_allocated = frappe.db.get_value('Leave Allocation', {
|
||||||
|
'name': compensatory_leave_request.leave_allocation
|
||||||
|
}, ['total_leaves_allocated'])
|
||||||
|
self.assertEqual(leaves_allocated, 2)
|
||||||
|
|
||||||
|
def test_creation_of_leave_ledger_entry_on_submit(self):
|
||||||
|
''' check creation of leave ledger entry on submission of leave request '''
|
||||||
|
employee = get_employee()
|
||||||
|
mark_attendance(employee)
|
||||||
|
compensatory_leave_request = get_compensatory_leave_request(employee.name)
|
||||||
|
compensatory_leave_request.submit()
|
||||||
|
|
||||||
|
filters = dict(transaction_name=compensatory_leave_request.leave_allocation)
|
||||||
|
leave_ledger_entry = frappe.get_all('Leave Ledger Entry', fields='*', filters=filters)
|
||||||
|
|
||||||
|
self.assertEquals(len(leave_ledger_entry), 1)
|
||||||
|
self.assertEquals(leave_ledger_entry[0].employee, compensatory_leave_request.employee)
|
||||||
|
self.assertEquals(leave_ledger_entry[0].leave_type, compensatory_leave_request.leave_type)
|
||||||
|
self.assertEquals(leave_ledger_entry[0].leaves, 1)
|
||||||
|
|
||||||
|
# check reverse leave ledger entry on cancellation
|
||||||
|
compensatory_leave_request.cancel()
|
||||||
|
leave_ledger_entry = frappe.get_all('Leave Ledger Entry', fields='*', filters=filters, order_by = 'creation desc')
|
||||||
|
|
||||||
|
self.assertEquals(len(leave_ledger_entry), 2)
|
||||||
|
self.assertEquals(leave_ledger_entry[0].employee, compensatory_leave_request.employee)
|
||||||
|
self.assertEquals(leave_ledger_entry[0].leave_type, compensatory_leave_request.leave_type)
|
||||||
|
self.assertEquals(leave_ledger_entry[0].leaves, -1)
|
||||||
|
|
||||||
|
def get_compensatory_leave_request(employee, leave_date=today()):
|
||||||
|
prev_comp_leave_req = frappe.db.get_value('Compensatory Leave Request',
|
||||||
|
dict(leave_type='Compensatory Off',
|
||||||
|
work_from_date=leave_date,
|
||||||
|
work_end_date=leave_date,
|
||||||
|
employee=employee), 'name')
|
||||||
|
if prev_comp_leave_req:
|
||||||
|
return frappe.get_doc('Compensatory Leave Request', prev_comp_leave_req)
|
||||||
|
|
||||||
|
return frappe.get_doc(dict(
|
||||||
|
doctype='Compensatory Leave Request',
|
||||||
|
employee=employee,
|
||||||
|
leave_type='Compensatory Off',
|
||||||
|
work_from_date=leave_date,
|
||||||
|
work_end_date=leave_date,
|
||||||
|
reason='test'
|
||||||
|
)).insert()
|
||||||
|
|
||||||
|
def mark_attendance(employee, date=today(), status='Present'):
|
||||||
|
if not frappe.db.exists(dict(doctype='Attendance', employee=employee.name, attendance_date=date, status='Present')):
|
||||||
|
attendance = frappe.get_doc({
|
||||||
|
"doctype": "Attendance",
|
||||||
|
"employee": employee.name,
|
||||||
|
"attendance_date": date,
|
||||||
|
"status": status
|
||||||
|
})
|
||||||
|
attendance.save()
|
||||||
|
attendance.submit()
|
||||||
|
|
||||||
|
def create_holiday_list():
|
||||||
|
if frappe.db.exists("Holiday List", "_Test Compensatory Leave"):
|
||||||
|
return
|
||||||
|
|
||||||
|
holiday_list = frappe.get_doc({
|
||||||
|
"doctype": "Holiday List",
|
||||||
|
"from_date": add_months(today(), -3),
|
||||||
|
"to_date": add_months(today(), 3),
|
||||||
|
"holidays": [
|
||||||
|
{
|
||||||
|
"description": "Test Holiday",
|
||||||
|
"holiday_date": today()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Test Holiday 1",
|
||||||
|
"holiday_date": add_days(today(), -1)
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"holiday_list_name": "_Test Compensatory Leave"
|
||||||
|
})
|
||||||
|
holiday_list.save()
|
@ -69,10 +69,14 @@ class LeaveAllocation(Document):
|
|||||||
|
|
||||||
def validate_allocation_overlap(self):
|
def validate_allocation_overlap(self):
|
||||||
leave_allocation = frappe.db.sql("""
|
leave_allocation = frappe.db.sql("""
|
||||||
select name from `tabLeave Allocation`
|
SELECT
|
||||||
where employee=%s and leave_type=%s and docstatus=1
|
name
|
||||||
and to_date >= %s and from_date <= %s""",
|
FROM `tabLeave Allocation`
|
||||||
(self.employee, self.leave_type, self.from_date, self.to_date))
|
WHERE
|
||||||
|
employee=%s AND leave_type=%s
|
||||||
|
AND name <> %s AND docstatus=1
|
||||||
|
AND to_date >= %s AND from_date <= %s""",
|
||||||
|
(self.employee, self.leave_type, self.name, self.from_date, self.to_date))
|
||||||
|
|
||||||
if leave_allocation:
|
if leave_allocation:
|
||||||
frappe.msgprint(_("{0} already allocated for Employee {1} for period {2} to {3}")
|
frappe.msgprint(_("{0} already allocated for Employee {1} for period {2} to {3}")
|
||||||
|
@ -549,10 +549,10 @@ def get_leaves_for_period(employee, leave_type, from_date, to_date):
|
|||||||
leave_days += leave_entry.leaves
|
leave_days += leave_entry.leaves
|
||||||
|
|
||||||
elif inclusive_period and leave_entry.transaction_type == 'Leave Allocation' \
|
elif inclusive_period and leave_entry.transaction_type == 'Leave Allocation' \
|
||||||
and not skip_expiry_leaves(leave_entry, to_date):
|
and leave_entry.is_expired and not skip_expiry_leaves(leave_entry, to_date):
|
||||||
leave_days += leave_entry.leaves
|
leave_days += leave_entry.leaves
|
||||||
|
|
||||||
else:
|
elif leave_entry.transaction_type == 'Leave Application':
|
||||||
if leave_entry.from_date < getdate(from_date):
|
if leave_entry.from_date < getdate(from_date):
|
||||||
leave_entry.from_date = from_date
|
leave_entry.from_date = from_date
|
||||||
if leave_entry.to_date > getdate(to_date):
|
if leave_entry.to_date > getdate(to_date):
|
||||||
@ -579,14 +579,15 @@ def skip_expiry_leaves(leave_entry, date):
|
|||||||
def get_leave_entries(employee, leave_type, from_date, to_date):
|
def get_leave_entries(employee, leave_type, from_date, to_date):
|
||||||
''' Returns leave entries between from_date and to_date '''
|
''' Returns leave entries between from_date and to_date '''
|
||||||
return frappe.db.sql("""
|
return frappe.db.sql("""
|
||||||
select employee, leave_type, from_date, to_date, leaves, transaction_type, is_carry_forward, transaction_name
|
SELECT
|
||||||
from `tabLeave Ledger Entry`
|
employee, leave_type, from_date, to_date, leaves, transaction_name, transaction_type,
|
||||||
where employee=%(employee)s and leave_type=%(leave_type)s
|
is_carry_forward, is_expired
|
||||||
and docstatus=1
|
FROM `tabLeave Ledger Entry`
|
||||||
and leaves<0
|
WHERE employee=%(employee)s AND leave_type=%(leave_type)s
|
||||||
and (from_date between %(from_date)s and %(to_date)s
|
AND docstatus=1 AND leaves<0
|
||||||
or to_date between %(from_date)s and %(to_date)s
|
AND (from_date between %(from_date)s AND %(to_date)s
|
||||||
or (from_date < %(from_date)s and to_date > %(to_date)s))
|
OR to_date between %(from_date)s AND %(to_date)s
|
||||||
|
OR (from_date < %(from_date)s AND to_date > %(to_date)s))
|
||||||
""", {
|
""", {
|
||||||
"from_date": from_date,
|
"from_date": from_date,
|
||||||
"to_date": to_date,
|
"to_date": to_date,
|
||||||
|
@ -43,10 +43,18 @@ class TestLeavePeriod(unittest.TestCase):
|
|||||||
leave_period.grant_leave_allocation(employee=employee_doc_name)
|
leave_period.grant_leave_allocation(employee=employee_doc_name)
|
||||||
self.assertEqual(get_leave_balance_on(employee_doc_name, leave_type, today()), 20)
|
self.assertEqual(get_leave_balance_on(employee_doc_name, leave_type, today()), 20)
|
||||||
|
|
||||||
def create_leave_period(from_date, to_date):
|
def create_leave_period(from_date, to_date, company=None):
|
||||||
|
leave_period = frappe.db.get_value('Leave Period',
|
||||||
|
dict(company=company or erpnext.get_default_company(),
|
||||||
|
from_date=from_date,
|
||||||
|
to_date=to_date,
|
||||||
|
is_active=1), 'name')
|
||||||
|
if leave_period:
|
||||||
|
return frappe.get_doc("Leave Period", leave_period)
|
||||||
|
|
||||||
leave_period = frappe.get_doc({
|
leave_period = frappe.get_doc({
|
||||||
"doctype": "Leave Period",
|
"doctype": "Leave Period",
|
||||||
"company": erpnext.get_default_company(),
|
"company": company or erpnext.get_default_company(),
|
||||||
"from_date": from_date,
|
"from_date": from_date,
|
||||||
"to_date": to_date,
|
"to_date": to_date,
|
||||||
"is_active": 1
|
"is_active": 1
|
||||||
|
@ -321,11 +321,11 @@ def allocate_earned_leaves():
|
|||||||
if new_allocation == allocation.total_leaves_allocated:
|
if new_allocation == allocation.total_leaves_allocated:
|
||||||
continue
|
continue
|
||||||
allocation.db_set("total_leaves_allocated", new_allocation, update_modified=False)
|
allocation.db_set("total_leaves_allocated", new_allocation, update_modified=False)
|
||||||
create_earned_leave_ledger_entry(allocation, earned_leaves, today)
|
create_additional_leave_ledger_entry(allocation, earned_leaves, today)
|
||||||
|
|
||||||
def create_earned_leave_ledger_entry(allocation, earned_leaves, date):
|
def create_additional_leave_ledger_entry(allocation, leaves, date):
|
||||||
''' Create leave ledger entry based on the earned leave frequency '''
|
''' Create leave ledger entry for leave types '''
|
||||||
allocation.new_leaves_allocated = earned_leaves
|
allocation.new_leaves_allocated = leaves
|
||||||
allocation.from_date = date
|
allocation.from_date = date
|
||||||
allocation.unused_leaves = 0
|
allocation.unused_leaves = 0
|
||||||
allocation.create_leave_ledger_entry()
|
allocation.create_leave_ledger_entry()
|
||||||
@ -389,6 +389,7 @@ def get_sal_slip_total_benefit_given(employee, payroll_period, component=False):
|
|||||||
|
|
||||||
def get_holidays_for_employee(employee, start_date, end_date):
|
def get_holidays_for_employee(employee, start_date, end_date):
|
||||||
holiday_list = get_holiday_list_for_employee(employee)
|
holiday_list = get_holiday_list_for_employee(employee)
|
||||||
|
|
||||||
holidays = frappe.db.sql_list('''select holiday_date from `tabHoliday`
|
holidays = frappe.db.sql_list('''select holiday_date from `tabHoliday`
|
||||||
where
|
where
|
||||||
parent=%(holiday_list)s
|
parent=%(holiday_list)s
|
||||||
|
@ -606,6 +606,7 @@ def get_bom_items_as_dict(bom, company, qty=1, fetch_exploded=1, fetch_scrap_ite
|
|||||||
item.image,
|
item.image,
|
||||||
bom.project,
|
bom.project,
|
||||||
item.stock_uom,
|
item.stock_uom,
|
||||||
|
item.item_group,
|
||||||
item.allow_alternative_item,
|
item.allow_alternative_item,
|
||||||
item_default.default_warehouse,
|
item_default.default_warehouse,
|
||||||
item_default.expense_account as expense_account,
|
item_default.expense_account as expense_account,
|
||||||
|
@ -615,6 +615,9 @@ def get_items_for_material_requests(doc, ignore_existing_ordered_qty=None):
|
|||||||
|
|
||||||
doc['mr_items'] = []
|
doc['mr_items'] = []
|
||||||
po_items = doc.get('po_items') if doc.get('po_items') else doc.get('items')
|
po_items = doc.get('po_items') if doc.get('po_items') else doc.get('items')
|
||||||
|
if not po_items:
|
||||||
|
frappe.throw(_("Items are required to pull the raw materials which is associated with it."))
|
||||||
|
|
||||||
company = doc.get('company')
|
company = doc.get('company')
|
||||||
warehouse = doc.get('for_warehouse')
|
warehouse = doc.get('for_warehouse')
|
||||||
|
|
||||||
|
@ -4,20 +4,16 @@ frappe.ui.form.on("Project", {
|
|||||||
setup(frm) {
|
setup(frm) {
|
||||||
frm.make_methods = {
|
frm.make_methods = {
|
||||||
'Timesheet': () => {
|
'Timesheet': () => {
|
||||||
let doctype = 'Timesheet';
|
open_form(frm, "Timesheet", "Timesheet Detail", "time_logs");
|
||||||
frappe.model.with_doctype(doctype, () => {
|
},
|
||||||
let new_doc = frappe.model.get_new_doc(doctype);
|
'Purchase Order': () => {
|
||||||
|
open_form(frm, "Purchase Order", "Purchase Order Item", "items");
|
||||||
// add a new row and set the project
|
},
|
||||||
let time_log = frappe.model.get_new_doc('Timesheet Detail');
|
'Purchase Receipt': () => {
|
||||||
time_log.project = frm.doc.name;
|
open_form(frm, "Purchase Receipt", "Purchase Receipt Item", "items");
|
||||||
time_log.parent = new_doc.name;
|
},
|
||||||
time_log.parentfield = 'time_logs';
|
'Purchase Invoice': () => {
|
||||||
time_log.parenttype = 'Timesheet';
|
open_form(frm, "Purchase Invoice", "Purchase Invoice Item", "items");
|
||||||
new_doc.time_logs = [time_log];
|
|
||||||
|
|
||||||
frappe.ui.form.make_quick_entry(doctype, null, null, new_doc);
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@ -123,3 +119,20 @@ frappe.ui.form.on("Project", {
|
|||||||
},
|
},
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function open_form(frm, doctype, child_doctype, parentfield) {
|
||||||
|
frappe.model.with_doctype(doctype, () => {
|
||||||
|
let new_doc = frappe.model.get_new_doc(doctype);
|
||||||
|
|
||||||
|
// add a new row and set the project
|
||||||
|
let new_child_doc = frappe.model.get_new_doc(child_doctype);
|
||||||
|
new_child_doc.project = frm.doc.name;
|
||||||
|
new_child_doc.parent = new_doc.name;
|
||||||
|
new_child_doc.parentfield = parentfield;
|
||||||
|
new_child_doc.parenttype = doctype;
|
||||||
|
new_doc[parentfield] = [new_child_doc];
|
||||||
|
|
||||||
|
frappe.ui.form.make_quick_entry(doctype, null, null, new_doc);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
@ -188,7 +188,8 @@ class Timesheet(Document):
|
|||||||
}, as_dict=True)
|
}, as_dict=True)
|
||||||
# check internal overlap
|
# check internal overlap
|
||||||
for time_log in self.time_logs:
|
for time_log in self.time_logs:
|
||||||
if not (time_log.from_time or time_log.to_time): continue
|
if not (time_log.from_time and time_log.to_time
|
||||||
|
and args.from_time and args.to_time): continue
|
||||||
|
|
||||||
if (fieldname != 'workstation' or args.get(fieldname) == time_log.get(fieldname)) and \
|
if (fieldname != 'workstation' or args.get(fieldname) == time_log.get(fieldname)) and \
|
||||||
args.idx != time_log.idx and ((args.from_time > time_log.from_time and args.from_time < time_log.to_time) or
|
args.idx != time_log.idx and ((args.from_time > time_log.from_time and args.from_time < time_log.to_time) or
|
||||||
|
Before Width: | Height: | Size: 3.8 KiB |
Before Width: | Height: | Size: 4.0 KiB |
BIN
erpnext/public/images/illustrations/customers-onboard.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
erpnext/public/images/illustrations/desk-onboard.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
erpnext/public/images/illustrations/letterhead-onboard.png
Normal file
After Width: | Height: | Size: 62 KiB |
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 3.1 KiB |
BIN
erpnext/public/images/illustrations/products-onboard.png
Normal file
After Width: | Height: | Size: 32 KiB |
BIN
erpnext/public/images/illustrations/supplier-onboard.png
Normal file
After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 7.7 KiB |
@ -24,7 +24,7 @@ import NotFound from './pages/NotFound.vue';
|
|||||||
function get_route_map() {
|
function get_route_map() {
|
||||||
const read_only_routes = {
|
const read_only_routes = {
|
||||||
'marketplace/home': Home,
|
'marketplace/home': Home,
|
||||||
'marketplace/search/:keyword': Search,
|
'marketplace/search/:category/:keyword': Search,
|
||||||
'marketplace/category/:category': Category,
|
'marketplace/category/:category': Category,
|
||||||
'marketplace/item/:item': Item,
|
'marketplace/item/:item': Item,
|
||||||
'marketplace/seller/:seller': Seller,
|
'marketplace/seller/:seller': Seller,
|
||||||
|
@ -3,6 +3,12 @@
|
|||||||
class="marketplace-page"
|
class="marketplace-page"
|
||||||
:data-page-name="page_name"
|
:data-page-name="page_name"
|
||||||
>
|
>
|
||||||
|
<search-input
|
||||||
|
:placeholder="search_placeholder"
|
||||||
|
:on_search="set_search_route"
|
||||||
|
v-model="search_value"
|
||||||
|
/>
|
||||||
|
|
||||||
<h5>{{ page_title }}</h5>
|
<h5>{{ page_title }}</h5>
|
||||||
|
|
||||||
<item-cards-container
|
<item-cards-container
|
||||||
@ -26,7 +32,13 @@ export default {
|
|||||||
item_id_fieldname: 'name',
|
item_id_fieldname: 'name',
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
empty_state_message: __(`No items in this category yet.`)
|
empty_state_message: __(`No items in this category yet.`),
|
||||||
|
|
||||||
|
search_value: '',
|
||||||
|
|
||||||
|
// Constants
|
||||||
|
search_placeholder: __('Search for anything ...'),
|
||||||
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -35,6 +47,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
|
this.search_value = '';
|
||||||
this.get_items();
|
this.get_items();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@ -51,7 +64,11 @@ export default {
|
|||||||
|
|
||||||
go_to_item_details_page(hub_item_name) {
|
go_to_item_details_page(hub_item_name) {
|
||||||
frappe.set_route(`marketplace/item/${hub_item_name}`);
|
frappe.set_route(`marketplace/item/${hub_item_name}`);
|
||||||
}
|
},
|
||||||
|
|
||||||
|
set_search_route() {
|
||||||
|
frappe.set_route('marketplace', 'search', this.category, this.search_value);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -98,7 +98,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
set_search_route() {
|
set_search_route() {
|
||||||
frappe.set_route('marketplace', 'search', this.search_value);
|
frappe.set_route('marketplace', 'search', 'All', this.search_value);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,8 +29,10 @@ export default {
|
|||||||
return {
|
return {
|
||||||
page_name: frappe.get_route()[1],
|
page_name: frappe.get_route()[1],
|
||||||
items: [],
|
items: [],
|
||||||
search_value: frappe.get_route()[2],
|
category: frappe.get_route()[2],
|
||||||
|
search_value: frappe.get_route()[3],
|
||||||
item_id_fieldname: 'name',
|
item_id_fieldname: 'name',
|
||||||
|
filters: {},
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
search_placeholder: __('Search for anything ...'),
|
search_placeholder: __('Search for anything ...'),
|
||||||
@ -40,7 +42,7 @@ export default {
|
|||||||
computed: {
|
computed: {
|
||||||
page_title() {
|
page_title() {
|
||||||
return this.items.length
|
return this.items.length
|
||||||
? __(`Results for "${this.search_value}"`)
|
? __(`Results for "${this.search_value}" ${this.category !== 'All'? `in category ${this.category}` : ''}`)
|
||||||
: __('No Items found.');
|
: __('No Items found.');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -49,14 +51,20 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
get_items() {
|
get_items() {
|
||||||
hub.call('get_items', { keyword: this.search_value })
|
if (this.category !== 'All') {
|
||||||
|
this.filters['hub_category'] = this.category;
|
||||||
|
}
|
||||||
|
hub.call('get_items', {
|
||||||
|
keyword: this.search_value,
|
||||||
|
filters: this.filters
|
||||||
|
})
|
||||||
.then((items) => {
|
.then((items) => {
|
||||||
this.items = items;
|
this.items = items;
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
set_route_and_get_items() {
|
set_route_and_get_items() {
|
||||||
frappe.set_route('marketplace', 'search', this.search_value);
|
frappe.set_route('marketplace', 'search', this.category, this.search_value);
|
||||||
this.get_items();
|
this.get_items();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -55,14 +55,25 @@ frappe.query_reports["GSTR-1"] = {
|
|||||||
report.page.add_inner_button(__("Download as JSON"), function () {
|
report.page.add_inner_button(__("Download as JSON"), function () {
|
||||||
var filters = report.get_values();
|
var filters = report.get_values();
|
||||||
|
|
||||||
const args = {
|
frappe.call({
|
||||||
cmd: 'erpnext.regional.report.gstr_1.gstr_1.get_json',
|
method: 'erpnext.regional.report.gstr_1.gstr_1.get_json',
|
||||||
|
args: {
|
||||||
data: report.data,
|
data: report.data,
|
||||||
report_name: report.report_name,
|
report_name: report.report_name,
|
||||||
filters: filters
|
filters: filters
|
||||||
|
},
|
||||||
|
callback: function(r) {
|
||||||
|
if (r.message) {
|
||||||
|
const args = {
|
||||||
|
cmd: 'erpnext.regional.report.gstr_1.gstr_1.download_json_file',
|
||||||
|
data: r.message.data,
|
||||||
|
report_name: r.message.report_name,
|
||||||
|
report_type: r.message.report_type
|
||||||
};
|
};
|
||||||
|
|
||||||
open_url_post(frappe.request.url, args);
|
open_url_post(frappe.request.url, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -532,16 +532,9 @@ class Gstr1Report(object):
|
|||||||
self.columns = self.invoice_columns + self.tax_columns + self.other_columns
|
self.columns = self.invoice_columns + self.tax_columns + self.other_columns
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_json():
|
def get_json(filters, report_name, data):
|
||||||
data = frappe._dict(frappe.local.form_dict)
|
filters = json.loads(filters)
|
||||||
|
report_data = json.loads(data)
|
||||||
del data["cmd"]
|
|
||||||
if "csrf_token" in data:
|
|
||||||
del data["csrf_token"]
|
|
||||||
|
|
||||||
filters = json.loads(data["filters"])
|
|
||||||
report_data = json.loads(data["data"])
|
|
||||||
report_name = data["report_name"]
|
|
||||||
gstin = get_company_gstin_number(filters["company"])
|
gstin = get_company_gstin_number(filters["company"])
|
||||||
|
|
||||||
fp = "%02d%s" % (getdate(filters["to_date"]).month, getdate(filters["to_date"]).year)
|
fp = "%02d%s" % (getdate(filters["to_date"]).month, getdate(filters["to_date"]).year)
|
||||||
@ -575,7 +568,11 @@ def get_json():
|
|||||||
out = get_export_json(res)
|
out = get_export_json(res)
|
||||||
gst_json["exp"] = out
|
gst_json["exp"] = out
|
||||||
|
|
||||||
download_json_file(report_name, filters["type_of_business"], gst_json)
|
return {
|
||||||
|
'report_name': report_name,
|
||||||
|
'report_type': filters['type_of_business'],
|
||||||
|
'data': gst_json
|
||||||
|
}
|
||||||
|
|
||||||
def get_b2b_json(res, gstin):
|
def get_b2b_json(res, gstin):
|
||||||
inv_type, out = {"Registered Regular": "R", "Deemed Export": "DE", "URD": "URD", "SEZ": "SEZ"}, []
|
inv_type, out = {"Registered Regular": "R", "Deemed Export": "DE", "URD": "URD", "SEZ": "SEZ"}, []
|
||||||
@ -722,11 +719,15 @@ def get_company_gstin_number(company):
|
|||||||
if gstin:
|
if gstin:
|
||||||
return gstin[0]["gstin"]
|
return gstin[0]["gstin"]
|
||||||
else:
|
else:
|
||||||
frappe.throw(_("Please set valid GSTIN No. in Company Address"))
|
frappe.throw(_("Please set valid GSTIN No. in Company Address for company {0}".format(
|
||||||
|
frappe.bold(company)
|
||||||
|
)))
|
||||||
|
|
||||||
def download_json_file(filename, report_type, data):
|
@frappe.whitelist()
|
||||||
|
def download_json_file():
|
||||||
''' download json content in a file '''
|
''' download json content in a file '''
|
||||||
frappe.response['filename'] = frappe.scrub("{0} {1}".format(filename, report_type)) + '.json'
|
data = frappe._dict(frappe.local.form_dict)
|
||||||
frappe.response['filecontent'] = json.dumps(data)
|
frappe.response['filename'] = frappe.scrub("{0} {1}".format(data['report_name'], data['report_type'])) + '.json'
|
||||||
|
frappe.response['filecontent'] = data['data']
|
||||||
frappe.response['content_type'] = 'application/json'
|
frappe.response['content_type'] = 'application/json'
|
||||||
frappe.response['type'] = 'download'
|
frappe.response['type'] = 'download'
|
||||||
|
@ -112,7 +112,6 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
|
|||||||
let allow_delivery = false;
|
let allow_delivery = false;
|
||||||
|
|
||||||
if (doc.docstatus==1) {
|
if (doc.docstatus==1) {
|
||||||
this.frm.add_custom_button(__('Pick List'), () => this.create_pick_list(), __('Create'));
|
|
||||||
|
|
||||||
if(this.frm.has_perm("submit")) {
|
if(this.frm.has_perm("submit")) {
|
||||||
if(doc.status === 'On Hold') {
|
if(doc.status === 'On Hold') {
|
||||||
@ -148,6 +147,8 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.frm.add_custom_button(__('Pick List'), () => this.create_pick_list(), __('Create'));
|
||||||
|
|
||||||
// delivery note
|
// delivery note
|
||||||
if(flt(doc.per_delivered, 6) < 100 && ["Sales", "Shopping Cart"].indexOf(doc.order_type)!==-1 && allow_delivery) {
|
if(flt(doc.per_delivered, 6) < 100 && ["Sales", "Shopping Cart"].indexOf(doc.order_type)!==-1 && allow_delivery) {
|
||||||
this.frm.add_custom_button(__('Delivery Note'), () => this.make_delivery_note_based_on_delivery_date(), __('Create'));
|
this.frm.add_custom_button(__('Delivery Note'), () => this.make_delivery_note_based_on_delivery_date(), __('Create'));
|
||||||
|
@ -3,18 +3,18 @@
|
|||||||
"app": "ERPNext",
|
"app": "ERPNext",
|
||||||
"creation": "2019-11-15 14:44:10.065014",
|
"creation": "2019-11-15 14:44:10.065014",
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "Setup Wizard Slide",
|
"doctype": "Onboarding Slide",
|
||||||
"domains": [],
|
"domains": [],
|
||||||
"help_links": [
|
"help_links": [
|
||||||
{
|
{
|
||||||
"label": "Customers",
|
"label": "Learn More",
|
||||||
"video_id": "zsrrVDk6VBs"
|
"video_id": "zsrrVDk6VBs"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
"image_src": "/assets/erpnext/images/illustrations/customer.png",
|
"image_src": "/assets/erpnext/images/illustrations/customers-onboard.png",
|
||||||
"max_count": 3,
|
"max_count": 3,
|
||||||
"modified": "2019-11-26 18:26:15.888794",
|
"modified": "2019-12-03 22:54:28.959549",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"name": "Add A Few Customers",
|
"name": "Add A Few Customers",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
@ -44,6 +44,5 @@
|
|||||||
],
|
],
|
||||||
"slide_order": 40,
|
"slide_order": 40,
|
||||||
"slide_title": "Add A Few Customers",
|
"slide_title": "Add A Few Customers",
|
||||||
"slide_type": "Create",
|
"slide_type": "Create"
|
||||||
"submit_method": ""
|
|
||||||
}
|
}
|
@ -283,7 +283,7 @@ class EmailDigest(Document):
|
|||||||
card.value = card.value *-1
|
card.value = card.value *-1
|
||||||
card.value = self.fmt_money(card.value,False if key in ("bank_balance", "credit_balance") else True)
|
card.value = self.fmt_money(card.value,False if key in ("bank_balance", "credit_balance") else True)
|
||||||
|
|
||||||
cache.setex(cache_key, card, 24 * 60 * 60)
|
cache.set_value(cache_key, card, expires_in_sec=24 * 60 * 60)
|
||||||
|
|
||||||
context.cards.append(card)
|
context.cards.append(card)
|
||||||
|
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"add_more_button": 0,
|
||||||
|
"app": "ERPNext",
|
||||||
|
"creation": "2019-12-04 19:21:39.995776",
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Onboarding Slide",
|
||||||
|
"domains": [],
|
||||||
|
"help_links": [],
|
||||||
|
"idx": 0,
|
||||||
|
"image_src": "/assets/erpnext/images/illustrations/desk-onboard.png",
|
||||||
|
"is_completed": 0,
|
||||||
|
"max_count": 3,
|
||||||
|
"modified": "2019-12-04 19:21:39.995776",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"name": "Welcome back to ERPNext!",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"slide_desc": "<p>Let's continue where you left from!</p>",
|
||||||
|
"slide_fields": [],
|
||||||
|
"slide_module": "Setup",
|
||||||
|
"slide_order": 0,
|
||||||
|
"slide_title": "Welcome back to ERPNext!",
|
||||||
|
"slide_type": "Continue"
|
||||||
|
}
|
@ -3,20 +3,20 @@
|
|||||||
"app": "ERPNext",
|
"app": "ERPNext",
|
||||||
"creation": "2019-11-26 17:01:26.671859",
|
"creation": "2019-11-26 17:01:26.671859",
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "Setup Wizard Slide",
|
"doctype": "Onboarding Slide",
|
||||||
"domains": [],
|
"domains": [],
|
||||||
"help_links": [],
|
"help_links": [],
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
"image_src": "/assets/erpnext/images/illustrations/onboard.png",
|
"image_src": "/assets/erpnext/images/illustrations/desk-onboard.png",
|
||||||
"max_count": 0,
|
"max_count": 0,
|
||||||
"modified": "2019-11-26 17:17:29.813299",
|
"modified": "2019-12-03 22:49:12.871260",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"name": "Welcome to ERPNext!",
|
"name": "Welcome to ERPNext!",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"slide_desc": "Setting up an ERP can be overwhelming. But don't worry, we have got your back!<br>\nLet's setup your company.\nThis wizard will help you onboard to ERPNext in a short time!",
|
"slide_desc": "Setting up an ERP can be overwhelming. But don't worry, we have got your back!\nLet's setup your company.\nThis wizard will help you onboard to ERPNext in a short time!",
|
||||||
"slide_fields": [],
|
"slide_fields": [],
|
||||||
"slide_module": "Setup",
|
"slide_module": "Setup",
|
||||||
"slide_order": 10,
|
"slide_order": 1,
|
||||||
"slide_title": "Welcome to ERPNext!",
|
"slide_title": "Welcome to ERPNext!",
|
||||||
"slide_type": "Information"
|
"slide_type": "Information"
|
||||||
}
|
}
|
@ -106,7 +106,8 @@ def get_exchange_rate(from_currency, to_currency, transaction_date=None, args=No
|
|||||||
# expire in 6 hours
|
# expire in 6 hours
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
value = response.json()["rates"][to_currency]
|
value = response.json()["rates"][to_currency]
|
||||||
cache.setex(key, value, 6 * 60 * 60)
|
|
||||||
|
cache.set_value(key, value, expires_in_sec=6 * 60 * 60)
|
||||||
return flt(value)
|
return flt(value)
|
||||||
except:
|
except:
|
||||||
frappe.log_error(title="Get Exchange Rate")
|
frappe.log_error(title="Get Exchange Rate")
|
||||||
|
@ -65,7 +65,7 @@ def update_packing_list_item(doc, packing_item_code, qty, main_item_row, descrip
|
|||||||
bin = get_bin_qty(packing_item_code, pi.warehouse)
|
bin = get_bin_qty(packing_item_code, pi.warehouse)
|
||||||
pi.actual_qty = flt(bin.get("actual_qty"))
|
pi.actual_qty = flt(bin.get("actual_qty"))
|
||||||
pi.projected_qty = flt(bin.get("projected_qty"))
|
pi.projected_qty = flt(bin.get("projected_qty"))
|
||||||
if old_packed_items_map:
|
if old_packed_items_map and old_packed_items_map.get((packing_item_code, main_item_row.item_code)):
|
||||||
pi.batch_no = old_packed_items_map.get((packing_item_code, main_item_row.item_code))[0].batch_no
|
pi.batch_no = old_packed_items_map.get((packing_item_code, main_item_row.item_code))[0].batch_no
|
||||||
pi.serial_no = old_packed_items_map.get((packing_item_code, main_item_row.item_code))[0].serial_no
|
pi.serial_no = old_packed_items_map.get((packing_item_code, main_item_row.item_code))[0].serial_no
|
||||||
pi.warehouse = old_packed_items_map.get((packing_item_code, main_item_row.item_code))[0].warehouse
|
pi.warehouse = old_packed_items_map.get((packing_item_code, main_item_row.item_code))[0].warehouse
|
||||||
|
@ -249,6 +249,8 @@ frappe.ui.form.on('Stock Entry', {
|
|||||||
}, __("Get items from"));
|
}, __("Get items from"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
frm.events.show_bom_custom_button(frm);
|
||||||
|
|
||||||
if (frm.doc.company) {
|
if (frm.doc.company) {
|
||||||
frm.trigger("toggle_display_account_head");
|
frm.trigger("toggle_display_account_head");
|
||||||
}
|
}
|
||||||
@ -262,6 +264,11 @@ frappe.ui.form.on('Stock Entry', {
|
|||||||
frm.trigger("setup_quality_inspection");
|
frm.trigger("setup_quality_inspection");
|
||||||
},
|
},
|
||||||
|
|
||||||
|
stock_entry_type: function(frm){
|
||||||
|
frm.remove_custom_button('Bill of Materials', "Get items from");
|
||||||
|
frm.events.show_bom_custom_button(frm);
|
||||||
|
},
|
||||||
|
|
||||||
purpose: function(frm) {
|
purpose: function(frm) {
|
||||||
frm.trigger('validate_purpose_consumption');
|
frm.trigger('validate_purpose_consumption');
|
||||||
frm.fields_dict.items.grid.refresh();
|
frm.fields_dict.items.grid.refresh();
|
||||||
@ -398,6 +405,85 @@ frappe.ui.form.on('Stock Entry', {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
show_bom_custom_button: function(frm){
|
||||||
|
if (frm.doc.docstatus === 0 &&
|
||||||
|
['Material Issue', 'Material Receipt', 'Material Transfer', 'Send to Subcontractor'].includes(frm.doc.purpose)) {
|
||||||
|
frm.add_custom_button(__('Bill of Materials'), function() {
|
||||||
|
frm.events.get_items_from_bom(frm);
|
||||||
|
}, __("Get items from"));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
get_items_from_bom: function(frm) {
|
||||||
|
let filters = function(){
|
||||||
|
return {filters: { docstatus:1 }};
|
||||||
|
}
|
||||||
|
|
||||||
|
let fields = [
|
||||||
|
{"fieldname":"bom", "fieldtype":"Link", "label":__("BOM"),
|
||||||
|
options:"BOM", reqd: 1, get_query: filters()},
|
||||||
|
{"fieldname":"source_warehouse", "fieldtype":"Link", "label":__("Source Warehouse"),
|
||||||
|
options:"Warehouse"},
|
||||||
|
{"fieldname":"target_warehouse", "fieldtype":"Link", "label":__("Target Warehouse"),
|
||||||
|
options:"Warehouse"},
|
||||||
|
{"fieldname":"qty", "fieldtype":"Float", "label":__("Quantity"),
|
||||||
|
reqd: 1, "default": 1},
|
||||||
|
{"fieldname":"fetch_exploded", "fieldtype":"Check",
|
||||||
|
"label":__("Fetch exploded BOM (including sub-assemblies)"), "default":1},
|
||||||
|
{"fieldname":"fetch", "label":__("Get Items from BOM"), "fieldtype":"Button"}
|
||||||
|
]
|
||||||
|
|
||||||
|
// Exclude field 'Target Warehouse' in case of Material Issue
|
||||||
|
if (frm.doc.purpose == 'Material Issue'){
|
||||||
|
fields.splice(2,1);
|
||||||
|
}
|
||||||
|
// Exclude field 'Source Warehouse' in case of Material Receipt
|
||||||
|
else if(frm.doc.purpose == 'Material Receipt'){
|
||||||
|
fields.splice(1,1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let d = new frappe.ui.Dialog({
|
||||||
|
title: __("Get Items from BOM"),
|
||||||
|
fields: fields
|
||||||
|
});
|
||||||
|
d.get_input("fetch").on("click", function() {
|
||||||
|
let values = d.get_values();
|
||||||
|
if(!values) return;
|
||||||
|
values["company"] = frm.doc.company;
|
||||||
|
if(!frm.doc.company) frappe.throw(__("Company field is required"));
|
||||||
|
frappe.call({
|
||||||
|
method: "erpnext.manufacturing.doctype.bom.bom.get_bom_items",
|
||||||
|
args: values,
|
||||||
|
callback: function(r) {
|
||||||
|
if (!r.message) {
|
||||||
|
frappe.throw(__("BOM does not contain any stock item"));
|
||||||
|
} else {
|
||||||
|
erpnext.utils.remove_empty_first_row(frm, "items");
|
||||||
|
$.each(r.message, function(i, item) {
|
||||||
|
let d = frappe.model.add_child(cur_frm.doc, "Stock Entry Detail", "items");
|
||||||
|
d.item_code = item.item_code;
|
||||||
|
d.item_name = item.item_name;
|
||||||
|
d.item_group = item.item_group;
|
||||||
|
d.s_warehouse = values.source_warehouse;
|
||||||
|
d.t_warehouse = values.target_warehouse;
|
||||||
|
d.uom = item.stock_uom;
|
||||||
|
d.stock_uom = item.stock_uom;
|
||||||
|
d.conversion_factor = item.conversion_factor ? item.conversion_factor : 1;
|
||||||
|
d.qty = item.qty;
|
||||||
|
d.expense_account = item.expense_account;
|
||||||
|
d.project = item.project;
|
||||||
|
frm.events.set_basic_rate(frm, d.doctype, d.name);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
d.hide();
|
||||||
|
refresh_field("items");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
d.show();
|
||||||
|
},
|
||||||
|
|
||||||
calculate_basic_amount: function(frm, item) {
|
calculate_basic_amount: function(frm, item) {
|
||||||
item.basic_amount = flt(flt(item.transfer_qty) * flt(item.basic_rate),
|
item.basic_amount = flt(flt(item.transfer_qty) * flt(item.basic_rate),
|
||||||
precision("basic_amount", item));
|
precision("basic_amount", item));
|
||||||
|
@ -3,13 +3,13 @@
|
|||||||
"app": "ERPNext",
|
"app": "ERPNext",
|
||||||
"creation": "2019-11-15 14:41:12.007359",
|
"creation": "2019-11-15 14:41:12.007359",
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "Setup Wizard Slide",
|
"doctype": "Onboarding Slide",
|
||||||
"domains": [],
|
"domains": [],
|
||||||
"help_links": [],
|
"help_links": [],
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
"image_src": "/assets/erpnext/images/illustrations/product.png",
|
"image_src": "/assets/erpnext/images/illustrations/products-onboard.png",
|
||||||
"max_count": 3,
|
"max_count": 3,
|
||||||
"modified": "2019-11-26 18:26:35.305755",
|
"modified": "2019-12-03 22:54:07.558632",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"name": "Add A Few Products You Buy Or Sell",
|
"name": "Add A Few Products You Buy Or Sell",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
@ -26,15 +26,9 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"align": "",
|
"align": "",
|
||||||
"fieldtype": "Column Break",
|
"fieldname": "item_price",
|
||||||
"reqd": 1
|
"fieldtype": "Currency",
|
||||||
},
|
"label": "Item Price",
|
||||||
{
|
|
||||||
"align": "",
|
|
||||||
"fieldname": "uom",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"label": "UOM",
|
|
||||||
"options": "UOM",
|
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -44,14 +38,14 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"align": "",
|
"align": "",
|
||||||
"fieldname": "item_price",
|
"fieldname": "uom",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Link",
|
||||||
"label": "Item Price",
|
"label": "UOM",
|
||||||
|
"options": "UOM",
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"slide_order": 30,
|
"slide_order": 30,
|
||||||
"slide_title": "Add A Few Products You Buy Or Sell",
|
"slide_title": "Add A Few Products You Buy Or Sell",
|
||||||
"slide_type": "Create",
|
"slide_type": "Create"
|
||||||
"submit_method": ""
|
|
||||||
}
|
}
|