Merge branch 'develop' into home-onboarding
This commit is contained in:
commit
56bc8a52f4
10
codecov.yml
10
codecov.yml
@ -8,6 +8,16 @@ coverage:
|
|||||||
target: auto
|
target: auto
|
||||||
threshold: 0.5%
|
threshold: 0.5%
|
||||||
|
|
||||||
|
patch:
|
||||||
|
default:
|
||||||
|
target: 85%
|
||||||
|
threshold: 0%
|
||||||
|
base: auto
|
||||||
|
branches:
|
||||||
|
- develop
|
||||||
|
if_ci_failed: ignore
|
||||||
|
only_pulls: true
|
||||||
|
|
||||||
comment:
|
comment:
|
||||||
layout: "diff, files"
|
layout: "diff, files"
|
||||||
require_changes: true
|
require_changes: true
|
||||||
|
|||||||
@ -297,8 +297,15 @@ class PurchaseInvoice(BuyingController):
|
|||||||
item.expense_account = stock_not_billed_account
|
item.expense_account = stock_not_billed_account
|
||||||
|
|
||||||
elif item.is_fixed_asset and not is_cwip_accounting_enabled(asset_category):
|
elif item.is_fixed_asset and not is_cwip_accounting_enabled(asset_category):
|
||||||
item.expense_account = get_asset_category_account('fixed_asset_account', item=item.item_code,
|
asset_category_account = get_asset_category_account('fixed_asset_account', item=item.item_code,
|
||||||
company = self.company)
|
company = self.company)
|
||||||
|
if not asset_category_account:
|
||||||
|
form_link = get_link_to_form('Asset Category', asset_category)
|
||||||
|
throw(
|
||||||
|
_("Please set Fixed Asset Account in {} against {}.").format(form_link, self.company),
|
||||||
|
title=_("Missing Account")
|
||||||
|
)
|
||||||
|
item.expense_account = asset_category_account
|
||||||
elif item.is_fixed_asset and item.pr_detail:
|
elif item.is_fixed_asset and item.pr_detail:
|
||||||
item.expense_account = asset_received_but_not_billed
|
item.expense_account = asset_received_but_not_billed
|
||||||
elif not item.expense_account and for_validate:
|
elif not item.expense_account and for_validate:
|
||||||
|
|||||||
@ -110,7 +110,7 @@ frappe.ui.form.on('Asset', {
|
|||||||
|
|
||||||
if (frm.doc.status != 'Fully Depreciated') {
|
if (frm.doc.status != 'Fully Depreciated') {
|
||||||
frm.add_custom_button(__("Adjust Asset Value"), function() {
|
frm.add_custom_button(__("Adjust Asset Value"), function() {
|
||||||
frm.trigger("create_asset_adjustment");
|
frm.trigger("create_asset_value_adjustment");
|
||||||
}, __("Manage"));
|
}, __("Manage"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -322,14 +322,14 @@ frappe.ui.form.on('Asset', {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
create_asset_adjustment: function(frm) {
|
create_asset_value_adjustment: function(frm) {
|
||||||
frappe.call({
|
frappe.call({
|
||||||
args: {
|
args: {
|
||||||
"asset": frm.doc.name,
|
"asset": frm.doc.name,
|
||||||
"asset_category": frm.doc.asset_category,
|
"asset_category": frm.doc.asset_category,
|
||||||
"company": frm.doc.company
|
"company": frm.doc.company
|
||||||
},
|
},
|
||||||
method: "erpnext.assets.doctype.asset.asset.create_asset_adjustment",
|
method: "erpnext.assets.doctype.asset.asset.create_asset_value_adjustment",
|
||||||
freeze: 1,
|
freeze: 1,
|
||||||
callback: function(r) {
|
callback: function(r) {
|
||||||
var doclist = frappe.model.sync(r.message);
|
var doclist = frappe.model.sync(r.message);
|
||||||
|
|||||||
@ -185,83 +185,84 @@ class Asset(AccountsController):
|
|||||||
if not self.available_for_use_date:
|
if not self.available_for_use_date:
|
||||||
return
|
return
|
||||||
|
|
||||||
for d in self.get('finance_books'):
|
|
||||||
self.validate_asset_finance_books(d)
|
|
||||||
|
|
||||||
start = self.clear_depreciation_schedule()
|
start = self.clear_depreciation_schedule()
|
||||||
|
|
||||||
|
for finance_book in self.get('finance_books'):
|
||||||
|
self.validate_asset_finance_books(finance_book)
|
||||||
|
|
||||||
# value_after_depreciation - current Asset value
|
# value_after_depreciation - current Asset value
|
||||||
if self.docstatus == 1 and d.value_after_depreciation:
|
if self.docstatus == 1 and finance_book.value_after_depreciation:
|
||||||
value_after_depreciation = flt(d.value_after_depreciation)
|
value_after_depreciation = flt(finance_book.value_after_depreciation)
|
||||||
else:
|
else:
|
||||||
value_after_depreciation = (flt(self.gross_purchase_amount) -
|
value_after_depreciation = (flt(self.gross_purchase_amount) -
|
||||||
flt(self.opening_accumulated_depreciation))
|
flt(self.opening_accumulated_depreciation))
|
||||||
|
|
||||||
d.value_after_depreciation = value_after_depreciation
|
finance_book.value_after_depreciation = value_after_depreciation
|
||||||
|
|
||||||
number_of_pending_depreciations = cint(d.total_number_of_depreciations) - \
|
number_of_pending_depreciations = cint(finance_book.total_number_of_depreciations) - \
|
||||||
cint(self.number_of_depreciations_booked)
|
cint(self.number_of_depreciations_booked)
|
||||||
|
|
||||||
has_pro_rata = self.check_is_pro_rata(d)
|
has_pro_rata = self.check_is_pro_rata(finance_book)
|
||||||
|
|
||||||
if has_pro_rata:
|
if has_pro_rata:
|
||||||
number_of_pending_depreciations += 1
|
number_of_pending_depreciations += 1
|
||||||
|
|
||||||
skip_row = False
|
skip_row = False
|
||||||
for n in range(start, number_of_pending_depreciations):
|
|
||||||
|
for n in range(start[finance_book.idx-1], number_of_pending_depreciations):
|
||||||
# If depreciation is already completed (for double declining balance)
|
# If depreciation is already completed (for double declining balance)
|
||||||
if skip_row: continue
|
if skip_row: continue
|
||||||
|
|
||||||
depreciation_amount = get_depreciation_amount(self, value_after_depreciation, d)
|
depreciation_amount = get_depreciation_amount(self, value_after_depreciation, finance_book)
|
||||||
|
|
||||||
if not has_pro_rata or n < cint(number_of_pending_depreciations) - 1:
|
if not has_pro_rata or n < cint(number_of_pending_depreciations) - 1:
|
||||||
schedule_date = add_months(d.depreciation_start_date,
|
schedule_date = add_months(finance_book.depreciation_start_date,
|
||||||
n * cint(d.frequency_of_depreciation))
|
n * cint(finance_book.frequency_of_depreciation))
|
||||||
|
|
||||||
# schedule date will be a year later from start date
|
# schedule date will be a year later from start date
|
||||||
# so monthly schedule date is calculated by removing 11 months from it
|
# so monthly schedule date is calculated by removing 11 months from it
|
||||||
monthly_schedule_date = add_months(schedule_date, - d.frequency_of_depreciation + 1)
|
monthly_schedule_date = add_months(schedule_date, - finance_book.frequency_of_depreciation + 1)
|
||||||
|
|
||||||
# if asset is being sold
|
# if asset is being sold
|
||||||
if date_of_sale:
|
if date_of_sale:
|
||||||
from_date = self.get_from_date(d.finance_book)
|
from_date = self.get_from_date(finance_book.finance_book)
|
||||||
depreciation_amount, days, months = self.get_pro_rata_amt(d, depreciation_amount,
|
depreciation_amount, days, months = self.get_pro_rata_amt(finance_book, depreciation_amount,
|
||||||
from_date, date_of_sale)
|
from_date, date_of_sale)
|
||||||
|
|
||||||
if depreciation_amount > 0:
|
if depreciation_amount > 0:
|
||||||
self.append("schedules", {
|
self.append("schedules", {
|
||||||
"schedule_date": date_of_sale,
|
"schedule_date": date_of_sale,
|
||||||
"depreciation_amount": depreciation_amount,
|
"depreciation_amount": depreciation_amount,
|
||||||
"depreciation_method": d.depreciation_method,
|
"depreciation_method": finance_book.depreciation_method,
|
||||||
"finance_book": d.finance_book,
|
"finance_book": finance_book.finance_book,
|
||||||
"finance_book_id": d.idx
|
"finance_book_id": finance_book.idx
|
||||||
})
|
})
|
||||||
|
|
||||||
break
|
break
|
||||||
|
|
||||||
# For first row
|
# For first row
|
||||||
if has_pro_rata and not self.opening_accumulated_depreciation and n==0:
|
if has_pro_rata and not self.opening_accumulated_depreciation and n==0:
|
||||||
depreciation_amount, days, months = self.get_pro_rata_amt(d, depreciation_amount,
|
depreciation_amount, days, months = self.get_pro_rata_amt(finance_book, depreciation_amount,
|
||||||
self.available_for_use_date, d.depreciation_start_date)
|
self.available_for_use_date, finance_book.depreciation_start_date)
|
||||||
|
|
||||||
# For first depr schedule date will be the start date
|
# For first depr schedule date will be the start date
|
||||||
# so monthly schedule date is calculated by removing month difference between use date and start date
|
# so monthly schedule date is calculated by removing month difference between use date and start date
|
||||||
monthly_schedule_date = add_months(d.depreciation_start_date, - months + 1)
|
monthly_schedule_date = add_months(finance_book.depreciation_start_date, - months + 1)
|
||||||
|
|
||||||
# For last row
|
# For last row
|
||||||
elif has_pro_rata and n == cint(number_of_pending_depreciations) - 1:
|
elif has_pro_rata and n == cint(number_of_pending_depreciations) - 1:
|
||||||
if not self.flags.increase_in_asset_life:
|
if not self.flags.increase_in_asset_life:
|
||||||
# In case of increase_in_asset_life, the self.to_date is already set on asset_repair submission
|
# In case of increase_in_asset_life, the self.to_date is already set on asset_repair submission
|
||||||
self.to_date = add_months(self.available_for_use_date,
|
self.to_date = add_months(self.available_for_use_date,
|
||||||
(n + self.number_of_depreciations_booked) * cint(d.frequency_of_depreciation))
|
(n + self.number_of_depreciations_booked) * cint(finance_book.frequency_of_depreciation))
|
||||||
|
|
||||||
depreciation_amount_without_pro_rata = depreciation_amount
|
depreciation_amount_without_pro_rata = depreciation_amount
|
||||||
|
|
||||||
depreciation_amount, days, months = self.get_pro_rata_amt(d,
|
depreciation_amount, days, months = self.get_pro_rata_amt(finance_book,
|
||||||
depreciation_amount, schedule_date, self.to_date)
|
depreciation_amount, schedule_date, self.to_date)
|
||||||
|
|
||||||
depreciation_amount = self.get_adjusted_depreciation_amount(depreciation_amount_without_pro_rata,
|
depreciation_amount = self.get_adjusted_depreciation_amount(depreciation_amount_without_pro_rata,
|
||||||
depreciation_amount, d.finance_book)
|
depreciation_amount, finance_book.finance_book)
|
||||||
|
|
||||||
monthly_schedule_date = add_months(schedule_date, 1)
|
monthly_schedule_date = add_months(schedule_date, 1)
|
||||||
schedule_date = add_days(schedule_date, days)
|
schedule_date = add_days(schedule_date, days)
|
||||||
@ -272,10 +273,10 @@ class Asset(AccountsController):
|
|||||||
self.precision("gross_purchase_amount"))
|
self.precision("gross_purchase_amount"))
|
||||||
|
|
||||||
# Adjust depreciation amount in the last period based on the expected value after useful life
|
# Adjust depreciation amount in the last period based on the expected value after useful life
|
||||||
if d.expected_value_after_useful_life and ((n == cint(number_of_pending_depreciations) - 1
|
if finance_book.expected_value_after_useful_life and ((n == cint(number_of_pending_depreciations) - 1
|
||||||
and value_after_depreciation != d.expected_value_after_useful_life)
|
and value_after_depreciation != finance_book.expected_value_after_useful_life)
|
||||||
or value_after_depreciation < d.expected_value_after_useful_life):
|
or value_after_depreciation < finance_book.expected_value_after_useful_life):
|
||||||
depreciation_amount += (value_after_depreciation - d.expected_value_after_useful_life)
|
depreciation_amount += (value_after_depreciation - finance_book.expected_value_after_useful_life)
|
||||||
skip_row = True
|
skip_row = True
|
||||||
|
|
||||||
if depreciation_amount > 0:
|
if depreciation_amount > 0:
|
||||||
@ -285,7 +286,7 @@ class Asset(AccountsController):
|
|||||||
# In pro rata case, for first and last depreciation, month range would be different
|
# In pro rata case, for first and last depreciation, month range would be different
|
||||||
month_range = months \
|
month_range = months \
|
||||||
if (has_pro_rata and n==0) or (has_pro_rata and n == cint(number_of_pending_depreciations) - 1) \
|
if (has_pro_rata and n==0) or (has_pro_rata and n == cint(number_of_pending_depreciations) - 1) \
|
||||||
else d.frequency_of_depreciation
|
else finance_book.frequency_of_depreciation
|
||||||
|
|
||||||
for r in range(month_range):
|
for r in range(month_range):
|
||||||
if (has_pro_rata and n == 0):
|
if (has_pro_rata and n == 0):
|
||||||
@ -311,27 +312,52 @@ class Asset(AccountsController):
|
|||||||
self.append("schedules", {
|
self.append("schedules", {
|
||||||
"schedule_date": date,
|
"schedule_date": date,
|
||||||
"depreciation_amount": amount,
|
"depreciation_amount": amount,
|
||||||
"depreciation_method": d.depreciation_method,
|
"depreciation_method": finance_book.depreciation_method,
|
||||||
"finance_book": d.finance_book,
|
"finance_book": finance_book.finance_book,
|
||||||
"finance_book_id": d.idx
|
"finance_book_id": finance_book.idx
|
||||||
})
|
})
|
||||||
else:
|
else:
|
||||||
self.append("schedules", {
|
self.append("schedules", {
|
||||||
"schedule_date": schedule_date,
|
"schedule_date": schedule_date,
|
||||||
"depreciation_amount": depreciation_amount,
|
"depreciation_amount": depreciation_amount,
|
||||||
"depreciation_method": d.depreciation_method,
|
"depreciation_method": finance_book.depreciation_method,
|
||||||
"finance_book": d.finance_book,
|
"finance_book": finance_book.finance_book,
|
||||||
"finance_book_id": d.idx
|
"finance_book_id": finance_book.idx
|
||||||
})
|
})
|
||||||
|
|
||||||
# used when depreciation schedule needs to be modified due to increase in asset life
|
# depreciation schedules need to be cleared before modification due to increase in asset life/asset sales
|
||||||
|
# JE: Journal Entry, FB: Finance Book
|
||||||
def clear_depreciation_schedule(self):
|
def clear_depreciation_schedule(self):
|
||||||
start = 0
|
start = []
|
||||||
for n in range(len(self.schedules)):
|
num_of_depreciations_completed = 0
|
||||||
if not self.schedules[n].journal_entry:
|
depr_schedule = []
|
||||||
del self.schedules[n:]
|
|
||||||
start = n
|
for schedule in self.get('schedules'):
|
||||||
break
|
|
||||||
|
# to update start when there are JEs linked with all the schedule rows corresponding to an FB
|
||||||
|
if len(start) == (int(schedule.finance_book_id) - 2):
|
||||||
|
start.append(num_of_depreciations_completed)
|
||||||
|
num_of_depreciations_completed = 0
|
||||||
|
|
||||||
|
# to ensure that start will only be updated once for each FB
|
||||||
|
if len(start) == (int(schedule.finance_book_id) - 1):
|
||||||
|
if schedule.journal_entry:
|
||||||
|
num_of_depreciations_completed += 1
|
||||||
|
depr_schedule.append(schedule)
|
||||||
|
else:
|
||||||
|
start.append(num_of_depreciations_completed)
|
||||||
|
num_of_depreciations_completed = 0
|
||||||
|
|
||||||
|
# to update start when all the schedule rows corresponding to the last FB are linked with JEs
|
||||||
|
if len(start) == (len(self.finance_books) - 1):
|
||||||
|
start.append(num_of_depreciations_completed)
|
||||||
|
|
||||||
|
# when the Depreciation Schedule is being created for the first time
|
||||||
|
if start == []:
|
||||||
|
start = [0] * len(self.finance_books)
|
||||||
|
else:
|
||||||
|
self.schedules = depr_schedule
|
||||||
|
|
||||||
return start
|
return start
|
||||||
|
|
||||||
def get_from_date(self, finance_book):
|
def get_from_date(self, finance_book):
|
||||||
@ -469,7 +495,6 @@ class Asset(AccountsController):
|
|||||||
|
|
||||||
asset_value_after_full_schedule = flt(
|
asset_value_after_full_schedule = flt(
|
||||||
flt(self.gross_purchase_amount) -
|
flt(self.gross_purchase_amount) -
|
||||||
flt(self.opening_accumulated_depreciation) -
|
|
||||||
flt(accumulated_depreciation_after_full_schedule), self.precision('gross_purchase_amount'))
|
flt(accumulated_depreciation_after_full_schedule), self.precision('gross_purchase_amount'))
|
||||||
|
|
||||||
if (row.expected_value_after_useful_life and
|
if (row.expected_value_after_useful_life and
|
||||||
@ -731,14 +756,14 @@ def create_asset_repair(asset, asset_name):
|
|||||||
return asset_repair
|
return asset_repair
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def create_asset_adjustment(asset, asset_category, company):
|
def create_asset_value_adjustment(asset, asset_category, company):
|
||||||
asset_maintenance = frappe.get_doc("Asset Value Adjustment")
|
asset_value_adjustment = frappe.new_doc("Asset Value Adjustment")
|
||||||
asset_maintenance.update({
|
asset_value_adjustment.update({
|
||||||
"asset": asset,
|
"asset": asset,
|
||||||
"company": company,
|
"company": company,
|
||||||
"asset_category": asset_category
|
"asset_category": asset_category
|
||||||
})
|
})
|
||||||
return asset_maintenance
|
return asset_value_adjustment
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def transfer_asset(args):
|
def transfer_asset(args):
|
||||||
|
|||||||
@ -955,6 +955,82 @@ class TestDepreciationBasics(AssetSetup):
|
|||||||
|
|
||||||
self.assertEqual(len(asset.schedules), 1)
|
self.assertEqual(len(asset.schedules), 1)
|
||||||
|
|
||||||
|
def test_clear_depreciation_schedule_for_multiple_finance_books(self):
|
||||||
|
asset = create_asset(
|
||||||
|
item_code = "Macbook Pro",
|
||||||
|
available_for_use_date = "2019-12-31",
|
||||||
|
do_not_save = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
asset.calculate_depreciation = 1
|
||||||
|
asset.append("finance_books", {
|
||||||
|
"depreciation_method": "Straight Line",
|
||||||
|
"frequency_of_depreciation": 1,
|
||||||
|
"total_number_of_depreciations": 3,
|
||||||
|
"expected_value_after_useful_life": 10000,
|
||||||
|
"depreciation_start_date": "2020-01-31"
|
||||||
|
})
|
||||||
|
asset.append("finance_books", {
|
||||||
|
"depreciation_method": "Straight Line",
|
||||||
|
"frequency_of_depreciation": 1,
|
||||||
|
"total_number_of_depreciations": 6,
|
||||||
|
"expected_value_after_useful_life": 10000,
|
||||||
|
"depreciation_start_date": "2020-01-31"
|
||||||
|
})
|
||||||
|
asset.append("finance_books", {
|
||||||
|
"depreciation_method": "Straight Line",
|
||||||
|
"frequency_of_depreciation": 12,
|
||||||
|
"total_number_of_depreciations": 3,
|
||||||
|
"expected_value_after_useful_life": 10000,
|
||||||
|
"depreciation_start_date": "2020-12-31"
|
||||||
|
})
|
||||||
|
asset.submit()
|
||||||
|
|
||||||
|
post_depreciation_entries(date="2020-04-01")
|
||||||
|
asset.load_from_db()
|
||||||
|
|
||||||
|
asset.clear_depreciation_schedule()
|
||||||
|
|
||||||
|
self.assertEqual(len(asset.schedules), 6)
|
||||||
|
|
||||||
|
for schedule in asset.schedules:
|
||||||
|
if schedule.idx <= 3:
|
||||||
|
self.assertEqual(schedule.finance_book_id, "1")
|
||||||
|
else:
|
||||||
|
self.assertEqual(schedule.finance_book_id, "2")
|
||||||
|
|
||||||
|
def test_depreciation_schedules_are_set_up_for_multiple_finance_books(self):
|
||||||
|
asset = create_asset(
|
||||||
|
item_code = "Macbook Pro",
|
||||||
|
available_for_use_date = "2019-12-31",
|
||||||
|
do_not_save = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
asset.calculate_depreciation = 1
|
||||||
|
asset.append("finance_books", {
|
||||||
|
"depreciation_method": "Straight Line",
|
||||||
|
"frequency_of_depreciation": 12,
|
||||||
|
"total_number_of_depreciations": 3,
|
||||||
|
"expected_value_after_useful_life": 10000,
|
||||||
|
"depreciation_start_date": "2020-12-31"
|
||||||
|
})
|
||||||
|
asset.append("finance_books", {
|
||||||
|
"depreciation_method": "Straight Line",
|
||||||
|
"frequency_of_depreciation": 12,
|
||||||
|
"total_number_of_depreciations": 6,
|
||||||
|
"expected_value_after_useful_life": 10000,
|
||||||
|
"depreciation_start_date": "2020-12-31"
|
||||||
|
})
|
||||||
|
asset.save()
|
||||||
|
|
||||||
|
self.assertEqual(len(asset.schedules), 9)
|
||||||
|
|
||||||
|
for schedule in asset.schedules:
|
||||||
|
if schedule.idx <= 3:
|
||||||
|
self.assertEqual(schedule.finance_book_id, 1)
|
||||||
|
else:
|
||||||
|
self.assertEqual(schedule.finance_book_id, 2)
|
||||||
|
|
||||||
def test_depreciation_entry_cancellation(self):
|
def test_depreciation_entry_cancellation(self):
|
||||||
asset = create_asset(
|
asset = create_asset(
|
||||||
item_code = "Macbook Pro",
|
item_code = "Macbook Pro",
|
||||||
|
|||||||
@ -124,6 +124,14 @@ frappe.ui.form.on("Request for Quotation",{
|
|||||||
dialog.show()
|
dialog.show()
|
||||||
},
|
},
|
||||||
|
|
||||||
|
schedule_date(frm) {
|
||||||
|
if(frm.doc.schedule_date){
|
||||||
|
frm.doc.items.forEach((item) => {
|
||||||
|
item.schedule_date = frm.doc.schedule_date;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
refresh_field("items");
|
||||||
|
},
|
||||||
preview: (frm) => {
|
preview: (frm) => {
|
||||||
let dialog = new frappe.ui.Dialog({
|
let dialog = new frappe.ui.Dialog({
|
||||||
title: __('Preview Email'),
|
title: __('Preview Email'),
|
||||||
@ -184,7 +192,13 @@ frappe.ui.form.on("Request for Quotation",{
|
|||||||
dialog.show();
|
dialog.show();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
frappe.ui.form.on("Request for Quotation Item", {
|
||||||
|
items_add(frm, cdt, cdn) {
|
||||||
|
if (frm.doc.schedule_date) {
|
||||||
|
frappe.model.set_value(cdt, cdn, 'schedule_date', frm.doc.schedule_date);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
frappe.ui.form.on("Request for Quotation Supplier",{
|
frappe.ui.form.on("Request for Quotation Supplier",{
|
||||||
supplier: function(frm, cdt, cdn) {
|
supplier: function(frm, cdt, cdn) {
|
||||||
var d = locals[cdt][cdn]
|
var d = locals[cdt][cdn]
|
||||||
|
|||||||
@ -12,6 +12,7 @@
|
|||||||
"vendor",
|
"vendor",
|
||||||
"column_break1",
|
"column_break1",
|
||||||
"transaction_date",
|
"transaction_date",
|
||||||
|
"schedule_date",
|
||||||
"status",
|
"status",
|
||||||
"amended_from",
|
"amended_from",
|
||||||
"suppliers_section",
|
"suppliers_section",
|
||||||
@ -246,16 +247,22 @@
|
|||||||
"fieldname": "sec_break_email_2",
|
"fieldname": "sec_break_email_2",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"hide_border": 1
|
"hide_border": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "schedule_date",
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"label": "Required Date"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "fa fa-shopping-cart",
|
"icon": "fa fa-shopping-cart",
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-11-05 22:04:29.017134",
|
"modified": "2021-11-24 17:47:49.909000",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Buying",
|
"module": "Buying",
|
||||||
"name": "Request for Quotation",
|
"name": "Request for Quotation",
|
||||||
|
"naming_rule": "By \"Naming Series\" field",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
|
|||||||
@ -19,7 +19,7 @@ class TestExitInterview(unittest.TestCase):
|
|||||||
frappe.db.sql('delete from `tabExit Interview`')
|
frappe.db.sql('delete from `tabExit Interview`')
|
||||||
|
|
||||||
def test_duplicate_interview(self):
|
def test_duplicate_interview(self):
|
||||||
employee = make_employee('employeeexit1@example.com')
|
employee = make_employee('employeeexitint1@example.com')
|
||||||
frappe.db.set_value('Employee', employee, 'relieving_date', getdate())
|
frappe.db.set_value('Employee', employee, 'relieving_date', getdate())
|
||||||
interview = create_exit_interview(employee)
|
interview = create_exit_interview(employee)
|
||||||
|
|
||||||
@ -27,7 +27,7 @@ class TestExitInterview(unittest.TestCase):
|
|||||||
self.assertRaises(frappe.DuplicateEntryError, doc.save)
|
self.assertRaises(frappe.DuplicateEntryError, doc.save)
|
||||||
|
|
||||||
def test_relieving_date_validation(self):
|
def test_relieving_date_validation(self):
|
||||||
employee = make_employee('employeeexit2@example.com')
|
employee = make_employee('employeeexitint2@example.com')
|
||||||
# unset relieving date
|
# unset relieving date
|
||||||
frappe.db.set_value('Employee', employee, 'relieving_date', None)
|
frappe.db.set_value('Employee', employee, 'relieving_date', None)
|
||||||
|
|
||||||
|
|||||||
@ -108,12 +108,11 @@ def get_data(filters):
|
|||||||
interview.status.as_('interview_status'), interview.employee_status.as_('employee_status'),
|
interview.status.as_('interview_status'), interview.employee_status.as_('employee_status'),
|
||||||
interview.reference_document_name.as_('questionnaire'), fnf.name.as_('full_and_final_statement'))
|
interview.reference_document_name.as_('questionnaire'), fnf.name.as_('full_and_final_statement'))
|
||||||
.distinct()
|
.distinct()
|
||||||
.orderby(employee.relieving_date, order=Order.asc)
|
|
||||||
.where(
|
.where(
|
||||||
((employee.relieving_date.isnotnull()) | (employee.relieving_date != ''))
|
((employee.relieving_date.isnotnull()) | (employee.relieving_date != ''))
|
||||||
& ((interview.name.isnull()) | ((interview.name.isnotnull()) & (interview.docstatus != 2)))
|
& ((interview.name.isnull()) | ((interview.name.isnotnull()) & (interview.docstatus != 2)))
|
||||||
& ((fnf.name.isnull()) | ((fnf.name.isnotnull()) & (fnf.docstatus != 2)))
|
& ((fnf.name.isnull()) | ((fnf.name.isnotnull()) & (fnf.docstatus != 2)))
|
||||||
)
|
).orderby(employee.relieving_date, order=Order.asc)
|
||||||
)
|
)
|
||||||
|
|
||||||
query = get_conditions(filters, query, employee, interview, fnf)
|
query = get_conditions(filters, query, employee, interview, fnf)
|
||||||
|
|||||||
@ -1,17 +1,15 @@
|
|||||||
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
|
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
# See license.txt
|
# See license.txt
|
||||||
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.utils import add_months, today
|
from frappe.utils import add_months, today
|
||||||
|
|
||||||
from erpnext import get_company_currency
|
from erpnext import get_company_currency
|
||||||
|
from erpnext.tests.utils import ERPNextTestCase
|
||||||
|
|
||||||
from .blanket_order import make_order
|
from .blanket_order import make_order
|
||||||
|
|
||||||
|
|
||||||
class TestBlanketOrder(unittest.TestCase):
|
class TestBlanketOrder(ERPNextTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
frappe.flags.args = frappe._dict()
|
frappe.flags.args = frappe._dict()
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
# License: GNU General Public License v3. See license.txt
|
# License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
|
|
||||||
import unittest
|
|
||||||
from collections import deque
|
from collections import deque
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
@ -18,10 +17,11 @@ from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import
|
|||||||
create_stock_reconciliation,
|
create_stock_reconciliation,
|
||||||
)
|
)
|
||||||
from erpnext.tests.test_subcontracting import set_backflush_based_on
|
from erpnext.tests.test_subcontracting import set_backflush_based_on
|
||||||
|
from erpnext.tests.utils import ERPNextTestCase
|
||||||
|
|
||||||
test_records = frappe.get_test_records('BOM')
|
test_records = frappe.get_test_records('BOM')
|
||||||
|
|
||||||
class TestBOM(unittest.TestCase):
|
class TestBOM(ERPNextTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
if not frappe.get_value('Item', '_Test Item'):
|
if not frappe.get_value('Item', '_Test Item'):
|
||||||
make_test_records('Item')
|
make_test_records('Item')
|
||||||
|
|||||||
@ -1,19 +1,16 @@
|
|||||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
# License: GNU General Public License v3. See license.txt
|
# License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
|
|
||||||
from erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool import update_cost
|
from erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool import update_cost
|
||||||
from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
|
from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
|
||||||
from erpnext.stock.doctype.item.test_item import create_item
|
from erpnext.stock.doctype.item.test_item import create_item
|
||||||
|
from erpnext.tests.utils import ERPNextTestCase
|
||||||
|
|
||||||
test_records = frappe.get_test_records('BOM')
|
test_records = frappe.get_test_records('BOM')
|
||||||
|
|
||||||
class TestBOMUpdateTool(unittest.TestCase):
|
class TestBOMUpdateTool(ERPNextTestCase):
|
||||||
def test_replace_bom(self):
|
def test_replace_bom(self):
|
||||||
current_bom = "BOM-_Test Item Home Desktop Manufactured-001"
|
current_bom = "BOM-_Test Item Home Desktop Manufactured-001"
|
||||||
|
|
||||||
|
|||||||
@ -75,6 +75,15 @@ frappe.ui.form.on('Job Card', {
|
|||||||
&& (frm.doc.items || !frm.doc.items.length || frm.doc.for_quantity == frm.doc.transferred_qty)) {
|
&& (frm.doc.items || !frm.doc.items.length || frm.doc.for_quantity == frm.doc.transferred_qty)) {
|
||||||
frm.trigger("prepare_timer_buttons");
|
frm.trigger("prepare_timer_buttons");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (frm.doc.work_order) {
|
||||||
|
frappe.db.get_value('Work Order', frm.doc.work_order,
|
||||||
|
'transfer_material_against').then((r) => {
|
||||||
|
if (r.message.transfer_material_against == 'Work Order') {
|
||||||
|
frm.set_df_property('items', 'hidden', 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
setup_corrective_job_card: function(frm) {
|
setup_corrective_job_card: function(frm) {
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
|
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
# See license.txt
|
# See license.txt
|
||||||
import unittest
|
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.utils import random_string
|
from frappe.utils import random_string
|
||||||
@ -12,9 +11,10 @@ from erpnext.manufacturing.doctype.job_card.job_card import (
|
|||||||
from erpnext.manufacturing.doctype.work_order.test_work_order import make_wo_order_test_record
|
from erpnext.manufacturing.doctype.work_order.test_work_order import make_wo_order_test_record
|
||||||
from erpnext.manufacturing.doctype.workstation.test_workstation import make_workstation
|
from erpnext.manufacturing.doctype.workstation.test_workstation import make_workstation
|
||||||
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
||||||
|
from erpnext.tests.utils import ERPNextTestCase
|
||||||
|
|
||||||
|
|
||||||
class TestJobCard(unittest.TestCase):
|
class TestJobCard(ERPNextTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
make_bom_for_jc_tests()
|
make_bom_for_jc_tests()
|
||||||
|
|
||||||
|
|||||||
@ -1,8 +1,5 @@
|
|||||||
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
|
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
# See license.txt
|
# See license.txt
|
||||||
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.utils import add_to_date, flt, now_datetime, nowdate
|
from frappe.utils import add_to_date, flt, now_datetime, nowdate
|
||||||
|
|
||||||
@ -17,9 +14,10 @@ from erpnext.stock.doctype.item.test_item import create_item
|
|||||||
from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import (
|
from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import (
|
||||||
create_stock_reconciliation,
|
create_stock_reconciliation,
|
||||||
)
|
)
|
||||||
|
from erpnext.tests.utils import ERPNextTestCase
|
||||||
|
|
||||||
|
|
||||||
class TestProductionPlan(unittest.TestCase):
|
class TestProductionPlan(ERPNextTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
for item in ['Test Production Item 1', 'Subassembly Item 1',
|
for item in ['Test Production Item 1', 'Subassembly Item 1',
|
||||||
'Raw Material Item 1', 'Raw Material Item 2']:
|
'Raw Material Item 1', 'Raw Material Item 2']:
|
||||||
|
|||||||
@ -1,17 +1,15 @@
|
|||||||
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
|
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
# See license.txt
|
# See license.txt
|
||||||
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.test_runner import make_test_records
|
from frappe.test_runner import make_test_records
|
||||||
|
|
||||||
from erpnext.manufacturing.doctype.job_card.job_card import OperationSequenceError
|
from erpnext.manufacturing.doctype.job_card.job_card import OperationSequenceError
|
||||||
from erpnext.manufacturing.doctype.work_order.test_work_order import make_wo_order_test_record
|
from erpnext.manufacturing.doctype.work_order.test_work_order import make_wo_order_test_record
|
||||||
from erpnext.stock.doctype.item.test_item import make_item
|
from erpnext.stock.doctype.item.test_item import make_item
|
||||||
|
from erpnext.tests.utils import ERPNextTestCase
|
||||||
|
|
||||||
|
|
||||||
class TestRouting(unittest.TestCase):
|
class TestRouting(ERPNextTestCase):
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
cls.item_code = "Test Routing Item - A"
|
cls.item_code = "Test Routing Item - A"
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
|
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
# License: GNU General Public License v3. See license.txt
|
# License: GNU General Public License v3. See license.txt
|
||||||
import unittest
|
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.utils import add_months, cint, flt, now, today
|
from frappe.utils import add_months, cint, flt, now, today
|
||||||
@ -95,7 +94,7 @@ class TestWorkOrder(ERPNextTestCase):
|
|||||||
|
|
||||||
def test_reserved_qty_for_partial_completion(self):
|
def test_reserved_qty_for_partial_completion(self):
|
||||||
item = "_Test Item"
|
item = "_Test Item"
|
||||||
warehouse = create_warehouse("Test Warehouse for reserved_qty - _TC")
|
warehouse = "_Test Warehouse - _TC"
|
||||||
|
|
||||||
bin1_at_start = get_bin(item, warehouse)
|
bin1_at_start = get_bin(item, warehouse)
|
||||||
|
|
||||||
|
|||||||
@ -1,8 +1,5 @@
|
|||||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors
|
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors
|
||||||
# See license.txt
|
# See license.txt
|
||||||
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.test_runner import make_test_records
|
from frappe.test_runner import make_test_records
|
||||||
|
|
||||||
@ -13,12 +10,13 @@ from erpnext.manufacturing.doctype.workstation.workstation import (
|
|||||||
WorkstationHolidayError,
|
WorkstationHolidayError,
|
||||||
check_if_within_operating_hours,
|
check_if_within_operating_hours,
|
||||||
)
|
)
|
||||||
|
from erpnext.tests.utils import ERPNextTestCase
|
||||||
|
|
||||||
test_dependencies = ["Warehouse"]
|
test_dependencies = ["Warehouse"]
|
||||||
test_records = frappe.get_test_records('Workstation')
|
test_records = frappe.get_test_records('Workstation')
|
||||||
make_test_records('Workstation')
|
make_test_records('Workstation')
|
||||||
|
|
||||||
class TestWorkstation(unittest.TestCase):
|
class TestWorkstation(ERPNextTestCase):
|
||||||
def test_validate_timings(self):
|
def test_validate_timings(self):
|
||||||
check_if_within_operating_hours("_Test Workstation 1", "Operation 1", "2013-02-02 11:00:00", "2013-02-02 19:00:00")
|
check_if_within_operating_hours("_Test Workstation 1", "Operation 1", "2013-02-02 11:00:00", "2013-02-02 19:00:00")
|
||||||
check_if_within_operating_hours("_Test Workstation 1", "Operation 1", "2013-02-02 10:00:00", "2013-02-02 20:00:00")
|
check_if_within_operating_hours("_Test Workstation 1", "Operation 1", "2013-02-02 10:00:00", "2013-02-02 20:00:00")
|
||||||
|
|||||||
@ -165,6 +165,7 @@ erpnext.patches.v12_0.set_updated_purpose_in_pick_list
|
|||||||
erpnext.patches.v12_0.set_default_payroll_based_on
|
erpnext.patches.v12_0.set_default_payroll_based_on
|
||||||
erpnext.patches.v12_0.repost_stock_ledger_entries_for_target_warehouse
|
erpnext.patches.v12_0.repost_stock_ledger_entries_for_target_warehouse
|
||||||
erpnext.patches.v12_0.update_end_date_and_status_in_email_campaign
|
erpnext.patches.v12_0.update_end_date_and_status_in_email_campaign
|
||||||
|
erpnext.patches.v13_0.validate_options_for_data_field
|
||||||
erpnext.patches.v13_0.move_tax_slabs_from_payroll_period_to_income_tax_slab #123
|
erpnext.patches.v13_0.move_tax_slabs_from_payroll_period_to_income_tax_slab #123
|
||||||
erpnext.patches.v12_0.fix_quotation_expired_status
|
erpnext.patches.v12_0.fix_quotation_expired_status
|
||||||
erpnext.patches.v12_0.rename_pos_closing_doctype
|
erpnext.patches.v12_0.rename_pos_closing_doctype
|
||||||
@ -287,7 +288,6 @@ execute:frappe.reload_doc("erpnext_integrations", "doctype", "Product Tax Catego
|
|||||||
erpnext.patches.v14_0.delete_einvoicing_doctypes
|
erpnext.patches.v14_0.delete_einvoicing_doctypes
|
||||||
erpnext.patches.v13_0.custom_fields_for_taxjar_integration #08-11-2021
|
erpnext.patches.v13_0.custom_fields_for_taxjar_integration #08-11-2021
|
||||||
erpnext.patches.v13_0.set_operation_time_based_on_operating_cost
|
erpnext.patches.v13_0.set_operation_time_based_on_operating_cost
|
||||||
erpnext.patches.v13_0.validate_options_for_data_field
|
|
||||||
erpnext.patches.v13_0.create_gst_payment_entry_fields #27-11-2021
|
erpnext.patches.v13_0.create_gst_payment_entry_fields #27-11-2021
|
||||||
erpnext.patches.v14_0.delete_shopify_doctypes
|
erpnext.patches.v14_0.delete_shopify_doctypes
|
||||||
erpnext.patches.v13_0.fix_invoice_statuses
|
erpnext.patches.v13_0.fix_invoice_statuses
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
# License: GNU General Public License v3. See license.txt
|
# License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
|
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
|
||||||
from frappe.model.utils.rename_field import rename_field
|
from frappe.model.utils.rename_field import rename_field
|
||||||
|
|
||||||
|
|
||||||
@ -12,5 +13,20 @@ def execute():
|
|||||||
|
|
||||||
if frappe.db.exists('DocType', 'Sales Invoice'):
|
if frappe.db.exists('DocType', 'Sales Invoice'):
|
||||||
frappe.reload_doc('accounts', 'doctype', 'sales_invoice', force=True)
|
frappe.reload_doc('accounts', 'doctype', 'sales_invoice', force=True)
|
||||||
|
|
||||||
|
# rename_field method assumes that the field already exists or the doc is synced
|
||||||
|
if not frappe.db.has_column('Sales Invoice', 'ksa_einv_qr'):
|
||||||
|
create_custom_fields({
|
||||||
|
'Sales Invoice': [
|
||||||
|
dict(
|
||||||
|
fieldname='ksa_einv_qr',
|
||||||
|
label='KSA E-Invoicing QR',
|
||||||
|
fieldtype='Attach Image',
|
||||||
|
read_only=1, no_copy=1, hidden=1
|
||||||
|
)
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
if frappe.db.has_column('Sales Invoice', 'qr_code'):
|
if frappe.db.has_column('Sales Invoice', 'qr_code'):
|
||||||
rename_field('Sales Invoice', 'qr_code', 'ksa_einv_qr')
|
rename_field('Sales Invoice', 'qr_code', 'ksa_einv_qr')
|
||||||
|
frappe.delete_doc_if_exists("Custom Field", "Sales Invoice-qr_code")
|
||||||
|
|||||||
@ -940,10 +940,12 @@ class SalarySlip(TransactionBase):
|
|||||||
|
|
||||||
def get_amount_based_on_payment_days(self, row, joining_date, relieving_date):
|
def get_amount_based_on_payment_days(self, row, joining_date, relieving_date):
|
||||||
amount, additional_amount = row.amount, row.additional_amount
|
amount, additional_amount = row.amount, row.additional_amount
|
||||||
|
timesheet_component = frappe.db.get_value("Salary Structure", self.salary_structure, "salary_component")
|
||||||
|
|
||||||
if (self.salary_structure and
|
if (self.salary_structure and
|
||||||
cint(row.depends_on_payment_days) and cint(self.total_working_days)
|
cint(row.depends_on_payment_days) and cint(self.total_working_days)
|
||||||
and not (row.additional_salary and row.default_amount) # to identify overwritten additional salary
|
and not (row.additional_salary and row.default_amount) # to identify overwritten additional salary
|
||||||
and (not self.salary_slip_based_on_timesheet or
|
and (row.salary_component != timesheet_component or
|
||||||
getdate(self.start_date) < joining_date or
|
getdate(self.start_date) < joining_date or
|
||||||
(relieving_date and getdate(self.end_date) > relieving_date)
|
(relieving_date and getdate(self.end_date) > relieving_date)
|
||||||
)):
|
)):
|
||||||
@ -952,7 +954,7 @@ class SalarySlip(TransactionBase):
|
|||||||
amount = flt((flt(row.default_amount) * flt(self.payment_days)
|
amount = flt((flt(row.default_amount) * flt(self.payment_days)
|
||||||
/ cint(self.total_working_days)), row.precision("amount")) + additional_amount
|
/ cint(self.total_working_days)), row.precision("amount")) + additional_amount
|
||||||
|
|
||||||
elif not self.payment_days and not self.salary_slip_based_on_timesheet and cint(row.depends_on_payment_days):
|
elif not self.payment_days and row.salary_component != timesheet_component and cint(row.depends_on_payment_days):
|
||||||
amount, additional_amount = 0, 0
|
amount, additional_amount = 0, 0
|
||||||
elif not row.amount:
|
elif not row.amount:
|
||||||
amount = flt(row.default_amount) + flt(row.additional_amount)
|
amount = flt(row.default_amount) + flt(row.additional_amount)
|
||||||
|
|||||||
@ -134,6 +134,57 @@ class TestSalarySlip(unittest.TestCase):
|
|||||||
|
|
||||||
frappe.db.set_value("Payroll Settings", None, "payroll_based_on", "Leave")
|
frappe.db.set_value("Payroll Settings", None, "payroll_based_on", "Leave")
|
||||||
|
|
||||||
|
def test_payment_days_in_salary_slip_based_on_timesheet(self):
|
||||||
|
from erpnext.hr.doctype.attendance.attendance import mark_attendance
|
||||||
|
from erpnext.projects.doctype.timesheet.test_timesheet import (
|
||||||
|
make_salary_structure_for_timesheet,
|
||||||
|
make_timesheet,
|
||||||
|
)
|
||||||
|
from erpnext.projects.doctype.timesheet.timesheet import (
|
||||||
|
make_salary_slip as make_salary_slip_for_timesheet,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Payroll based on attendance
|
||||||
|
frappe.db.set_value("Payroll Settings", None, "payroll_based_on", "Attendance")
|
||||||
|
|
||||||
|
emp = make_employee("test_employee_timesheet@salary.com", company="_Test Company")
|
||||||
|
frappe.db.set_value("Employee", emp, {"relieving_date": None, "status": "Active"})
|
||||||
|
|
||||||
|
# mark attendance
|
||||||
|
month_start_date = get_first_day(nowdate())
|
||||||
|
month_end_date = get_last_day(nowdate())
|
||||||
|
|
||||||
|
first_sunday = frappe.db.sql("""
|
||||||
|
select holiday_date from `tabHoliday`
|
||||||
|
where parent = 'Salary Slip Test Holiday List'
|
||||||
|
and holiday_date between %s and %s
|
||||||
|
order by holiday_date
|
||||||
|
""", (month_start_date, month_end_date))[0][0]
|
||||||
|
|
||||||
|
mark_attendance(emp, add_days(first_sunday, 1), 'Absent', ignore_validate=True) # counted as absent
|
||||||
|
|
||||||
|
# salary structure based on timesheet
|
||||||
|
make_salary_structure_for_timesheet(emp)
|
||||||
|
timesheet = make_timesheet(emp, simulate=True, is_billable=1)
|
||||||
|
salary_slip = make_salary_slip_for_timesheet(timesheet.name)
|
||||||
|
salary_slip.start_date = month_start_date
|
||||||
|
salary_slip.end_date = month_end_date
|
||||||
|
salary_slip.save()
|
||||||
|
salary_slip.submit()
|
||||||
|
|
||||||
|
no_of_days = self.get_no_of_days()
|
||||||
|
days_in_month = no_of_days[0]
|
||||||
|
no_of_holidays = no_of_days[1]
|
||||||
|
|
||||||
|
self.assertEqual(salary_slip.payment_days, days_in_month - no_of_holidays - 1)
|
||||||
|
|
||||||
|
# gross pay calculation based on attendance (payment days)
|
||||||
|
gross_pay = 78100 - ((78000 / (days_in_month - no_of_holidays)) * flt(salary_slip.leave_without_pay + salary_slip.absent_days))
|
||||||
|
|
||||||
|
self.assertEqual(salary_slip.gross_pay, flt(gross_pay, 2))
|
||||||
|
|
||||||
|
frappe.db.set_value("Payroll Settings", None, "payroll_based_on", "Leave")
|
||||||
|
|
||||||
def test_component_amount_dependent_on_another_payment_days_based_component(self):
|
def test_component_amount_dependent_on_another_payment_days_based_component(self):
|
||||||
from erpnext.hr.doctype.attendance.attendance import mark_attendance
|
from erpnext.hr.doctype.attendance.attendance import mark_attendance
|
||||||
from erpnext.payroll.doctype.salary_structure.test_salary_structure import (
|
from erpnext.payroll.doctype.salary_structure.test_salary_structure import (
|
||||||
|
|||||||
@ -34,10 +34,6 @@ class TestTimesheet(unittest.TestCase):
|
|||||||
for dt in ["Salary Slip", "Salary Structure", "Salary Structure Assignment", "Timesheet"]:
|
for dt in ["Salary Slip", "Salary Structure", "Salary Structure Assignment", "Timesheet"]:
|
||||||
frappe.db.sql("delete from `tab%s`" % dt)
|
frappe.db.sql("delete from `tab%s`" % dt)
|
||||||
|
|
||||||
if not frappe.db.exists("Salary Component", "Timesheet Component"):
|
|
||||||
frappe.get_doc({"doctype": "Salary Component", "salary_component": "Timesheet Component"}).insert()
|
|
||||||
|
|
||||||
|
|
||||||
def test_timesheet_billing_amount(self):
|
def test_timesheet_billing_amount(self):
|
||||||
emp = make_employee("test_employee_6@salary.com")
|
emp = make_employee("test_employee_6@salary.com")
|
||||||
|
|
||||||
@ -160,6 +156,9 @@ def make_salary_structure_for_timesheet(employee, company=None):
|
|||||||
salary_structure_name = "Timesheet Salary Structure Test"
|
salary_structure_name = "Timesheet Salary Structure Test"
|
||||||
frequency = "Monthly"
|
frequency = "Monthly"
|
||||||
|
|
||||||
|
if not frappe.db.exists("Salary Component", "Timesheet Component"):
|
||||||
|
frappe.get_doc({"doctype": "Salary Component", "salary_component": "Timesheet Component"}).insert()
|
||||||
|
|
||||||
salary_structure = make_salary_structure(salary_structure_name, frequency, company=company, dont_submit=True)
|
salary_structure = make_salary_structure(salary_structure_name, frequency, company=company, dont_submit=True)
|
||||||
salary_structure.salary_component = "Timesheet Component"
|
salary_structure.salary_component = "Timesheet Component"
|
||||||
salary_structure.salary_slip_based_on_timesheet = 1
|
salary_structure.salary_slip_based_on_timesheet = 1
|
||||||
|
|||||||
@ -114,9 +114,11 @@ def get_items(filters):
|
|||||||
|
|
||||||
items = frappe.db.sql("""
|
items = frappe.db.sql("""
|
||||||
select
|
select
|
||||||
`tabSales Invoice Item`.name, `tabSales Invoice Item`.base_price_list_rate,
|
`tabSales Invoice Item`.gst_hsn_code,
|
||||||
`tabSales Invoice Item`.gst_hsn_code, `tabSales Invoice Item`.stock_qty,
|
`tabSales Invoice Item`.stock_uom,
|
||||||
`tabSales Invoice Item`.stock_uom, `tabSales Invoice Item`.base_net_amount,
|
sum(`tabSales Invoice Item`.stock_qty) as stock_qty,
|
||||||
|
sum(`tabSales Invoice Item`.base_net_amount) as base_net_amount,
|
||||||
|
sum(`tabSales Invoice Item`.base_price_list_rate) as base_price_list_rate,
|
||||||
`tabSales Invoice Item`.parent, `tabSales Invoice Item`.item_code,
|
`tabSales Invoice Item`.parent, `tabSales Invoice Item`.item_code,
|
||||||
`tabGST HSN Code`.description
|
`tabGST HSN Code`.description
|
||||||
from `tabSales Invoice`, `tabSales Invoice Item`, `tabGST HSN Code`
|
from `tabSales Invoice`, `tabSales Invoice Item`, `tabGST HSN Code`
|
||||||
@ -124,6 +126,8 @@ def get_items(filters):
|
|||||||
and `tabSales Invoice`.docstatus = 1
|
and `tabSales Invoice`.docstatus = 1
|
||||||
and `tabSales Invoice Item`.gst_hsn_code is not NULL
|
and `tabSales Invoice Item`.gst_hsn_code is not NULL
|
||||||
and `tabSales Invoice Item`.gst_hsn_code = `tabGST HSN Code`.name %s %s
|
and `tabSales Invoice Item`.gst_hsn_code = `tabGST HSN Code`.name %s %s
|
||||||
|
group by
|
||||||
|
`tabSales Invoice Item`.parent, `tabSales Invoice Item`.item_code
|
||||||
|
|
||||||
""" % (conditions, match_conditions), filters, as_dict=1)
|
""" % (conditions, match_conditions), filters, as_dict=1)
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,89 @@
|
|||||||
|
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
|
||||||
|
from unittest import TestCase
|
||||||
|
|
||||||
|
import frappe
|
||||||
|
|
||||||
|
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
||||||
|
from erpnext.regional.doctype.gstr_3b_report.test_gstr_3b_report import (
|
||||||
|
make_company as setup_company,
|
||||||
|
)
|
||||||
|
from erpnext.regional.doctype.gstr_3b_report.test_gstr_3b_report import (
|
||||||
|
make_customers as setup_customers,
|
||||||
|
)
|
||||||
|
from erpnext.regional.doctype.gstr_3b_report.test_gstr_3b_report import (
|
||||||
|
set_account_heads as setup_gst_settings,
|
||||||
|
)
|
||||||
|
from erpnext.regional.report.hsn_wise_summary_of_outward_supplies.hsn_wise_summary_of_outward_supplies import (
|
||||||
|
execute as run_report,
|
||||||
|
)
|
||||||
|
from erpnext.stock.doctype.item.test_item import make_item
|
||||||
|
|
||||||
|
|
||||||
|
class TestHSNWiseSummaryReport(TestCase):
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
setup_company()
|
||||||
|
setup_customers()
|
||||||
|
setup_gst_settings()
|
||||||
|
make_item("Golf Car", properties={ "gst_hsn_code": "999900" })
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def tearDownClass(cls):
|
||||||
|
frappe.db.rollback()
|
||||||
|
|
||||||
|
def test_hsn_summary_for_invoice_with_duplicate_items(self):
|
||||||
|
si = create_sales_invoice(
|
||||||
|
company="_Test Company GST",
|
||||||
|
customer = "_Test GST Customer",
|
||||||
|
currency = "INR",
|
||||||
|
warehouse = "Finished Goods - _GST",
|
||||||
|
debit_to = "Debtors - _GST",
|
||||||
|
income_account = "Sales - _GST",
|
||||||
|
expense_account = "Cost of Goods Sold - _GST",
|
||||||
|
cost_center = "Main - _GST",
|
||||||
|
do_not_save=1
|
||||||
|
)
|
||||||
|
|
||||||
|
si.items = []
|
||||||
|
si.append("items", {
|
||||||
|
"item_code": "Golf Car",
|
||||||
|
"gst_hsn_code": "999900",
|
||||||
|
"qty": "1",
|
||||||
|
"rate": "120",
|
||||||
|
"cost_center": "Main - _GST"
|
||||||
|
})
|
||||||
|
si.append("items", {
|
||||||
|
"item_code": "Golf Car",
|
||||||
|
"gst_hsn_code": "999900",
|
||||||
|
"qty": "1",
|
||||||
|
"rate": "140",
|
||||||
|
"cost_center": "Main - _GST"
|
||||||
|
})
|
||||||
|
si.append("taxes", {
|
||||||
|
"charge_type": "On Net Total",
|
||||||
|
"account_head": "Output Tax IGST - _GST",
|
||||||
|
"cost_center": "Main - _GST",
|
||||||
|
"description": "IGST @ 18.0",
|
||||||
|
"rate": 18
|
||||||
|
})
|
||||||
|
si.posting_date = "2020-11-17"
|
||||||
|
si.submit()
|
||||||
|
si.reload()
|
||||||
|
|
||||||
|
[columns, data] = run_report(filters=frappe._dict({
|
||||||
|
"company": "_Test Company GST",
|
||||||
|
"gst_hsn_code": "999900",
|
||||||
|
"company_gstin": si.company_gstin,
|
||||||
|
"from_date": si.posting_date,
|
||||||
|
"to_date": si.posting_date
|
||||||
|
}))
|
||||||
|
|
||||||
|
filtered_rows = list(filter(lambda row: row['gst_hsn_code'] == "999900", data))
|
||||||
|
self.assertTrue(filtered_rows)
|
||||||
|
|
||||||
|
hsn_row = filtered_rows[0]
|
||||||
|
self.assertEquals(hsn_row['stock_qty'], 2.0)
|
||||||
|
self.assertEquals(hsn_row['total_amount'], 306.8)
|
||||||
@ -2,8 +2,6 @@
|
|||||||
# License: GNU General Public License v3. See license.txt
|
# License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.test_runner import make_test_records
|
from frappe.test_runner import make_test_records
|
||||||
from frappe.utils import flt
|
from frappe.utils import flt
|
||||||
@ -11,7 +9,7 @@ from frappe.utils import flt
|
|||||||
from erpnext.accounts.party import get_due_date
|
from erpnext.accounts.party import get_due_date
|
||||||
from erpnext.exceptions import PartyDisabled, PartyFrozen
|
from erpnext.exceptions import PartyDisabled, PartyFrozen
|
||||||
from erpnext.selling.doctype.customer.customer import get_credit_limit, get_customer_outstanding
|
from erpnext.selling.doctype.customer.customer import get_credit_limit, get_customer_outstanding
|
||||||
from erpnext.tests.utils import create_test_contact_and_address
|
from erpnext.tests.utils import ERPNextTestCase, create_test_contact_and_address
|
||||||
|
|
||||||
test_ignore = ["Price List"]
|
test_ignore = ["Price List"]
|
||||||
test_dependencies = ['Payment Term', 'Payment Terms Template']
|
test_dependencies = ['Payment Term', 'Payment Terms Template']
|
||||||
@ -19,7 +17,7 @@ test_records = frappe.get_test_records('Customer')
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
class TestCustomer(unittest.TestCase):
|
class TestCustomer(ERPNextTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
if not frappe.get_value('Item', '_Test Item'):
|
if not frappe.get_value('Item', '_Test Item'):
|
||||||
make_test_records('Item')
|
make_test_records('Item')
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import unittest
|
|||||||
import frappe
|
import frappe
|
||||||
|
|
||||||
from erpnext.controllers.queries import item_query
|
from erpnext.controllers.queries import item_query
|
||||||
|
from erpnext.tests.utils import ERPNextTestCase
|
||||||
|
|
||||||
test_dependencies = ['Item', 'Customer', 'Supplier']
|
test_dependencies = ['Item', 'Customer', 'Supplier']
|
||||||
|
|
||||||
@ -17,7 +18,7 @@ def create_party_specific_item(**args):
|
|||||||
psi.based_on_value = args.get('based_on_value')
|
psi.based_on_value = args.get('based_on_value')
|
||||||
psi.insert()
|
psi.insert()
|
||||||
|
|
||||||
class TestPartySpecificItem(unittest.TestCase):
|
class TestPartySpecificItem(ERPNextTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.customer = frappe.get_last_doc("Customer")
|
self.customer = frappe.get_last_doc("Customer")
|
||||||
self.supplier = frappe.get_last_doc("Supplier")
|
self.supplier = frappe.get_last_doc("Supplier")
|
||||||
|
|||||||
@ -1,15 +1,15 @@
|
|||||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
# License: GNU General Public License v3. See license.txt
|
# License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.utils import add_days, add_months, flt, getdate, nowdate
|
from frappe.utils import add_days, add_months, flt, getdate, nowdate
|
||||||
|
|
||||||
|
from erpnext.tests.utils import ERPNextTestCase
|
||||||
|
|
||||||
test_dependencies = ["Product Bundle"]
|
test_dependencies = ["Product Bundle"]
|
||||||
|
|
||||||
|
|
||||||
class TestQuotation(unittest.TestCase):
|
class TestQuotation(ERPNextTestCase):
|
||||||
def test_make_quotation_without_terms(self):
|
def test_make_quotation_without_terms(self):
|
||||||
quotation = make_quotation(do_not_save=1)
|
quotation = make_quotation(do_not_save=1)
|
||||||
self.assertFalse(quotation.get('payment_schedule'))
|
self.assertFalse(quotation.get('payment_schedule'))
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
# License: GNU General Public License v3. See license.txt
|
# License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import unittest
|
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
import frappe.permissions
|
import frappe.permissions
|
||||||
@ -28,12 +27,14 @@ from erpnext.selling.doctype.sales_order.sales_order import (
|
|||||||
)
|
)
|
||||||
from erpnext.stock.doctype.item.test_item import make_item
|
from erpnext.stock.doctype.item.test_item import make_item
|
||||||
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
||||||
|
from erpnext.tests.utils import ERPNextTestCase
|
||||||
|
|
||||||
|
|
||||||
class TestSalesOrder(unittest.TestCase):
|
class TestSalesOrder(ERPNextTestCase):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
|
super().setUpClass()
|
||||||
cls.unlink_setting = int(frappe.db.get_value("Accounts Settings", "Accounts Settings",
|
cls.unlink_setting = int(frappe.db.get_value("Accounts Settings", "Accounts Settings",
|
||||||
"unlink_advance_payment_on_cancelation_of_order"))
|
"unlink_advance_payment_on_cancelation_of_order"))
|
||||||
|
|
||||||
@ -42,6 +43,7 @@ class TestSalesOrder(unittest.TestCase):
|
|||||||
# reset config to previous state
|
# reset config to previous state
|
||||||
frappe.db.set_value("Accounts Settings", "Accounts Settings",
|
frappe.db.set_value("Accounts Settings", "Accounts Settings",
|
||||||
"unlink_advance_payment_on_cancelation_of_order", cls.unlink_setting)
|
"unlink_advance_payment_on_cancelation_of_order", cls.unlink_setting)
|
||||||
|
super().tearDownClass()
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
frappe.set_user("Administrator")
|
frappe.set_user("Administrator")
|
||||||
|
|||||||
@ -2,8 +2,6 @@
|
|||||||
# For license information, please see license.txt
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
from frappe.utils import add_months, nowdate
|
from frappe.utils import add_months, nowdate
|
||||||
|
|
||||||
from erpnext.selling.doctype.sales_order.sales_order import make_material_request
|
from erpnext.selling.doctype.sales_order.sales_order import make_material_request
|
||||||
@ -11,9 +9,10 @@ from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_orde
|
|||||||
from erpnext.selling.report.pending_so_items_for_purchase_request.pending_so_items_for_purchase_request import (
|
from erpnext.selling.report.pending_so_items_for_purchase_request.pending_so_items_for_purchase_request import (
|
||||||
execute,
|
execute,
|
||||||
)
|
)
|
||||||
|
from erpnext.tests.utils import ERPNextTestCase
|
||||||
|
|
||||||
|
|
||||||
class TestPendingSOItemsForPurchaseRequest(unittest.TestCase):
|
class TestPendingSOItemsForPurchaseRequest(ERPNextTestCase):
|
||||||
def test_result_for_partial_material_request(self):
|
def test_result_for_partial_material_request(self):
|
||||||
so = make_sales_order()
|
so = make_sales_order()
|
||||||
mr=make_material_request(so.name)
|
mr=make_material_request(so.name)
|
||||||
|
|||||||
@ -2,15 +2,14 @@
|
|||||||
# For license information, please see license.txt
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
|
|
||||||
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
|
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
|
||||||
from erpnext.selling.report.sales_analytics.sales_analytics import execute
|
from erpnext.selling.report.sales_analytics.sales_analytics import execute
|
||||||
|
from erpnext.tests.utils import ERPNextTestCase
|
||||||
|
|
||||||
|
|
||||||
class TestAnalytics(unittest.TestCase):
|
class TestAnalytics(ERPNextTestCase):
|
||||||
def test_sales_analytics(self):
|
def test_sales_analytics(self):
|
||||||
frappe.db.sql("delete from `tabSales Order` where company='_Test Company 2'")
|
frappe.db.sql("delete from `tabSales Order` where company='_Test Company 2'")
|
||||||
|
|
||||||
|
|||||||
@ -43,9 +43,9 @@ class Bin(Document):
|
|||||||
frappe.qb
|
frappe.qb
|
||||||
.from_(wo)
|
.from_(wo)
|
||||||
.from_(wo_item)
|
.from_(wo_item)
|
||||||
.select(Case()
|
.select(Sum(Case()
|
||||||
.when(wo.skip_transfer == 0, Sum(wo_item.required_qty - wo_item.transferred_qty))
|
.when(wo.skip_transfer == 0, wo_item.required_qty - wo_item.transferred_qty)
|
||||||
.else_(Sum(wo_item.required_qty - wo_item.consumed_qty))
|
.else_(wo_item.required_qty - wo_item.consumed_qty))
|
||||||
)
|
)
|
||||||
.where(
|
.where(
|
||||||
(wo_item.item_code == self.item_code)
|
(wo_item.item_code == self.item_code)
|
||||||
|
|||||||
@ -361,8 +361,7 @@
|
|||||||
"fieldname": "valuation_method",
|
"fieldname": "valuation_method",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"label": "Valuation Method",
|
"label": "Valuation Method",
|
||||||
"options": "\nFIFO\nMoving Average",
|
"options": "\nFIFO\nMoving Average"
|
||||||
"set_only_once": 1
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "is_stock_item",
|
"depends_on": "is_stock_item",
|
||||||
@ -1035,7 +1034,7 @@
|
|||||||
"image_field": "image",
|
"image_field": "image",
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-12-03 08:32:03.869294",
|
"modified": "2021-12-14 04:13:16.857534",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Item",
|
"name": "Item",
|
||||||
|
|||||||
@ -1,451 +1,140 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"actions": [],
|
||||||
"allow_import": 0,
|
|
||||||
"allow_rename": 0,
|
|
||||||
"autoname": "hash",
|
"autoname": "hash",
|
||||||
"beta": 0,
|
|
||||||
"creation": "2013-04-08 13:10:16",
|
"creation": "2013-04-08 13:10:16",
|
||||||
"custom": 0,
|
|
||||||
"docstatus": 0,
|
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"document_type": "Document",
|
"document_type": "Document",
|
||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"item_code",
|
||||||
|
"column_break_2",
|
||||||
|
"item_name",
|
||||||
|
"batch_no",
|
||||||
|
"desc_section",
|
||||||
|
"description",
|
||||||
|
"quantity_section",
|
||||||
|
"qty",
|
||||||
|
"net_weight",
|
||||||
|
"column_break_10",
|
||||||
|
"stock_uom",
|
||||||
|
"weight_uom",
|
||||||
|
"page_break",
|
||||||
|
"dn_detail"
|
||||||
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "item_code",
|
"fieldname": "item_code",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 1,
|
"in_global_search": 1,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Item Code",
|
"label": "Item Code",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Item",
|
"options": "Item",
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"print_width": "100px",
|
"print_width": "100px",
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0,
|
|
||||||
"width": "100px"
|
"width": "100px"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "column_break_2",
|
"fieldname": "column_break_2",
|
||||||
"fieldtype": "Column Break",
|
"fieldtype": "Column Break"
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"fetch_from": "item_code.item_name",
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "item_name",
|
"fieldname": "item_name",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Item Name",
|
"label": "Item Name",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "item_code.item_name",
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"print_width": "200px",
|
"print_width": "200px",
|
||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0,
|
|
||||||
"width": "200px"
|
"width": "200px"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "batch_no",
|
"fieldname": "batch_no",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Batch No",
|
"label": "Batch No",
|
||||||
"length": 0,
|
"options": "Batch"
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Batch",
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 1,
|
"collapsible": 1,
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "desc_section",
|
"fieldname": "desc_section",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"hidden": 0,
|
"label": "Description"
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Description",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "description",
|
"fieldname": "description",
|
||||||
"fieldtype": "Text Editor",
|
"fieldtype": "Text Editor",
|
||||||
"hidden": 0,
|
"label": "Description"
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Description",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "quantity_section",
|
"fieldname": "quantity_section",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"hidden": 0,
|
"label": "Quantity"
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Quantity",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "qty",
|
"fieldname": "qty",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Quantity",
|
"label": "Quantity",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"print_width": "100px",
|
"print_width": "100px",
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0,
|
|
||||||
"width": "100px"
|
"width": "100px"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "net_weight",
|
"fieldname": "net_weight",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Net Weight",
|
"label": "Net Weight",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"print_width": "100px",
|
"print_width": "100px",
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0,
|
|
||||||
"width": "100px"
|
"width": "100px"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "column_break_10",
|
"fieldname": "column_break_10",
|
||||||
"fieldtype": "Column Break",
|
"fieldtype": "Column Break"
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "stock_uom",
|
"fieldname": "stock_uom",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "UOM",
|
"label": "UOM",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "UOM",
|
"options": "UOM",
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"print_width": "100px",
|
"print_width": "100px",
|
||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0,
|
|
||||||
"width": "100px"
|
"width": "100px"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "weight_uom",
|
"fieldname": "weight_uom",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Weight UOM",
|
"label": "Weight UOM",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "UOM",
|
"options": "UOM",
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"print_width": "100px",
|
"print_width": "100px",
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0,
|
|
||||||
"width": "100px"
|
"width": "100px"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 1,
|
"allow_on_submit": 1,
|
||||||
"bold": 0,
|
"default": "0",
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "page_break",
|
"fieldname": "page_break",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
"label": "Page Break"
|
||||||
"label": "Page Break",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "dn_detail",
|
"fieldname": "dn_detail",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
"label": "DN Detail"
|
||||||
"label": "DN Detail",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"hide_heading": 0,
|
|
||||||
"hide_toolbar": 0,
|
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"image_view": 0,
|
|
||||||
"in_create": 0,
|
|
||||||
|
|
||||||
"is_submittable": 0,
|
|
||||||
"issingle": 0,
|
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"max_attachments": 0,
|
"links": [],
|
||||||
"modified": "2018-06-01 07:21:58.220980",
|
"modified": "2021-12-14 01:22:00.715935",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Packing Slip Item",
|
"name": "Packing Slip Item",
|
||||||
|
"naming_rule": "Random",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [],
|
"permissions": [],
|
||||||
"quick_entry": 0,
|
"sort_field": "modified",
|
||||||
"read_only": 0,
|
"sort_order": "DESC",
|
||||||
"read_only_onload": 0,
|
"track_changes": 1
|
||||||
"show_name_in_global_search": 0,
|
|
||||||
"track_changes": 1,
|
|
||||||
"track_seen": 0
|
|
||||||
}
|
}
|
||||||
@ -48,6 +48,7 @@ def get_item_info(filters):
|
|||||||
conditions = [get_item_group_condition(filters.get("item_group"))]
|
conditions = [get_item_group_condition(filters.get("item_group"))]
|
||||||
if filters.get("brand"):
|
if filters.get("brand"):
|
||||||
conditions.append("item.brand=%(brand)s")
|
conditions.append("item.brand=%(brand)s")
|
||||||
|
conditions.append("is_stock_item = 1")
|
||||||
|
|
||||||
return frappe.db.sql("""select name, item_name, description, brand, item_group,
|
return frappe.db.sql("""select name, item_name, description, brand, item_group,
|
||||||
safety_stock, lead_time_days from `tabItem` item where {}"""
|
safety_stock, lead_time_days from `tabItem` item where {}"""
|
||||||
|
|||||||
@ -420,13 +420,13 @@ def handle_status_change(doc, apply_sla_for_resolution):
|
|||||||
if is_open_status(prev_status) and is_hold_status(doc.status):
|
if is_open_status(prev_status) and is_hold_status(doc.status):
|
||||||
# Issue is on hold -> Set on_hold_since
|
# Issue is on hold -> Set on_hold_since
|
||||||
doc.on_hold_since = now_time
|
doc.on_hold_since = now_time
|
||||||
|
reset_expected_response_and_resolution(doc)
|
||||||
|
|
||||||
# Replied to Open
|
# Replied to Open
|
||||||
if is_hold_status(prev_status) and is_open_status(doc.status):
|
if is_hold_status(prev_status) and is_open_status(doc.status):
|
||||||
# Issue was on hold -> Calculate Total Hold Time
|
# Issue was on hold -> Calculate Total Hold Time
|
||||||
calculate_hold_hours()
|
calculate_hold_hours()
|
||||||
# Issue is open -> reset resolution_date
|
# Issue is open -> reset resolution_date
|
||||||
reset_expected_response_and_resolution(doc)
|
|
||||||
reset_resolution_metrics(doc)
|
reset_resolution_metrics(doc)
|
||||||
|
|
||||||
# Open to Closed
|
# Open to Closed
|
||||||
@ -440,7 +440,6 @@ def handle_status_change(doc, apply_sla_for_resolution):
|
|||||||
# Issue was closed -> Calculate Total Hold Time from resolution_date
|
# Issue was closed -> Calculate Total Hold Time from resolution_date
|
||||||
calculate_hold_hours()
|
calculate_hold_hours()
|
||||||
# Issue is open -> reset resolution_date
|
# Issue is open -> reset resolution_date
|
||||||
reset_expected_response_and_resolution(doc)
|
|
||||||
reset_resolution_metrics(doc)
|
reset_resolution_metrics(doc)
|
||||||
|
|
||||||
# Closed to Replied
|
# Closed to Replied
|
||||||
@ -449,6 +448,7 @@ def handle_status_change(doc, apply_sla_for_resolution):
|
|||||||
calculate_hold_hours()
|
calculate_hold_hours()
|
||||||
# Issue is on hold -> Set on_hold_since
|
# Issue is on hold -> Set on_hold_since
|
||||||
doc.on_hold_since = now_time
|
doc.on_hold_since = now_time
|
||||||
|
reset_expected_response_and_resolution(doc)
|
||||||
|
|
||||||
# Replied to Closed
|
# Replied to Closed
|
||||||
if is_hold_status(prev_status) and is_fulfilled_status(doc.status):
|
if is_hold_status(prev_status) and is_fulfilled_status(doc.status):
|
||||||
@ -640,28 +640,35 @@ def on_communication_update(doc, status):
|
|||||||
if not parent.meta.has_field('service_level_agreement'):
|
if not parent.meta.has_field('service_level_agreement'):
|
||||||
return
|
return
|
||||||
|
|
||||||
for_resolution = frappe.db.get_value('Service Level Agreement', parent.service_level_agreement, 'apply_sla_for_resolution')
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
doc.sent_or_received == "Received" # a reply is received
|
doc.sent_or_received == "Received" # a reply is received
|
||||||
and parent.get('status') == 'Open' # issue status is set as open from communication.py
|
and parent.get('status') == 'Open' # issue status is set as open from communication.py
|
||||||
and parent._doc_before_save
|
and parent.get_doc_before_save()
|
||||||
and parent.get('status') != parent._doc_before_save.get('status') # status changed
|
and parent.get('status') != parent._doc_before_save.get('status') # status changed
|
||||||
):
|
):
|
||||||
# undo the status change in db
|
# undo the status change in db
|
||||||
# since prev status is fetched from db
|
# since prev status is fetched from db
|
||||||
frappe.db.set_value(parent.doctype, parent.name, 'status', parent._doc_before_save.get('status'))
|
frappe.db.set_value(
|
||||||
|
parent.doctype, parent.name,
|
||||||
|
'status', parent._doc_before_save.get('status'),
|
||||||
|
update_modified=False
|
||||||
|
)
|
||||||
|
|
||||||
elif (
|
elif (
|
||||||
doc.sent_or_received == "Sent" # a reply is sent
|
doc.sent_or_received == "Sent" # a reply is sent
|
||||||
and parent.get('first_responded_on') # first_responded_on is set from communication.py
|
and parent.get('first_responded_on') # first_responded_on is set from communication.py
|
||||||
and parent._doc_before_save
|
and parent.get_doc_before_save()
|
||||||
and not parent._doc_before_save.get('first_responded_on') # first_responded_on was not set
|
and not parent._doc_before_save.get('first_responded_on') # first_responded_on was not set
|
||||||
):
|
):
|
||||||
# reset first_responded_on since it will be handled/set later on
|
# reset first_responded_on since it will be handled/set later on
|
||||||
parent.first_responded_on = None
|
parent.first_responded_on = None
|
||||||
parent.flags.on_first_reply = True
|
parent.flags.on_first_reply = True
|
||||||
|
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
||||||
|
for_resolution = frappe.db.get_value('Service Level Agreement', parent.service_level_agreement, 'apply_sla_for_resolution')
|
||||||
|
|
||||||
handle_status_change(parent, for_resolution)
|
handle_status_change(parent, for_resolution)
|
||||||
update_response_and_resolution_metrics(parent, for_resolution)
|
update_response_and_resolution_metrics(parent, for_resolution)
|
||||||
update_agreement_status(parent, for_resolution)
|
update_agreement_status(parent, for_resolution)
|
||||||
@ -670,12 +677,10 @@ def on_communication_update(doc, status):
|
|||||||
|
|
||||||
|
|
||||||
def reset_expected_response_and_resolution(doc):
|
def reset_expected_response_and_resolution(doc):
|
||||||
update_values = {}
|
|
||||||
if doc.meta.has_field("first_responded_on") and not doc.get('first_responded_on'):
|
if doc.meta.has_field("first_responded_on") and not doc.get('first_responded_on'):
|
||||||
update_values['response_by'] = None
|
doc.response_by = None
|
||||||
if doc.meta.has_field("resolution_by") and not doc.get('resolution_date'):
|
if doc.meta.has_field("resolution_by") and not doc.get('resolution_date'):
|
||||||
update_values['resolution_by'] = None
|
doc.resolution_by = None
|
||||||
doc.db_set(update_values)
|
|
||||||
|
|
||||||
|
|
||||||
def set_response_by(doc, start_date_time, priority):
|
def set_response_by(doc, start_date_time, priority):
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user