Skip to content

Commit

Permalink
Merge pull request #3697 from ONE-F-M/staging
Browse files Browse the repository at this point in the history
Sprint 134 - Staging to Test-Production(V15.1.3)
  • Loading branch information
pjamsheer authored Oct 10, 2024
2 parents 0c3e392 + 9ebbaf3 commit a43bc66
Show file tree
Hide file tree
Showing 14 changed files with 282 additions and 164 deletions.
2 changes: 1 addition & 1 deletion one_fm/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
from one_fm.permissions import get_custom_user_permissions


__version__ = '15.1.2'
__version__ = '15.1.3'

user_permission.get_user_permissions = get_custom_user_permissions
StockController.make_batches = make_batches_with_supplier_batch_id
Expand Down
11 changes: 11 additions & 0 deletions one_fm/change_log/v15/v15_1_3.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Version 15.1.3 Release Notes

### Feature Update
- Interview Question fetch in the order provided in Template
- Client Confirmation copy attachment in Sales Invoice
- Shows external projects only in MoM
- More space in interview feedback answer field
- Client Confirmation copy attachment in Sales Invoice

### Bug Fixes
- Remove attendance creation for today joined employees
2 changes: 1 addition & 1 deletion one_fm/hiring/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -684,7 +684,7 @@ def get_interview_skill_and_question_set(interview_round, interviewer=False, int
interview_feedback = frappe.get_doc('Interview Feedback', feedback_exists)
return interview_feedback.interview_question_assessment, interview_feedback.skill_assessment, feedback_exists
else:
question = frappe.get_all('Interview Questions', filters ={'parent': interview_round}, fields=['questions', 'answer', 'weight'])
question = frappe.get_all('Interview Questions', filters ={'parent': interview_round}, fields=['questions', 'answer', 'weight'], order_by='idx')
skill = frappe.get_all('Expected Skill Set', filters ={'parent': interview_round}, fields=['skill'])
return question, skill, False

Expand Down
240 changes: 153 additions & 87 deletions one_fm/one_fm/custom/sales_invoice.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions one_fm/one_fm/sales_invoice_custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -941,6 +941,8 @@ def calculate_total_working_days(project = None, item_code = None, first_day =No
return total_working_day

def before_submit_sales_invoice(doc, method):
if not doc.custom_client_confirmation_copy:
frappe.throw('Please Attach Client Confirmation Copy')
if doc.contracts:
is_po_for_invoice = frappe.db.get_value('Contracts', doc.contracts, 'is_po_for_invoice')
if is_po_for_invoice == 1 and not doc.po:
Expand Down
102 changes: 52 additions & 50 deletions one_fm/operations/doctype/contracts/contracts.py
Original file line number Diff line number Diff line change
Expand Up @@ -1028,56 +1028,58 @@ def send_contract_reminders(is_scheduled_event=True):
Args:
is_scheduled_event -> Boolean (Default True) If method is triggered from anywhere else than the scheduled event, Pass "False" to avoid email trigger check from "ONEFM General Setting"
"""

contracts_due_internal_notification = frappe.get_all("Contracts",{'contract_end_internal_notification_date':getdate()},['contract_end_internal_notification',\
'contract_end_internal_notification_date','engagement_type','contract_termination_decision_period','contract_termination_decision_period_date','name','start_date','end_date','duration','client'])
contracts_due_termination_notification = frappe.get_all("Contracts",{'contract_termination_decision_period_date':getdate()},['contract_end_internal_notification',\
'contract_end_internal_notification_date','engagement_type','contract_termination_decision_period','contract_termination_decision_period_date','name','start_date','end_date','duration','client'])

relevant_roles = ["Finance Manager",'Legal Manager','Projects Manager','Operations Manager']
active_users = frappe.get_all("User",{'enabled':1})
active_users_ = [i.name for i in active_users] if active_users else []
active_users_.remove("Administrator")
relevant_users = frappe.get_all("Has Role",{'role':['IN',relevant_roles],'parent':['IN',active_users_]},['distinct parent'])
users = [i.parent for i in relevant_users]
if contracts_due_internal_notification:
contracts_due_internal_notification_list = [[i.contract_termination_decision_period,i.contract_end_internal_notification,\
get_date_str(i.contract_termination_decision_period_date),i.name,get_date_str(i.start_date),get_date_str(i.contract_end_internal_notification_date),\
get_date_str(i.end_date),i.duration,i.client,i.engagement_type, i.contract] for i in contracts_due_internal_notification]
for each in contracts_due_internal_notification_list:
context = {"project": each[8],
"contract_end_internal_notif_period": each[1],
"start_date": each[4],
"engagement_type": each[9],
"contract_end_internal_notif_date": each[5],
"contract_termination_decision_period": each[0],
"contract_termination_decision_date": each[2],
"end_date": each[6],
"duration": each[7],
"document_id": each[3],
"link": frappe.utils.get_url_to_form("Contracts",each[3]),
"attachment": frappe.utils.get_url(each[10]) if each[10] else None}
msg = frappe.render_template('one_fm/templates/emails/contracts_reminder.html', context=context)
sendemail(recipients=[users], subject="Expiring Contracts", content=msg, is_scheduler_email=is_scheduled_event)
if contracts_due_termination_notification:
contracts_due_termination_notification_list = [[i.contract_termination_decision_period,i.contract_end_internal_notification,\
get_date_str(i.contract_termination_decision_period_date),i.name,get_date_str(i.start_date),get_date_str(i.contract_end_internal_notification_date),\
get_date_str(i.end_date),i.duration,i.client,i.engagement_type, i.contract] for i in contracts_due_termination_notification]
for each in contracts_due_termination_notification_list:
context = {"project": each[8],
"contract_end_internal_notif_period": each[1],
"start_date": each[4],
"engagement_type": each[9],
"contract_end_internal_notif_date": each[5],
"contract_termination_decision_period": each[0],
"contract_termination_decision_date": each[2],
"end_date": each[6],
"duration": each[7],
"document_id": each[3],
"link": frappe.utils.get_url_to_form("Contracts",each[3]),
"attachment": frappe.utils.get_url(each[10]) if each[10] else None}
msg = frappe.render_template('one_fm/templates/emails/contracts_reminder.html', context=context)
sendemail(recipients=[users], subject="Expiring Contracts", content=msg,is_scheduler_email=is_scheduled_event)
try:
contracts_due_internal_notification = frappe.get_all("Contracts",{'contract_end_internal_notification_date':getdate()},['contract_end_internal_notification',\
'contract_end_internal_notification_date','engagement_type','contract_termination_decision_period','contract_termination_decision_period_date','name','start_date','end_date','duration','client'])
contracts_due_termination_notification = frappe.get_all("Contracts",{'contract_termination_decision_period_date':getdate()},['contract_end_internal_notification',\
'contract_end_internal_notification_date','engagement_type','contract_termination_decision_period','contract_termination_decision_period_date','name','start_date','end_date','duration','client'])

relevant_roles = ["Finance Manager",'Legal Manager','Projects Manager','Operations Manager']
active_users = frappe.get_all("User",{'enabled':1})
active_users_ = [i.name for i in active_users] if active_users else []
active_users_.remove("Administrator")
relevant_users = frappe.get_all("Has Role",{'role':['IN',relevant_roles],'parent':['IN',active_users_]},['distinct parent'])
users = [i.parent for i in relevant_users]
if contracts_due_internal_notification:
contracts_due_internal_notification_list = [[i.contract_termination_decision_period,i.contract_end_internal_notification,\
get_date_str(i.contract_termination_decision_period_date),i.name,get_date_str(i.start_date),get_date_str(i.contract_end_internal_notification_date),\
get_date_str(i.end_date),i.duration,i.client,i.engagement_type, i.contract] for i in contracts_due_internal_notification]
for each in contracts_due_internal_notification_list:
context = {"project": each[8],
"contract_end_internal_notif_period": each[1],
"start_date": each[4],
"engagement_type": each[9],
"contract_end_internal_notif_date": each[5],
"contract_termination_decision_period": each[0],
"contract_termination_decision_date": each[2],
"end_date": each[6],
"duration": each[7],
"document_id": each[3],
"link": frappe.utils.get_url_to_form("Contracts",each[3]),
"attachment": frappe.utils.get_url(each[10]) if each[10] else None}
msg = frappe.render_template('one_fm/templates/emails/contracts_reminder.html', context=context)
sendemail(recipients=users, subject="Expiring Contracts", content=msg, is_scheduler_email=is_scheduled_event)
if contracts_due_termination_notification:
contracts_due_termination_notification_list = [[i.contract_termination_decision_period,i.contract_end_internal_notification,\
get_date_str(i.contract_termination_decision_period_date),i.name,get_date_str(i.start_date),get_date_str(i.contract_end_internal_notification_date),\
get_date_str(i.end_date),i.duration,i.client,i.engagement_type, i.contract] for i in contracts_due_termination_notification]
for each in contracts_due_termination_notification_list:
context = {"project": each[8],
"contract_end_internal_notif_period": each[1],
"start_date": each[4],
"engagement_type": each[9],
"contract_end_internal_notif_date": each[5],
"contract_termination_decision_period": each[0],
"contract_termination_decision_date": each[2],
"end_date": each[6],
"duration": each[7],
"document_id": each[3],
"link": frappe.utils.get_url_to_form("Contracts",each[3]),
"attachment": frappe.utils.get_url(each[10]) if each[10] else None}
msg = frappe.render_template('one_fm/templates/emails/contracts_reminder.html', context=context)
sendemail(recipients=users, subject="Expiring Contracts", content=msg,is_scheduler_email=is_scheduled_event)
except Exception as e:
frappe.log_error(e)

@frappe.whitelist()
def renew_contracts_by_termination_date():
Expand Down
4 changes: 3 additions & 1 deletion one_fm/operations/doctype/mom/mom.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"fieldtype": "Link",
"hidden": 1,
"label": "Site",
"mandatory_depends_on": "eval:doc.project_type == 'External'",
"options": "Operations Site"
},
{
Expand Down Expand Up @@ -199,6 +200,7 @@
"fieldname": "supervisor",
"fieldtype": "Link",
"label": "Supervisor",
"mandatory_depends_on": "eval:doc.project_type == 'External'",
"options": "Employee"
},
{
Expand All @@ -216,7 +218,7 @@
],
"is_submittable": 1,
"links": [],
"modified": "2024-09-30 14:17:34.273265",
"modified": "2024-10-01 12:49:10.048427",
"modified_by": "Administrator",
"module": "Operations",
"name": "MOM",
Expand Down
2 changes: 2 additions & 0 deletions one_fm/overrides/attendance.py
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,7 @@ def mark_daily_attendance(start_date, end_date):
"{owner}", "{creation}", "{creation}", "{i.description}"
),"""

today = getdate()
# Mark DayOff Attendance
#Find Employees with no schedule but have Day Off in the company holiday. Mainly for head office employees
day_off_no_schedule = frappe.db.sql(f"""
Expand All @@ -566,6 +567,7 @@ def mark_daily_attendance(start_date, end_date):
AND '{start_date}' BETWEEN hl.from_date AND hl.to_date
AND e.status='Active'
AND e.name NOT IN (SELECT employee from `tabEmployee Schedule`es where es.date = '{start_date}' AND es.employee_availability='Day Off')
AND e.date_of_joining < '{today}'
""",
as_dict=1)

Expand Down
8 changes: 2 additions & 6 deletions one_fm/overrides/attendance_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,8 @@ def get_shift_assignment(self, attendance_date):
"""
Check if shift exist for employee
"""
if(frappe.db.exists("Shift Assignment",
{'employee':self.employee, 'docstatus':1, 'status':'Active', 'start_date':attendance_date})):
shift_assignment = frappe.db.get_list("Shift Assignment",
{'employee':self.employee, 'docstatus':1, 'status':'Active', 'start_date':attendance_date})
return frappe.get_doc("Shift Assignment", shift_assignment[0].name)
return False
shift_check = frappe.db.exists("Shift Assignment",{'employee':self.employee, 'docstatus':1, 'status':'Active', 'start_date':attendance_date})
return frappe.get_doc("Shift Assignment", shift_check) if shift_check else False

def create_attendance(self):
date_range = pd.date_range(self.from_date, self.to_date)
Expand Down
3 changes: 2 additions & 1 deletion one_fm/patches.txt
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,5 @@ one_fm.patches.v15_0.patch_employee_checkin
one_fm.patches.v15_0.submit_shift_permissions
one_fm.patches.v15_0.update_shift_request
one_fm.patches.v15_0.add_task_assignments_to_form_field
one_fm.patches.v15_0.disable_wrong_shift_request
one_fm.patches.v15_0.disable_wrong_shift_request
one_fm.patches.v15_0.update_job_offer_00057_erf
10 changes: 10 additions & 0 deletions one_fm/patches/v15_0/update_job_offer_00057_erf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import frappe

def execute():
frappe.db.sql(
"""
UPDATE `tabJob Offer`
SET one_fm_erf = 'ERF-2024-00031'
WHERE name = 'HR-OFF-2024-00577'
"""
)
8 changes: 4 additions & 4 deletions one_fm/public/faq/js/chatgpt.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,13 @@ function generateResponse(prompt){
// Here you can add your answer-generating code
if(prompt){
frappe.call({
method: 'one_fm.www.knowledge_base.chatgpt.get_completion',
args: {'prompt': prompt},
method: 'one_fm.wiki_chat_bot.main.ask_question',
args: {'question': prompt},
callback: function(r) {
console.log(r)

if(r.message != 'None') {
$('#chat-bubble').hide();
addMessage("received", r.message);
addMessage("received", r.data.answer);
}
else{
$('#chat-bubble').hide();
Expand Down
3 changes: 1 addition & 2 deletions one_fm/public/js/doctype_js/interview.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,6 @@ frappe.ui.form.on('Interview', {
primary_action_label: __("Save"),
primary_action: function(values) {
create_interview_feedback(frm, values, feedback_exists, 'save');
d.hide();
},
secondary_action_label: __("Save and Submit"),
secondary_action: function() {
Expand All @@ -130,7 +129,7 @@ frappe.ui.form.on('Interview', {
fieldname: 'weight',
label: __('Weight'),
}, {
fieldtype: 'Data',
fieldtype: 'Small Text',
fieldname: 'applicant_answer',
label: __('Applicant Answer'),
in_list_view: 1,
Expand Down
49 changes: 38 additions & 11 deletions one_fm/wiki_chat_bot/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,21 @@
import json

import frappe
from llama_index.core import SimpleDirectoryReader,PromptHelper,VectorStoreIndex
from langchain.llms import OpenAI
from llama_index.core import SimpleDirectoryReader,PromptHelper,VectorStoreIndex,PromptTemplate
from llama_index.llms.openai import OpenAI
from llama_index.embeddings.openai import OpenAIEmbedding
from llama_index.core import StorageContext, load_index_from_storage

from one_fm.api.v1.utils import response



from one_fm.api.v1.utils import response

def create_vector_index():
try:
os.environ["OPENAI_API_KEY"] = frappe.local.conf.CHATGPT_APIKEY
docs = SimpleDirectoryReader(get_folder_path()).load_data()
vector_index = VectorStoreIndex.from_documents(docs)

vector_index.storage_context.persist(persist_dir="vector_index.json")
embedding_model = OpenAIEmbedding(model_name="gpt-4o-mini")
vector_index = VectorStoreIndex.from_documents(docs,embedding=embedding_model)
vector_index.storage_context.persist(persist_dir="vector_index")
return vector_index
except:
frappe.log_error(frappe.get_traceback(), "Error while adding to bot memory(Chat-BOT)")
Expand All @@ -28,10 +28,37 @@ def ask_question(question: str = None):
os.environ["OPENAI_API_KEY"] = frappe.local.conf.CHATGPT_APIKEY
if not question:
return response("Bad Request !", 400, error="Question can not be empty")
storage_context = StorageContext.from_defaults(persist_dir="vector_index.json")
storage_context = StorageContext.from_defaults(persist_dir="vector_index")
index = load_index_from_storage(storage_context)

query_engine = index.as_query_engine()
prompt_template_str = (
"Context information is below.\n"
"---------------------\n"
"{context_str}\n"
"---------------------\n"
"Given the context information and not prior knowledge, "
"You do not need to introduce yourself or say who you are when you are not asked directly\n"
"You are an AI assistant called Lumina.\n"
"You Work for One Faciities Management, A company with it's Headquarters in Kuwait\n"
"Whenever Lumina does not find the required data,ask the user to upload the most updated data to enable you answer the question appropriately\n"
"Respond to the user in the same language they use"
"Query: {query_str}\n"
"Answer: "
)
refine_prompt_str = (
"We have the opportunity to refine the original answer "
"(only if needed) with some more context below.\n"
"------------\n"
"If you did not respond to the question in the same language it was asked,translate your answer to the same language as the question unless the preferred languaged was specified in the query\n"
"------------\n"
"Given the new context, refine the original answer to better "
"answer the question: {query_str}. "

"Original Answer: {existing_answer}"
)
text_qa_template = PromptTemplate(prompt_template_str)
refined_text_qa_template = PromptTemplate(refine_prompt_str)
llm = OpenAI(model="gpt-4o-mini")
query_engine = index.as_query_engine(llm=llm,text_qa_template=text_qa_template,refine_template=refined_text_qa_template)
answer = query_engine.query(question)
return response(message="Success", status_code=200, data={"question": question, "answer": answer.response})
except Exception as e:
Expand Down

0 comments on commit a43bc66

Please sign in to comment.