This commit is contained in:
Deena 2025-09-17 15:08:29 +05:30
parent ebdc1d3ca1
commit ceb5e58e3e
90 changed files with 2394 additions and 460 deletions

View File

@ -11,7 +11,7 @@
'version': '17.0.1.0.0',
# any module necessary for this one to work correctly
'depends': ['base','web','mail'],
'depends': ['base','web','mail','one2many_search_widget'],
# always loaded
'data': [
@ -19,6 +19,7 @@
'security/record_rules.xml',
'security/ir.model.access.csv',
'data/cron_jobs.xml',
'views/pareto_chart_widget_view.xml',
'views/sos_audit_log_view.xml',
'views/menu.xml',
'views/sos_quote_generation.xml',
@ -26,6 +27,7 @@
'views/sos_fg_view.xml',
'views/sos_sfg_view.xml',
'views/sos_material_view.xml',
'views/sos_budget_plan_view.xml',
'views/sos_material_bom_view.xml',
'views/sos_sfg_bom_view.xml',
'views/sos_fg_bom_view.xml',
@ -81,6 +83,8 @@
'views/sos_service_call_log_report.xml',
'views/sos_transfer_challan_return_from_customer_view.xml',
'views/sos_shelflife_register_view.xml',
'views/sos_prf_view.xml',
'wizard/sfg_bom_bulk_upload_view.xml',
'wizard/mon_bulk_upload_view.xml',
'wizard/missing_component_wizard.xml',
@ -115,6 +119,7 @@
'report/sos_boq_labels.xml',
'report/shelflife_report.xml',
'report/sos_boq_report.xml',
'report/sos_budget_plan_summary.xml',
'data/send_indent_plan_email_template.xml',
'data/selection_item.xml'
@ -130,6 +135,8 @@
'sos_inventory/static/src/components/**/*.js',
'sos_inventory/static/src/components/**/*.xml',
'sos_inventory/static/src/components/**/*.scss',
'sos_inventory/static/src/js/pareto_chart_widget.js',
'sos_inventory/static/src/xml/pareto_chart_template.xml',

View File

@ -4,7 +4,9 @@ import io
import xlsxwriter
class MaterialBackupExportController(http.Controller):
@http.route('/supplier/form', auth='public')
def index(self, **kw):
return "Hello, world"
@http.route(['/download/material/backup/<string:year>/<string:item_type>'], type='http', auth='user')
def download_backup_by_year(self, year, item_type, **kwargs):
# Safely map table names

View File

@ -59,3 +59,5 @@ from . import sos_service_call_log_report
from . import sos_inhouse_validation_reports_files
from . import sos_transfer_challan_return_from_customer
from . import sos_shelflife_register
from . import sos_budget_plan
from . import sos_prf

View File

@ -0,0 +1,159 @@
# -*- coding: utf-8 -*-
from odoo import models, fields, api
from odoo.exceptions import UserError
from math import ceil
class SOS_Budget_Plan(models.Model):
_name = 'sos_budget_plan'
_description = 'Budget Plan'
name= fields.Char(string="Plan Name")
sfg_option = fields.Boolean('Semi-Finished Goods')
fg_option = fields.Boolean('Finished Goods',default=True)
fg_name = fields.Many2one('sos_fg', string='FG Name')
sfg_name = fields.Many2one('sos_sfg', string="SFG Name")
quantity = fields.Integer(string="Quantity",required=True)
@api.onchange('sfg_option')
def _onchange_sfg_option(self):
if self.sfg_option:
self.fg_option = False
@api.onchange('fg_option')
def _onchange_fg_option(self):
if self.fg_option:
self.sfg_option = False
def create_budget_plan(self):
self.ensure_one()
if not self.quantity:
raise UserError("Quantity must be greater than zero.")
if not (self.fg_option or getattr(self, 'sfg_option', False)):
raise UserError("Select Finished Goods or SFG option.")
# --- Accumulator keyed by product/component id ---
# { prod_id: {'name': str, 'needed': float, 'inhand': float, 'price': float} }
agg = {}
def _accumulate(line, needed_qty):
"""Accumulate needed qty for a line's primary component."""
prod = line.primary_component_id
if not prod:
return
pid = prod.id
rec = agg.get(pid)
if rec:
rec['needed'] += float(needed_qty or 0.0)
else:
agg[pid] = {
'name': prod.part_no or prod.name or '',
'needed': float(needed_qty or 0.0),
'inhand': float(prod.inhand_stock_qty or 0.0), # take once per product
'price': float(prod.unit_price or 0.0),
'pack': float(getattr(prod, 'std_packing_qty', 0.0) or 0.0),
}
label_name = "" # what we'll display in the report header
if self.fg_option:
# -------- FG → explode SFGs + add direct materials --------
product_bom = self.env['sos_fg_bom'].search([
('fg_name', '=', self.fg_name.id),
('is_primary', '=', True)
], limit=1)
if not product_bom:
raise UserError("BOM for product is Missing")
label_name = self.fg_name.display_name
# 1) FG BOM lines that reference SFG BOMs
fg_sfg_lines = self.env['sos_fg_bom_line'].search([('bom_id', '=', product_bom.id)])
for sfg_line in fg_sfg_lines:
sfg_bom = sfg_line.sfg_bom_id # Many2one to SFG BOM
if not sfg_bom:
continue
# Required SFG qty = (FG qty * SFG-per-FG) - SFG in-hand (clamped ≥ 0)
inhand_sfg = float((sfg_bom.name and sfg_bom.name.inhand_stock_qty) or 0.0)
requested = float(self.quantity or 0.0) * float(sfg_line.quantity or 0.0)
required_sfg_qty = max(requested - inhand_sfg, 0.0)
if not required_sfg_qty:
continue
# Explode SFG BOM to components for the required SFG qty
for comp in sfg_bom.sfg_bom_line_ids:
needed_qty = float(comp.quantity or 0.0) * required_sfg_qty
_accumulate(comp, needed_qty)
# 2) Direct materials under the FG BOM
direct_material_lines = self.env['sos_sfg_bom_line'].search([('fg_bom_id', '=', product_bom.id)])
for mat in direct_material_lines:
needed_qty = float(mat.quantity or 0.0) * float(self.quantity or 0.0)
_accumulate(mat, needed_qty)
else:
# -------- SFG-only path --------
if not getattr(self, 'sfg_option', False):
raise UserError("SFG option must be selected.")
sfg_bom = self.env['sos_sfg_bom'].search([('name', '=', self.sfg_name.id)], limit=1)
if not sfg_bom:
raise UserError("SFG BOM is missing.")
label_name = self.sfg_name.display_name
# Required SFG qty = requested - in-hand (clamped ≥ 0)
inhand_sfg = float((sfg_bom.name and sfg_bom.name.inhand_stock_qty) or 0.0)
requested = float(self.quantity or 0.0)
required_sfg_qty = max(requested - inhand_sfg, 0.0)
if required_sfg_qty:
for comp in sfg_bom.sfg_bom_line_ids:
needed_qty = float(comp.quantity or 0.0) * required_sfg_qty
_accumulate(comp, needed_qty)
# ---- Build final list from aggregator (merge duplicates; consider inhand once) ----
materials = []
for rec in agg.values():
raw_to_purchase = max(rec['needed'] - rec['inhand'], 0.0)
if raw_to_purchase <= 0:
continue
pack = float(getattr(rec, 'pack', 0.0) or rec.get('pack') or 0.0)
if pack > 0:
pack_count = ceil(raw_to_purchase / pack)
to_purchase = pack_count * pack
else:
pack_count = 0
to_purchase = raw_to_purchase
materials.append({
'material_name': rec['name'],
'needed_quantity': rec['needed'],
'inhand_quantity': rec['inhand'],
'actual_needed': raw_to_purchase,
'std_packing_qty': pack,
'packs_to_buy': pack_count,
'to_purchase': to_purchase,
'price': rec['price'],
'cost': round(to_purchase * rec['price'], 2),
})
if not materials:
raise UserError("No materials need to be purchased.")
# Optional ordering: most to purchase first
materials.sort(key=lambda r: (-r['to_purchase'], r['material_name']))
total_cost = sum(m['cost'] for m in materials)
action = self.env.ref('sos_inventory.action_material_budget_summary')
return action.report_action(
self,
data={
'fg_name': label_name, # shown as header label
'total_quantity': self.quantity,
'materials': materials,
'total_cost': total_cost,
'currency_symbol': self.env.company.currency_id.symbol or '',
}
)

View File

@ -41,7 +41,7 @@ class SOS_CCRF(models.Model):
batch_release_date = fields.Date(string="Invoice Date")
brcoa_no = fields.Char(string="BRCOA No")
brcoa_date = fields.Date(string="BRCOA Date")
other_complaints = fields.Text(string="Details of any other Complaints received from this Production run")
other_complaints = fields.Text(string="Defective Details/Symptom")
data_collected_by = fields.Many2one('res.users', string='Data Collected By')
data_collected_image = fields.Image(related="data_collected_by.signature_image",string='Data Collected Sign',readonly=True)
data_collected_on = fields.Datetime(string="Data Collected On")
@ -56,12 +56,17 @@ class SOS_CCRF(models.Model):
investigation_carriedout_image = fields.Image(related="investigation_carriedout_by.signature_image",string='Investigation Carried Out Sign',readonly=True)
investigation_carriedout_on = fields.Datetime(string="Investigation Carried Out On")
root_cause = fields.Html(string="Root Cause",
default="<p>Why #1</p><p>Why #2</p><p>Why #3</p>")
default="<p>Why #1 :</p><p>Why #2 :</p><p>Why #3 :</p><p>Why #4 :</p><p>Why #5 :</p>")
#team_formation = fields.Html(string="Team Formation",
#default="<p>Name :</p><br><p>Department :</p><br><p>Roles :</p>")
#problem_description = fields.Html(string="Problem Description",
#default="<p>Clear Statement :</p><br><p>Photo :</p><br><p>What :</p><br><p>Where :</p><br><p>When :</p><br><p>Why :</p><br><p>Who :</p><br><p>How :</p><br><p>How many :</p>")
rootcause_carriedout_by = fields.Many2one('res.users', string='Root Cause Carried Out By')
rootcause_carriedout_image = fields.Image(related="rootcause_carriedout_by.signature_image",string='Root Cause Carried Out Sign',readonly=True)
rootcause_carriedout_on = fields.Datetime(string="Root Cause Carried Out On")
corrective_action = fields.Html(string="Corrective Action")
preventive_action = fields.Html(string="Preventive Action")
corrective_action = fields.Html(string="D5:Permanent Corrective Action")
implement_validate_corrective_action = fields.Html(string="D6:Implement & Validate Corrective Actions")
preventive_action = fields.Html(string="D7:Preventive Recurrence")
capa_status = fields.Selection([ ('open', 'OPEN'),('close', 'Close')], default='open' , string="Status")
status_reviewed_by = fields.Many2one('res.users', string='Status Reviewed By')
status_reviewed_image = fields.Image(related="status_reviewed_by.signature_image",string='Status Reviewed Out Sign',readonly=True)
@ -76,6 +81,11 @@ class SOS_CCRF(models.Model):
rootcause_verifiedout_image = fields.Image(related="rootcause_verifiedout_by.signature_image",string='Root Cause Verified By Sign',readonly=True)
rootcause_verifiedout_on = fields.Datetime(string="Root Cause Verified On")
helper_field = fields.Many2many('sos_fg', string="Helper Field")
team_recognition = fields.Html(string="Team Recognition (Image)")
capa_line_ids = fields.One2many('sos_ccrf_capa_line', 'ccrf_id', string="CAPA Line Ids",copy=True)
team_formation_line_ids = fields.One2many('sos_ccrf_team_formation_line', 'ccrf_id', string="Team Formation Line Ids",copy=True)
problem_description_line_ids = fields.One2many('sos_ccrf_problem_description_line', 'ccrf_id', string="Problem Description Line Ids",copy=True)
@api.onchange('fg_name')
def _onchange_fg_name(self):
if self.fg_name:
@ -141,3 +151,37 @@ class SOS_CCRF(models.Model):
'rootcause_verifiedout_on'
)
class CCRF_Model_CAPA_Line(models.Model):
_name = 'sos_ccrf_capa_line'
_description = 'CAPA Lines'
ccrf_id = fields.Many2one('sos_ccrf', string="CCRF Reference", ondelete="cascade")
issue = fields.Char(string="Issue")
corrective_action = fields.Html(string="D5:Permanent Corrective Action")
implement_validate_corrective_action = fields.Html(string="D6:Implement & Validate Corrective Actions")
preventive_action = fields.Html(string="D7:Preventive Recurrence")
class CCRF_Model_TEAM_FORMATION_Line(models.Model):
_name = 'sos_ccrf_team_formation_line'
_description = 'Team Formation Lines'
ccrf_id = fields.Many2one('sos_ccrf', string="CCRF Reference", ondelete="cascade")
name = fields.Char(string="Name")
department = fields.Char(string="Department")
role = fields.Char(string="Role")
class CCRF_Model_PROBLEM_DESCRIPTION_Line(models.Model):
_name = 'sos_ccrf_problem_description_line'
_description = 'Problem Description Lines'
ccrf_id = fields.Many2one('sos_ccrf', string="CCRF Reference", ondelete="cascade")
clear_statement = fields.Text(string="Clear Statement")
photos = fields.Html(string="Photos")
what = fields.Text(string="What")
where = fields.Text(string="Where")
when = fields.Text(string="When")
why = fields.Text(string="Why")
who = fields.Text(string="Who")
how = fields.Text(string="How")
how_many = fields.Text(string="How many")

View File

@ -288,7 +288,7 @@ class Sequence_Generator(models.AbstractModel):
reporting_user = env['res.users'].search([('id', '=', users.reporting_to.id)])
email_to = reporting_user.login
else:
email_to = "ramachandran.r@sosaley.in"
email_to = "ramachandran.r@sosaley.com"
mail_values = {
'subject': subject,
'body_html': body_html,

View File

@ -60,7 +60,8 @@ class sos__dc(models.Model):
dept_in_charge_image = fields.Image(related="dept_in_charge_name.signature_image",string='Department In-Charge Sign',readonly=True)
dept_in_charge_approved_on = fields.Datetime(string="Approved On")
remarks = fields.Text(string="Remarks")
courier = fields.Char(string="Courier Name")
lr_no = fields.Char(string="LR No")
@api.onchange('dock_audit_no')
def _onchange_dock_audit_no(self):
if self.dock_audit_no:
@ -220,7 +221,7 @@ class sos__dc(models.Model):
"""
subject = f"Delivery Challan Approval Request - {self.dc_no}"
send_email = self.env['sos_common_scripts']
send_email.send_direct_email(self.env,"sos_dc",self.id,"ramachandran.r@sosaley.in",subject,body_html)
send_email.send_direct_email(self.env,"sos_dc",self.id,"ramachandran.r@sosaley.com",subject,body_html)
# Email part ends
sequence_util = self.env['sos_common_scripts']
sequence_util.action_assign_signature(

View File

@ -22,7 +22,12 @@ class SosTestingParameters(models.Model):
communication_type = fields.Selection([
('wired', 'Wired'),
('wireless', 'Wireless')
], string="Communication Type", default='wired')
], string="Communication Type")
slave_type = fields.Selection([
('15S Individual Slave', '15S Individual Slave'),
('90S Electrical Panel', '90S Electrical Panel'),
('60S Electrical Panel', '60S Electrical Panel')
], string="Slave Type")
fg_ids = fields.One2many('sos_fg_deliverables', 'ref_id', string= 'FG Deliverables',copy=True)
sfg_ids = fields.One2many('sos_sfg_deliverables', 'ref_id', string= 'SFG Deliverables',copy=True)
material_ids = fields.One2many('sos_material_deliverables', 'ref_id', string='Material Deliverables',copy=True)
@ -57,6 +62,7 @@ class SOS_SFG_Deliverables(models.Model):
item_type = fields.Selection([
('Master Panel', 'Master Panel'),
('CT Module', 'CT Module'),
('Battery Count', 'Battery Count'),
('Slave Module', 'Slave Module'),
('Internet Module', 'Internet Module')
], string="Type",default="Master Panel")
@ -82,6 +88,7 @@ class SOS_Material_Deliverables(models.Model):
item_type = fields.Selection([
('Master Panel', 'Master Panel'),
('CT Module', 'CT Module'),
('Battery Count', 'Battery Count'),
('Slave Module', 'Slave Module'),
('Internet Module', 'Internet Module')
], string="Type",default="Master Panel")

View File

@ -8,4 +8,34 @@ class sos_department(models.Model):
name = fields.Char(string="Department Name")
short_form = fields.Char(string="Short Text")
process_incharge = fields.Many2one('res.users', string='Process Incharge')
users_line_ids = fields.One2many('sos_departments_user_lines','ref_id', string='Users')
_sql_constraints = [
('uniq_department_name', 'unique(name)', 'Department name must be unique.'),
]
class sos_department_line(models.Model):
_name = 'sos_departments_user_lines'
_description = 'Users in department'
ref_id = fields.Many2one('sos_departments', string="Departments", ondelete="cascade")
users = fields.Many2one('res.users', string='Users')
# @api.onchange('users')
# def _onchange_users(self):
# if self.users:
# domain = [('users', '=', self.users.id)]
# if self.id and isinstance(self.id, int): # Only add if it's a real ID
# domain.append(('id', '!=', self.id))
# existing_line = self.env['sos_departments_user_lines'].search(domain, limit=1)
# if existing_line:
# return {
# 'warning': {
# 'title': 'User Already Assigned',
# 'message': f"This user is already assigned to {existing_line.ref_id.name}."
# }
# }

View File

@ -96,7 +96,7 @@ class sos_Disposal_Register(models.Model):
<p>Below Item is waiting for your approval to Dispose</p>
"""
sequence_util.send_direct_email(self.env,"sos_disposal_register",self.id,"ramachandran.r@sosaley.in","Disposal Approved",body_html)
sequence_util.send_direct_email(self.env,"sos_disposal_register",self.id,"ramachandran.r@sosaley.com","Disposal Approved",body_html)
# Email part ends
return sequence_util.action_assign_signature(
self,

View File

@ -30,7 +30,9 @@ class SOS_Dock_Audit(models.Model):
],
string="Product Type",required=True)
quantity = fields.Integer(string="Quantity")
warranty = fields.Integer(string="Warranty(In Months)")
invoice_no = fields.Char(string="Invoice No")
invoice_date = fields.Date(string="Invoice Date")
customer_name = fields.Char(string="Customer Name")
lead_time = fields.Datetime(string="Lead Time")
customer_po_no = fields.Char(string="PO No")
@ -140,6 +142,18 @@ class SOS_Dock_Audit(models.Model):
})
def action_acc_esign_btn(self):
required_fields = ['payment_status', 'invoice_date','invoice_no', 'billing_address', 'gst_no', 'shipping_address']
missing_fields = []
for field in required_fields:
if not self[field]: # Check if field is empty/False
missing_fields.append(field)
# If any fields are missing, show warning
if missing_fields:
warning_msg = "Please fill the following required fields before proceeding:\n"
warning_msg += "\n".join([f"👉 {field.replace('_', ' ').title()}" for field in missing_fields])
raise UserError(warning_msg)
sequence_util = self.env['sos_common_scripts']
sequence_util.action_assign_signature(
self,
@ -151,7 +165,7 @@ class SOS_Dock_Audit(models.Model):
body_html = f"""
<p>Below Dock Audit is waiting for your Approval</p>
"""
sequence_util.send_direct_email(self.env,"sos_dock_audit",self.id,"ramachandran.r@sosaley.in","Dock Audit Approval",body_html)
sequence_util.send_direct_email(self.env,"sos_dock_audit",self.id,"ramachandran.r@sosaley.com","Dock Audit Approval",body_html)
# Email part ends
def action_auditor_esign_btn(self):
sequence_util = self.env['sos_common_scripts']
@ -206,7 +220,7 @@ class SOS_Dock_Audit(models.Model):
self.shipping_address = self.deliverables_boq_id.sales_id.shipping_address
self.gst_no = self.deliverables_boq_id.sales_id.gst_no
self.payment_status = self.deliverables_boq_id.sales_id.payment_status
self.customer_name = self.deliverables_boq_id.sales_id.customer_name
self.customer_name = self.deliverables_boq_id.sales_id.customer_name.customer_name
self.fg_name = self.deliverables_boq_id.sales_id.fg_name
self.quantity = self.deliverables_boq_id.sales_id.qty
self.lead_time = self.deliverables_boq_id.sales_id.lead_time

View File

@ -30,6 +30,11 @@ class SOS_FG_Plan(models.Model):
prepared_by = fields.Many2one('res.users', string='Planned By')
prepared_image = fields.Image(related="prepared_by.signature_image",string='Prepared By Sign',readonly=True)
prepared_on = fields.Datetime(string="Planned On")
accounts_approved_on = fields.Datetime(string="Accounts Approved On")
accounts_approved_name = fields.Many2one('res.users', string='Accounts Sign')
accounts_approved_by_image = fields.Image(related="accounts_approved_name.signature_image",string='Accounts Sign',readonly=True)
blowup = fields.Boolean(string="Blowup Done",default=False)
target_date = fields.Date(string="Target Date",required=True)
indent_start_date = fields.Date(string="Indent Start Date", default=lambda self: fields.Date.context_today(self))
@ -116,30 +121,46 @@ class SOS_FG_Plan(models.Model):
month = today.strftime('%m')
base_sequence_prefix = f"SOS/{form_name}/{fy}/{month}/"
# Search for latest record for this fiscal year (not just this month)
records = self.env[model_name].sudo().search(
[(field_name, 'like', f"{base_sequence_prefix}%")],
[(field_name, 'like', f"SOS/{form_name}/{fy}/%")],
order=f"{field_name} desc",
limit=1
)
if records:
last_sequence = records[0][field_name]
last_suffix = last_sequence.split('/')[-1]
if last_suffix.isdigit():
new_suffix = f"{last_suffix}a"
parts = last_sequence.split('/')
last_month = parts[-2]
last_suffix = parts[-1]
# Extract numeric part only (ignore trailing alphabet)
numeric_part = ''.join(filter(str.isdigit, last_suffix))
if last_month != month:
# New month: increment numeric part, no suffix
new_number = int(numeric_part) + 1
new_suffix = f"{new_number:03d}"
else:
base_num = last_suffix[:-1]
last_alpha = last_suffix[-1]
next_alpha = chr(ord(last_alpha) + 1) if last_alpha < 'z' else 'a'
new_suffix = f"{base_num}{next_alpha}"
# Same month: check for alphabet suffix
alpha_part = ''.join(filter(str.isalpha, last_suffix))
if alpha_part:
next_alpha = chr(ord(alpha_part) + 1) if alpha_part < 'z' else 'a'
new_suffix = f"{numeric_part}{next_alpha}"
else:
# First duplicate in same month
new_suffix = f"{numeric_part}a"
else:
new_suffix = '016'
# No previous records at all
new_suffix = '001'
return f"{base_sequence_prefix}{new_suffix}"
def _get_default_lines(self,model,column):
products = self.env[model].search([])
default_lines = []
@ -170,9 +191,10 @@ class SOS_FG_Plan(models.Model):
def send_indent_plan_email(self, email_ids):
valid_emails = [email for email in email_ids if email]
template = self.env.ref('sos_inventory.send_indent_plan_email_template')
if template:
template.email_to = ','.join(email_ids)
template.email_to = ','.join(valid_emails)
template.send_mail(self.id, force_send=True)
def get_unique_emails(self):
group_refs = [
@ -290,6 +312,21 @@ class SOS_FG_Plan(models.Model):
else:
worksheet.write(row, 1, value)
def action_acc_approver_esign_btn(self):
sequence_util = self.env['sos_common_scripts']
body_html = f"""
<p>Below <b>Indent Budget</b> is waiting for your Approval</p>
"""
send_email = self.env['sos_common_scripts']
send_email.send_direct_email(self.env,"sos_fg_plan",self.id,"ramachandran.r@sosaley.com","Indent Budget Approval",body_html)
result = sequence_util.action_assign_signature(
self,
'accounts_approved_name',
'accounts_approved_on',
'sos_inventory.sos_finance_user'
)
def action_top_approver_esign_btn(self):
sequence_util = self.env['sos_common_scripts']
@ -334,11 +371,9 @@ class SOS_FG_Plan(models.Model):
body_html = f"""
<p>Below <b>Indent Budget</b> is waiting for your Approval</p>
"""
send_email = self.env['sos_common_scripts']
send_email.send_direct_email(self.env,"sos_fg_plan",self.id,"ramachandran.r@sosaley.in","Indent Budget Approval",body_html)
sequence_util = self.env['sos_common_scripts']
result = sequence_util.action_assign_signature(
send_email.send_group_email(self.env,'sos_fg_plan',self.id,"deenalaura.m@sosaley.in","Indent Budget Approval",body_html,'sos_inventory.sos_finance_user')
result = send_email.action_assign_signature(
self,
'prepared_by',
'prepared_on'

View File

@ -125,6 +125,7 @@ class FIR_BRR(models.Model):
sos_record.target_date ,
date.today()
)
week_number = min(week_number, 8)
field_name = f'qc_week_{week_number}'
fgplan_field_name = f'planned_week_{week_number}'
# FG Plan update

View File

@ -77,6 +77,7 @@ class sos__grn(models.Model):
print(f"Failed to find report action: {e}")
def action_report_esign_btn(self):
sequence_util = self.env['sos_common_scripts']
sequence_util.action_assign_signature(
self,
@ -84,6 +85,7 @@ class sos__grn(models.Model):
'stores_approval_on',
'sos_inventory.sos_scg_group_user'
)
self.generate_supplier_service()
if self.received_goods_type == "Materials":
for item in self.line_ids:
component = self.env['sos_material'].browse(item.component_id.id)
@ -148,7 +150,180 @@ class sos__grn(models.Model):
def _compute_sequence(self):
sequence_util = self.env['sos_common_scripts']
return sequence_util.generate_sequence('sos_grn','GRN', 'grn_no')
def generate_supplier_service(self):
grn = self.env['sos_grn'].browse(self.id)
if self.received_goods_type == "Materials":
# Check for existing register line
already_created = self.env['sos_mat_outsourcing_vendor_register_lines'].search([
('ref_id.supplier_name', '=', grn.supplier_name.id),
('po_no', '=', grn.po_no.id)
], limit=1)
# Get or create the main register
outsource = self.env['sos_mat_outsourcing_vendor_register'].search(
[('supplier_name', '=', grn.supplier_name.id)], limit=1)
if not outsource:
outsource = self.env['sos_mat_outsourcing_vendor_register'].create({
'supplier_name': grn.supplier_name.id,
})
ir_records = self.env['sos_ir'].search([('po_no', '=', grn.po_no.id)])
iqi_records = self.env['sos_iqi'].search([('ir_id_unique_id', 'in', ir_records.ids)])
grn_records = self.env['sos_grn'].search([('po_no', '=', grn.po_no.id)])
grn_line_records = self.env['sos_grn_line'].search([('grn_id', 'in', grn_records.ids)])
# Prepare material_names list
mat_record = [line.component_id.id for line in grn_line_records if line.component_id and line.component_id.part_no]
material_names = [(6, 0, mat_record)]
# Sum all GRN line quantities including current
total_received_qty = sum(grn_line_records.mapped('received_qty')) #+ new_received
total_approved_qty = sum(grn_line_records.mapped('approved_qty')) #+ new_approved
total_rejected_qty = sum(grn_line_records.mapped('rejected_qty')) #+ new_rejected
delivery_values = grn_records.mapped('delivery')
delivery_numbers = [float(value) for value in delivery_values if value]
existing_count = len(delivery_numbers)
responsive_values = grn_records.mapped('responsiveness')
responsive_numbers = [float(value) for value in responsive_values if value]
reports_values = grn_records.mapped('reports')
reports_numbers = [float(value) for value in reports_values if value]
avg_delivery_marks = sum(delivery_numbers) / (existing_count)
delivery = (
30 if 27.5 < avg_delivery_marks <= 30 else
25 if 22.5 < avg_delivery_marks <= 27.5 else
10 if avg_delivery_marks < 20 else
0
)
avg_report_marks = sum(reports_numbers) / (existing_count)
report = (
30 if 27.5 < avg_report_marks <= 30 else
25 if 22.5 < avg_report_marks <= 27.5 else
10 if avg_report_marks < 20 else
0
)
avg_responsiveness_marks = sum(responsive_numbers) / (existing_count)
responsiveness = (
10 if 5 <= avg_responsiveness_marks <= 10 else
5 if avg_responsiveness_marks < 5 else
0
)
if already_created:
updated_vals = {
'received_qty': total_received_qty,
'approved_qty': total_approved_qty,
'rejected_qty': total_rejected_qty,
'delivery_marks': delivery,
'responsiveness_marks': responsiveness,
'report_marks': report,
'iqi_references': [(6, 0, iqi_records.ids)],
'grn_references': [(6, 0, grn_records.ids)],
'material_names': material_names,
}
already_created.write(updated_vals)
else:
self.env['sos_mat_outsourcing_vendor_register_lines'].create({
'ref_id': outsource.id,
'po_no': grn.po_no.id,
'received_qty': total_received_qty,
'approved_qty': total_approved_qty,
'rejected_qty': total_rejected_qty,
'iqi_references': [(6, 0, iqi_records.ids)],
'grn_references': [(6, 0, grn_records.ids)],
'material_names': material_names,
'delivery_marks': delivery,
'responsiveness_marks': responsiveness,
'report_marks': report,
})
elif self.received_goods_type == "SFG":
# Check for existing register line
already_created = self.env['sos_outsourcing_vendor_monitoring_register_lines'].search([
('ref_id.service_provider_name', '=', grn.service_provider_name.id),
('wo_no', '=', grn.wo_no.id)
], limit=1)
# Get or create the main register
outsource = self.env['sos_outsourcing_vendor_monitoring_register'].search(
[('service_provider_name', '=', grn.service_provider_name.id)], limit=1)
if not outsource:
outsource = self.env['sos_outsourcing_vendor_monitoring_register'].create({
'service_provider_name': grn.service_provider_name.id,
})
# Related documents
ir_records = self.env['sos_ir'].search([('wo_no', '=', grn.wo_no.id)])
iqi_records = self.env['sos_iqi'].search([('ir_id_unique_id', 'in', ir_records.ids)])
dc_records = self.env['sos_dc'].search([('wo_no', '=', grn.wo_no.id)])
grn_records = self.env['sos_grn'].search([('wo_no', '=', grn.wo_no.id)])
grn_line_records = self.env['sos_grn_line_sfg'].search([('grn_id', 'in', grn_records.ids)])
# Prepare material_names list
sfg_ids = [line.component_id.id for line in grn_line_records if line.component_id and line.component_id.name]
# Include current line component manually if not yet saved in DB
current_component_id = self.line_ids.component_id
if current_component_id and current_component_id not in sfg_ids:
sfg_ids.append(current_component_id)
sfg_names = [(6, 0, sfg_ids)]
total_received_qty = sum(grn_line_records.mapped('received_qty'))
total_approved_qty = sum(grn_line_records.mapped('approved_qty'))
total_rejected_qty = sum(grn_line_records.mapped('rejected_qty'))
delivery_values = grn_line_records.mapped('delivery')
delivery_numbers = [float(value) for value in delivery_values if value]
existing_count = len(delivery_numbers)
responsive_values = grn_line_records.mapped('responsiveness')
responsive_numbers = [float(value) for value in responsive_values if value]
reports_values = grn_line_records.mapped('reports')
reports_numbers = [float(value) for value in reports_values if value]
avg_delivery_marks = sum(delivery_numbers) / (existing_count)
avg_responsiveness_marks = sum(responsive_numbers) / (existing_count)
avg_report_marks = sum(reports_numbers) / (existing_count)
if already_created:
#print(f" sfg_names : if {sfg_names}")
updated_vals = {
'received_qty': total_received_qty,
'approved_qty': total_approved_qty,
'rejected_qty': total_rejected_qty,
'delivery_marks': avg_delivery_marks,
'responsiveness_marks': avg_responsiveness_marks,
'report_marks': avg_report_marks,
'iqi_references': [(6, 0, iqi_records.ids)],
'dc_references': [(6, 0, dc_records.ids)],
'grn_references': [(6, 0, grn_records.ids)],
'sfg_names': sfg_names,
}
already_created.write(updated_vals)
else:
#print(f" sfg_names : else {sfg_names}")
self.env['sos_outsourcing_vendor_monitoring_register_lines'].create({
'ref_id': outsource.id,
'iqi_references': [(6, 0, iqi_records.ids)],
'dc_references': [(6, 0, dc_records.ids)],
'grn_references': [(6, 0, grn_records.ids)],
'sfg_names': sfg_names,
'wo_no': grn.wo_no.id,
'received_qty': total_received_qty,
'approved_qty': total_approved_qty,
'rejected_qty': total_rejected_qty,
'delivery_marks': avg_delivery_marks,
'responsiveness_marks': avg_responsiveness_marks,
'report_marks': avg_report_marks,
})
class sos_grn_line(models.Model):
_name = 'sos_grn_line'
_description = 'GRN Material Lines'
@ -169,14 +344,19 @@ class sos_grn_line(models.Model):
@api.depends('received_qty', 'rejected_qty')
def _compute_quality(self):
for record in self:
if record.rejected_qty != 0:
record.quality_marks = (100 - ((record.rejected_qty / record.received_qty) * 100))
if record.quality_marks >= 80 and record.quality_marks <=99:
record.quality_marks = 25
record.quality_marks = self._calculate_quality_marks(record.received_qty, record.rejected_qty)
def _calculate_quality_marks(self, received, rejected):
if received != 0:
q = 100 - ((rejected / received) * 100)
if 91 <= q <= 100:
return 30
elif 80 <= q <= 90:
return 25
else:
record.quality_marks = 10
else:
record.quality_marks = 100
return 10
return 0
@api.model
def write(self, vals):
@ -295,14 +475,20 @@ class sos_grn_line_sfg(models.Model):
@api.depends('received_qty', 'rejected_qty')
def _compute_sfgquality(self):
for record in self:
if record.rejected_qty != 0:
record.quality_marks = (100 - ((record.rejected_qty / record.received_qty) * 100))
if record.quality_marks >= 80 and record.quality_marks <=99:
record.quality_marks = 25
record.quality_marks = self._calculate_quality_marks(record.received_qty, record.rejected_qty)
def _calculate_quality_marks(self, received, rejected):
if received != 0:
q = 100 - ((rejected / received) * 100)
if 91 <= q <= 100:
return 30
elif 80 <= q <= 90:
return 25
else:
record.quality_marks = 10
else:
record.quality_marks = 100
return 10
return 0
class sos_grn_line_fg(models.Model):
_name = 'sos_grn_line_fg'

View File

@ -276,25 +276,26 @@ class SOS_IR(models.Model):
else:
iqi_record = self.env['sos_iqi'].create({
'iqi_no': sequence_util.generate_sequence('sos_iqi', 'IQI', 'iqi_no'),
'material_option':True,
'sfg_option':False,
'supplier_name': self.supplier_name.id,
'batch_no':item.batch_no,
'received_qty':item.qty,
'uom': item.component_id.uom,
'material_name': item.component_id.id,
'material_code': item.component_id.material_code,
'in_tact':self.boxes_sealing,
'invoice_no':self.dc_no_char,
'invoice_date':self.dc_date,
'ir_id_unique_id':self.id,
'from_origin':"Vendor",
'unit_price':item.unit_price
if item.qty > 0:
iqi_record = self.env['sos_iqi'].create({
'iqi_no': sequence_util.generate_sequence('sos_iqi', 'IQI', 'iqi_no'),
'material_option':True,
'sfg_option':False,
'supplier_name': self.supplier_name.id,
'batch_no':item.batch_no,
'received_qty':item.qty,
'uom': item.component_id.uom,
'material_name': item.component_id.id,
'material_code': item.component_id.material_code,
'in_tact':self.boxes_sealing,
'invoice_no':self.dc_no_char,
'invoice_date':self.dc_date,
'ir_id_unique_id':self.id,
'from_origin':"Vendor",
'unit_price':item.unit_price
})
})
# Email part
body_html = f"""
<p>Below <b>IQI</b> is waiting for your Approval</p>
@ -383,7 +384,7 @@ class SOS_IR(models.Model):
"""
subject = f"Inward Approval Request - {self.ir_no}"
send_email = self.env['sos_common_scripts']
send_email.send_direct_email(self.env,"sos_ir",self.id,"ramachandran.r@sosaley.in",subject,body_html)
send_email.send_direct_email(self.env,"sos_ir",self.id,"ramachandran.r@sosaley.com",subject,body_html)
# Email part ends
sequence_util = self.env['sos_common_scripts']
return sequence_util.action_assign_signature(

View File

@ -35,11 +35,25 @@ class SOS_Mat_Oursourcing_Monitor_Lines(models.Model):
rejection_percentage = fields.Float(string="Rejected Percentage", compute="_compute_rejected_percentage", store=True)
rejection_percentage_display = fields.Char('Rejection Percentage', compute='_compute_rejected_percentage_display')
ppm = fields.Integer(string="PPM",compute="_compute_ppm")
quality_marks = fields.Float(string="Quality Marks")
quality_marks = fields.Float(string="Quality Marks",compute="_compute_sfgquality")
delivery_marks = fields.Float(string="Delivery Marks")
responsiveness_marks = fields.Float(string="Responsiveness Marks")
report_marks = fields.Float(string="Report Marks")
def _calculate_quality_marks(self, received, rejected):
if received != 0:
q = 100 - ((rejected / received) * 100)
if 91 <= q <= 100:
return 30
elif 80 <= q <= 90:
return 25
else:
return 10
return 0
@api.depends('received_qty', 'rejected_qty')
def _compute_sfgquality(self):
for record in self:
record.quality_marks = self._calculate_quality_marks(record.received_qty, record.rejected_qty)
@api.depends('rejection_percentage')
def _compute_rejected_percentage_display(self):

View File

@ -16,6 +16,7 @@ class sos__mon(models.Model):
filled_by = fields.Many2one('res.users', string='Filled By', readonly=True,required=True,default=lambda self: self.env.user)
logged_inuser_group=fields.Boolean(string='Group Name',compute='compute_user_grp',store=True)
dept = fields.Many2one('sos_departments',string="Department")
customer_name = fields.Many2one('sos_inventory_customers',string="Customer Name")
purpose = fields.Char(string="Purpose")
product_name = fields.Many2one('sos_fg', string='Material/Product Name & No')
indent_ref_no = fields.Many2one('sos_fg_plan',string="Indent Reference No")
@ -33,7 +34,7 @@ class sos__mon(models.Model):
top_management_name = fields.Many2one('res.users', string='Top Management Approver')
top_management_approval_image = fields.Image(related="top_management_name.signature_image",string='Top Management Approval',readonly=True)
top_management_approved_on = fields.Datetime(string="Approved On")
deliverables_boq_id = fields.Many2one('sos_deliverables_boq', string="Load From Deliverables/BOQ Id")
stores_approved_by = fields.Many2one('res.users', string='Stores Approved By')
stores_approved_image = fields.Image(related="stores_approved_by.signature_image",string='Stores Approval Sign',readonly=True)
stores_approved_on = fields.Datetime(string="Approved On")
@ -43,9 +44,9 @@ class sos__mon(models.Model):
('fg', 'FG')
], string='Auto Load Items' ,default=False)
material_option = fields.Boolean('Materials', default=True)
sfg_option = fields.Boolean('Semi-Finished Goods')
fg_option = fields.Boolean('Finished Goods')
order_type = fields.Char(string='order_type',copy=True)
sfg_option = fields.Boolean('Semi-Finished Goods', default=False)
fg_option = fields.Boolean('Finished Goods', default=False)
order_type = fields.Char(string='order_type',copy=True,compute="_compute_order_type")
line_ids_material = fields.One2many('sos_mon_line_material', 'mon_id', string="Materials",copy=True)
line_ids_sfg = fields.One2many('sos_mon_line_sfg', 'mon_id', string="Semi-Finished Goods",copy=True)
line_ids_fg = fields.One2many('sos_mon_line_fg', 'mon_id', string="Finished Goods",copy=True)
@ -73,6 +74,74 @@ class sos__mon(models.Model):
approx_value = fields.Monetary(compute='_compute_approx_value', string="Approximate Value", currency_field='currency_id', readonly=True,store=True)
status = fields.Selection([ ('open', 'Open'),('close', 'Closed')], default='open' , string="Status")
active = fields.Boolean(default=True)
is_ce_user_created = fields.Boolean(
compute='_compute_is_ce_user_created',
store=True,
string='Created by CE User'
)
@api.depends('create_uid')
def _compute_is_ce_user_created(self):
ce_groups = [
self.env.ref('sos_inventory.sos_ce_user').id
]
for record in self:
record.is_ce_user_created = any(
gid in record.create_uid.groups_id.ids for gid in ce_groups
)
@api.depends('material_option', 'sfg_option', 'fg_option')
def _compute_order_type(self):
for rec in self:
parts = []
if rec.material_option:
parts.append('Material')
if rec.sfg_option:
parts.append('SFG')
if rec.fg_option:
parts.append('FG')
rec.order_type = ",".join(parts) if parts else False
@api.onchange('deliverables_boq_id')
def _onchange_deliverables_boq_id(self):
self.material_option = True
self.sfg_option = True
self.fg_option = True
if self.deliverables_boq_id:
self.line_ids_material = [(5, 0, 0)] # Clear existing records
self.line_ids_material = [
(0, 0, {
'component_id': line.component_id, # Replace 'field1' with the actual field names
'uom': line.uom,
'material_code': line.material_code,
'quantity': line.quantity
# Add other fields to copy here
}) for line in self.deliverables_boq_id.line_ids_installation_kit
]
self.line_ids_fg = [(5, 0, 0)] # Clear existing records
self.line_ids_fg = [
(0, 0, {
'component_id': line.component_id, # Replace 'field1' with the actual field names
'quantity': line.quantity
}) for line in self.deliverables_boq_id.line_ids_fg
]
self.line_ids_sfg = [(5, 0, 0)] # Clear existing records
self.line_ids_sfg = [
(0, 0, {
'component_id': line.component_id, # Replace 'field1' with the actual field names
'quantity': line.quantity
# Add other fields to copy here
}) for line in self.deliverables_boq_id.line_ids_sfg
]
self.line_ids_material = [
(0, 0, {
'uom': line.uom,
'material_code': line.material_code,
'component_id': line.component_id,
'quantity': line.quantity
# Add other fields to copy here
}) for line in self.deliverables_boq_id.line_ids_material
]
@api.constrains('indent_ref_no', 'auto_load_fg_items')
def _check_duplicate_fg_items_per_indent(self):
for record in self:
@ -461,7 +530,7 @@ class Mon_Line_Material(models.Model):
uom = fields.Selection([('meters', 'Meters'),('Nos', 'Nos'),('coils', 'Coils'), ('litre', 'litre'), ('kg', 'Kilogram'), ('Packs', 'Packs')], string="Uom")
specifications = fields.Char(string="Specifications")
quantity = fields.Float(string="Quantity",required=True,default=1)
location = fields.Char(string="Location")
location = fields.Char(string="Location",related='component_id.location')
company_id = fields.Many2one('res.company', store=True, copy=False,
string="Company",
default=lambda self: self.env.user.company_id.id)

View File

@ -46,6 +46,20 @@ class sos__mrn(models.Model):
default=lambda
self: self.env.user.company_id.currency_id.id)
approx_value = fields.Monetary(compute='_compute_approx_value', string="Approximate Value", currency_field='currency_id', readonly=True)
is_ce_user_created = fields.Boolean(
compute='_compute_is_ce_user_created',
store=True,
string='Created by CE User'
)
@api.depends('create_uid')
def _compute_is_ce_user_created(self):
ce_groups = [
self.env.ref('sos_inventory.sos_ce_user').id
]
for record in self:
record.is_ce_user_created = any(
gid in record.create_uid.groups_id.ids for gid in ce_groups
)
@api.onchange('min_no')
def _onchange_min_no(self):
if self.min_no:

View File

@ -52,7 +52,8 @@ class NCMR_Model(models.Model):
finished_fg_assy = fields.Char()
finished_fg_assy_responsibility = fields.Text(string="Production Assy FG Responsibility")
description_of_nc = fields.Html(string="Description of Non-Conformities")
root_cause_of_nc = fields.Html(string="Root Cause of Non-Conformities")
root_cause_of_nc = fields.Html(string="Root Cause",
default="<p>Why #1 :</p><p>Why #2 :</p><p>Why #3 :</p><p>Why #4 :</p><p>Why #5 :</p>")
containment_action_of_nc = fields.Html(string="Containment Action to close the Non-Conformities")
comments_on_capa = fields.Html(string="Comments on Corrective / Preventive Action ")
qa_comments=fields.Text(string="QA Comments")
@ -105,6 +106,14 @@ class NCMR_Model(models.Model):
rework_rd_approval_sign = fields.Image(related="rework_rd_approval_by.signature_image",string='Rework - R&D In-Charge',readonly=True)
rework_rd_approval_on = fields.Datetime(string="Approved On")
responsible_department = fields.Many2one('sos_departments', string='Department')
responsible_name = fields.Many2one('res.users',string='Responsible Person',domain="[('id', 'in', allowed_user_ids)]")
allowed_user_ids = fields.Many2many('res.users', compute='_compute_allowed_users')
rca_responsible_department = fields.Many2many('sos_departments', string='Department')
rca_responsible_name = fields.Many2many('res.users',string='Responsible Person',relation='sos_ncmr_rca_responsible_name_rel',domain="[('id', 'in', rca_allowed_user_ids)]")
rca_allowed_user_ids = fields.Many2many('res.users', compute='_rca_compute_allowed_users')
combined_incoming_doc_ref = fields.Reference(
selection=[
('sos_iqi', 'IQI Ref No'),
@ -120,8 +129,155 @@ class NCMR_Model(models.Model):
supplier_name = fields.Many2one('sos_suppliers',string="Supplier Name",compute="_get_supplier_name")
service_provider_name = fields.Many2one('sos_service_providers',string="Service Provider Name",compute="_get_service_provider_name")
customer_name = fields.Many2one('sos_inventory_customers', string="Customer Name",compute="_get_customer_name")
outsourcing_return_ref_no = fields.Many2one('sos_sfg_outsourcing_return_register',string="Outsourcing Return Ref No")
problem_description_line_ids = fields.One2many('sos_ncmr_problem_description_line', 'ncmr_id', string="Problem Description Line Ids",copy=True)
@api.depends('responsible_department')
def _compute_allowed_users(self):
for rec in self:
rec.allowed_user_ids = rec.responsible_department.users_line_ids.mapped('users')
@api.depends('rca_responsible_department')
def _rca_compute_allowed_users(self):
for rec in self:
rec.rca_allowed_user_ids = rec.rca_responsible_department.users_line_ids.mapped('users')
@api.model
def get_service_suppliers_by_goods_type(self,goods_type=False,startDate=False, endDate=False):
if goods_type == 'Material':
query = """
SELECT id,supplier_name
FROM sos_suppliers
WHERE id IN (
SELECT b.supplier_name
FROM sos_ncmr a, sos_iqi b
WHERE a.incoming_doc_ref = b.id
AND a.material_option = 't'
AND a.ncmr_date BETWEEN %s AND %s
)
"""
elif goods_type == 'SFG':
query = """
SELECT id,service_provider_name
FROM sos_service_providers
WHERE id IN (
SELECT b.service_provider_name
FROM sos_ncmr a, sos_iqi b
WHERE a.incoming_doc_ref = b.id
AND a.sfg_option = 't'
AND a.ncmr_date BETWEEN %s AND %s
)
"""
self.env.cr.execute(query,(startDate, endDate))
rows = self.env.cr.fetchall()
result = [{'id': '', 'supplier_name': 'Select Supplier/Service'}]
result += [{'id': row[0], 'supplier_name': row[1]} for row in rows]
return result
@api.model
def get_pareto_data(self, start_date=False, end_date=False, goodstype=False, categoryId=False, servicesupplier=False):
if not start_date or not end_date:
raise ValueError("Start date and end date must be provided.")
base_where_clause = "a.ncmr_date BETWEEN %s AND %s"
where_params = [start_date, end_date]
base_groupby_clause = "d.name"
if goodstype == 'Material':
join_clause = """
JOIN sos_material_defective_line_sos_ncmr_line_rel c ON b.id = c.sos_ncmr_line_id
JOIN sos_material_defective_line d ON c.sos_material_defective_line_id = d.id
JOIN sos_material_configuration e ON a.material_category = e.id
"""
if categoryId and categoryId != 'Select Category':
join_clause += " JOIN sos_material f ON a.material_name = f.id JOIN sos_material_types g ON f.material_type_id = g.id"
base_where_clause += " AND g.name = %s"
#base_groupby_clause += ",g.name"
where_params.append(categoryId)
if servicesupplier and servicesupplier !='Select Supplier/Service' and servicesupplier !='undefined':
join_clause += " LEFT JOIN sos_iqi h ON a.incoming_doc_ref = h.id LEFT JOIN sos_suppliers i ON h.supplier_name = i.id"
base_where_clause += " AND h.supplier_name = %s"
base_groupby_clause += ",h.supplier_name"
where_params.append(servicesupplier)
elif goodstype == 'SFG':
join_clause = """
JOIN sos_ncmr_line_sos_sfg_defective_line_rel c ON b.id = c.sos_ncmr_line_id
JOIN sos_sfg_defective_line d ON c.sos_sfg_defective_line_id = d.id
JOIN sos_sfg_configuration e ON a.sfg_category = e.id
"""
if categoryId and categoryId != 'Select Category':
join_clause += " JOIN sos_sfg f ON a.sfg_name = f.id"
base_where_clause += " AND f.category = %s"
base_groupby_clause += ",f.category"
where_params.append(categoryId)
if servicesupplier and servicesupplier !='Select Supplier/Service' and servicesupplier !='undefined':
join_clause += " LEFT JOIN sos_iqi g ON a.incoming_doc_ref = g.id LEFT JOIN sos_service_providers h ON g.service_provider_name = h.id"
base_where_clause += " AND g.service_provider_name = %s"
base_groupby_clause += ",g.service_provider_name"
where_params.append(servicesupplier)
elif goodstype == 'FG':
join_clause = """
JOIN sos_defectives_sos_ncmr_line_rel c ON b.id = c.sos_ncmr_line_id
JOIN sos_defectives d ON c.sos_defectives_id = d.id
JOIN sos_testing_parameters f ON a.fg_category = f.id
JOIN sos_fg e ON e.id = f.fg_name
"""
if categoryId and categoryId != 'Select Category':
base_where_clause += " AND e.fg_type = %s"
base_groupby_clause += ",e.fg_type"
where_params.append(categoryId)
# Build the full SQL query dynamically
query = f"""
SELECT
defect_name,
count,
ROUND(count * 100.0 / total, 2) AS individual_percent,
ROUND(SUM(count) OVER (ORDER BY count DESC ROWS UNBOUNDED PRECEDING) * 100.0 / total, 2) AS cumulative_percent
FROM (
SELECT
d.name AS defect_name,
COUNT(*) AS count,
SUM(COUNT(*)) OVER () AS total
FROM
sos_ncmr a
JOIN sos_ncmr_line b ON a.id = b.ncmr_id
{join_clause}
WHERE
{base_where_clause}
GROUP BY
{base_groupby_clause}
) AS sub
ORDER BY
count DESC;
"""
self.env.cr.execute(query, tuple(where_params))
result = self.env.cr.fetchall()
data = []
for row in result:
data.append({
'defect_name': row[0], # defect_name
'count': row[1], # count
'cumulative_percent': row[3], # cumulative_percent
})
return data
#Excel Write
def action_ncmr_report_orm_btn(self, from_date, to_date,force_download=True):
output = io.BytesIO()
@ -559,17 +715,18 @@ class NCMR_Model(models.Model):
'sos_inventory.sos_qa_user'
)
else:
if all_closed or record.aodr_no:
self.status = 'closed'
if self.rework_action == "inhouse":
s_no_list = self.line_ids.mapped('s_no') # Collect all s_no values
s_no_str = ', '.join(map(str, s_no_list))
iqi_record = self.env['sos_iqi'].create({
'iqi_no': send_email.generate_sequence('sos_iqi', 'R-IQI', 'iqi_no'),
'material_option':self.material_option,
'sfg_option':self.sfg_option,
'received_qty': self.rejected_qty,
'iqi_type':'rework',
'material_name': self.material_name,
'material_name': self.material_name.id,
'material_code': self.material_name.material_code,
'sfg_name': self.sfg_name.id,
'sfg_code': self.sfg_name.sfg_code,
@ -577,7 +734,9 @@ class NCMR_Model(models.Model):
'service_provider_name':self.incoming_doc_ref.service_provider_name.id,
'old_iqi_ref': self.incoming_doc_ref.id,
'ir_id_unique_id':self.incoming_doc_ref.ir_id_unique_id.id,
'in_tact':self.incoming_doc_ref.in_tact
'in_tact':self.incoming_doc_ref.in_tact,
'ncmr_ref':self.id,
'serial_no':s_no_str
# 'test_report':self.incoming_doc_ref.test_report,
# 'test_report_no':self.incoming_doc_ref.test_report_no,
# 'test_report_doc':self.incoming_doc_ref.test_report_doc,
@ -585,7 +744,7 @@ class NCMR_Model(models.Model):
})
print(iqi_record)
# Rework Iteration starts
sos_record = self.env['sos_iqi'].search([('id', '=', self.incoming_doc_ref.id)], limit=1)
if sos_record:
@ -622,7 +781,27 @@ class NCMR_Model(models.Model):
'sos_inventory.sos_scg_group_user'
)
if self.rework_action == "inhouse":
# Email part
if self.material_option:
orr_record = self.env['sos_sfg_outsourcing_return_register'].create({
'orr_no': orr_no,
'iqi_no':self.incoming_doc_ref.id,
'iqi_date':self.incoming_doc_ref.iqi_date,
'material_name':self.incoming_doc_ref.material_name,
'goods_type':'Materials',
'returned_qty':self.incoming_doc_ref.rejected_qty,
'rework_type':'inhouse'
})
else:
orr_record = self.env['sos_sfg_outsourcing_return_register'].create({
'orr_no': orr_no,
'iqi_no':self.incoming_doc_ref.id,
'iqi_date':self.incoming_doc_ref.iqi_date,
'sfg_name':self.incoming_doc_ref.sfg_name,
'goods_type':'SFG',
'returned_qty':self.incoming_doc_ref.rejected_qty,
'rework_type':'inhouse'
})
self.outsourcing_return_ref_no = orr_record.id
body_html = f"""
<p>Below <b>NCMR</b> is waiting for your Action</p>
"""
@ -636,7 +815,8 @@ class NCMR_Model(models.Model):
'material_name':self.incoming_doc_ref.material_name,
'supplier_name':self.incoming_doc_ref.supplier_name.id,
'goods_type':'Materials',
'returned_qty':self.incoming_doc_ref.rejected_qty
'returned_qty':self.incoming_doc_ref.rejected_qty,
'rework_type':'outsourcing'
})
else:
if self.material_option:
@ -647,7 +827,8 @@ class NCMR_Model(models.Model):
'material_name':self.incoming_doc_ref.material_name,
'supplier_name':self.incoming_doc_ref.supplier_name.id,
'goods_type':'Materials',
'returned_qty':self.incoming_doc_ref.rejected_qty
'returned_qty':self.incoming_doc_ref.rejected_qty,
'rework_type':'outsourcing'
})
else:
orr_record = self.env['sos_sfg_outsourcing_return_register'].create({
@ -657,7 +838,8 @@ class NCMR_Model(models.Model):
'service_provider_name':self.incoming_doc_ref.service_provider_name,
'sfg_name':self.incoming_doc_ref.sfg_name,
'goods_type':'SFG',
'returned_qty':self.incoming_doc_ref.rejected_qty
'returned_qty':self.incoming_doc_ref.rejected_qty,
'rework_type':'outsourcing'
})
self.outsourcing_return_ref_no = orr_record.id
@ -685,6 +867,26 @@ class NCMR_Model(models.Model):
)
def action_qa_esign_btn(self):
if self.responsible_department:
if self.material_option:
material = self.material_name.part_no
elif self.sfg_option:
material = self.sfg_name.name
else:
material = self.fg_name.name
result_col = f"NCMR Ref No :{getattr(self, 'ncmr_no', '')}"
new_action_record = self.env['sos_brm_action'].create({
'cross_dept_action': 'cross_dept',
'department': 3,
'responsible_person': getattr(self.responsible_name, 'id', False),
'assigned_by': getattr(self.qa_by, 'id', False),
'assigned_from_dept': 3,
'assigned_to_dept': getattr(self.responsible_department, 'id', False),
'name': f"NCMR Assigned : {material}",
'result': result_col
})
if self.action_group:
sequence_util = self.env['sos_common_scripts']
sequence_util.action_assign_signature(
@ -767,8 +969,9 @@ class NCMR_Model_CAPA_Line(models.Model):
ncmr_id = fields.Many2one('sos_ncmr', string="NCMR Reference", ondelete="cascade")
issue = fields.Char(string="Issue")
corrective_action = fields.Html(string="Corrective Action")
preventive_action = fields.Html(string="Preventive Action")
corrective_action = fields.Html(string="D5: Permanent Corrective Action")
implement_validate_corrective_action = fields.Html(string="D6:Implement & Validate Corrective Actions")
preventive_action = fields.Html(string="D7:Preventive Recurrence")
class NCMR_Model_Line(models.Model):
_name = 'sos_ncmr_line'
@ -897,3 +1100,18 @@ class NCMR_Model_Line(models.Model):
def _compute_fg_defective_count(self):
for record in self:
record.fg_defective_count = len(record.fg_defectives)
class NCMR_Model_PROBLEM_DESCRIPTION_Line(models.Model):
_name = 'sos_ncmr_problem_description_line'
_description = 'Problem Description Lines'
ncmr_id = fields.Many2one('sos_ncmr', string="NCMR Reference", ondelete="cascade")
clear_statement = fields.Html(string="Clear Statement")
photos = fields.Html(string="Photos")
what = fields.Text(string="What")
where = fields.Text(string="Where")
when = fields.Text(string="When")
why = fields.Text(string="Why")
who = fields.Text(string="Who")
how = fields.Text(string="How")
how_many = fields.Text(string="How many")

View File

@ -182,7 +182,7 @@ class SOS_Order_Delivery_Plan(models.Model):
<p>Below <b>Order Delivery Plan</b> is waiting for your Approval</p>
"""
send_email = self.env['sos_common_scripts']
send_email.send_direct_email(self.env,"sos_order_delivery_plan",self.id,"ramachandran.r@sosaley.in","Order Delivery Plan",body_html)
send_email.send_direct_email(self.env,"sos_order_delivery_plan",self.id,"ramachandran.r@sosaley.com","Order Delivery Plan",body_html)
# Email part ends
sequence_util = self.env['sos_common_scripts']
return sequence_util.action_assign_signature(

View File

@ -36,12 +36,25 @@ class SOS_Oursourcing_Monitor_Lines(models.Model):
rejection_percentage = fields.Float(string="Rejected Percentage", compute="_compute_rejected_percentage", store=True)
rejection_percentage_display = fields.Char('Rejection Percentage', compute='_compute_rejected_percentage_display')
ppm = fields.Integer(string="PPM",compute="_compute_ppm")
quality_marks = fields.Float(string="Quality Marks")
quality_marks = fields.Float(string="Quality Marks",compute="_compute_sfgquality")
delivery_marks = fields.Float(string="Delivery Marks")
responsiveness_marks = fields.Float(string="Responsiveness Marks")
report_marks = fields.Float(string="Report Marks")
def _calculate_quality_marks(self, received, rejected):
if received != 0:
q = 100 - ((rejected / received) * 100)
if 91 <= q <= 100:
return 30
elif 80 <= q <= 90:
return 25
else:
return 10
return 0
@api.depends('received_qty', 'rejected_qty')
def _compute_sfgquality(self):
for record in self:
record.quality_marks = self._calculate_quality_marks(record.received_qty, record.rejected_qty)
@api.depends('rejection_percentage')
def _compute_rejected_percentage_display(self):
for record in self:

View File

@ -143,7 +143,7 @@ class sos__po(models.Model):
"""
subject = f"Purchase Order Approval Request - {self.po_no}"
send_email = self.env['sos_common_scripts']
send_email.send_direct_email(self.env,"sos_po",self.id,"ramachandran.r@sosaley.in",subject,body_html)
send_email.send_direct_email(self.env,"sos_po",self.id,"ramachandran.r@sosaley.com",subject,body_html)
# Email part ends
sequence_util = self.env['sos_common_scripts']
return sequence_util.action_assign_signature(

79
sos_inventory/models/sos_prf.py Executable file
View File

@ -0,0 +1,79 @@
from odoo import models, fields, api
class SOS_prf(models.Model):
_name = 'sos_prf'
_description = 'Purchase Requisition Form'
_rec_name = 'prf_no'
_order = 'id desc'
prf_no = fields.Char(string="PRF No", readonly= True, required= True, default=lambda self: self._generate_id())
prf_date = fields.Date(string="PRF Date", required=True, default=fields.Date.today)
line_ids = fields.One2many('sos_prf_lines', 'ref_id', string="PRF Lines",copy=True)
requested_by_name = fields.Many2one('res.users', string='Requested by')
requested_by_image = fields.Image(related="requested_by_name.signature_image",string='Requested by Sign',readonly=True)
requested_on = fields.Datetime(string="Requested On")
dept_in_charge_name = fields.Many2one('res.users', string='Department In-Charge')
dept_in_charge_image = fields.Image(related="dept_in_charge_name.signature_image",string='Department In-Charge Sign',readonly=True)
dept_in_charge_approved_on = fields.Datetime(string="Approved On")
top_management_name = fields.Many2one('res.users', string='Top Management Approver')
top_management_approval_image = fields.Image(related="top_management_name.signature_image",string='Top Management Approval',readonly=True)
top_management_approved_on = fields.Datetime(string="Approved On")
status = fields.Selection([('open', 'Open'),('close', 'Closed')], default='open' , string="Status")
def action_top_esign_btn(self):
sequence_util = self.env['sos_common_scripts']
subject = f"PRF Approved - {self.prf_no}"
body_html = f"""
<p>Below <b>PRF</b> got approved</p>
"""
sequence_util.send_direct_email(self.env,"sos_prf",self.id,"praveenkumar.m@sosaley.com",subject,body_html)
sequence_util.action_assign_signature(
self,
'top_management_name',
'top_management_approved_on',
'sos_inventory.sos_management_user'
)
def action_dept_esign_btn(self):
# Email part
body_html = f"""
<p>Below <b>PRF</b> is waiting for your Approval</p>
"""
sequence_util = self.env['sos_common_scripts']
subject = f"PRF Approval Request - {self.prf_no}"
sequence_util.send_direct_email(self.env,"sos_prf",self.id,"ramachandran.r@sosaley.com",subject,body_html)
# Email part ends
sequence_util.action_assign_signature(
self,
'dept_in_charge_name',
'dept_in_charge_approved_on',
'sos_inventory.sos_scg_group_manager'
)
def action_stores_esign_btn(self):
# Email part
body_html = f"""
<p>Below <b>PRF</b> is waiting for your Approval</p>
"""
sequence_util = self.env['sos_common_scripts']
sequence_util.send_group_email(self.env,'sos_prf',self.id,"deenalaura.m@sosaley.in","PRF Approval Request",body_html,'sos_inventory.sos_scg_group_manager')
# Email part ends
sequence_util.action_assign_signature(
self,
'requested_by_name',
'requested_on',
'sos_inventory.sos_scg_group_user'
)
def _generate_id(self):
sequence_util = self.env['sos_common_scripts']
return sequence_util.generate_sequence('sos_prf','PRF', 'prf_no')
class SOS_prf_Lines(models.Model):
_name = 'sos_prf_lines'
_description = 'Purchase Requisition Form Lines'
ref_id = fields.Many2one('sos_prf', string="PRF", ondelete="cascade")
com_type = fields.Selection([('exits', 'In-stock'),('new', 'New')], string="Availability", default='exits')
component_id = fields.Many2one('sos_material', string="Material Name")
new_component_id = fields.Char(string="New Part No")
qty = fields.Integer(string="Required Qty")
req_date = fields.Date(string="Required Date")
mon_ref_no = fields.Many2one('sos_mon',string="MON Ref No")

View File

@ -68,35 +68,35 @@ class SOS_Quote_Generation(models.Model):
supplier_dict = {}
for record in self.line_ids:
if record.supplier1_name:
supplier = record.supplier1_name
if record.final_supplier1_name:
supplier = record.final_supplier1_name
if supplier not in supplier_dict:
supplier_dict[supplier] = []
supplier_dict[supplier].append({
'name': record.material_name,
'product_qty': record.supplier1_qty,
'price_unit': record.supplier1_quoted_price
'product_qty': record.final_supplier1_qty,
'price_unit': record.final_supplier1_quoted_price
})
if record.supplier2_name:
supplier = record.supplier2_name
if supplier not in supplier_dict:
supplier_dict[supplier] = []
supplier_dict[supplier].append({
'name': record.material_name,
'product_qty': record.supplier2_qty,
'price_unit': record.supplier2_quoted_price
})
# if record.supplier2_name:
# supplier = record.supplier2_name
# if supplier not in supplier_dict:
# supplier_dict[supplier] = []
# supplier_dict[supplier].append({
# 'name': record.material_name,
# 'product_qty': record.supplier2_qty,
# 'price_unit': record.supplier2_quoted_price
# })
if record.supplier3_name:
supplier = record.supplier3_name
if supplier not in supplier_dict:
supplier_dict[supplier] = []
supplier_dict[supplier].append({
'name': record.material_name,
'product_qty': record.supplier3_qty,
'price_unit': record.supplier3_quoted_price
})
# if record.supplier3_name:
# supplier = record.supplier3_name
# if supplier not in supplier_dict:
# supplier_dict[supplier] = []
# supplier_dict[supplier].append({
# 'name': record.material_name,
# 'product_qty': record.supplier3_qty,
# 'price_unit': record.supplier3_quoted_price
# })
for x, y in supplier_dict.items():
sequence_util = self.env['sos_common_scripts']
po_no = sequence_util.generate_sequence('sos_po','PO', 'po_no')
@ -112,6 +112,7 @@ class SOS_Quote_Generation(models.Model):
})
message = 'Purchase Order(s) successfully generated.'
return {
'type': 'ir.actions.client',
'tag': 'display_notification',

View File

@ -26,6 +26,7 @@ class SOS_SalesOrder(models.Model):
string="Product Name",required=True)
line_ids = fields.One2many('sos_sales_order_line', 'ref_id',copy=True)
customer_name = fields.Many2one('sos_inventory_customers',string="Customer Name")
customer_location = fields.Char(string="Customer Location")
lead_time = fields.Datetime(string="Lead Time")
customer_po_no = fields.Char(string="PO No")
customer_po_date = fields.Datetime(string="PO Date")

View File

@ -106,7 +106,7 @@ class Sequence_Generator(models.AbstractModel):
reporting_user = env['res.users'].search([('id', '=', users.reporting_to.id)])
# email_to = reporting_user.login
else:
# email_to = "ramachandran.r@sosaley.in"
# email_to = "ramachandran.r@sosaley.com"
print("test")
mail_values = {
'subject': subject,

View File

@ -23,7 +23,17 @@ class SOS_Service_Call_Log_Report(models.Model):
action_taken = fields.Text(string="Action taken or required action to be taken")
Date_of_action_Completed = fields.Datetime(string="Date of action completed")
status = fields.Selection([('open', 'Open'),('close', 'Closed')], default='open' , string="Status")
dock_audit_ref_no = fields.Many2one('sos_dock_audit',string="Dock Audit Ref No")
warranty = fields.Integer(string="Warranty(In Months)")
invoice_no = fields.Char(string="Invoice No")
invoice_date = fields.Date(string="Invoice Date")
@api.onchange('dock_audit_ref_no')
def _onchange_dock_audit_ref_no(self):
if self.dock_audit_ref_no:
self.warranty = self.dock_audit_ref_no.warranty
self.invoice_no = self.dock_audit_ref_no.invoice_no
self.invoice_date = self.dock_audit_ref_no.invoice_date
def _generate_id(self):
sequence_util = self.env['sos_common_scripts']
return sequence_util.generate_sequence('sos_service_call_log_report','SCLR', 'ref_no')

View File

@ -111,6 +111,6 @@ class SOS_SFG_Line(models.Model):
ir_no = fields.Many2one('sos_ir', string="Material Inward Ref No")
min_no = fields.Many2one('sos_min', string="Material Issue Ref No")
mrn_no = fields.Many2one('sos_mrn', string="Material Return Ref No")
iqi_no = fields.Many2one('sos_iqi', string="In-house Inspection Ref No")
iqi_no = fields.Many2one('sos_iqi', string="IQI Ref No")

View File

@ -22,6 +22,10 @@ class sos_Outsourcing_Return(models.Model):
material_name = fields.Many2one('sos_material',string="Material Name",related="iqi_no.material_name")
returned_qty = fields.Integer(string="Returned Quantity",related="iqi_no.rejected_qty")
remarks = fields.Text(string="Remarks")
rework_type = fields.Selection([
('outsourcing', 'Out-Sourcing'),
('inhouse', 'In-House')
], string='Rework Type',default="outsourcing",copy=True)
def _generate_id(self):
sequence_util = self.env['sos_common_scripts']
return sequence_util.generate_sequence('sos_sfg_outsourcing_return_register','ORR', 'orr_no')

View File

@ -138,7 +138,7 @@ class sos__wo(models.Model):
"""
subject = f"Work Order Approval Request - {self.wo_no}"
send_email = self.env['sos_common_scripts']
send_email.send_direct_email(self.env,"sos_wo",self.id,"ramachandran.r@sosaley.in",subject,body_html)
send_email.send_direct_email(self.env,"sos_wo",self.id,"ramachandran.r@sosaley.com",subject,body_html)
# Email part ends
sequence_util = self.env['sos_common_scripts']
return sequence_util.action_assign_signature(

View File

@ -9,269 +9,296 @@
<field name="binding_type">report</field>
</record>
<template id="labels_print">
<t t-call="web.html_container">
<t t-foreach="docs" t-as="o">
<t t-call="web.basic_layout">
<!-- Include Bootstrap CSS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous"/>
<template id="labels_print">
<t t-call="web.html_container">
<t t-foreach="docs" t-as="o">
<t t-call="web.basic_layout">
<style>
/* Reset default margins and ensure full-page layout */
.page {
margin: 3mm; /* Reduced margins */
padding: 0;
width: 100%;
box-sizing: border-box;
overflow: hidden; /* Prevent content from overflowing */
}
<style type="text/css">
/* Container for each set of tables (FG, SFG, Materials) */
.label-set {
page-break-inside: avoid; /* Try to keep the entire set on one page */
page-break-after: always; /* Force a page break after each set */
}
/* Avoid clipping; wkhtmltopdf + overflow hidden can break pagination */
.page {
margin: 3mm;
padding: 0;
width: 100%;
box-sizing: border-box;
overflow: visible;
}
/* Ensure tables stay within their column */
.table-container {
width: 100%; /* Full width of the column */
page-break-inside: avoid; /* Prevent table from splitting across pages */
box-sizing: border-box;
}
/* Keep each whole set on one page when possible */
.label-set {
page-break-inside: avoid !important;
break-inside: avoid !important;
page-break-after: always; /* one set per page; remove if not desired */
}
/* Clearfix for the last table if odd number of tables */
.label-set::after {
content: "";
display: block;
clear: both;
}
/* Pair wrapper: keep both columns together */
.pair-block,
.row,
.col-6,
.table-container,
.label-table,
.label-table tr,
.label-table td {
page-break-inside: avoid !important;
break-inside: avoid !important;
}
/* Compact table styling */
.label-table {
width: 100%;
border-collapse: collapse;
}
/* Replace Bootstrap flex with inline-block for PDF reliability */
.row {
display: block !important;
margin: 0 0 12px 0;
}
.col-6 {
display: inline-block !important;
vertical-align: top;
width: 49.8%;
box-sizing: border-box;
padding-left: 5px;
padding-right: 5px;
}
.label-table td {
font-size: 15px; /* Smaller font size for compactness */
padding: 2mm; /* Reduced padding */
border: 1px solid #000;
box-sizing: border-box;
}
/* Table layout and text wrapping for long content */
.table-container { width: 100%; box-sizing: border-box; }
.label-table {
width: 100%;
border-collapse: collapse;
table-layout: fixed; /* keep widths predictable */
}
.label-table td {
font-size: 15px;
padding: 2mm;
border: 1px solid #000;
box-sizing: border-box;
white-space: normal; /* allow wrapping */
word-wrap: break-word;
word-break: break-word; /* break long tokens */
}
.label-table .column {
font-weight: bold;
font-size: 14px;
width: 20%;
}
.label-table .column {
font-weight: bold;
font-size: 14px;
width:20%
}
.empty-col { font-size: 10px; text-align: center; padding: 2mm; }
/* Adjust Bootstrap row margins for PDF */
.row {
margin-left: 0;
margin-right: 0;
margin-bottom:40px;
}
/* Vendor-ish safety (wkhtmltopdf is WebKit) */
@media print {
.pair-block, .row, .col-6, .table-container, .label-table, .label-table tr, .label-table td {
-webkit-region-break-inside: avoid;
}
}
</style>
/* Adjust Bootstrap column padding for PDF */
[class*="col-"] {
padding-left: 5px;
padding-right: 5px;
}
<div class="page">
<t t-set="counter" t-value="0"/>
<t t-foreach="range(o.master_quantity)" t-as="line_items">
<t t-set="counter" t-value="counter + 1"/>
/* Style for empty column placeholder */
.empty-col {
font-size: 10px;
text-align: center;
padding: 2mm;
}
</style>
<div class="label-set">
<div class="page">
<!-- Build a single list with: (category_id, record, index) -->
<t t-set="all_tables" t-value="[]"/>
<!-- FG -->
<t t-set="fg_counter" t-value="0"/>
<t t-foreach="o.line_ids_fg or []" t-as="fg_item">
<t t-set="fg_counter" t-value="fg_counter + 1"/>
<t t-set="all_tables" t-value="all_tables + [(1, fg_item, fg_counter)]"/>
</t>
<t t-set="counter" t-value="0"/>
<t t-foreach="range(o.master_quantity)" t-as="line_items">
<t t-set="counter" t-value="counter + 1"/>
<div class="label-set">
<!-- Combine all tables into a single list -->
<t t-set="all_tables" t-value="[]"/>
<!-- Add FG tables -->
<t t-set="fg_counter" t-value="0"/>
<t t-foreach="o.line_ids_fg or []" t-as="fg_item">
<t t-set="fg_counter" t-value="fg_counter + 1"/>
<t t-set="all_tables" t-value="all_tables + [(1, fg_item, fg_counter)]"/>
</t>
<!-- Add SFG tables -->
<t t-set="sfg_counter" t-value="0"/>
<t t-foreach="o.line_ids_sfg or []" t-as="sfg_item">
<t t-set="sfg_counter" t-value="sfg_counter + 1"/>
<t t-set="all_tables" t-value="all_tables + [(2, sfg_item, sfg_counter)]"/>
</t>
<!-- Add Materials tables -->
<t t-set="material_counter" t-value="0"/>
<t t-foreach="o.line_ids_material or []" t-as="material_item">
<t t-set="material_counter" t-value="material_counter + 1"/>
<t t-set="all_tables" t-value="all_tables + [(3, material_item, material_counter)]"/>
</t>
<!-- Add Installation Kit tables -->
<t t-set="installation_kit_counter" t-value="0"/>
<t t-foreach="o.line_ids_installation_kit or []" t-as="installation_kit_item">
<t t-set="installation_kit_counter" t-value="installation_kit_counter + 1"/>
<t t-set="all_tables" t-value="all_tables + [(4, installation_kit_item, installation_kit_counter)]"/>
</t>
<!-- Add Miscelleneous tables -->
<t t-set="miscellaneous_counter" t-value="0"/>
<t t-foreach="o.line_ids_miscellaneous or []" t-as="miscellaneous_item">
<t t-set="miscellaneous_counter" t-value="miscellaneous_counter + 1"/>
<t t-set="all_tables" t-value="all_tables + [(5, miscellaneous_item, miscellaneous_counter)]"/>
</t>
<!-- SFG -->
<t t-set="sfg_counter" t-value="0"/>
<t t-foreach="o.line_ids_sfg or []" t-as="sfg_item">
<t t-set="sfg_counter" t-value="sfg_counter + 1"/>
<t t-set="all_tables" t-value="all_tables + [(2, sfg_item, sfg_counter)]"/>
</t>
<!-- Materials -->
<t t-set="material_counter" t-value="0"/>
<t t-foreach="o.line_ids_material or []" t-as="material_item">
<t t-set="material_counter" t-value="material_counter + 1"/>
<t t-set="all_tables" t-value="all_tables + [(3, material_item, material_counter)]"/>
</t>
<!-- Installation Kit -->
<t t-set="installation_kit_counter" t-value="0"/>
<t t-foreach="o.line_ids_installation_kit or []" t-as="installation_kit_item">
<t t-set="installation_kit_counter" t-value="installation_kit_counter + 1"/>
<t t-set="all_tables" t-value="all_tables + [(4, installation_kit_item, installation_kit_counter)]"/>
</t>
<!-- Miscellaneous -->
<t t-set="miscellaneous_counter" t-value="0"/>
<t t-foreach="o.line_ids_miscellaneous or []" t-as="miscellaneous_item">
<t t-set="miscellaneous_counter" t-value="miscellaneous_counter + 1"/>
<t t-set="all_tables" t-value="all_tables + [(5, miscellaneous_item, miscellaneous_counter)]"/>
</t>
<!-- Process all tables in pairs -->
<t t-foreach="range(0, len(all_tables), 2)" t-as="pair_index">
<div class="row">
<!-- First table in the pair -->
<!-- Render in pairs -->
<t t-foreach="range(0, len(all_tables), 2)" t-as="pair_index">
<div class="pair-block">
<div class="row">
<!-- Left column -->
<div class="col-6">
<t t-set="table_info" t-value="all_tables[pair_index]"/>
<t t-set="table_info" t-value="all_tables[pair_index]"/>
<t t-set="category" t-value="table_info[0]"/>
<t t-set="item" t-value="table_info[1]"/>
<t t-set="index" t-value="table_info[2]"/>
<div class="table-container">
<table class="label-table">
<tbody>
<tr>
<td class="column">Set No</td>
<td>#<t t-esc="counter"/>/<t t-esc="o.master_quantity"/></td>
</tr>
<tr>
<td class="column">Battery</td>
<td><t t-esc="o.batter_or_cells"/></td>
</tr>
<tr>
<td class="column">No</td>
<td><t t-esc="category"/>.<t t-esc="index"/></td>
</tr>
<tr>
<td class="column">Name</td>
<td>
<t t-if="'component_id' in item._fields">
<t t-esc="item.component_id.name or item.component_id.part_no or item.name or 'N/A'"/>
</t>
<t t-else="">
<t t-esc="item.name or 'N/A'"/>
</t>
</td>
</tr>
<tr>
<td class="column">UOM</td>
<td>
<t t-if="'uom' in item._fields">
<t t-esc="item.uom or 'N/A'"/>
</t>
<t t-else="">
<t t-esc="'N/A'"/>
</t>
</td>
</tr>
<tr>
<td class="column">Qty</td>
<td>
<t t-if="category == 5">
<t t-esc="(item.quantity or 0) // (o.master_quantity or 1)"/>
</t>
<t t-else="">
<t t-if="'singet_set_qty' in item._fields">
<t t-esc="item.singet_set_qty or 'N/A'"/>
</t>
<t t-else="">
<t t-esc="item.quantity or 'N/A'"/>
</t>
</t>
</td>
</tr>
<tr>
<td class="column">S.No</td>
<td></td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- Right column -->
<div class="col-6">
<t t-if="pair_index + 1 &lt; len(all_tables)">
<t t-set="table_info" t-value="all_tables[pair_index + 1]"/>
<t t-set="category" t-value="table_info[0]"/>
<t t-set="item" t-value="table_info[1]"/>
<t t-set="index" t-value="table_info[2]"/>
<div class="table-container">
<table class="label-table">
<tbody>
<tr>
<td class="column">Set No</td>
<td>#<t t-esc="counter"/>/<t t-esc="o.master_quantity"/></td>
</tr>
<tr>
<td class="column">Battery</td>
<td><t t-esc="o.batter_or_cells"/></td>
</tr>
<tr>
<td class="column">No</td>
<td><t t-esc="category"/>.<t t-esc="index"/></td>
</tr>
<tr>
<td class="column">Name</td>
<td>
<t t-if="'component_id' in item">
<t t-esc="item.component_id.name or item.component_id.part_no or item.name or 'N/A'"/>
</t>
<t t-else="">
<t t-esc="item.name or 'N/A'"/>
</t>
</td>
</tr>
<tr>
<td class="column">UOM</td>
<td>
<t t-if="'uom' in item">
<t t-esc="item.uom or 'N/A'"/>
</t>
<t t-else="">
<t t-esc="'N/A'"/>
</t>
</td>
</tr>
<tr>
<td class="column">Qty</td>
<td>
<t t-if="'singet_set_qty' in item">
<t t-esc="item.singet_set_qty or 'N/A'"/>
</t>
<t t-else="">
<t t-esc="item.quantity or 'N/A'"/>
</t>
</td>
</tr>
<tr>
<td class="column">S.No</td>
<td></td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- Second table in the pair (if exists) -->
<div class="col-6">
<t t-if="pair_index + 1 &lt; len(all_tables)">
<t t-set="table_info" t-value="all_tables[pair_index + 1]"/>
<t t-set="category" t-value="table_info[0]"/>
<t t-set="item" t-value="table_info[1]"/>
<t t-set="index" t-value="table_info[2]"/>
<div class="table-container">
<table class="label-table">
<tbody>
<tr>
<td class="column">Set No</td>
<td>#<t t-esc="counter"/>/<t t-esc="o.master_quantity"/></td>
</tr>
<tr>
<td class="column">Battery</td>
<td><t t-esc="o.batter_or_cells"/></td>
</tr>
<tr>
<td class="column">No</td>
<td><t t-esc="category"/>.<t t-esc="index"/></td>
</tr>
<tr>
<td class="column">Name</td>
<td>
<t t-if="'component_id' in item">
<t t-esc="item.component_id.name or item.component_id.part_no or item.name or 'N/A'"/>
</t>
<t t-else="">
<t t-esc="item.name or 'N/A'"/>
</t>
</td>
</tr>
<tr>
<td class="column">UOM</td>
<td>
<t t-if="'uom' in item">
<t t-esc="item.uom or 'N/A'"/>
</t>
<t t-else="">
<t t-esc="'N/A'"/>
</t>
</td>
</tr>
<tr>
<td class="column">Qty</td>
<td>
<t t-if="'singet_set_qty' in item">
<t t-esc="item.singet_set_qty or 'N/A'"/>
</t>
<t t-else="">
<t t-esc="item.quantity or 'N/A'"/>
</t>
</td>
</tr>
<tr>
<td class="column">S.No</td>
<td></td>
</tr>
</tbody>
</table>
</div>
</t>
<t t-else="">
<div class="empty-col"></div>
</t>
</div>
</div> <!-- Close row -->
</t>
</div>
<table class="label-table">
<tbody>
<tr>
<td class="column">Set No</td>
<td>#<t t-esc="counter"/>/<t t-esc="o.master_quantity"/></td>
</tr>
<tr>
<td class="column">Battery</td>
<td><t t-esc="o.batter_or_cells"/></td>
</tr>
<tr>
<td class="column">No</td>
<td><t t-esc="category"/>.<t t-esc="index"/></td>
</tr>
<tr>
<td class="column">Name</td>
<td>
<t t-if="'component_id' in item._fields">
<t t-if="category in [3, 4]">
<t t-esc="item.component_id.part_no or item.name or 'N/A'"/>
</t>
<t t-else="">
<t t-esc="item.component_id.name or item.name or 'N/A'"/>
</t>
</t>
</div>
</t>
</t>
</t>
<t t-else="">
<t t-esc="item.name or 'N/A'"/>
</t>
</td>
</tr>
<tr>
<td class="column">UOM</td>
<td>
<t t-if="'uom' in item._fields">
<t t-esc="item.uom or 'N/A'"/>
</t>
<t t-else="">
<t t-esc="'N/A'"/>
</t>
</td>
</tr>
<tr>
<td class="column">Qty</td>
<td>
<t t-if="category == 5">
<t t-esc="(item.quantity or 0) // (o.master_quantity or 1)"/>
</t>
<t t-else="">
<t t-if="'singet_set_qty' in item._fields">
<t t-esc="item.singet_set_qty or 'N/A'"/>
</t>
<t t-else="">
<t t-esc="item.quantity or 'N/A'"/>
</t>
</t>
</td>
</tr>
<tr>
<td class="column">S.No</td>
<td></td>
</tr>
</tbody>
</table>
</div>
</t>
<t t-else="">
<div class="empty-col"></div>
</t>
</div>
</div> <!-- /row -->
</div> <!-- /pair-block -->
</t>
</div> <!-- /label-set -->
</t>
</div> <!-- /page -->
</t>
</t>
</t>
</template>
</odoo>

View File

@ -0,0 +1,91 @@
<odoo>
<record id="action_material_budget_summary" model="ir.actions.report">
<field name="name">Material Budget Summary</field>
<field name="model">sos_budget_plan</field> <!-- Adjust model if needed -->
<field name="report_type">qweb-html</field>
<field name="report_name">sos_inventory.report_material_budget_summary</field>
<field name="print_report_name">Material_Budget_Summary</field>
</record>
<template id="report_material_budget_summary">
<t t-call="web.basic_layout">
<t t-call="web.html_container">
<style>
body { font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; color: #1f2937; }
.page { max-width: 1200px; margin: 0 auto; padding: 20px; }
.header { border-bottom: 2px solid #e5e7eb; padding-bottom: 16px; margin-bottom: 24px; }
.header h3 { font-size: 24px; font-weight: 700; margin: 0; color: #1e40af; }
.header .info { font-size: 14px; color: #4b5563; margin-top: 8px; }
.header .info strong { font-weight: 600; }
.section-title { font-size: 18px; font-weight: 600; color: #1e40af; margin: 24px 0 12px; }
.count { font-size: 14px; color: #4b5563; margin-bottom: 8px; }
.table-custom { width: 100%; border-collapse: separate; border-spacing: 0; background-color: #fff; box-shadow: 0 1px 3px rgba(0,0,0,0.1); border-radius: 8px; overflow: hidden; }
.table-custom th { background-color: #e4e0f9; color: #1e40af; font-weight: 600; padding: 12px; text-align: left; border-bottom: 1px solid #d1d5db; }
.table-custom td { padding: 12px; border-bottom: 1px solid #e5e7eb; }
.table-custom tr:last-child td { border-bottom: none; }
.table-custom th.center, .table-custom td.center { text-align: center; }
.no-data { text-align: center; color: #6b7280; padding: 16px; font-style: italic; }
.total-cost{
display:flex; justify-content:flex-end; align-items:center; gap:8px;
padding:8px 10px; margin:10px 0 6px;
background:#fff; border:1px solid #e5e7eb; border-radius:8px;
font-size:14px;
}
.total-cost .label{ color:#4b5563; }
.total-cost .amount{ font-weight:700; font-size:16px; }
</style>
<div class="page">
<div class="header">
<h3>Budget Summary</h3>
<div class="info">
<strong>Name:</strong> <t t-esc="fg_name or '—'"/>
| <strong>Planned Quantity:</strong> <t t-esc="total_quantity or '—'"/>
</div>
</div>
<!-- Material Quantities Table -->
<h4 class="section-title">Materials to Purchase</h4>
<div class="count">Total Materials: <t t-esc="len(materials)"/></div>
<div class="total-cost">
<span class="label">Estimated Purchase Cost:</span>
<span class="amount">
<t t-esc="currency_symbol"/> <t t-esc="'{:,.2f}'.format(total_cost)"/>
</span>
</div>
<table class="table-custom">
<thead>
<tr>
<th>Material Name</th>
<th class="center">Needed Qty</th>
<th class="center">In-Hand Qty</th>
<th class="center">Actual Needed Qty</th>
<th class="center">Standard Packing Qty</th>
<th class="center">To Purchase</th>
<th class="center">cost</th>
</tr>
</thead>
<tbody>
<t t-if="materials">
<t t-foreach="materials" t-as="material">
<tr>
<td><t t-esc="material['material_name']"/></td>
<td class="center"><t t-esc="material['needed_quantity']"/></td>
<td class="center"><t t-esc="material['inhand_quantity']"/></td>
<td class="center"><t t-esc="material['actual_needed']"/></td>
<td class="center"><t t-esc="material['std_packing_qty']"/></td>
<td class="center"><t t-esc="material['to_purchase']"/></td>
<td class="center"><t t-esc="material['cost']"/></td>
</tr>
</t>
</t>
<t t-if="not materials or len(materials) == 0">
<tr><td colspan="4" class="no-data">No materials need to be purchased.</td></tr>
</t>
</tbody>
</table>
</div>
</t>
</t>
</template>
</odoo>

View File

@ -72,15 +72,17 @@
<table class="table table-bordered">
<tbody>
<t t-if="o.material_option == True">
<tr style="background-color:#ccc"><td class="column" colspan="6">Materials</td></tr>
<tr style="background-color:#ccc"><td class="column" colspan="7">Materials</td></tr>
<tr class="column">
<td>S.No</td>
<td class="column">Material Code</td>
<td class="column">Name</td>
<td class="column">Location</td>
<td class="column">In-Hand Quantity</td>
<td class="column">Requested Quantity</td>
<td class="column">Given Quantity</td>
</tr>
<t t-foreach="o.line_ids_material" t-as="line_items">
@ -95,6 +97,9 @@
<td><t t-esc="line_items.new_component_id"/></td>
</t>
<t t-if="line_items.com_type == 'exits'">
<td><t t-esc="line_items.location"/></td>
</t>
<t t-if="line_items.com_type == 'exits'">
<td><t t-esc="line_items.inhand_stock_qty"/></td>
</t>
<t t-if="line_items.com_type == 'new'">

View File

@ -56,6 +56,9 @@ access_ir_act_window_healthcare_user,Access Action Window for Health Care Users,
access_ir_act_window_sos_inside_sales_user,Access Action Window for Inside sales Users,base.model_ir_actions_act_window,sos_inventory.sos_inside_sales_user,1,0,0,0
access_ir_act_window_hr_user,Access Action Window for Human Care Resources Users,base.model_ir_actions_act_window,sos_inventory.sos_hr_user,1,0,0,0
access_ir_act_window_sos_marketing_user,Access Action Window for Marketing Users,base.model_ir_actions_act_window,sos_inventory.sos_marketing_user,1,0,0,0
access_ir_act_window_sos_sales_reviewer,Access Action Window for Sales Reviewer,base.model_ir_actions_act_window,sos_inventory.sos_sales_reviewer,1,0,0,0
access_ir_act_window_finance_head_user,Access Action Window for Finance Head,base.model_ir_actions_act_window,sos_inventory.sos_finance_head_user,1,0,0,0
access_ir_act_window_sales_sapl_user,Access Action Window for Sales SAPL Users,base.model_ir_actions_act_window,sos_inventory.sos_sales_sapl_user,1,0,0,0
access_sos_return_fir,sos_return_fir access,model_sos_return_fir,base.group_user,1,1,1,1
access_sos_return_fir_line,sos_return_fir_line access,model_sos_return_fir_line,base.group_user,1,1,1,1
access_sos_return_calibration_devices_fir,sos_return_calibration_devices_fir access,model_sos_return_calibration_devices_fir,base.group_user,1,1,1,1
@ -182,6 +185,7 @@ access_sos_transfer_challan_specification_lines,sos_transfer_challan_specificati
access_sos_fir_serial_no_lines,sos_fir_serial_no_lines access,model_sos_fir_serial_no_lines,base.group_user,1,1,1,1
access_sos_miscellaneous_deliverables,sos_miscellaneous_deliverables access,model_sos_miscellaneous_deliverables,base.group_user,1,1,1,1
access_sos_mat_outsourcing_vendor_register,sos_mat_outsourcing_vendor_register access,model_sos_mat_outsourcing_vendor_register,base.group_user,1,1,1,1
access_sos_mat_outsourcing_vendor_register_lines,sos_mat_outsourcing_vendor_register_lines access,model_sos_mat_outsourcing_vendor_register_lines,base.group_user,1,1,1,1
access_stock_movement_report_wizard,stock_movement_report_wizard access,model_stock_movement_report_wizard,base.group_user,1,1,1,1
access_sos_inventory_customers,sos_inventory_customers access,model_sos_inventory_customers,base.group_user,1,1,1,1
access_sos_master_list_customer,sos_master_list_customer access,model_sos_master_list_customer,base.group_user,1,1,1,1
@ -197,5 +201,11 @@ access_sos_parameter_fir,sos_parameter_fir access,model_sos_parameter_fir,base.g
access_sos_transfer_challan_return_from_customer_lines,sos_transfer_challan_return_from_customer_lines access,model_sos_transfer_challan_return_from_customer_lines,base.group_user,1,1,1,1
access_sos_transfer_challan_return_from_customer,sos_transfer_challan_return_from_customer access,model_sos_transfer_challan_return_from_customer,base.group_user,1,1,1,1
access_sos_shelflife_register,sos_shelflife_register access,model_sos_shelflife_register,base.group_user,1,1,1,1
access_sos_budget_plan,sos_budget_plan access,model_sos_budget_plan,base.group_user,1,1,1,1
access_sos_prf,sos_prf access,model_sos_prf,base.group_user,1,1,1,1
access_sos_prf_lines,sos_prf_lines access,model_sos_prf_lines,base.group_user,1,1,1,1
access_sos_departments_user_lines,sos_departments_user_lines access,model_sos_departments_user_lines,base.group_user,1,1,1,1
access_sos_ncmr_problem_description_line,access_sos_ncmr_problem_description_line access,model_sos_ncmr_problem_description_line,base.group_user,1,1,1,1
access_sos_ccrf_capa_line,access_sos_ccrf_capa_line access,model_sos_ccrf_capa_line,base.group_user,1,1,1,1
access_sos_ccrf_team_formation_line,access_sos_ccrf_team_formation_line access,model_sos_ccrf_team_formation_line,base.group_user,1,1,1,1
access_sos_ccrf_problem_description_line,access_sos_ccrf_problem_description_line access,model_sos_ccrf_problem_description_line,base.group_user,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
56 access_ir_act_window_sos_inside_sales_user Access Action Window for Inside sales Users base.model_ir_actions_act_window sos_inventory.sos_inside_sales_user 1 0 0 0
57 access_ir_act_window_hr_user Access Action Window for Human Care Resources Users base.model_ir_actions_act_window sos_inventory.sos_hr_user 1 0 0 0
58 access_ir_act_window_sos_marketing_user Access Action Window for Marketing Users base.model_ir_actions_act_window sos_inventory.sos_marketing_user 1 0 0 0
59 access_ir_act_window_sos_sales_reviewer Access Action Window for Sales Reviewer base.model_ir_actions_act_window sos_inventory.sos_sales_reviewer 1 0 0 0
60 access_ir_act_window_finance_head_user Access Action Window for Finance Head base.model_ir_actions_act_window sos_inventory.sos_finance_head_user 1 0 0 0
61 access_ir_act_window_sales_sapl_user Access Action Window for Sales SAPL Users base.model_ir_actions_act_window sos_inventory.sos_sales_sapl_user 1 0 0 0
62 access_sos_return_fir sos_return_fir access model_sos_return_fir base.group_user 1 1 1 1
63 access_sos_return_fir_line sos_return_fir_line access model_sos_return_fir_line base.group_user 1 1 1 1
64 access_sos_return_calibration_devices_fir sos_return_calibration_devices_fir access model_sos_return_calibration_devices_fir base.group_user 1 1 1 1
185 access_sos_fir_serial_no_lines sos_fir_serial_no_lines access model_sos_fir_serial_no_lines base.group_user 1 1 1 1
186 access_sos_miscellaneous_deliverables sos_miscellaneous_deliverables access model_sos_miscellaneous_deliverables base.group_user 1 1 1 1
187 access_sos_mat_outsourcing_vendor_register sos_mat_outsourcing_vendor_register access model_sos_mat_outsourcing_vendor_register base.group_user 1 1 1 1
188 access_sos_mat_outsourcing_vendor_register_lines sos_mat_outsourcing_vendor_register_lines access model_sos_mat_outsourcing_vendor_register_lines base.group_user 1 1 1 1
189 access_stock_movement_report_wizard stock_movement_report_wizard access model_stock_movement_report_wizard base.group_user 1 1 1 1
190 access_sos_inventory_customers sos_inventory_customers access model_sos_inventory_customers base.group_user 1 1 1 1
191 access_sos_master_list_customer sos_master_list_customer access model_sos_master_list_customer base.group_user 1 1 1 1
201 access_sos_transfer_challan_return_from_customer_lines sos_transfer_challan_return_from_customer_lines access model_sos_transfer_challan_return_from_customer_lines base.group_user 1 1 1 1
202 access_sos_transfer_challan_return_from_customer sos_transfer_challan_return_from_customer access model_sos_transfer_challan_return_from_customer base.group_user 1 1 1 1
203 access_sos_shelflife_register sos_shelflife_register access model_sos_shelflife_register base.group_user 1 1 1 1
204 access_sos_budget_plan sos_budget_plan access model_sos_budget_plan base.group_user 1 1 1 1
205 access_sos_prf sos_prf access model_sos_prf base.group_user 1 1 1 1
206 access_sos_prf_lines sos_prf_lines access model_sos_prf_lines base.group_user 1 1 1 1
207 access_sos_departments_user_lines sos_departments_user_lines access model_sos_departments_user_lines base.group_user 1 1 1 1
208 access_sos_ncmr_problem_description_line access_sos_ncmr_problem_description_line access model_sos_ncmr_problem_description_line base.group_user 1 1 1 1
209 access_sos_ccrf_capa_line access_sos_ccrf_capa_line access model_sos_ccrf_capa_line base.group_user 1 1 1 1
210 access_sos_ccrf_team_formation_line access_sos_ccrf_team_formation_line access model_sos_ccrf_team_formation_line base.group_user 1 1 1 1
211 access_sos_ccrf_problem_description_line access_sos_ccrf_problem_description_line access model_sos_ccrf_problem_description_line base.group_user 1 1 1 1

View File

@ -153,4 +153,24 @@
</record>
<record id="sos_mon_ce_team_rule" model="ir.rule">
<field name="name">MON: CE Team View CE Records</field>
<field name="model_id" ref="model_sos_mon"/>
<field name="domain_force">[('is_ce_user_created', '=', True)]</field>
<field name="groups" eval="[(4, ref('sos_inventory.sos_ce_user'))]"/>
<field name="perm_read" eval="1"/>
<field name="perm_write" eval="1"/>
<field name="perm_create" eval="1"/>
<field name="perm_unlink" eval="0"/>
</record>
<record id="sos_mrn_ce_team_rule" model="ir.rule">
<field name="name">MRN: CE Team View CE Records</field>
<field name="model_id" ref="model_sos_mrn"/>
<field name="domain_force">[('is_ce_user_created', '=', True)]</field>
<field name="groups" eval="[(4, ref('sos_inventory.sos_ce_user'))]"/>
<field name="perm_read" eval="1"/>
<field name="perm_write" eval="1"/>
<field name="perm_create" eval="1"/>
<field name="perm_unlink" eval="0"/>
</record>
</odoo>

View File

@ -75,6 +75,12 @@
<field name="category_id"
ref="sos_inventory.module_category_sos_inventory"/>
</record>
<!-- Finance Head Group -->
<record id="sos_finance_head_user" model="res.groups">
<field name="name">Finance Head</field>
<field name="category_id"
ref="sos_inventory.module_category_sos_inventory"/>
</record>
<!-- Logistics Group -->
<record id="sos_logistics_user" model="res.groups">
<field name="name">Logistics User</field>
@ -97,7 +103,7 @@
<field name="category_id"
ref="sos_inventory.module_category_sos_inventory"/>
</record>
<!-- Finance Group -->
<!-- Haelthcare Group -->
<record id="sos_healthcare_user" model="res.groups">
<field name="name">Healthcare User</field>
<field name="category_id"
@ -121,6 +127,17 @@
<field name="category_id"
ref="sos_inventory.module_category_sos_inventory"/>
</record>
<!-- Sales/Inside Reviewer -->
<record id="sos_sales_reviewer" model="res.groups">
<field name="name">Sales Reviewer</field>
<field name="category_id"
ref="sos_inventory.module_category_sos_inventory"/>
</record>
<!-- Sales/Inside Reviewer -->
<record id="sos_sales_sapl_user" model="res.groups">
<field name="name">Sales SAPL</field>
<field name="category_id"
ref="sos_inventory.module_category_sos_inventory"/>
</record>
</data>
</odoo>

View File

@ -2,6 +2,13 @@ body{
font-size: 15px;
font-family: sans-serif;
}
.modal.o_technical_modal .modal-content .modal-header .modal-title {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
color: #fff;
}
.btn-primary {
color: #FFFFFF;
background-color: #71639e !important;

View File

@ -0,0 +1,344 @@
/** @odoo-module **/
import { registry } from "@web/core/registry";
import { Component, onWillStart, useRef, onMounted } from "@odoo/owl";
import { useService } from "@web/core/utils/hooks";
export class ParetoChartWidget extends Component {
static template = "sos_inventory.ParetoChartWidget";
setup() {
this.orm = useService("orm");
this.action = useService("action");
this.chartRef = useRef("paretoChart");
this.startDateRef = useRef("startDate");
this.endDateRef = useRef("endDate");
this.noDataMessageRef = useRef("endDate");
this.goodsTypeRef = useRef("goodsType");
this.categoryRef = useRef("category");
this.servicesupplierRef = useRef("servicesupplier");
this.chartInstance = null; // Track chart instance
// 👉 Pre-fill start & end date when component is mounted
onMounted(() => {
const today = new Date();
// First and last day of the month
const firstDay = new Date(today.getFullYear(), today.getMonth(), 1);
const lastDay = new Date(today.getFullYear(), today.getMonth() + 1, 0);
const formatDate = (date) => {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
};
this.startDateRef.el.value = formatDate(firstDay);
this.endDateRef.el.value = formatDate(lastDay);
//this.onGoodsTypeChange();
//this.onServiceSupplierChange();
this.loadData();
});
}
async onServiceSupplierChange() {
const goods_type = this.goodsTypeRef.el.value;
const servicesupplierSelect = this.servicesupplierRef.el;
const startDate = this.startDateRef.el.value || false;
const endDate = this.endDateRef.el.value || false;
//alert(selectedGoodsType+" FFFF :::: "+categorySelect);
// Clear existing options
servicesupplierSelect.innerHTML = '';//last 12.16 22/08/2025
const defaultOption = document.createElement('option');
defaultOption.text = 'Select Supplier/Service';
defaultOption.disabled = true;
defaultOption.selected = true;
servicesupplierSelect.appendChild(defaultOption);
//alert(" selectedGoodsType :::: "+selectedGoodsType);
if (goods_type != "FG"){
try {
// Call backend route to get categories by goodsType
const categories = await this.orm.call('sos_ncmr', 'get_service_suppliers_by_goods_type', [goods_type, startDate, endDate]);
if (!categories.length) {
servicesupplierSelect.innerHTML = '';
const option = document.createElement('option');
option.text = 'No Data Found';
option.disabled = true;
servicesupplierSelect.appendChild(option);
} else {
servicesupplierSelect.innerHTML = '';
categories.forEach(cat => {
const option = document.createElement('option');
option.text = cat.supplier_name;
option.value = cat.id;
servicesupplierSelect.appendChild(option);
});
}
} catch (error) {
console.error('Failed to fetch categories:', error);
const option = document.createElement('option');
option.text = 'Error loading categories';
option.disabled = true;
servicesupplierSelect.appendChild(option);
}
}
}
async onGoodsTypeChange() {
this.onServiceSupplierChange();
const selectedGoodsType = this.goodsTypeRef.el.value;
const categorySelect = this.categoryRef.el;
categorySelect.innerHTML = '';
const defaultOption = document.createElement('option');
defaultOption.text = 'Select Category';
defaultOption.disabled = true;
defaultOption.selected = true;
categorySelect.appendChild(defaultOption);
let categories = [];
if (selectedGoodsType == 'Material'){
categories = [
{'id': '', 'name': 'Select Category'},
{'id': 'Active Components', 'name': 'Active Components'},
{'id': 'Passive Components', 'name': 'Passive Components'},
{'id': 'Fabrication', 'name': 'Fabrication'},
{'id': 'Accessories', 'name': 'Accessories'},
{'id': 'Cables & Connectors', 'name': 'Cables & Connectors'},
{'id': 'Metal', 'name': 'Metal'},
]
}else if (selectedGoodsType == 'SFG'){
categories = [
{'id': '', 'name': 'Select Category'},
{'id': 'pcba', 'name': 'PCB Board'},
{'id': 'cables', 'name': 'Cables & Connectors'},
{'id': 'others', 'name': 'Others'},
]
}else if (selectedGoodsType == 'FG'){
categories = [
{'id': '', 'name': 'Select Category'},
{'id': 'Health Care', 'name': 'Health Care'},
{'id': 'BHMS', 'name': 'BHMS'},
{'id': 'LV-BMS', 'name': 'BMS-LV'},
{'id': 'HV-BMS', 'name': 'BMS-HV'},
{'id': 'SBMS', 'name': 'SBMS'},
{'id': 'Motor Controller', 'name': 'Motor Controller'},
]
}
try {
// Call backend route to get categories by goodsType
if (!categories.length) {
const option = document.createElement('option');
option.text = 'No Categories Found';
option.disabled = true;
categorySelect.appendChild(option);
} else {
categorySelect.innerHTML = '';
categories.forEach(cat => {
const option = document.createElement('option');
option.text = cat.name;
option.value = cat.id;
categorySelect.appendChild(option);
});
}
} catch (error) {
console.error('Failed to fetch categories:', error);
const option = document.createElement('option');
option.text = 'Error loading categories';
option.disabled = true;
categorySelect.appendChild(option);
}
}
async onFilterClick() {
await this.loadData();
}
showNoDataMessage() {
this.noDataMessageRef.el.style.display = "block";
if (this.chartInstance) {
this.chartInstance.destroy();
this.chartInstance = null;
}
}
hideNoDataMessage() {
this.noDataMessageRef.el.style.display = "none";
}
async loadData() {
const startDate = this.startDateRef.el.value || false;
const endDate = this.endDateRef.el.value || false;
const goodstype = this.goodsTypeRef.el.value || false;
const categoryId = this.categoryRef.el.value || false;
const servicesupplier = this.servicesupplierRef.el.value || false;
//alert(" categoryId :: "+categoryId);
if (!startDate || !endDate) {
// Optionally show a message or just return
console.warn("Start and end date are required.");
return;
}
const domain = [];
if (startDate) domain.push(['date', '>=', startDate]);
if (endDate) domain.push(['date', '<=', endDate]);
let records = [];
try {
records = await this.orm.call("sos_ncmr", "get_pareto_data", [startDate, endDate, goodstype, categoryId, servicesupplier]); //, goodstype , categoryId
} catch (error) {
console.error("Failed to fetch pareto data:", error);
//this.showNoDataMessage();
//return;
}
// if (!records || records.length === 0) {
// this.showNoDataMessage();
// return;
// }
//this.hideNoDataMessage();
this.renderChart(records); // ✅ cleaner and reusable
// const records = await this.orm.searchRead("pareto.defect.report", [], [
// "defect_name", "count", "individual_percent", "cumulative_percent"
// ]);
}
renderChart(records){
const labels = records.map(r => r.defect_name);
const counts = records.map(r => r.count);
const cumulative = records.map(r => r.cumulative_percent);
//const ctx = this.el.querySelector("#paretoChart").getContext("2d");
const threshold = 80;
let thresholdLine = [];
let thresholdReached = false;
for (let i = 0; i < cumulative.length; i++) {
if (!thresholdReached) {
thresholdLine.push(threshold);
if (cumulative[i] >= threshold) {
// Insert drop to bottom after this
thresholdLine.push(0); // drop vertically
thresholdLine.push(null); // stop the line
break;
}
}
}
const ctx = this.chartRef.el.getContext("2d");
// Plugin to draw lines after bars
const drawLinesLastPlugin = {
id: 'drawLinesLast',
afterDatasetsDraw(chart) {
const ctx = chart.ctx;
chart.data.datasets.forEach((dataset, index) => {
if (dataset.type === 'line') {
const meta = chart.getDatasetMeta(index);
meta.controller.draw(ctx);
}
});
}
};
// ✅ Destroy previous chart if exists
if (this.chartInstance) {
this.chartInstance.destroy();
}
// ✅ Create new chart and store it
this.chartInstance = new Chart(ctx, {
type: 'bar',
data: {
labels,
datasets: [
{
label: "Defect Qty",
data: counts,
backgroundColor: "steelblue",
yAxisID: 'y',
order: 1,
},
{
label: "Cumulative %",
data: cumulative,
type: "line",
borderColor: "green",
yAxisID: 'y1',
tension: 0.4,
order: 2,
},
{
label: "80% Threshold",
data: new Array(records.length).fill(80),
//data: thresholdLine,
type: "line",
borderColor: "red",
borderDash: [5, 5],
pointRadius: 0,
fill: false,
yAxisID: 'y1',
spanGaps: false,
stepped: true,
order: 3,
}
]
},
options: {
responsive: true,
plugins: {
legend: { position: "bottom" },
title: {
display: true,
text: "" //Pareto Chart - cables, Cables & Connectors
}
},
scales: {
y: {
type: 'linear',
position: 'left',
title: { display: true, text: 'Defect Count' },
beginAtZero: true
},
y1: {
type: 'linear',
position: 'right',
title: { display: true, text: 'Percentage' },
beginAtZero: true,
min: 0,
max: 120,
ticks: {
callback: function (value) {
return value + "%";
}
},
grid: { drawOnChartArea: false }
}
}
},
plugins: [drawLinesLastPlugin],
});
}
}
//ParetoChartWidget.template = "sos_inventory.ParetoChartWidget";
registry.category("actions").add("pareto_chart_widget", ParetoChartWidget);

View File

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates>
<t t-name="sos_inventory.ParetoChartWidget">
<!-- <div class="container mt-3">
<canvas t-ref="paretoChart" id="paretoChart" width="100%" height="40"></canvas>
</div> -->
<div class="o_pareto_chart_container">
<div class="o_date_filter_controls" style="margin-left:20px;">
<table>
<tr>
<td>
<div class="form-group">
<label>Start Date:</label><br />
<input type="date" t-ref="startDate" class="form-control" style="min-width: 150px;" />
</div>
</td>
<td>
<div class="form-group">
<label>End Date:</label><br />
<input type="date" t-ref="endDate" class="form-control" style="min-width: 150px;" />
</div>
</td>
<td>
<div class="form-group">
<label>Goods Type:</label><br />
<select t-ref="goodsType" t-on-change="onGoodsTypeChange" class="form-select" style="min-width: 150px;">
<option selected="selected">Material</option>
<option>SFG</option>
<option>FG</option>
</select>
</div>
</td>
<td>
<div class="form-group">
<label>Category:</label><br />
<select t-ref="category" class="form-select" style="min-width: 150px;">
<option selected="selected" disabled="disabled">Select Category</option>
</select>
</div>
</td>
<td>
<div class="form-group">
<label>Supplier/Service:</label><br />
<select t-ref="servicesupplier" class="form-select" style="min-width: 150px;">
<option selected="selected" disabled="disabled">Select Supplier/Service</option>
</select>
</div>
</td>
<td>
<div class="form-group" style="margin-top: 1.5rem;">
<button t-on-click="onFilterClick" class="btn btn-primary">Graph</button>
</div>
</td>
</tr>
</table>
</div>
<!-- Placeholder for No Data -->
<div t-ref="noDataMessage" class="o_no_data_message" style="display: none; text-align: center; font-weight: bold; color: red; margin: 2rem;">
No Data Available for Selected Dates
</div>
<!-- <button class="btn btn-primary o-default-button" style="display:none;">Ok</button> -->
<canvas t-ref="paretoChart" id="paretoChart" width="1600" height="600"></canvas>
</div>
</t>
</templates>

View File

@ -46,10 +46,7 @@
<menuitem id="mop_forms_menu_root"
name="MOP FORMS"
parent="forms_menu_root"/>
<!-- CE Forms -->
<menuitem id="ce_forms_menu_root"
name="CE FORMS"
parent="forms_menu_root"/>
<!-- End CE Forms -->
<menuitem id="mme_forms_menu_root"
name="MME FORMS"

View File

@ -0,0 +1,8 @@
<odoo>
<record id="action_pareto_chart" model="ir.actions.client">
<field name="name">Pareto Chart Analysis</field>
<field name="tag">pareto_chart_widget</field>
</record>
</odoo>

View File

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="action_budget_plan_list" model="ir.actions.act_window">
<field name="name">Budget Plan</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">sos_budget_plan</field>
<field name="view_mode">tree,form</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
No Plan Found
</p>
</field>
</record>
<record id="sos_budget_plan_view_tree" model="ir.ui.view">
<field name="name">sos_budget_plan.view.tree</field>
<field name="model">sos_budget_plan</field>
<field name="arch" type="xml">
<tree>
<field name="name"/>
<field name="quantity"/>
<field name="write_uid" string="Last Edited By" optional="hide"/>
<field name="write_date" string="Last Edited On" optional="hide"/>
</tree>
</field>
</record>
<record id="sos_budget_plan_form_view" model="ir.ui.view">
<field name="name">Form</field>
<field name="model">sos_budget_plan</field>
<field name="arch" type="xml">
<form string="Budget Plan">
<sheet>
<h2 style="text-align: center;text-transform: uppercase;text-shadow: 1px 1p 1px #140718;color: #65407c;padding:5px;">Budget Plan</h2><hr></hr><br></br>
<table class="table table" style="box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px;">
<tr><td><group><field name="sfg_option"/></group></td>
<td><group><field name="fg_option"/></group></td></tr>
</table>
<br></br>
<group>
<!-- Left Column -->
<group>
<field name="name"/>
<field name="quantity"/>
</group>
<!-- Right Column -->
<group>
<field name="fg_name" invisible="sfg_option"/>
<field name="sfg_name" invisible="fg_option"/>
</group>
<button class="btn btn-primary" type="object" name="create_budget_plan" string="Execute"/>
</group>
</sheet>
</form>
</field>
</record>
<menuitem id="budget_plan_menu"
name="Budget Plan"
parent="indent_menu_root"
action = "action_budget_plan_list"/>
</odoo>

View File

@ -59,22 +59,24 @@
<field name="helper_field" invisible="1"/>
<field name="sub_fg_name" domain="[('id', 'in', helper_field)]"/>
<field name="batch_no"/>
<field name="invoice_no"/>
<field name="batch_release_date"/>
<field name="quantity_dispatched"/>
</group>
</group>
<notebook>
<page string="Data Collection">
<group>
<page string="D0: Defective Details/Symptom">
<group>
<!-- <group>
<field name="invoice_no"/>
<field name="batch_release_date"/>
<field name="quantity_dispatched"/>
</group>
</group> -->
<group>
<field name="brcoa_no"/>
<field name="brcoa_date"/>
<!-- <field name="brcoa_no"/>
<field name="brcoa_date"/> -->
<field name="other_complaints"/>
</group>
</group>
@ -102,13 +104,13 @@
</div>
</templates>
</page>
<page string="Investigation Details">
<page string="D0:Investigation Details">
<group>
<group>
<field name="device_failed_to_meet_specification"/>
<field name="labelling_error"/>
<field name="ifu"/>
<field name="interin_containment_actions"/>
<!-- <field name="interin_containment_actions"/> -->
</group>
<group>
<field name="packaging_details"/>
@ -140,8 +142,59 @@
</div>
</templates>
</page>
<page string="Root Cause">
<page string="D1:Team Formation &amp; Document Reference">
<!-- <field name="team_formation" /> -->
<field name="team_formation_line_ids">
<tree editable="bottom">
<field name="name"/>
<field name="department"/>
<field name="role"/>
</tree>
</field>
<div class="oe_note">
<strong>Note:</strong> <div>Form a cross-functional team with process knowledge and authority. Assign roles and responsibilities.</div>
</div>
</page>
<page string="D2:Problem Description">
<!-- <field name="problem_description"/> -->
<field name="problem_description_line_ids">
<tree editable="bottom">
<field name="clear_statement"/>
<field name="photos"/>
<field name="what"/>
<field name="where"/>
<field name="when"/>
<field name="why"/>
<field name="who"/>
<field name="how"/>
<field name="how_many"/>
</tree>
</field>
<div class="oe_note">
<strong>Note:</strong> <div>Clearly define the problem: What, Where, When, Why, Who, How,How Much.Use measurable facts(not assumptions).</div>
</div>
</page>
<page string="D3:Containment Actions">
<group>
<group>
<field name="interin_containment_actions"/>
</group>
</group>
<div class="oe_note">
<strong>Note:</strong> <div>Temporary actions to isolate the problem and protect the customer.Example: Stop shipment,sort defective parts,replace or rework.</div>
</div>
</page>
<page string="D4:Root Cause Analysis">
<field name="root_cause"/>
<div class="oe_note">
<strong>Note:</strong> <div>Identify the true root cause.Tools:5 Whys,Fishbone Diagram,Fault Tree Analysis,FMEA. </div>
</div>
<templates>
<div class="row">
<div class="col-4">
@ -183,7 +236,35 @@
</templates>
</page>
<page string="CAPA">
<group>
<field name="capa_line_ids">
<tree editable="bottom">
<field name="issue"/>
<field name="corrective_action"/>
<field name="implement_validate_corrective_action"/>
<field name="preventive_action"/>
</tree>
</field>
<table>
<tr>
<td>
<div class="oe_note">
<strong>Note:</strong><div> Define and verify permanent corrective actions to eliminate the root cause.Validate effectiveness before full implementation.</div>
</div>
</td>
<td>
<div class="oe_note">
<strong>Note:</strong> <div>Apply the corrective actions,monitor effectiveness, and confirm the issue is resolved.</div>
</div>
</td>
<td>
<div class="oe_note">
<strong>Note:</strong> <div>Update processes, procedures, training, FMEA, control plans, Process Automation flows.Ensure similar issues don't happen elsewhere. </div>
</div>
</td>
</tr>
</table>
<!-- <group>
<group>
<field name="corrective_action"/>
</group>
@ -193,7 +274,7 @@
<group>
<field name="capa_status"/>
</group>
</group>
</group> -->
<templates>
<div class="row">
<div class="col-6"></div>
@ -217,7 +298,7 @@
</div>
</templates>
</page>
<page string="Response">
<page string="D8: Team Recognition">
<group>
<group>
<field name="response_sent_date"/>
@ -228,7 +309,16 @@
<group>
<field name="qa_comments"/>
</group>
<group>
<field name="capa_status"/>
</group>
<group>
<field name="team_recognition" />
</group>
</group>
<div class="oe_note">
<strong>Note:</strong> <div>Appreciate and recognize the team's effort.Helps motivation and organizational learning. </div>
</div>
<templates>
<div class="row">
<div class="col-6"></div>
@ -266,9 +356,5 @@
</form>
</field>
</record>
<menuitem id="ccrf_menu"
name="Customer Complaint Registration Form(CCRF)"
parent="ce_forms_menu_root" action="action_ccrf_list" groups="sos_inventory.sos_production_user,sos_inventory.sos_rd_user,sos_inventory.sos_healthcare_user,sos_inventory.sos_qa_user,sos_inventory.sos_qc_user,sos_inventory.sos_ce_user,sos_inventory.sos_scg_group_user,sos_inventory.sos_management_user,sos_inventory.sos_sales_user"/>
</odoo>

View File

@ -84,7 +84,7 @@ name="action_report_dc_btn"><i class="fa fa-print"></i> Print</button>
<notebook>
<page string="Outsourcing" invisible="dc_type != 'out_souce_return' and dc_type != 'out_souce_noreturn'">
<field name="line_ids" readonly="top_management_name">
<field name="line_ids" widget="one2many_search" readonly="top_management_name">
<tree editable="bottom">
<field name="component_id"/>
<field name="display_name"/>
@ -97,7 +97,7 @@ name="action_report_dc_btn"><i class="fa fa-print"></i> Print</button>
</field>
</page>
<page string="Materials" invisible="dc_type == 'out_souce_return' or dc_type == 'out_souce_noreturn'">
<field name="line_ids_materials" readonly="top_management_name">
<field name="line_ids_materials" widget="one2many_search" readonly="top_management_name">
<tree editable="bottom">
<field name="component_id"/>
<field name="display_name"/>
@ -143,7 +143,10 @@ name="action_report_dc_btn"><i class="fa fa-print"></i> Print</button>
</field>
</page>
<page string="Logistice Details">
<field name="courier"/>
<field name="lr_no"/>
</page>
</notebook>
<br></br>
@ -217,7 +220,7 @@ name="action_report_dc_btn"><i class="fa fa-print"></i> Print</button>
</record>
<menuitem id="delivery_challan_menu"
name="Delivery Challan (DC)"
parent="scg_forms_menu_root" action="action_dc_form_list" groups="sos_inventory.sos_healthcare_user,sos_inventory.sos_scg_group_manager,sos_inventory.sos_logistics_user,sos_inventory.sos_scg_group_user,sos_inventory.sos_management_user,sos_inventory.sos_finance_user,sos_inventory.sos_sys_admin_user"/>
parent="scg_forms_menu_root" action="action_dc_form_list" groups="sos_inventory.sos_healthcare_user,sos_inventory.sos_scg_group_manager,sos_inventory.sos_logistics_user,sos_inventory.sos_scg_group_user,sos_inventory.sos_management_user,sos_inventory.sos_finance_user,sos_inventory.sos_sys_admin_user,sos_inventory.sos_ce_user,sos_inventory.sos_sales_user"/>
</odoo>

View File

@ -363,7 +363,7 @@
</record>
<menuitem id="sos_deliverables_boq_menu"
name="Deliverables List / BOQ"
parent="sales_root_menu" action="action_sos_deliverables_boq_form_list" groups="sos_inventory.sos_scg_group_user,sos_inventory.sos_ce_user,sos_inventory.sos_finance_user,sos_inventory.sos_scg_group_manager,sos_inventory.sos_rd_user,sos_inventory.sos_production_user,sos_inventory.sos_qc_user,sos_inventory.sos_qa_user" />
parent="sales_root_menu" action="action_sos_deliverables_boq_form_list" groups="sos_inventory.sos_scg_group_user,sos_inventory.sos_ce_user,sos_inventory.sos_finance_user,sos_inventory.sos_scg_group_manager,sos_inventory.sos_rd_user,sos_inventory.sos_production_user,sos_inventory.sos_qc_user,sos_inventory.sos_qa_user,sos_inventory.sos_logistics_user" />
</odoo>

View File

@ -8,7 +8,8 @@
<sheet>
<group>
<field name="fg_name"/>
<field name="communication_type"/>
<field name="communication_type" invisible="fg_name != 'BHMS 12V'"/>
<field name="slave_type" invisible="fg_name != 'BHMS 1.2V'"/>
<!-- <field name="customer_name"/>
<field name="customer_location"/> -->
</group>
@ -91,6 +92,7 @@
<tree string="Deliverables">
<field name="fg_name"/>
<field name="communication_type"/>
<field name="slave_type"/>
<field name="fg_ids"/>
<field name="sfg_ids"/>
<field name="material_ids"/>

View File

@ -11,6 +11,7 @@
<field name="model">sos_departments</field>
<field name="arch" type="xml">
<tree>
<field name="short_form"/>
<field name="name"/>
</tree>
</field>
@ -20,9 +21,18 @@
<field name="model">sos_departments</field>
<field name="arch" type="xml">
<form string="Model Form">
<sheet>
<group><field name="name"/></group>
</sheet>
<sheet>
<group><field name="name"/></group>
<group><field name="short_form"/></group>
<group><field name="process_incharge"/></group>
<field name="users_line_ids">
<tree editable="bottom">
<field name="users"/>
</tree>
</field>
</sheet>
</form>
</field>
</record>

View File

@ -216,13 +216,15 @@
<field name="payment_status"/>
<field name="billing_address"/>
<field name="shipping_address"/>
</group>
<group>
<field name="invoice_no"/>
<field name="invoice_date"/>
<field name="warranty"/>
<field name="gst_no"/>
<field name="shipping_address"/>
</group>
</group>

View File

@ -12,6 +12,8 @@
decoration-warning="indent_status == 'hold'"
decoration-danger="indent_status == 'open' or indent_status == 'cancel'"
/>
<field name="prepared_by" string="Prepared By" widget="many2one_avatar_user"/>
<field name="approved_by" string="Approved By" widget="many2one_avatar_user"/>
</tree>
@ -184,7 +186,8 @@
<templates>
<div class="row" style="margin-top:100px">
<div class="col-4"> <table class="table_custom">
<div class="col-4"> <table class="table_custom" style="box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px;background-color: #fff;border: solid 4px #9689c1;">
<tr style="border-bottom: solid 1px #ccc;">
<td style="padding: 8px;" class="column"><b>Prepared By Sign</b>
<button string="Approve" invisible="prepared_image" class="btn-primary custom_btn" type="object" name="action_approve_esign_btn"></button>
@ -200,9 +203,26 @@
<td><field name="prepared_by" readonly="1"/></td>
</tr>
</table> </div>
<div class="col-4"> </div>
<div class="col-4"> <table class="table_custom" style="box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px;background-color: #fff;border: solid 4px #9689c1;">
<tr style="border-bottom: solid 1px #ccc;">
<td style="padding: 8px;" class="column"><b>Accounts Approval Sign</b>
<button string="Approve" invisible="accounts_approved_by_image" class="btn-primary custom_btn" type="object" name="action_acc_approver_esign_btn"></button>
</td>
<td><field name="accounts_approved_by_image" widget="image"/></td>
</tr>
<tr invisible="accounts_approved_by_image == False">
<td style="padding: 8px;" class="column"><b>Approved On</b></td>
<td><field name="accounts_approved_on" readonly="1"/></td>
</tr>
<tr invisible="accounts_approved_by_image == False">
<td style="padding: 8px;" class="column"><b>Approved By</b></td>
<td><field name="accounts_approved_name" readonly="1"/></td>
</tr>
</table> </div>
<div class="col-4">
<table class="table_custom">
<table class="table_custom" style="box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px;background-color: #fff;border: solid 4px #9689c1;">
<tr style="border-bottom: solid 1px #ccc;">
<td style="padding: 8px;" class="column"><b>Top Management Approval Sign</b>
<button string="Approve" invisible="approval_image" class="btn-primary custom_btn" type="object" name="action_top_approver_esign_btn"></button>
@ -234,4 +254,5 @@
parent="indent_menu_root"
action = "action_fg_plan_list"/>
</odoo>

View File

@ -10,8 +10,11 @@
<field name="fg_type" string="Type" icon="fa-list-ul" enable_counters="1"/>
</searchpanel>
<field name="display_name" string="Display Name"/>
<field name="name" string="Name"/>
</search>
</field>
</record>

View File

@ -248,7 +248,7 @@
</field>
</record>
<!-- Menuitem -->
<menuitem id="menu_sos_fir" sequence="5" name="Final Inspection Report (FIR)" parent="mop_forms_menu_root" action="action_sos_fir" groups="sos_inventory.sos_healthcare_user,sos_inventory.sos_scg_group_user,sos_inventory.sos_qc_user,sos_inventory.sos_management_user,sos_inventory.sos_ce_user"/>
<menuitem id="menu_sos_fir" sequence="5" name="Final Inspection Report (FIR)" parent="mop_forms_menu_root" action="action_sos_fir" groups="sos_inventory.sos_healthcare_user,sos_inventory.sos_scg_group_user,sos_inventory.sos_qc_user,sos_inventory.sos_management_user,sos_inventory.sos_ce_user,sos_inventory.sos_sales_user"/>
</data>
</odoo>

View File

@ -52,7 +52,6 @@
</record>
<menuitem id="sos_master_list_customer_menu" name="Customer Property Master"
parent="scg_forms_menu_root" action="action_sos_master_list_customer_form_list" />
<!-- groups="sos_inventory.sos_sales_user,sos_inventory.sos_scg_group_manager,sos_inventory.sos_scg_group_user,sos_inventory.sos_ce_user,sos_inventory.sos_rd_user,sos_inventory.sos_production_user,sos_inventory.sos_qc_user,sos_inventory.sos_management_user,sos_inventory.sos_logistics_user,sos_inventory.sos_finance_user" -->
parent="scg_forms_menu_root" action="action_sos_master_list_customer_form_list" groups="sos_inventory.sos_scg_group_manager,sos_inventory.sos_scg_group_user,sos_inventory.sos_ce_user,sos_inventory.sos_rd_user,sos_inventory.sos_production_user,sos_inventory.sos_qc_user,sos_inventory.sos_management_user"/>
</odoo>

View File

@ -19,6 +19,8 @@
<field name="ref_no" optional="hide"/>
<field name="stores_approval_name" string="Stores Approved By" widget="many2one_avatar_user"/>
<field name="scg_manager_approval_name" string="SCG Manager Approved By" widget="many2one_avatar_user"/>
<field name="receiver_approval_name" string="Received By" widget="many2one_avatar_user"/>
@ -105,7 +107,7 @@
<span>Not Issued</span>
</div>
<field name="line_ids_material">
<field name="line_ids_material" widget="one2many_search">
<tree editable="bottom" decoration-success="quantity &lt;= issued_quantity and quantity != 0" decoration-warning="quantity &gt; issued_quantity and issued_quantity != 0">
<field name="s_no"/>

View File

@ -30,6 +30,7 @@
<field name="mon_date"/>
<field name="min_no" string="Issue Note Ref No" />
<field name="purpose"/>
<field name="customer_name" groups="sos_inventory.sos_ce_user"/>
<field name="approx_value" string="Order Value" optional="hide"/>
<field name="prepared_by_name" string="Prepared By" widget="many2one_avatar_user"/>
<field name="dept_in_charge_name" string="Department In-Charge" widget="many2one_avatar_user"/>
@ -59,8 +60,8 @@
</header>
<sheet>
<widget name="web_ribbon" text="Open" bg_color="bg-danger" invisible="status == 'close'"/>
<widget name="web_ribbon" text="Closed" bg_color="bg-success" invisible="status == 'open'"/>
<widget name="web_ribbon" text="Open" bg_color="bg-danger" invisible="status == 'close'"/>
<widget name="web_ribbon" text="Closed" bg_color="bg-success" invisible="status == 'open'"/>
<h2 style="text-align: center;text-transform: uppercase;text-shadow: 1px 1p 1px #140718;color: #65407c;padding:5px;">Material / SFG / FG <br></br>Order Note</h2><hr></hr><br></br>
@ -88,6 +89,7 @@
<field name="auto_load_fg_items" invisible="auto_load == 'sfg' or auto_load == False" />
<field name="set_qty" invisible="auto_load == 'fg' or auto_load == False" />
<field name="fg_set_qty" invisible="auto_load != 'fg'" />
<field name="deliverables_boq_id" groups="sos_inventory.sos_management_user,sos_inventory.sos_scg_group_user" />
</group>
<!-- Second Column -->
@ -110,7 +112,7 @@
<notebook>
<page string="Materials" invisible="material_option == False">
<div style="float:right;margin-bottom: 10px;"> <button string="Bulk Upload Materials" type="object" name="action_bulk_upload" class="oe_highlight"/></div>
<field name="line_ids_material" readonly="dept_in_charge_name">
<field name="line_ids_material" widget="one2many_search" readonly="dept_in_charge_name">
<tree editable="bottom">
<field name="s_no"/>
<field name="com_type" decoration-info="com_type == 'new'"/>

View File

@ -72,7 +72,7 @@
<notebook>
<page string="Materials" invisible="material_option == False">
<field name="line_ids_material">
<field name="line_ids_material" readonly="stores_approved_by">
<tree editable="bottom">
<field name="component_id" string="Material Name"/>
<field name="approx_price"/>
@ -104,10 +104,11 @@
<field name="approx_value" widget="monetary" options="{'currency_field': 'currency_id'}" class="oe_inline"/>
</div>
</div>
<br></br>
<br></br> <br></br>
</page>
<page string="Semi-Finished Goods" invisible="sfg_option == False">
<field name="line_ids_sfg">
<field name="line_ids_sfg" readonly="stores_approved_by">
<tree editable="bottom">
<field name="component_id"/>
<field name="qp_no"/>
@ -128,7 +129,7 @@
</page>
<page string="Finished Goods" invisible="fg_option == False">
<field name="line_ids_fg">
<field name="line_ids_fg" readonly="stores_approved_by">
<tree editable="bottom">
<field name="component_id"/>
<field name="batch_No"/>

View File

@ -17,6 +17,7 @@
<tree>
<header>
<button name="%(action_ncmr_wizard)d" string="Report" type="action" class="oe_highlight" display="always"/>
<button name="%(action_pareto_chart)d" string="Pareto" type="action" class="oe_highlight" display="always"/>
</header>
<field name="ncmr_no"/>
<field name="ncmr_date"/>
@ -59,17 +60,20 @@
</table>
<br></br>
<!-- Topmost Group -->
<group>
<!-- First Column -->
<group>
<field name="rca_required" invisible="1"/>
<field name="allowed_user_ids" invisible="1"/>
<field name="rca_allowed_user_ids" invisible="1"/>
<field name="status" invisible="1"/>
<field name="department"/>
<field name="ncmr_no"/>
<field name="ncmr_type" invisible="1"/>
<field name="ncmr_date"/>
<field name="outsourcing_return_ref_no" invisible="rework_action == 'inhouse'"/>
<field name="outsourcing_return_ref_no"/>
</group>
<!-- Second Column -->
<group>
@ -77,7 +81,7 @@
<field name="fg_name"/>
<field name="fg_category" invisible="1"/>
<field name="customer_name" invisible="customer_name == False"/>
<!-- <field name="customer_name" invisible="customer_name == False"/> -->
<field name="batch_no"/>
<field name="rejected_qty" invisible="fg_option != True"/>
@ -109,7 +113,7 @@
border-radius: 5px;margin-bottom: 20px;"/>
</group>
<notebook>
<page string="Defective Details" invisible="fg_option != True">
<page string="D0: Defective Details/Symptom" invisible="fg_option != True">
<field name="line_ids">
<tree editable="bottom">
@ -126,7 +130,7 @@
<label for="fg_unique_defect_count"/><field name="fg_unique_defect_count" nolabel="1" readonly="True"/>
</group>
</page>
<page string="Defective Details" invisible="sfg_option != True">
<page string="D0: Defective Details/Symptom" invisible="sfg_option != True">
<field name="line_ids">
<tree editable="bottom">
@ -144,7 +148,7 @@
<label for="unique_defect_count"/><field name="unique_defect_count" nolabel="1" readonly="True"/>
</group>
</page>
<page string="Defective Details" invisible="material_option != True">
<page string="D0: Defective Details/Symptom" invisible="material_option != True">
<field name="line_ids">
<tree editable="bottom">
@ -161,7 +165,7 @@
<label for="material_unique_defect_count"/><field name="material_unique_defect_count" nolabel="1" readonly="True"/>
</group>
</page>
<page string="Document Reference">
<page string="D1: Team Formation &amp; Document Reference">
<table class="table table-bordered">
<thead>
<tr>
@ -173,8 +177,8 @@
</tr>
</thead>
<tbody>
<tr><td class="column">In-Coming Material (RM/PM)</td><td><field name="incoming_doc_ref"/></td><td><field name="incoming_responsibility"/></td></tr>
<tr><td class="column">Return In-Coming Material (RM/PM)</td><td><field name="return_incoming_doc_ref"/></td><td><field name="return_incoming_resposibility"/></td></tr>
<tr><td class="column">In-Coming Material (RM/PM)</td><td><field name="incoming_doc_ref"/></td><td><group><field name="responsible_department" invisible="not incoming_doc_ref"/><field name="responsible_name" invisible="not incoming_doc_ref"/></group></td></tr>
<tr><td class="column">Return In-Coming Material (RM/PM)</td><td><field name="return_incoming_doc_ref"/></td><td><group><field name="responsible_department" invisible="not return_incoming_doc_ref"/><field name="responsible_name" invisible="not return_incoming_doc_ref"/></group></td></tr>
<!--
<tr><td class="column">Dispensed Material (RM/PM)</td><td><field name="dispensed_doc_ref"/></td><td><field name="dispensed_responsibility"/></td></tr>
@ -183,61 +187,130 @@
<tr><td class="column">Approved Stored Material (RM/PM)</td><td><field name="approved_doc_ref"/></td><td><field name="approved_responsibility"/></td></tr>
<tr><td class="column">Customer Complaint</td><td><field name="customer_complaint_doc_ref"/></td><td><field name="customer_complaint_responsibility"/></td></tr>
-->
<tr><td class="column">Approved Finished Products</td><td><field name="fir_incoming_doc_ref"/></td><td><field name="approved_fg_responsibility"/></td></tr>
<tr><td class="column">Returned Finished Products</td><td><field name="return_fg_incoming_doc_ref"/></td><td><field name="returned_fg_responsibility"/></td></tr>
<tr><td class="column">Finished Products(Production Assy)</td><td><field name="fg_incoming_doc_ref"/></td><td><field name="finished_fg_assy_responsibility"/></td></tr>
<tr><td class="column">Approved Finished Products</td><td><field name="fir_incoming_doc_ref"/></td><td><group><field name="responsible_department" invisible="not fir_incoming_doc_ref"/><field name="responsible_name" invisible="not fir_incoming_doc_ref"/></group></td></tr>
<tr><td class="column">Returned Finished Products</td><td><field name="return_fg_incoming_doc_ref"/></td><td><group><field name="responsible_department" invisible="not return_fg_incoming_doc_ref"/><field name="responsible_name" invisible="not return_fg_incoming_doc_ref"/></group></td></tr>
<tr><td class="column">Finished Products(Production Assy)</td><td><field name="fg_incoming_doc_ref"/></td><td><group><field name="responsible_department" invisible="not fg_incoming_doc_ref"/><field name="responsible_name" invisible="not fg_incoming_doc_ref"/></group></td></tr>
<tr><td class="column">RCA Committee</td><td></td><td><group><field name="rca_responsible_department" widget="many2many_tags"/><field name="rca_responsible_name" widget="many2many_tags"/></group></td></tr>
<tr>
<td colspan="3">
<div class="oe_note">
<strong>Note:</strong> <div>Form a cross-functional team with process knowledge and authority. Assign roles and responsibilities.</div>
</div>
</td>
</tr>
</tbody>
</table>
</page>
<page string="Description of NC">
<field name="description_of_nc"/>
<page string="D2:Problem Description">
<!-- <field name="description_of_nc"/> -->
<field name="problem_description_line_ids">
<tree editable="bottom">
<field name="clear_statement"/>
<field name="photos"/>
<field name="what"/>
<field name="where"/>
<field name="when"/>
<field name="why"/>
<field name="who"/>
<field name="how"/>
<field name="how_many"/>
</tree>
</field>
<div class="oe_note">
<strong>Note:</strong> <div>Clearly define the problem: What, Where, When, Why, Who, How,How Much.Use measurable facts(not assumptions).</div>
</div>
</page>
<page string="Root Cause of NC" invisible="rca_required == 'no'">
<field name="root_cause_of_nc"/>
</page>
<page string="Containment Action" invisible="rca_required == 'no'">
<page string="D3: Containment Action" >
<field name="containment_action_of_nc"/>
<div class="oe_note">
<strong>Note:</strong> <div>Temporary actions to isolate the problem and protect the customer.Example: Stop shipment, sort defective parts,replace or rework.</div>
</div>
</page>
<page string="D4: Root Cause Analysis" invisible="rca_required == 'no'">
<field name="root_cause_of_nc"/>
<div class="oe_note">
<strong>Note:</strong> <div>Identify the true root cause.Tools:5 Whys, Fishbone Diagram (Ishikawa),Fault Tree Analysis,FMEA. </div>
</div>
</page>
<page string="CAPA" invisible="rca_required == 'no'">
<field name="capa_line_ids">
<tree editable="bottom">
<field name="issue"/>
<field name="corrective_action"/>
<field name="implement_validate_corrective_action"/>
<field name="preventive_action"/>
</tree>
</field>
<table>
<tr>
<td>
<div class="oe_note">
<strong>Note:</strong><div> Define and verify permanent corrective actions to eliminate the root cause.Validate effectiveness before full implementation.</div>
</div>
</td>
<td>
<div class="oe_note">
<strong>Note:</strong> <div>Apply the corrective actions,monitor effectiveness, and confirm the issue is resolved.</div>
</div>
</td>
<td>
<div class="oe_note">
<strong>Note:</strong> <div>Update processes, procedures, training, FMEA, control plans, Process Automation flows.Ensure similar issues don't happen elsewhere. </div>
</div>
</td>
</tr>
</table>
</page>
<page string="Comments on CAPA" invisible="rca_required == 'no'">
<!-- <page string="Comments on CAPA" invisible="rca_required == 'no'">
<field name="comments_on_capa"/>
</page>
<page string="Status" invisible="sfg_option != True">
</page> -->
<page string="D8: Team Recognition" invisible="sfg_option != True">
<field name="defective_status_ids">
<tree editable="bottom">
<field name="defective_id"/>
<field name="aodr_no"/>
<field name="status"/>
</tree>
</field>
<tree editable="bottom">
<field name="defective_id"/>
<field name="aodr_no"/>
<field name="status"/>
</tree>
</field>
<group>
<field name="comments_on_capa" nolabel="0"/>
</group>
<div class="oe_note">
<strong>Note:</strong> <div>Appreciate and recognize the team's effort.Helps motivation and organizational learning. </div>
</div>
</page>
<page string="Status" invisible="material_option != True">
<page string="D8: Team Recognition" invisible="material_option != True">
<field name="defective_status_ids">
<tree editable="bottom">
<field name="material_defective_id"/>
<field name="aodr_no"/>
<field name="status"/>
</tree>
</field>
<tree editable="bottom">
<field name="material_defective_id"/>
<field name="aodr_no"/>
<field name="status"/>
</tree>
</field>
<group>
<field name="comments_on_capa" nolabel="0"/>
</group>
<div class="oe_note">
<strong>Note:</strong> <div>Appreciate and recognize the team's effort.Helps motivation and organizational learning. </div>
</div>
</page>
<page string="Status" invisible="fg_option != True">
<page string="D8: Team Recognition" invisible="fg_option != True">
<field name="defective_status_ids">
<tree editable="bottom">
<field name="fg_defective_id"/>
<field name="aodr_no"/>
<field name="status"/>
</tree>
</field>
<tree editable="bottom">
<field name="fg_defective_id"/>
<field name="aodr_no"/>
<field name="status"/>
</tree>
</field>
<group>
<field name="comments_on_capa" nolabel="0"/>
</group>
<div class="oe_note">
<strong>Note:</strong> <div>Appreciate and recognize the team's effort.Helps motivation and organizational learning. </div>
</div>
</page>
@ -424,6 +497,8 @@
</record>
<menuitem id="ncmr_menu"
name="Non-Conforming Material Report (NCMR)"
parent="cnp_forms_menu_root" action="action_ncmr_form_list" groups="sos_inventory.sos_healthcare_user,sos_inventory.sos_scg_group_user,sos_inventory.sos_qa_user,sos_inventory.sos_rd_user,sos_inventory.sos_qc_user,sos_inventory.sos_management_user,sos_inventory.sos_production_user"/>
parent="cnp_forms_menu_root" action="action_ncmr_form_list"
/>
</odoo>

View File

@ -0,0 +1,144 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="action_prf_list" model="ir.actions.act_window">
<field name="name">PRF</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">sos_prf</field>
<field name="view_mode">tree,form</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
No Plan Found
</p>
</field>
</record>
<record id="sos_prf_view_tree" model="ir.ui.view">
<field name="name">sos_prf.view.tree</field>
<field name="model">sos_prf</field>
<field name="arch" type="xml">
<tree>
<field name="prf_no"/>
<field name="prf_date"/>
<field name="status" widget="badge" decoration-success="status == 'close'" decoration-danger="status == 'open'"/>
<field name="write_uid" string="Last Edited By" optional="hide"/>
<field name="write_date" string="Last Edited On" optional="hide"/>
</tree>
</field>
</record>
<record id="sos_prf_form_view" model="ir.ui.view">
<field name="name">Form</field>
<field name="model">sos_prf</field>
<field name="arch" type="xml">
<form string="Purchase Requisition Form">
<sheet>
<widget name="web_ribbon" text="Open" bg_color="bg-danger" invisible="status == 'close'"/>
<widget name="web_ribbon" text="Closed" bg_color="bg-success" invisible="status != 'close'"/>
<group>
<!-- Left Column -->
<group>
<field name="prf_no"/>
<field name="status"/>
</group>
<!-- Right Column -->
<group>
<field name="prf_date"/>
</group>
</group>
<br></br>
<field name="line_ids">
<tree editable="bottom">
<field name="com_type"/>
<field name="component_id" required="com_type == 'exits'" readonly="com_type == 'new'"/>
<field name="new_component_id" required="com_type == 'new'" readonly="com_type == 'exits'"/>
<field name="qty"/>
<field name="req_date"/>
<field name="mon_ref_no"/>
</tree>
</field>
<br></br>
<templates>
<div class="row">
<div class="col-4">
<table class="table_custom" style="box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px;background-color: #fff;border: solid 4px #9689c1;">
<tr style="border-bottom: solid 1px #ccc;">
<td style="padding: 8px;" class="column"><b>Stores Incharge</b>
<button string="Approve" invisible="requested_by_image" class="btn-primary custom_btn" type="object" name="action_stores_esign_btn"></button>
</td>
<td><field name="requested_by_image" widget="image"/></td>
</tr>
<tr invisible="requested_by_image == False">
<td style="padding: 8px;" class="column"><b>Requested On</b></td>
<td><field name="requested_on" readonly="1"/></td>
</tr>
<tr invisible="requested_by_image == False">
<td style="padding: 8px;" class="column"><b>Requested By</b></td>
<td><field name="requested_by_name" readonly="1"/></td>
</tr>
</table>
</div>
<div class="col-4">
<table class="table_custom" style="box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px;background-color: #fff;border: solid 4px #9689c1;">
<tr style="border-bottom: solid 1px #ccc;">
<td style="padding: 8px;" class="column"><b>Dept-Incharge Sign</b>
<button string="Approve" invisible="dept_in_charge_image" class="btn-primary custom_btn" type="object" name="action_dept_esign_btn"></button>
</td>
<td><field name="dept_in_charge_image" widget="image"/></td>
</tr>
<tr invisible="dept_in_charge_image == False">
<td style="padding: 8px;" class="column"><b>Approved On</b></td>
<td><field name="dept_in_charge_approved_on" readonly="1"/></td>
</tr>
<tr invisible="dept_in_charge_image == False">
<td style="padding: 8px;" class="column"><b>Approved By</b></td>
<td><field name="dept_in_charge_name" readonly="1"/></td>
</tr>
</table>
</div>
<div class="col-4">
<table class="table_custom" style="box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px;background-color: #fff;border: solid 4px #9689c1;">
<tr>
<td style="padding: 8px;" class="column"><b>Top Management</b>
<button string="Approve" invisible="top_management_approval_image" class="btn-primary custom_btn" type="object" name="action_top_esign_btn"></button>
</td>
<td><field name="top_management_approval_image" widget="image"/></td>
</tr>
<tr invisible="top_management_approval_image == False">
<td style="padding: 8px;" class="column"><b>Approved On</b></td>
<td><field name="top_management_approved_on" readonly="1"/></td>
</tr>
<tr invisible="top_management_approval_image == False">
<td style="padding: 8px;" class="column"><b>Approved By</b></td>
<td><field name="top_management_name" readonly="1"/></td>
</tr>
</table></div>
</div>
</templates>
</sheet>
</form>
</field>
</record>
<menuitem id="prf_menu"
name="Purchase Requisition Form(PRF)"
parent="scg_forms_menu_root"
action = "action_prf_list" groups="sos_inventory.sos_scg_group_manager,sos_inventory.sos_scg_group_user" />
</odoo>

View File

@ -18,6 +18,7 @@
<field name="lead_time"/>
<field name="customer_po_no"/>
<field name="customer_po_date"/>
<field name="customer_location"/>
</group>
<group>
<!-- <field name="email_to" widget="many2many_tags"/> -->
@ -29,6 +30,7 @@
<page string="Commercial Details">
<group>
<group>
<field name="purpose_of_delivery"/>
<field name="payment_status"/>
<field name="payment_terms"/>
@ -38,6 +40,8 @@
</group>
<group>
<field name="customer_name"/>
<field name="customer_location"/>
<field name="billing_address"/>
<field name="shipping_address"/>

View File

@ -37,6 +37,10 @@
<field name="ref_no"/>
<field name="customer_name"/>
<field name="contact_person"/>
<field name="dock_audit_ref_no"/>
<field name="invoice_no" invisible="dock_audit_ref_no == False"/>
<field name="invoice_date" invisible="dock_audit_ref_no == False"/>
<field name="warranty" invisible="dock_audit_ref_no == False"/>
<field name="location"/>
<field name="mobile_no"/>
<field name="email"/>
@ -74,9 +78,6 @@
</form>
</field>
</record>
<menuitem id="sos_service_call_log_report_menu"
name="Service Call Log Report" parent="ce_forms_menu_root"
action="action_sos_service_call_log_report_form_list" groups="sos_inventory.sos_ce_user,sos_inventory.sos_management_user"/>
</odoo>

View File

@ -46,15 +46,16 @@
<group>
<field name="orr_no"/>
<field name="iqi_no"/>
<field name="rework_type"/>
<field name="goods_type"/>
<field name="service_provider_name" invisible="goods_type != 'SFG'"/>
<field name="supplier_name" invisible="goods_type != 'Materials'"/>
<field name="service_provider_name" invisible="goods_type != 'SFG' or rework_type !='outsourcing'"/>
<field name="supplier_name" invisible="goods_type != 'Materials' or rework_type !='outsourcing'"/>
<field name="returned_qty"/>
</group>
<group>
<field name="iqi_date"/>
<field name="sfg_name" invisible="goods_type != 'SFG'"/>
<field name="material_name" invisible="goods_type != 'Materials'"/>
<field name="sfg_name" invisible="goods_type != 'SFG' or rework_type !='outsourcing'"/>
<field name="material_name" invisible="goods_type != 'Materials' or rework_type !='outsourcing'"/>
<field name="remarks"/>
</group>
</group>

View File

@ -6,7 +6,7 @@
<field name="res_model">sos_wo</field>
<field name="view_mode">tree,form,kanban</field>
</record>
<record id="action_amend_server_action" model="ir.actions.server">
<record id="action_amend_wo_server_action" model="ir.actions.server">
<field name="name">📝 Amend</field>
<field name="model_id" ref="model_sos_wo"/>
<field name="binding_model_id" ref="model_sos_wo"/>
@ -15,6 +15,7 @@
action = model.action_amend()
</field>
</record>
<record id="sos_wo_view_panel" model="ir.ui.view">
<field name="name">sos_wo.search</field>
<field name="model">sos_wo</field>