diff --git a/sos_installation_commision/__manifest__.py b/sos_installation_commision/__manifest__.py
index b423c71..fb123d5 100755
--- a/sos_installation_commision/__manifest__.py
+++ b/sos_installation_commision/__manifest__.py
@@ -23,6 +23,7 @@
# always loaded
'data': [
'security/ir.model.access.csv',
+ 'security/record_rules.xml',
'views/menu.xml',
'views/sos_travel_plan_view.xml',
'views/sos_travel_reimbursement_bills_view.xml',
diff --git a/sos_installation_commision/models/__pycache__/sos_travel_plan.cpython-310.pyc b/sos_installation_commision/models/__pycache__/sos_travel_plan.cpython-310.pyc
index 6cde4b9..7db9c07 100644
Binary files a/sos_installation_commision/models/__pycache__/sos_travel_plan.cpython-310.pyc and b/sos_installation_commision/models/__pycache__/sos_travel_plan.cpython-310.pyc differ
diff --git a/sos_installation_commision/models/sos_travel_plan.py b/sos_installation_commision/models/sos_travel_plan.py
index 59b44e9..312c99e 100755
--- a/sos_installation_commision/models/sos_travel_plan.py
+++ b/sos_installation_commision/models/sos_travel_plan.py
@@ -44,6 +44,7 @@ class sos_travel_plan(models.Model):
]
)
prepared_by_name = fields.Many2one('res.users', string='Prepared by')
+ reporting_to = fields.Many2one('res.users',related="prepared_by_name.reporting_to", string='Prepared by')
prepared_by_image = fields.Image(related="prepared_by_name.signature_image",string='Prepared by Sign',readonly=True)
prepared_on = fields.Datetime(string="Approved On")
dept_in_charge_name = fields.Many2one('res.users', string='Department In-Charge')
diff --git a/sos_installation_commision/security/record_rules.xml b/sos_installation_commision/security/record_rules.xml
new file mode 100644
index 0000000..1e44a4d
--- /dev/null
+++ b/sos_installation_commision/security/record_rules.xml
@@ -0,0 +1,34 @@
+
+
+
+
+ Travel Plan: Own Records - Read Access
+
+ [(1, '=', 1)]
+
+
+
+
+
+
+
+ Travel Plan: All Records - Read Access
+
+[
+'|',
+('create_uid', '=', user.id),
+('reporting_to', '=', user.id)
+]
+
+
+
+
+
+
+
+
diff --git a/sos_installation_commision/views/menu.xml b/sos_installation_commision/views/menu.xml
index 87f4134..98e1a4d 100755
--- a/sos_installation_commision/views/menu.xml
+++ b/sos_installation_commision/views/menu.xml
@@ -1,14 +1,10 @@
-
-
-
-
-
-
-
-
+
+
+
+
\ No newline at end of file
diff --git a/sos_installation_commision/views/sos_travel_plan_view.xml b/sos_installation_commision/views/sos_travel_plan_view.xml
index 8ddfd48..512fa6f 100755
--- a/sos_installation_commision/views/sos_travel_plan_view.xml
+++ b/sos_installation_commision/views/sos_travel_plan_view.xml
@@ -230,6 +230,6 @@ name="action_report_travel_plan"> Print
+ action="action_travel_plan_list" groups="sos_inventory.sos_ce_user,sos_inventory.sos_ce_head,sos_inventory.sos_finance_user,sos_inventory.sos_management_user,sos_inventory.sos_hr_user,sos_inventory.sos_sales_user,sos_inventory.sos_qc_user,sos_inventory.sos_production_user,sos_inventory.sos_qc_user,sos_inventory.sos_qa_user,sos_inventory.sos_rd_user"/>
diff --git a/sos_inventory/__manifest__.py b/sos_inventory/__manifest__.py
index f813946..c54744d 100755
--- a/sos_inventory/__manifest__.py
+++ b/sos_inventory/__manifest__.py
@@ -80,6 +80,7 @@
'views/sos_serviceprovider_report.xml',
'views/sos_master_customer_property.xml',
'views/sos_service_call_log_report.xml',
+ 'views/sos_transfer_challan_return_from_customer_view.xml',
'wizard/sfg_bom_bulk_upload_view.xml',
'wizard/mon_bulk_upload_view.xml',
'wizard/missing_component_wizard.xml',
diff --git a/sos_inventory/models/__init__.py b/sos_inventory/models/__init__.py
index 104356e..fa11e2f 100755
--- a/sos_inventory/models/__init__.py
+++ b/sos_inventory/models/__init__.py
@@ -56,4 +56,5 @@ from . import sos_mat_outsourcing_vendor_register
from . import sos_inventory_customers
from . import sos_master_customer_property
from . import sos_service_call_log_report
-from . import sos_inhouse_validation_reports_files
\ No newline at end of file
+from . import sos_inhouse_validation_reports_files
+from . import sos_transfer_challan_return_from_customer
\ No newline at end of file
diff --git a/sos_inventory/models/__pycache__/__init__.cpython-310.pyc b/sos_inventory/models/__pycache__/__init__.cpython-310.pyc
index e6a7e9c..95c81f2 100644
Binary files a/sos_inventory/models/__pycache__/__init__.cpython-310.pyc and b/sos_inventory/models/__pycache__/__init__.cpython-310.pyc differ
diff --git a/sos_inventory/models/__pycache__/sos_common_scripts.cpython-310.pyc b/sos_inventory/models/__pycache__/sos_common_scripts.cpython-310.pyc
index 16e163d..24caafa 100644
Binary files a/sos_inventory/models/__pycache__/sos_common_scripts.cpython-310.pyc and b/sos_inventory/models/__pycache__/sos_common_scripts.cpython-310.pyc differ
diff --git a/sos_inventory/models/__pycache__/sos_dock_audit.cpython-310.pyc b/sos_inventory/models/__pycache__/sos_dock_audit.cpython-310.pyc
index 8e3b11a..0b3a6b5 100644
Binary files a/sos_inventory/models/__pycache__/sos_dock_audit.cpython-310.pyc and b/sos_inventory/models/__pycache__/sos_dock_audit.cpython-310.pyc differ
diff --git a/sos_inventory/models/__pycache__/sos_fg_bom.cpython-310.pyc b/sos_inventory/models/__pycache__/sos_fg_bom.cpython-310.pyc
index 35db6fd..18b37ef 100644
Binary files a/sos_inventory/models/__pycache__/sos_fg_bom.cpython-310.pyc and b/sos_inventory/models/__pycache__/sos_fg_bom.cpython-310.pyc differ
diff --git a/sos_inventory/models/__pycache__/sos_material.cpython-310.pyc b/sos_inventory/models/__pycache__/sos_material.cpython-310.pyc
index 7a8bb91..3db2d01 100644
Binary files a/sos_inventory/models/__pycache__/sos_material.cpython-310.pyc and b/sos_inventory/models/__pycache__/sos_material.cpython-310.pyc differ
diff --git a/sos_inventory/models/__pycache__/sos_min.cpython-310.pyc b/sos_inventory/models/__pycache__/sos_min.cpython-310.pyc
index cd3925c..2e4d3dc 100644
Binary files a/sos_inventory/models/__pycache__/sos_min.cpython-310.pyc and b/sos_inventory/models/__pycache__/sos_min.cpython-310.pyc differ
diff --git a/sos_inventory/models/__pycache__/sos_mrn.cpython-310.pyc b/sos_inventory/models/__pycache__/sos_mrn.cpython-310.pyc
index c85dc3c..e6baf1d 100644
Binary files a/sos_inventory/models/__pycache__/sos_mrn.cpython-310.pyc and b/sos_inventory/models/__pycache__/sos_mrn.cpython-310.pyc differ
diff --git a/sos_inventory/models/__pycache__/sos_return_fir.cpython-310.pyc b/sos_inventory/models/__pycache__/sos_return_fir.cpython-310.pyc
index fc06462..8b340a4 100644
Binary files a/sos_inventory/models/__pycache__/sos_return_fir.cpython-310.pyc and b/sos_inventory/models/__pycache__/sos_return_fir.cpython-310.pyc differ
diff --git a/sos_inventory/models/__pycache__/sos_sfg.cpython-310.pyc b/sos_inventory/models/__pycache__/sos_sfg.cpython-310.pyc
index 023cb1c..0bd559c 100644
Binary files a/sos_inventory/models/__pycache__/sos_sfg.cpython-310.pyc and b/sos_inventory/models/__pycache__/sos_sfg.cpython-310.pyc differ
diff --git a/sos_inventory/models/__pycache__/sos_sfg_bom.cpython-310.pyc b/sos_inventory/models/__pycache__/sos_sfg_bom.cpython-310.pyc
index 9172a53..975df11 100644
Binary files a/sos_inventory/models/__pycache__/sos_sfg_bom.cpython-310.pyc and b/sos_inventory/models/__pycache__/sos_sfg_bom.cpython-310.pyc differ
diff --git a/sos_inventory/models/__pycache__/sos_sfg_quote_generation.cpython-310.pyc b/sos_inventory/models/__pycache__/sos_sfg_quote_generation.cpython-310.pyc
index 8fa8447..ab6598e 100644
Binary files a/sos_inventory/models/__pycache__/sos_sfg_quote_generation.cpython-310.pyc and b/sos_inventory/models/__pycache__/sos_sfg_quote_generation.cpython-310.pyc differ
diff --git a/sos_inventory/models/__pycache__/sos_transfer_challan.cpython-310.pyc b/sos_inventory/models/__pycache__/sos_transfer_challan.cpython-310.pyc
index aac0c0d..fbe8824 100644
Binary files a/sos_inventory/models/__pycache__/sos_transfer_challan.cpython-310.pyc and b/sos_inventory/models/__pycache__/sos_transfer_challan.cpython-310.pyc differ
diff --git a/sos_inventory/models/__pycache__/sos_wo.cpython-310.pyc b/sos_inventory/models/__pycache__/sos_wo.cpython-310.pyc
index 2070291..8276c38 100644
Binary files a/sos_inventory/models/__pycache__/sos_wo.cpython-310.pyc and b/sos_inventory/models/__pycache__/sos_wo.cpython-310.pyc differ
diff --git a/sos_inventory/models/sos_common_scripts.py b/sos_inventory/models/sos_common_scripts.py
index d287bce..b6b30f6 100755
--- a/sos_inventory/models/sos_common_scripts.py
+++ b/sos_inventory/models/sos_common_scripts.py
@@ -306,7 +306,8 @@ class Sequence_Generator(models.AbstractModel):
record.write({field_name: current_value + value_to_add})
return True
return False
-def extract_value(self, input_string):
+ @staticmethod
+ def extract_value(input_string):
total = 0
items = input_string.split(",") # handles both single and comma-separated values
diff --git a/sos_inventory/models/sos_dock_audit.py b/sos_inventory/models/sos_dock_audit.py
index eadfd5d..1e55618 100755
--- a/sos_inventory/models/sos_dock_audit.py
+++ b/sos_inventory/models/sos_dock_audit.py
@@ -417,36 +417,36 @@ class sos_dock_audit_Line_Material(models.Model):
@api.model
def create(self, vals):
#print(f" First line in create ")
- component_id = vals.get('component_id')
+ #component_id = vals.get('component_id')
ref_id = vals.get('ref_id')
- sequence_util = self.env['sos_common_scripts']
+ #sequence_util = self.env['sos_common_scripts']
- if component_id and ref_id:
- # Search for an existing line with the same component_id and ref_id
- existing = self.search([
- ('component_id', '=', component_id),
- ('ref_id', '=', ref_id)
- ], limit=1)
+ # if component_id and ref_id:
+ # # Search for an existing line with the same component_id and ref_id
+ # existing = self.search([
+ # ('component_id', '=', component_id),
+ # ('ref_id', '=', ref_id)
+ # ], limit=1)
- if existing:
- # Merge logic: add quantities
- existing.singet_set_qty += vals.get('singet_set_qty', 1)
- #existing.total_set += vals.get('total_set', 1)
+ # if existing:
+ # # Merge logic: add quantities
+ # existing.singet_set_qty += vals.get('singet_set_qty', 1)
+ # #existing.total_set += vals.get('total_set', 1)
- previousval = 0
- if vals.get('ref_id'):
- parent = self.env['sos_dock_audit'].browse(vals['ref_id'])
- existing.phase = str(existing.phase) + "," + str(parent.phase_version + 1)
- previousval = sequence_util.extract_value(existing.total_phase_count)
+ # previousval = 0
+ # if vals.get('ref_id'):
+ # parent = self.env['sos_dock_audit'].browse(vals['ref_id'])
+ # existing.phase = str(existing.phase) + "," + str(parent.phase_version + 1)
+ # previousval = sequence_util.extract_value(existing.total_phase_count)
- if existing.total_phase_count:
- #existing.total_phase_count = str(existing.total_phase_count)+","+str(existing.singet_set_qty * existing.total_set)
- existing.total_phase_count = str(existing.total_phase_count)+","+"P"+str(parent.phase_version + 1 or '') +"-"+str((existing.singet_set_qty * existing.total_set)- int(previousval))
- else:
- existing.total_phase_count = str(existing.singet_set_qty * existing.total_set)
+ # if existing.total_phase_count:
+ # #existing.total_phase_count = str(existing.total_phase_count)+","+str(existing.singet_set_qty * existing.total_set)
+ # existing.total_phase_count = str(existing.total_phase_count)+","+"P"+str(parent.phase_version + 1 or '') +"-"+str((existing.singet_set_qty * existing.total_set)- int(previousval))
+ # else:
+ # existing.total_phase_count = str(existing.singet_set_qty * existing.total_set)
- # quantity and total_price are computed, so they'll auto update
- return existing
+ # # quantity and total_price are computed, so they'll auto update
+ # return existing
#print(f"Initial : singet {vals.get('singet_set_qty', 1)}")
#print(f"Initial : total {vals.get('total_set', 1)}")
@@ -508,37 +508,37 @@ class sos_dock_audit_Line_sfg(models.Model):
@api.model
def create(self, vals):
#print(f" First line in create ")
- component_id = vals.get('component_id')
+ #component_id = vals.get('component_id')
ref_id = vals.get('ref_id')
- sequence_util = self.env['sos_common_scripts']
+ #sequence_util = self.env['sos_common_scripts']
- if component_id and ref_id:
- # Search for an existing line with the same component_id and ref_id
- existing = self.search([
- ('component_id', '=', component_id),
- ('ref_id', '=', ref_id)
- ], limit=1)
+ # if component_id and ref_id:
+ # # Search for an existing line with the same component_id and ref_id
+ # existing = self.search([
+ # ('component_id', '=', component_id),
+ # ('ref_id', '=', ref_id)
+ # ], limit=1)
- if existing:
- # Merge logic: add quantities
- existing.singet_set_qty += vals.get('singet_set_qty', 1)
- #existing.total_set += vals.get('total_set', 1)
+ # if existing:
+ # # Merge logic: add quantities
+ # existing.singet_set_qty += vals.get('singet_set_qty', 1)
+ # #existing.total_set += vals.get('total_set', 1)
- previousval = 0
- if vals.get('ref_id'):
+ # previousval = 0
+ # if vals.get('ref_id'):
- parent = self.env['sos_dock_audit'].browse(vals['ref_id'])
- existing.phase = str(existing.phase) + "," + str(parent.phase_version + 1)
- previousval = sequence_util.extract_value(existing.total_phase_count)
+ # parent = self.env['sos_dock_audit'].browse(vals['ref_id'])
+ # existing.phase = str(existing.phase) + "," + str(parent.phase_version + 1)
+ # previousval = sequence_util.extract_value(existing.total_phase_count)
- if existing.total_phase_count:
- #existing.total_phase_count = str(existing.total_phase_count)+","+str(existing.singet_set_qty * existing.total_set)
- existing.total_phase_count = str(existing.total_phase_count)+","+"P"+str(parent.phase_version + 1 or '') +"-"+str((existing.singet_set_qty * existing.total_set)- int(previousval))
- else:
- existing.total_phase_count = str(existing.singet_set_qty * existing.total_set)
+ # if existing.total_phase_count:
+ # #existing.total_phase_count = str(existing.total_phase_count)+","+str(existing.singet_set_qty * existing.total_set)
+ # existing.total_phase_count = str(existing.total_phase_count)+","+"P"+str(parent.phase_version + 1 or '') +"-"+str((existing.singet_set_qty * existing.total_set)- int(previousval))
+ # else:
+ # existing.total_phase_count = str(existing.singet_set_qty * existing.total_set)
- # quantity and total_price are computed, so they'll auto update
- return existing
+ # # quantity and total_price are computed, so they'll auto update
+ # return existing
singleset = vals.get('singet_set_qty', 1)
total_set = vals.get('total_set', 1)
@@ -603,34 +603,34 @@ class sos_dock_audit_Line_fg(models.Model):
@api.model
def create(self, vals):
- component_id = vals.get('component_id')
+ #component_id = vals.get('component_id')
ref_id = vals.get('ref_id')
- sequence_util = self.env['sos_common_scripts']
+ #sequence_util = self.env['sos_common_scripts']
- if component_id and ref_id:
- # Search for an existing line with the same component_id and ref_id
- existing = self.search([
- ('component_id', '=', component_id),
- ('ref_id', '=', ref_id)
- ], limit=1)
+ # if component_id and ref_id:
+ # # Search for an existing line with the same component_id and ref_id
+ # existing = self.search([
+ # ('component_id', '=', component_id),
+ # ('ref_id', '=', ref_id)
+ # ], limit=1)
- if existing:
- # Merge logic: add quantities
- existing.singet_set_qty += vals.get('singet_set_qty', 1)
- #existing.total_set += vals.get('total_set', 1)
- previousval = 0
- if vals.get('ref_id'):
- parent = self.env['sos_dock_audit'].browse(vals['ref_id'])
- existing.phase = str(existing.phase) + "," + str(parent.phase_version + 1)
- previousval = sequence_util.extract_value(existing.total_phase_count)
- if existing.total_phase_count:
- #print(f" TT :: {existing.total_phase_count} ")
- existing.total_phase_count = str(existing.total_phase_count)+","+"P"+str(parent.phase_version + 1 or '') +"-"+str((existing.singet_set_qty * existing.total_set)- int(previousval))
- else:
- existing.total_phase_count = "P"+str(existing.phase)+"-"+str(existing.singet_set_qty * existing.total_set)
+ ##if existing:
+ # # Merge logic: add quantities
+ # existing.singet_set_qty += vals.get('singet_set_qty', 1)
+ # #existing.total_set += vals.get('total_set', 1)
+ ## previousval = 0
+ ## if vals.get('ref_id'):
+ ## parent = self.env['sos_dock_audit'].browse(vals['ref_id'])
+ # existing.phase = str(existing.phase) + "," + str(parent.phase_version + 1)
+ ## previousval = sequence_util.extract_value(existing.total_phase_count)
+ ## if existing.total_phase_count:
+ # #print(f" TT :: {existing.total_phase_count} ")
+ ## existing.total_phase_count = str(existing.total_phase_count)+","+"P"+str(parent.phase_version + 1 or '') +"-"+str((existing.singet_set_qty * existing.total_set)- int(previousval))
+ # else:
+ # existing.total_phase_count = "P"+str(existing.phase)+"-"+str(existing.singet_set_qty * existing.total_set)
- # quantity and total_price are computed, so they'll auto update
- return existing
+ # # quantity and total_price are computed, so they'll auto update
+ #return existing
singleset = vals.get('singet_set_qty', 1)
total_set = vals.get('total_set', 1)
@@ -670,7 +670,8 @@ class sos_dock_audit_MiscellaneousCost(models.Model):
@api.model
def create(self, vals):
- if vals.get('ref_id'):
+ ref_id = vals.get('ref_id')
+ if ref_id:
parent = self.env['sos_dock_audit'].browse(vals['ref_id'])
vals['phase'] = (parent.phase_version+1) or 0
return super().create(vals)
@@ -718,39 +719,39 @@ class sos_dock_audit_Material_installationkit(models.Model):
@api.model
def create(self, vals):
- component_id = vals.get('component_id')
+ #component_id = vals.get('component_id')
ref_id = vals.get('ref_id')
- sequence_util = self.env['sos_common_scripts']
+ #sequence_util = self.env['sos_common_scripts']
- if component_id and ref_id:
- # Search for an existing line with the same component_id and ref_id
- existing = self.search([
- ('component_id', '=', component_id),
- ('ref_id', '=', ref_id)
- ], limit=1)
+ # if component_id and ref_id:
+ # # Search for an existing line with the same component_id and ref_id
+ # existing = self.search([
+ # ('component_id', '=', component_id),
+ # ('ref_id', '=', ref_id)
+ # ], limit=1)
- if existing:
- # Merge logic: add quantities
- existing.singet_set_qty += vals.get('singet_set_qty', 1)
- #existing.total_set += vals.get('total_set', 1)
+ # if existing:
+ # # Merge logic: add quantities
+ # existing.singet_set_qty += vals.get('singet_set_qty', 1)
+ # #existing.total_set += vals.get('total_set', 1)
- previousval = 0
- if vals.get('ref_id'):
- parent = self.env['sos_dock_audit'].browse(vals['ref_id'])
- existing.phase = str(existing.phase) + "," + str(parent.phase_version + 1)
- #print(f" Total_phase_count :{existing.total_phase_count}")
- #print(f" Extract Value :{self.extract_value(existing.total_phase_count)}")
- previousval = sequence_util.extract_value(existing.total_phase_count)
- #print(f" : previousval : {previousval}")
- if existing.total_phase_count:
- #existing.total_phase_count = str(existing.total_phase_count)+","+str(existing.singet_set_qty * existing.total_set)
- #print(f" Total Count : {existing.singet_set_qty * existing.total_set}")
- existing.total_phase_count = str(existing.total_phase_count)+","+"P"+str(parent.phase_version + 1 or '') +"-"+str((existing.singet_set_qty * existing.total_set)- int(previousval))
- else:
- existing.total_phase_count = str(existing.singet_set_qty * existing.total_set)
+ # previousval = 0
+ # if vals.get('ref_id'):
+ # parent = self.env['sos_dock_audit'].browse(vals['ref_id'])
+ # existing.phase = str(existing.phase) + "," + str(parent.phase_version + 1)
+ # #print(f" Total_phase_count :{existing.total_phase_count}")
+ # #print(f" Extract Value :{self.extract_value(existing.total_phase_count)}")
+ # previousval = sequence_util.extract_value(existing.total_phase_count)
+ # #print(f" : previousval : {previousval}")
+ # if existing.total_phase_count:
+ # #existing.total_phase_count = str(existing.total_phase_count)+","+str(existing.singet_set_qty * existing.total_set)
+ # #print(f" Total Count : {existing.singet_set_qty * existing.total_set}")
+ # existing.total_phase_count = str(existing.total_phase_count)+","+"P"+str(parent.phase_version + 1 or '') +"-"+str((existing.singet_set_qty * existing.total_set)- int(previousval))
+ # else:
+ # existing.total_phase_count = str(existing.singet_set_qty * existing.total_set)
- # quantity and total_price are computed, so they'll auto update
- return existing
+ # # quantity and total_price are computed, so they'll auto update
+ # return existing
singleset = vals.get('singet_set_qty', 1)
total_set = vals.get('total_set', 1)
diff --git a/sos_inventory/models/sos_fg_bom.py b/sos_inventory/models/sos_fg_bom.py
index abb7397..ea4c557 100755
--- a/sos_inventory/models/sos_fg_bom.py
+++ b/sos_inventory/models/sos_fg_bom.py
@@ -73,6 +73,7 @@ class SOS_Fg_Bom(models.Model):
def _compute_overall_total(self):
for record in self:
record.overall_total = record.sfg_cost + record.component_cost + record.miscellaneous_cost
+ record._sync_unit_price_with_fg()
@api.depends('sfg_bom_line_ids.unit_price')
def _compute_component_cost(self):
diff --git a/sos_inventory/models/sos_material.py b/sos_inventory/models/sos_material.py
index 1d220b4..9ea2156 100755
--- a/sos_inventory/models/sos_material.py
+++ b/sos_inventory/models/sos_material.py
@@ -66,6 +66,18 @@ class SOS_Material(models.Model):
record.name or record.part_no
)
)
+ def write(self, vals):
+ unit_price_changed = 'unit_price' in vals
+ res = super().write(vals)
+
+ if unit_price_changed:
+ bom_lines = self.env['sos_sfg_bom_line'].search([
+ ('primary_component_id', 'in', self.ids)
+ ])
+ bom_ids = bom_lines.mapped('bom_id')
+ for bom in bom_ids:
+ bom._sync_unit_price_with_sfg()
+ return res
@api.depends('unit_price','inhand_stock_qty')
def _compute_stock_val(self):
for val in self:
diff --git a/sos_inventory/models/sos_min.py b/sos_inventory/models/sos_min.py
index 01347e2..d2efc37 100755
--- a/sos_inventory/models/sos_min.py
+++ b/sos_inventory/models/sos_min.py
@@ -13,7 +13,7 @@ class sos__min(models.Model):
min_no = fields.Char(string="MIN/SIN/FIN No", readonly= True, required= True, default=lambda self: self._generate_id())
min_date = fields.Date(string="MIN/SIN/FIN Date",required=True)
- mon_no = fields.Many2one('sos_mon',string="MON/SON/FON No")
+ mon_no = fields.Many2one('sos_mon',string="MON/SON/FON No",readonly= True)
mon_date = fields.Date(string="MON/SON/FON Date")
ref_no = fields.Char(string="Purpose")
indent_ref_no = fields.Many2one('sos_fg_plan',string="Indent Reference No")
@@ -31,7 +31,7 @@ class sos__min(models.Model):
line_ids_material = fields.One2many('sos_min_line_material', 'min_id', string="Materials",copy=True)
line_ids_sfg = fields.One2many('sos_min_line_sfg', 'min_id', string="Semi-Finished Goods",copy=True)
line_ids_fg = fields.One2many('sos_min_line_fg', 'min_id', string="Finished Goods",copy=True)
- mon_created_by = fields.Many2one('res.users', string='Requested By')
+ mon_created_by = fields.Many2one('res.users', string='Requested By',readonly= True)
reporting_to = fields.Many2one('res.users', string='Reporting To')
issued_to = fields.Many2one('res.users', string='Issued To')
status = fields.Selection([ ('open', 'Open'),('close', 'Closed')], default='open' , string="Status")
diff --git a/sos_inventory/models/sos_mrn.py b/sos_inventory/models/sos_mrn.py
index f8820e7..e4835bf 100755
--- a/sos_inventory/models/sos_mrn.py
+++ b/sos_inventory/models/sos_mrn.py
@@ -115,10 +115,18 @@ class sos__mrn(models.Model):
def action_report_esign_btn1(self):
sequence_util = self.env['sos_common_scripts']
splitted=self.order_type.split(",")
+ if self.return_type == "customer":
+ sequence_util = self.env['sos_common_scripts']
+ tc_no = sequence_util.generate_sequence('sos_transfer_challan_return_from_customer','TC', 'tc_no')
+ customer_return_record = self.env['sos_transfer_challan_return_from_customer'].create({
+ 'tc_no': tc_no,
+ 'mrn_ref_no':self.id
+ })
for eachone in splitted:
if eachone == "Material":
for item in self.line_ids_material:
component = self.env['sos_material'].browse(item.component_id.id)
+
if item.qa_decision == "to_stores":
current_qty = getattr(component, 'inhand_stock_qty', 0)
new_qty = current_qty + item.quantity
@@ -137,6 +145,16 @@ class sos__mrn(models.Model):
'mrn_id':self.id
})
+ if customer_return_record and self.return_type == "customer" and item.qa_decision != "to_stores":
+ create_vals = {
+ 'tc_id': customer_return_record.id,
+ 'item_name': component.name,
+ 'item_type': 'Material',
+ 'return_incoming_doc_ref':iqi_record.id if iqi_record else None
+ }
+ new_line = self.env['sos_transfer_challan_return_from_customer_lines'].create(create_vals)
+
+
# Email part
body_html = f"""
Below Return-IQI is waiting for your Approval
@@ -147,6 +165,7 @@ class sos__mrn(models.Model):
elif eachone == "SFG":
for item in self.line_ids_sfg:
component = self.env['sos_sfg'].browse(item.component_id.id)
+
if item.qa_decision == "to_stores":
current_qty = getattr(component, 'inhand_stock_qty', 0)
new_qty = current_qty + item.quantity
@@ -174,9 +193,19 @@ class sos__mrn(models.Model):
send_email = self.env['sos_common_scripts']
send_email.send_group_email(self.env,'sos_return_iqi',iqi_record.id,"deenalaura.m@sosaley.in","Return-IQI Inspection Request",body_html,'sos_inventory.sos_qc_user')
# Email part ends
+ if customer_return_record and self.return_type == "customer" and item.qa_decision != "to_stores":
+ create_vals = {
+ 'tc_id': customer_return_record.id,
+ 'item_name': component.name,
+ 'item_type': 'SFG',
+ 'return_incoming_doc_ref':iqi_record.id if iqi_record else None
+ }
+ new_line = self.env['sos_transfer_challan_return_from_customer_lines'].create(create_vals)
+
else:
for item in self.line_ids_fg:
component = self.env['sos_fg'].browse(item.component_id.id)
+
if item.qa_decision == "to_stores":
current_qty = getattr(component, 'inhand_stock_qty', 0)
new_qty = current_qty + item.quantity
@@ -213,7 +242,15 @@ class sos__mrn(models.Model):
send_email = self.env['sos_common_scripts']
send_email.send_group_email(self.env,'sos_return_fir',return_fir_record.id,"deenalaura.m@sosaley.in","Return-BRR Inspection Request",body_html,'sos_inventory.sos_qc_user')
# Email part ends
-
+ if customer_return_record and self.return_type == "customer" and item.qa_decision != "to_stores":
+ create_vals = {
+ 'tc_id': customer_return_record.id,
+ 'item_name': component.name,
+ 'item_type': 'FG',
+ 'return_fg_incoming_doc_ref':return_fir_record.id if return_fir_record else None
+ }
+ new_line = self.env['sos_transfer_challan_return_from_customer_lines'].create(create_vals)
+
return sequence_util.action_assign_signature(
self,
'stores_approved_by',
diff --git a/sos_inventory/models/sos_return_fir.py b/sos_inventory/models/sos_return_fir.py
index a7ef543..dd9434b 100755
--- a/sos_inventory/models/sos_return_fir.py
+++ b/sos_inventory/models/sos_return_fir.py
@@ -75,6 +75,8 @@ class Return_FIR_BRR(models.Model):
'return_fg_incoming_doc_ref':self.id,
'department':'Quality Control'
})
+ if ncmr_record:
+ self.ncmr_ref = ncmr_record.id
ncmr_body_html = f"""
Below NCMR is waiting for your Inspection
"""
diff --git a/sos_inventory/models/sos_sfg.py b/sos_inventory/models/sos_sfg.py
index 2f293bd..88b25b8 100755
--- a/sos_inventory/models/sos_sfg.py
+++ b/sos_inventory/models/sos_sfg.py
@@ -53,6 +53,17 @@ class SOS_SFG(models.Model):
)
last_batch_no = fields.Char(string="Batch No")
last_serial_no = fields.Integer(string="Last Serial No")
+ def write(self, vals):
+ assembling_changed = 'assembling_charges' in vals
+ res = super().write(vals)
+
+ if assembling_changed:
+ for sfg in self:
+ bom_records = self.env['sos_sfg_bom'].search([('name', '=', sfg.id)])
+ for bom in bom_records:
+ bom._compute_overall_total() # If you want to force recompute manually
+ bom._sync_unit_price_with_sfg()
+ return res
@api.onchange('category')
def onchange_category(self):
if self.category:
@@ -62,7 +73,6 @@ class SOS_SFG(models.Model):
}
prefix = prefix_map.get(self.category, "SO")
- # Search all records starting with this prefix
matching_records = self.env['sos_sfg'].search([
('code_no', 'like', prefix + '%')
])
diff --git a/sos_inventory/models/sos_sfg_bom.py b/sos_inventory/models/sos_sfg_bom.py
index c6f1aef..b46e5fc 100755
--- a/sos_inventory/models/sos_sfg_bom.py
+++ b/sos_inventory/models/sos_sfg_bom.py
@@ -38,8 +38,10 @@ class SOS_Sfg_Bom(models.Model):
'material_code' : 'Material Code',
'primary_component_id': 'Material Name',
'quantity': 'Quantity',
- 'location':'Location',
- 'inhand_stock_qty':'Inhand Qty'
+ 'inhand_stock_qty':'Inhand Qty',
+ 'uom':'UOM',
+ 'unit_price':'Unit Price',
+ 'total_cost':'Total'
}
self._write_sheet(workbook, 'Materials', self.sfg_bom_line_ids, headers)
@@ -62,12 +64,26 @@ class SOS_Sfg_Bom(models.Model):
}
def _write_sheet(self, workbook, sheet_name, line_records, headers):
- """Write data to each sheet based on specified fields and custom headers."""
worksheet = workbook.add_worksheet(sheet_name)
- for col, header in enumerate(headers.values()):
- worksheet.write(0, col, header)
+
+ number_format = workbook.add_format({'num_format': '#,##0.00'})
+ bold_format = workbook.add_format({'bold': True})
+ name_value = getattr(self, 'name', False)
+ worksheet.write(0, 0, 'Name',bold_format) # Label for the first value
+ worksheet.write(0, 1, name_value.display_name if name_value else '') # Handle Many2one field
- for row, record in enumerate(line_records, start=1):
+ assembling_charges = getattr(self, 'assembling_charges', 0.0)
+ worksheet.write(1, 0, 'Assembly Charges',bold_format) # Label for the second value
+ worksheet.write(1, 1, assembling_charges, number_format) # Assume numeric (float)
+
+ overall_total = getattr(self, 'overall_total', 0.0)
+ worksheet.write(2, 0, 'Overall Cost (Including assembling charges)',bold_format) # Label for the third value
+ worksheet.write(2, 1, overall_total, number_format) # Assume numeric (float)
+
+ for col, header in enumerate(headers.values()):
+ worksheet.write(4, col, header,bold_format)
+
+ for row, record in enumerate(line_records, start=5):
for col, field in enumerate(headers.keys()):
value = getattr(record, field, '')
if isinstance(value, models.Model):
@@ -123,7 +139,7 @@ class SOS_Sfg_Bom_Line(models.Model):
_description = 'BOM Lines of Semi-Finished Goods'
bom_id = fields.Many2one('sos_sfg_bom', string="SFG BOM Reference")
- fg_bom_id = fields.Many2one('sos_fg_bom', string="SFG BOM Reference")
+ fg_bom_id = fields.Many2one('sos_fg_bom', string="FG BOM Reference")
primary_component_id = fields.Many2one('sos_material', string='Part No', required=True)
location = fields.Char(related="primary_component_id.location")
inhand_stock_qty = fields.Float(related="primary_component_id.inhand_stock_qty")
diff --git a/sos_inventory/models/sos_sfg_quote_generation.py b/sos_inventory/models/sos_sfg_quote_generation.py
index e940e6b..0335a9b 100755
--- a/sos_inventory/models/sos_sfg_quote_generation.py
+++ b/sos_inventory/models/sos_sfg_quote_generation.py
@@ -162,6 +162,13 @@ class SOS_SFG_Quote_Generation_Line(models.Model):
# final_supplier3_qty = fields.Integer(string='Qty')
best_supplier = fields.Many2one('sos_service_providers', string="Best Deal", compute='_compute_best_supplier', store=True)
available_suppliers = fields.Many2many('sos_service_providers', compute='_compute_available_suppliers')
+ remarks = fields.Char(string="Remarks")
+ @api.onchange('required_qty', 'inprogress_qty', 'status')
+ def _onchange_check_inprogress_qty(self):
+ for rec in self:
+ if (rec.inprogress_qty >= rec.required_qty and rec.required_qty != 0) or rec.status == 'close':
+ rec.inprogress_qty = 0
+
@api.depends('required_qty', 'inprogress_qty')
def _compute_from_ir(self):
for record in self:
diff --git a/sos_inventory/models/sos_transfer_challan.py b/sos_inventory/models/sos_transfer_challan.py
index 0a1f34e..c4bf71c 100755
--- a/sos_inventory/models/sos_transfer_challan.py
+++ b/sos_inventory/models/sos_transfer_challan.py
@@ -180,7 +180,7 @@ class TC_Model_Line(models.Model):
'fir_date':self.production_to_qc_transfer_on,
'batch_size':self.production_to_qc_transfer_qty,
'sampling_size':self.production_to_qc_transfer_qty,
- 'plan_ref_no':plan_ref_no.id,
+ 'plan_ref_no':plan_ref_no.id if plan_ref_no else '',
'batch_No':self.batch_no,
'fir_no': sequence_util.generate_sequence('sos_fir_brr','BRR', 'fir_no'),
@@ -204,7 +204,7 @@ class TC_Model_Line(models.Model):
# Email part ends
self.production_to_qc_transfer_by = self.env.user.id
sequence_util = self.env['sos_common_scripts']
- if self.tc_id.fg_option:
+ if self.tc_id.fg_option and self.tc_id.plan_ref_no:
week_number = sequence_util.calculate_week_number(
self.tc_id.indent_start_date,
self.tc_id.indent_target_date,
diff --git a/sos_inventory/models/sos_transfer_challan_return_from_customer.py b/sos_inventory/models/sos_transfer_challan_return_from_customer.py
new file mode 100755
index 0000000..2b93b59
--- /dev/null
+++ b/sos_inventory/models/sos_transfer_challan_return_from_customer.py
@@ -0,0 +1,81 @@
+from odoo import models, fields, api
+from datetime import date,datetime
+from odoo.exceptions import ValidationError,UserError
+import re
+
+class SOS_TC_Return_Customer(models.Model):
+ _name = 'sos_transfer_challan_return_from_customer'
+ _description = 'Transfer Challan'
+ _rec_name="tc_no"
+ _order = 'tc_no desc'
+
+
+ tc_no = fields.Char(string="TC No",default=lambda self: self._generate_id(),readonly= True, required= True)
+ line_ids = fields.One2many('sos_transfer_challan_return_from_customer_lines', 'tc_id', string="Finished Goods",copy=True, ondelete='cascade')
+ mrn_ref_no = fields.Many2one('sos_mrn',string="Return Note Reference",readonly= True)
+ def _generate_id(self):
+ sequence_util = self.env['sos_common_scripts']
+ return sequence_util.generate_sequence('sos_transfer_challan_return_from_customer','TC', 'tc_no')
+
+class SOS_TC_Return_CustomerLines(models.Model):
+ _name = 'sos_transfer_challan_return_from_customer_lines'
+ _description = 'tc Lines'
+
+ tc_id = fields.Many2one('sos_transfer_challan_return_from_customer')
+ item_name = fields.Char(string="Item Name")
+ item_type = fields.Selection([ ('FG', 'FG'),('SFG', 'SFG'),('Material', 'Material')],
+ default='FG' , string="Type")
+
+ return_incoming_doc_ref = fields.Many2one('sos_return_iqi',string="IQI Ref No",readonly=True)
+ return_fg_incoming_doc_ref = fields.Many2one('sos_return_fir',string="Return FIR ref",readonly=True)
+ combined_incoming_doc_ref = fields.Reference(
+ selection=[
+ ('sos_return_iqi', 'Return IQI Ref No'),
+ ('sos_return_fir', 'FIR Ref')
+ ],
+ string="Incoming Document Reference",
+ compute="_compute_combined_incoming_doc_ref",
+ store=False
+ )
+ stores_handovered_by = fields.Many2one('res.users', string='Stores Handovered By',related="tc_id.mrn_ref_no.stores_approved_by")
+ stores_handovered_on = fields.Datetime(string="Stores Handovered On",related="tc_id.mrn_ref_no.stores_approved_on")
+ qc_tested_by = fields.Many2one('res.users', string='QC Tested By',compute="_compute_qc_tested_by")
+ qc_tested_on = fields.Datetime(string="QC Tested On")
+ received_qty = fields.Integer(string="Received Quantity")
+ approved_qty = fields.Integer(string="Approved Quantity")
+ rejected_qty = fields.Integer(string="Rejected Quantity")
+ ncmr_ref = fields.Many2one('sos_ncmr',string="NCMR Ref(If any)")
+ stores_received_by = fields.Many2one('res.users', string='Stores Received By')
+ stores_received_on = fields.Datetime(string="Stores Received On")
+
+ @api.depends('item_type','return_incoming_doc_ref','return_fg_incoming_doc_ref')
+ def _compute_qc_tested_by(self):
+ for record in self:
+ if record.item_type == "FG":
+ record.qc_tested_by = record.return_fg_incoming_doc_ref.qc_by_name
+ record.qc_tested_on = record.return_fg_incoming_doc_ref.qc_tested_on
+ record.qc_tested_on = record.return_fg_incoming_doc_ref.qc_tested_on
+ record.received_qty = record.return_fg_incoming_doc_ref.batch_size
+ record.approved_qty = record.return_fg_incoming_doc_ref.approved_qty
+ record.rejected_qty = record.return_fg_incoming_doc_ref.rejected_qty
+ record.ncmr_ref = record.return_fg_incoming_doc_ref.ncmr_ref
+ record.stores_received_by = record.return_fg_incoming_doc_ref.stores_received_by
+ record.stores_received_on = record.return_fg_incoming_doc_ref.stores_received_on
+ else:
+ record.qc_tested_by = record.return_incoming_doc_ref.qc_by_name
+ record.qc_tested_on = record.return_incoming_doc_ref.qc_tested_on
+ record.received_qty = record.return_incoming_doc_ref.received_qty
+ record.approved_qty = record.return_incoming_doc_ref.approved_qty
+ record.rejected_qty = record.return_incoming_doc_ref.rejected_qty
+ record.ncmr_ref = record.return_incoming_doc_ref.ncmr_ref
+ record.stores_received_by = record.return_incoming_doc_ref.stores_received_by
+ record.stores_received_on = record.return_incoming_doc_ref.stores_received_on
+ @api.depends('return_incoming_doc_ref','return_fg_incoming_doc_ref')
+ def _compute_combined_incoming_doc_ref(self):
+ for record in self:
+ if record.return_incoming_doc_ref:
+ record.combined_incoming_doc_ref = f'sos_return_iqi,{record.return_incoming_doc_ref.id}'
+ elif record.return_fg_incoming_doc_ref:
+ record.combined_incoming_doc_ref = f'sos_return_fir,{record.return_fg_incoming_doc_ref.id}'
+ else:
+ record.combined_incoming_doc_ref = False
\ No newline at end of file
diff --git a/sos_inventory/models/sos_wo.py b/sos_inventory/models/sos_wo.py
index 4bccfd4..45167a7 100755
--- a/sos_inventory/models/sos_wo.py
+++ b/sos_inventory/models/sos_wo.py
@@ -61,7 +61,7 @@ class sos__wo(models.Model):
total_qty = fields.Integer(string='Total Line Items Count', compute='_compute_total_qty', store=True)
received_qty = fields.Integer(string='Received Line Items Count', store=True)
payment_method=fields.Selection([('neft', 'NEFT'),('credit', 'Credit'),('credit_card', 'Credit Card')],string="Payment Method")
- wo_status = fields.Selection([ ('open', 'Open'),('close', 'Closed')], default='open' , string="Status")
+ wo_status = fields.Selection([ ('amend', 'Amended'),('open', 'Open'),('close', 'Closed')], default='open' , string="Status")
progress = fields.Float(string="Completion Percentage", compute='_compute_progress', store=True)
dc_no = fields.Many2one('sos_dc',string="DC Reference No", readonly= True)
stores_approved_by = fields.Many2one('res.users', string='Manager Approval By')
@@ -82,7 +82,20 @@ class sos__wo(models.Model):
# record = super(sos__wo, self).create(vals)
# record.action_esign_btn()
# return record
-
+ def action_amend(self):
+ active_ids = self.env.context.get('active_ids', [])
+ records = self.browse(active_ids)
+ for record in records:
+ record.wo_status = 'amend'
+ return {
+ 'type': 'ir.actions.client',
+ 'tag': 'display_notification',
+ 'params': {
+ 'title': 'Amended',
+ 'message': 'The selected record(s) have been amended.',
+ 'sticky': False,
+ }
+ }
def action_esign_btn(self):
sequence_util = self.env['sos_common_scripts']
sequence_util.action_assign_signature(
@@ -143,7 +156,13 @@ class sos__wo(models.Model):
record.progress = (progress_sum / line_count) * 100
record.wo_status = 'close' if record.progress == 100 else 'open'
-
+ @api.onchange('progress')
+ def _onchange_progress_status(self):
+ for record in self:
+ if record.progress == 100:
+ record.wo_status = 'close'
+ else:
+ record.wo_status = 'open'
# @api.depends('total_qty', 'received_qty')
# def _compute_progress(self):
# print(self.received_qty,self.total_qty)
diff --git a/sos_inventory/security/ir.model.access.csv b/sos_inventory/security/ir.model.access.csv
index 769e66a..d6e7c17 100755
--- a/sos_inventory/security/ir.model.access.csv
+++ b/sos_inventory/security/ir.model.access.csv
@@ -193,4 +193,7 @@ access_sos_transfer_challan_summary_lines,sos_transfer_challan_summary_lines acc
access_material_backup_export,material_backup_export access,model_material_backup_export,base.group_user,1,1,1,1
access_ncmr_report_wizard,ncmr_report_wizard access,model_ncmr_report_wizard,base.group_user,1,1,1,1
access_sos_parameter_fir,sos_parameter_fir access,model_sos_parameter_fir,base.group_user,1,1,1,1
+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
+
diff --git a/sos_inventory/views/sos_dock_audit_view.xml b/sos_inventory/views/sos_dock_audit_view.xml
index aa17bad..f2d52c0 100755
--- a/sos_inventory/views/sos_dock_audit_view.xml
+++ b/sos_inventory/views/sos_dock_audit_view.xml
@@ -46,7 +46,7 @@
Print -->
+ confirm="Are you sure to make revision for next phase?" invisible="not top_management_name">Make Revision
Dock Audit
diff --git a/sos_inventory/views/sos_sfg_quote_generation.xml b/sos_inventory/views/sos_sfg_quote_generation.xml
index 733cdf2..10cf786 100755
--- a/sos_inventory/views/sos_sfg_quote_generation.xml
+++ b/sos_inventory/views/sos_sfg_quote_generation.xml
@@ -27,7 +27,7 @@
-
+
diff --git a/sos_inventory/views/sos_transfer_challan_return_from_customer_view.xml b/sos_inventory/views/sos_transfer_challan_return_from_customer_view.xml
new file mode 100755
index 0000000..641e637
--- /dev/null
+++ b/sos_inventory/views/sos_transfer_challan_return_from_customer_view.xml
@@ -0,0 +1,115 @@
+
+
+
+ Transfer Challan
+ ir.actions.act_window
+ sos_transfer_challan_return_from_customer
+ tree,form
+
+
+ sos_transfer_challan_return_from_customer.view.tree
+ sos_transfer_challan_return_from_customer
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Form
+ sos_transfer_challan_return_from_customer
+
+
+
+
+
+
+
+
diff --git a/sos_inventory/views/sos_transfer_challan_view.xml b/sos_inventory/views/sos_transfer_challan_view.xml
index 5b933c7..1283a1d 100755
--- a/sos_inventory/views/sos_transfer_challan_view.xml
+++ b/sos_inventory/views/sos_transfer_challan_view.xml
@@ -5,7 +5,7 @@
ir.actions.act_window
sos_transfer_challan
tree,form,kanban
- {'group_by': 'plan_ref_no'}
+
No Data
@@ -245,7 +245,7 @@ If no 'to' is used, values will be stored as entered."/>
diff --git a/sos_inventory/views/sos_wo_view.xml b/sos_inventory/views/sos_wo_view.xml
index 078391c..c26475a 100755
--- a/sos_inventory/views/sos_wo_view.xml
+++ b/sos_inventory/views/sos_wo_view.xml
@@ -6,6 +6,15 @@
sos_wo
tree,form,kanban
+
+ 📝 Amend
+
+
+ code
+
+ action = model.action_amend()
+
+
sos_wo.search
sos_wo
@@ -38,7 +47,8 @@
-
+
+
@@ -58,9 +68,11 @@
-
+
+
-
+
+
Work Order
diff --git a/sos_sales/models/__pycache__/sos_case_diary.cpython-310.pyc b/sos_sales/models/__pycache__/sos_case_diary.cpython-310.pyc
index 0309ae4..67bf31f 100644
Binary files a/sos_sales/models/__pycache__/sos_case_diary.cpython-310.pyc and b/sos_sales/models/__pycache__/sos_case_diary.cpython-310.pyc differ
diff --git a/sos_sales/models/__pycache__/sos_proposal_boq.cpython-310.pyc b/sos_sales/models/__pycache__/sos_proposal_boq.cpython-310.pyc
index 7d1c742..9153736 100644
Binary files a/sos_sales/models/__pycache__/sos_proposal_boq.cpython-310.pyc and b/sos_sales/models/__pycache__/sos_proposal_boq.cpython-310.pyc differ
diff --git a/sos_sales/models/__pycache__/sos_proposal_builder.cpython-310.pyc b/sos_sales/models/__pycache__/sos_proposal_builder.cpython-310.pyc
index bd8501a..0d45c1a 100755
Binary files a/sos_sales/models/__pycache__/sos_proposal_builder.cpython-310.pyc and b/sos_sales/models/__pycache__/sos_proposal_builder.cpython-310.pyc differ
diff --git a/sos_sales/models/__pycache__/sos_proposal_customer_requirement.cpython-310.pyc b/sos_sales/models/__pycache__/sos_proposal_customer_requirement.cpython-310.pyc
index 5d5672d..dec46a8 100644
Binary files a/sos_sales/models/__pycache__/sos_proposal_customer_requirement.cpython-310.pyc and b/sos_sales/models/__pycache__/sos_proposal_customer_requirement.cpython-310.pyc differ
diff --git a/sos_sales/models/__pycache__/sos_sales_achievement_report.cpython-310.pyc b/sos_sales/models/__pycache__/sos_sales_achievement_report.cpython-310.pyc
index 36986a6..e84ce6c 100644
Binary files a/sos_sales/models/__pycache__/sos_sales_achievement_report.cpython-310.pyc and b/sos_sales/models/__pycache__/sos_sales_achievement_report.cpython-310.pyc differ
diff --git a/sos_sales/models/__pycache__/sos_sales_action_plan.cpython-310.pyc b/sos_sales/models/__pycache__/sos_sales_action_plan.cpython-310.pyc
index ff71298..48fe35c 100644
Binary files a/sos_sales/models/__pycache__/sos_sales_action_plan.cpython-310.pyc and b/sos_sales/models/__pycache__/sos_sales_action_plan.cpython-310.pyc differ
diff --git a/sos_sales/models/sos_case_diary.py b/sos_sales/models/sos_case_diary.py
index 6fb1d13..1ae5fae 100755
--- a/sos_sales/models/sos_case_diary.py
+++ b/sos_sales/models/sos_case_diary.py
@@ -67,7 +67,6 @@ class sos_case_diary(models.Model):
],
string="Service Type")
line_ids = fields.One2many('sos_case_diary_line', 'ref_id', string="Action")
- accounts_line_ids = fields.One2many('sos_billing_collection', 'ref_id', string="Billed & Collected")
status = fields.Selection([('open', 'Open'),
('close', 'Closed without Order'),
('close_order', 'Closed with Order') ],default='open',string='Status')
@@ -81,15 +80,7 @@ class sos_case_diary(models.Model):
po_copy = fields.Binary(string="PO Copy")
po_copy_filename=fields.Char(string="PO DocumentFile Name")
order_expected_on = fields.Date(string="Order Expected On")
- line_ids_billed= fields.One2many(
- 'sos_billing_collection', 'ref_id',
- domain=[('action_status', '=', 'Billed')]
- )
- line_ids_collected = fields.One2many(
- 'sos_billing_collection', 'ref_id',
- domain=[('action_status', '=', 'Collected')]
- )
@api.depends('end_customer_name', 'quote_no')
def _compute_display_name(self):
for rec in self:
@@ -704,68 +695,3 @@ class SosCaseTransferHistory(models.Model):
)
return {'type': 'ir.actions.act_window_close'}
-class SosBillingCollection(models.Model):
- _name = 'sos_billing_collection'
- _description = 'Billing & Collection Details'
-
- ref_id = fields.Many2one('sos_case_diary', ondelete="cascade")
- customer_name = fields.Many2one(
- 'sos_customers',
- string="Customer Name",
- related='ref_id.customer_name',
- store=True,
- readonly=True
-)
- sales_person = fields.Many2one(
- 'res.users',
- string='Sales Executive', related="ref_id.sales_person",store=True)
- action_status = fields.Selection([
- ('Billed', 'Billed'),
- ('Collected', 'Collected')
- ], string='Action')
- date_of_action = fields.Date(string="Date")
- currency_id = fields.Many2one(
- 'res.currency',
- string='Currency',
- default=lambda self: self.env['res.currency'].search([('name', '=', 'INR')], limit=1).id or False
- )
- value = fields.Monetary(currency_field='currency_id',string="Value(In Lakhs)")
- def _get_financial_year(self, ref_date=None):
- ref_date = ref_date or date.today()
- start_year = ref_date.year if ref_date.month >= 4 else ref_date.year - 1
- end_year = start_year + 1
- return f"FY {start_year}-{end_year}"
- @api.model
- def create(self, vals):
- # Get date and convert to datetime if it's a string
- date_of_action = vals.get('date_of_action')
- if isinstance(date_of_action, str):
- date_of_action = datetime.strptime(date_of_action, '%Y-%m-%d').date()
-
- fy = self._get_financial_year(date_of_action)
- month_name = date_of_action.strftime('%B').lower()
- action_status = vals.get('action_status', '').lower()
- value = vals.get('value', 0.0)
-
- # Compose the dynamic field
- actual_field = f"{action_status}_target_{month_name}"
-
- # Get sales person from related ref_id
- ref_id = vals.get('ref_id')
- sales_person = None
- if ref_id:
- ref = self.env['sos_case_diary'].browse(ref_id)
- sales_person = ref.sales_person
-
- if sales_person:
- report = self.env['sos_sales_achievement_report'].search([
- ('financial_year', '=', fy),
- ('sales_person', '=', sales_person.id)
- ], limit=1)
- if report and hasattr(report, actual_field):
- current_value = getattr(report, actual_field, 0.0) or 0.0
- report.write({actual_field: current_value + value})
-
- return super(SosBillingCollection, self).create(vals)
-
-
diff --git a/sos_sales/models/sos_proposal_boq.py b/sos_sales/models/sos_proposal_boq.py
index 6ca61f2..daaa84c 100755
--- a/sos_sales/models/sos_proposal_boq.py
+++ b/sos_sales/models/sos_proposal_boq.py
@@ -5,6 +5,7 @@ class Battery_Installation_Requirement(models.Model):
_name = 'sos_proposal_boq'
_description = 'Battery Installation Details'
_rec_name="proposal_id"
+ _order = 'proposal_id asc'
_sql_constraints = [
('unique_proposal_id', 'unique(proposal_id)', 'Proposal ID must be unique!')
]
@@ -56,7 +57,7 @@ class Battery_Installation_Requirement(models.Model):
warranty_cost = fields.Monetary(string="Warranty Cost", compute='_compute_warranty', store=True, currency_field='currency_id')
margin = fields.Monetary(string="Opreational Cost",compute='_compute_margin', currency_field='currency_id')
margin_per_battery = fields.Monetary(string="Opreational Cost per Battery", currency_field='currency_id')
- warranty_percentage = fields.Integer(string="Warranty(%)")
+ warranty_percentage = fields.Float(string="Warranty(%)")
final_cost = fields.Monetary(string="Final Cost", compute='_compute_final_cost', store=True, currency_field='currency_id')
final_cost_per_battery = fields.Monetary(string="Final Cost", compute='_compute_final_cost_per_battery', store=True, currency_field='currency_id')
communication_type = fields.Selection([
@@ -87,6 +88,11 @@ class Battery_Installation_Requirement(models.Model):
line_ids_fg_ups8 = fields.One2many('sos_proposal_boq_fg', 'ref_id', string="FG UPS 8",compute='_compute_line_ids_by_ups',store=False)
line_ids_fg_ups9 = fields.One2many('sos_proposal_boq_fg', 'ref_id', string="FG UPS 9",compute='_compute_line_ids_by_ups',store=False)
line_ids_fg_ups10 = fields.One2many('sos_proposal_boq_fg', 'ref_id', string="FG UPS 10",compute='_compute_line_ids_by_ups',store=False)
+ line_ids_fg_ups11 = fields.One2many('sos_proposal_boq_fg', 'ref_id', string="FG UPS 11",compute='_compute_line_ids_by_ups',store=False)
+ line_ids_fg_ups12 = fields.One2many('sos_proposal_boq_fg', 'ref_id', string="FG UPS 12",compute='_compute_line_ids_by_ups',store=False)
+ line_ids_fg_ups13 = fields.One2many('sos_proposal_boq_fg', 'ref_id', string="FG UPS 13",compute='_compute_line_ids_by_ups',store=False)
+ line_ids_fg_ups14 = fields.One2many('sos_proposal_boq_fg', 'ref_id', string="FG UPS 14",compute='_compute_line_ids_by_ups',store=False)
+ line_ids_fg_ups15 = fields.One2many('sos_proposal_boq_fg', 'ref_id', string="FG UPS 15",compute='_compute_line_ids_by_ups',store=False)
#SFG Fields
line_ids_sfg_ups1 = fields.One2many('sos_proposal_boq_sfg', 'ref_id', string="SFG UPS 1",compute='_compute_line_ids_by_ups',store=False)
@@ -99,6 +105,12 @@ class Battery_Installation_Requirement(models.Model):
line_ids_sfg_ups8 = fields.One2many('sos_proposal_boq_sfg', 'ref_id', string="SFG UPS 8",compute='_compute_line_ids_by_ups',store=False)
line_ids_sfg_ups9 = fields.One2many('sos_proposal_boq_sfg', 'ref_id', string="SFG UPS 9",compute='_compute_line_ids_by_ups',store=False)
line_ids_sfg_ups10 = fields.One2many('sos_proposal_boq_sfg', 'ref_id', string="SFG UPS 10",compute='_compute_line_ids_by_ups',store=False)
+ line_ids_sfg_ups11 = fields.One2many('sos_proposal_boq_sfg', 'ref_id', string="SFG UPS 11",compute='_compute_line_ids_by_ups',store=False)
+ line_ids_sfg_ups12 = fields.One2many('sos_proposal_boq_sfg', 'ref_id', string="SFG UPS 12",compute='_compute_line_ids_by_ups',store=False)
+ line_ids_sfg_ups13 = fields.One2many('sos_proposal_boq_sfg', 'ref_id', string="SFG UPS 13",compute='_compute_line_ids_by_ups',store=False)
+ line_ids_sfg_ups14 = fields.One2many('sos_proposal_boq_sfg', 'ref_id', string="SFG UPS 14",compute='_compute_line_ids_by_ups',store=False)
+ line_ids_sfg_ups15 = fields.One2many('sos_proposal_boq_sfg', 'ref_id', string="SFG UPS 15",compute='_compute_line_ids_by_ups',store=False)
+
#Material Fields
line_ids_material_ups1 = fields.One2many('sos_proposal_boq_material', 'ref_id', string="Material UPS 1",compute='_compute_line_ids_by_ups',store=False)
@@ -111,6 +123,12 @@ class Battery_Installation_Requirement(models.Model):
line_ids_material_ups8 = fields.One2many('sos_proposal_boq_material', 'ref_id', string="Material UPS 8",compute='_compute_line_ids_by_ups',store=False)
line_ids_material_ups9 = fields.One2many('sos_proposal_boq_material', 'ref_id', string="Material UPS 9",compute='_compute_line_ids_by_ups',store=False)
line_ids_material_ups10 = fields.One2many('sos_proposal_boq_material', 'ref_id', string="Material UPS 10",compute='_compute_line_ids_by_ups',store=False)
+ line_ids_material_ups11 = fields.One2many('sos_proposal_boq_material', 'ref_id', string="Material UPS 11",compute='_compute_line_ids_by_ups',store=False)
+ line_ids_material_ups12 = fields.One2many('sos_proposal_boq_material', 'ref_id', string="Material UPS 12",compute='_compute_line_ids_by_ups',store=False)
+ line_ids_material_ups13 = fields.One2many('sos_proposal_boq_material', 'ref_id', string="Material UPS 13",compute='_compute_line_ids_by_ups',store=False)
+ line_ids_material_ups14 = fields.One2many('sos_proposal_boq_material', 'ref_id', string="Material UPS 14",compute='_compute_line_ids_by_ups',store=False)
+ line_ids_material_ups15 = fields.One2many('sos_proposal_boq_material', 'ref_id', string="Material UPS 15",compute='_compute_line_ids_by_ups',store=False)
+
#Installation Kit Fields
line_ids_installation_kit_ups1 = fields.One2many('sos_proposal_line_material_installation', 'ref_id', string="Installation Kit UPS 1",compute='_compute_line_ids_by_ups',store=False)
@@ -123,6 +141,11 @@ class Battery_Installation_Requirement(models.Model):
line_ids_installation_kit_ups8 = fields.One2many('sos_proposal_line_material_installation', 'ref_id', string="Installation Kit UPS 8",compute='_compute_line_ids_by_ups',store=False)
line_ids_installation_kit_ups9 = fields.One2many('sos_proposal_line_material_installation', 'ref_id', string="Installation Kit UPS 9",compute='_compute_line_ids_by_ups',store=False)
line_ids_installation_kit_ups10 = fields.One2many('sos_proposal_line_material_installation', 'ref_id', string="Installation Kit UPS 10",compute='_compute_line_ids_by_ups',store=False)
+ line_ids_installation_kit_ups11 = fields.One2many('sos_proposal_line_material_installation', 'ref_id', string="Installation Kit UPS 11",compute='_compute_line_ids_by_ups',store=False)
+ line_ids_installation_kit_ups12 = fields.One2many('sos_proposal_line_material_installation', 'ref_id', string="Installation Kit UPS 12",compute='_compute_line_ids_by_ups',store=False)
+ line_ids_installation_kit_ups13 = fields.One2many('sos_proposal_line_material_installation', 'ref_id', string="Installation Kit UPS 13",compute='_compute_line_ids_by_ups',store=False)
+ line_ids_installation_kit_ups14 = fields.One2many('sos_proposal_line_material_installation', 'ref_id', string="Installation Kit UPS 14",compute='_compute_line_ids_by_ups',store=False)
+ line_ids_installation_kit_ups15 = fields.One2many('sos_proposal_line_material_installation', 'ref_id', string="Installation Kit UPS 15",compute='_compute_line_ids_by_ups',store=False)
#Miscellaneous Kit Fields
line_ids_miscellaneous_ups1 = fields.One2many('sos_proposal_miscellaneous_items', 'ref_id', string="Miscellaneous UPS 1",compute='_compute_line_ids_by_ups',store=False)
@@ -135,6 +158,11 @@ class Battery_Installation_Requirement(models.Model):
line_ids_miscellaneous_ups8 = fields.One2many('sos_proposal_miscellaneous_items', 'ref_id', string="Miscellaneous UPS 8",compute='_compute_line_ids_by_ups',store=False)
line_ids_miscellaneous_ups9 = fields.One2many('sos_proposal_miscellaneous_items', 'ref_id', string="Miscellaneous UPS 9",compute='_compute_line_ids_by_ups',store=False)
line_ids_miscellaneous_ups10 = fields.One2many('sos_proposal_miscellaneous_items', 'ref_id', string="Miscellaneous UPS 10",compute='_compute_line_ids_by_ups',store=False)
+ line_ids_miscellaneous_ups11 = fields.One2many('sos_proposal_miscellaneous_items', 'ref_id', string="Miscellaneous UPS 11",compute='_compute_line_ids_by_ups',store=False)
+ line_ids_miscellaneous_ups12 = fields.One2many('sos_proposal_miscellaneous_items', 'ref_id', string="Miscellaneous UPS 12",compute='_compute_line_ids_by_ups',store=False)
+ line_ids_miscellaneous_ups13 = fields.One2many('sos_proposal_miscellaneous_items', 'ref_id', string="Miscellaneous UPS 13",compute='_compute_line_ids_by_ups',store=False)
+ line_ids_miscellaneous_ups14 = fields.One2many('sos_proposal_miscellaneous_items', 'ref_id', string="Miscellaneous UPS 14",compute='_compute_line_ids_by_ups',store=False)
+ line_ids_miscellaneous_ups15 = fields.One2many('sos_proposal_miscellaneous_items', 'ref_id', string="Miscellaneous UPS 15",compute='_compute_line_ids_by_ups',store=False)
#ups totals
ups1_total = fields.Float(string="UPS 1 Total Cost",compute='_compute_ups_total',store=True)
@@ -147,6 +175,11 @@ class Battery_Installation_Requirement(models.Model):
ups8_total = fields.Float(string="UPS 8 Total Cost", compute='_compute_ups_total', store=True)
ups9_total = fields.Float(string="UPS 9 Total Cost", compute='_compute_ups_total', store=True)
ups10_total = fields.Float(string="UPS 10 Total Cost", compute='_compute_ups_total', store=True)
+ ups11_total = fields.Float(string="UPS 11 Total Cost", compute='_compute_ups_total', store=True)
+ ups12_total = fields.Float(string="UPS 12 Total Cost", compute='_compute_ups_total', store=True)
+ ups13_total = fields.Float(string="UPS 13 Total Cost", compute='_compute_ups_total', store=True)
+ ups14_total = fields.Float(string="UPS 14 Total Cost", compute='_compute_ups_total', store=True)
+ ups15_total = fields.Float(string="UPS 15 Total Cost", compute='_compute_ups_total', store=True)
extra_lines_cost = fields.Monetary(string="Cost", compute='_compute_extra_lines_cost', store=True, currency_field='currency_id')
packing_and_forwarding = fields.Monetary(string="Packing & Forwarding", currency_field='currency_id',store=True)
@@ -161,6 +194,11 @@ class Battery_Installation_Requirement(models.Model):
line_ids_spare_ups8 = fields.One2many('sos_proposal_line_spare_ups8','ref_id', string="Spare UPS 8")
line_ids_spare_ups9 = fields.One2many('sos_proposal_line_spare_ups9','ref_id', string="Spare UPS 9")
line_ids_spare_ups10 = fields.One2many('sos_proposal_line_spare_ups10','ref_id', string="Spare UPS 10")
+ line_ids_spare_ups11 = fields.One2many('sos_proposal_line_spare_ups11','ref_id', string="Spare UPS 11")
+ line_ids_spare_ups12 = fields.One2many('sos_proposal_line_spare_ups12','ref_id', string="Spare UPS 12")
+ line_ids_spare_ups13 = fields.One2many('sos_proposal_line_spare_ups13','ref_id', string="Spare UPS 13")
+ line_ids_spare_ups14 = fields.One2many('sos_proposal_line_spare_ups14','ref_id', string="Spare UPS 14")
+ line_ids_spare_ups15 = fields.One2many('sos_proposal_line_spare_ups15','ref_id', string="Spare UPS 15")
@api.model
def create(self, vals):
res = super().create(vals)
@@ -254,11 +292,16 @@ class Battery_Installation_Requirement(models.Model):
'line_ids_spare_ups7.total_price', 'line_ids_spare_ups7.production_cost',
'line_ids_spare_ups8.total_price', 'line_ids_spare_ups8.production_cost',
'line_ids_spare_ups9.total_price', 'line_ids_spare_ups9.production_cost',
- 'line_ids_spare_ups10.total_price', 'line_ids_spare_ups10.production_cost'
+ 'line_ids_spare_ups10.total_price', 'line_ids_spare_ups10.production_cost',
+ 'line_ids_spare_ups11.total_price', 'line_ids_spare_ups11.production_cost',
+ 'line_ids_spare_ups12.total_price', 'line_ids_spare_ups12.production_cost',
+ 'line_ids_spare_ups13.total_price', 'line_ids_spare_ups13.production_cost',
+ 'line_ids_spare_ups14.total_price', 'line_ids_spare_ups14.production_cost',
+ 'line_ids_spare_ups15.total_price', 'line_ids_spare_ups15.production_cost'
)
def _compute_ups_total(self):
for rec in self:
- for i in range(1, 11):
+ for i in range(1, 16):
total = 0
for field_name in [
'line_ids_fg',
@@ -293,6 +336,11 @@ class Battery_Installation_Requirement(models.Model):
rec.line_ids_fg_ups8 = rec.line_ids_fg.filtered(lambda l: l.ups_index == 8)
rec.line_ids_fg_ups9 = rec.line_ids_fg.filtered(lambda l: l.ups_index == 9)
rec.line_ids_fg_ups10 = rec.line_ids_fg.filtered(lambda l: l.ups_index == 10)
+ rec.line_ids_fg_ups11 = rec.line_ids_fg.filtered(lambda l: l.ups_index == 11)
+ rec.line_ids_fg_ups12 = rec.line_ids_fg.filtered(lambda l: l.ups_index == 12)
+ rec.line_ids_fg_ups13 = rec.line_ids_fg.filtered(lambda l: l.ups_index == 13)
+ rec.line_ids_fg_ups14 = rec.line_ids_fg.filtered(lambda l: l.ups_index == 14)
+ rec.line_ids_fg_ups15 = rec.line_ids_fg.filtered(lambda l: l.ups_index == 15)
rec.line_ids_sfg_ups1 = rec.line_ids_sfg.filtered(lambda l: l.ups_index == 1)
rec.line_ids_sfg_ups2 = rec.line_ids_sfg.filtered(lambda l: l.ups_index == 2)
@@ -304,6 +352,11 @@ class Battery_Installation_Requirement(models.Model):
rec.line_ids_sfg_ups8 = rec.line_ids_sfg.filtered(lambda l: l.ups_index == 8)
rec.line_ids_sfg_ups9 = rec.line_ids_sfg.filtered(lambda l: l.ups_index == 9)
rec.line_ids_sfg_ups10 = rec.line_ids_sfg.filtered(lambda l: l.ups_index == 10)
+ rec.line_ids_sfg_ups11 = rec.line_ids_sfg.filtered(lambda l: l.ups_index == 11)
+ rec.line_ids_sfg_ups12 = rec.line_ids_sfg.filtered(lambda l: l.ups_index == 12)
+ rec.line_ids_sfg_ups13 = rec.line_ids_sfg.filtered(lambda l: l.ups_index == 13)
+ rec.line_ids_sfg_ups14 = rec.line_ids_sfg.filtered(lambda l: l.ups_index == 14)
+ rec.line_ids_sfg_ups15 = rec.line_ids_sfg.filtered(lambda l: l.ups_index == 15)
rec.line_ids_material_ups1 = rec.line_ids_material.filtered(lambda l: l.ups_index == 1)
rec.line_ids_material_ups2 = rec.line_ids_material.filtered(lambda l: l.ups_index == 2)
@@ -315,6 +368,12 @@ class Battery_Installation_Requirement(models.Model):
rec.line_ids_material_ups8 = rec.line_ids_material.filtered(lambda l: l.ups_index == 8)
rec.line_ids_material_ups9 = rec.line_ids_material.filtered(lambda l: l.ups_index == 9)
rec.line_ids_material_ups10 = rec.line_ids_material.filtered(lambda l: l.ups_index == 10)
+ rec.line_ids_material_ups11 = rec.line_ids_material.filtered(lambda l: l.ups_index == 11)
+ rec.line_ids_material_ups12 = rec.line_ids_material.filtered(lambda l: l.ups_index == 12)
+ rec.line_ids_material_ups13 = rec.line_ids_material.filtered(lambda l: l.ups_index == 13)
+ rec.line_ids_material_ups14 = rec.line_ids_material.filtered(lambda l: l.ups_index == 14)
+ rec.line_ids_material_ups15 = rec.line_ids_material.filtered(lambda l: l.ups_index == 15)
+
rec.line_ids_installation_kit_ups1 = rec.line_ids_installation_kit.filtered(lambda l: l.ups_index == 1)
rec.line_ids_installation_kit_ups2 = rec.line_ids_installation_kit.filtered(lambda l: l.ups_index == 2)
@@ -326,6 +385,11 @@ class Battery_Installation_Requirement(models.Model):
rec.line_ids_installation_kit_ups8 = rec.line_ids_installation_kit.filtered(lambda l: l.ups_index == 8)
rec.line_ids_installation_kit_ups9 = rec.line_ids_installation_kit.filtered(lambda l: l.ups_index == 9)
rec.line_ids_installation_kit_ups10 = rec.line_ids_installation_kit.filtered(lambda l: l.ups_index == 10)
+ rec.line_ids_installation_kit_ups11 = rec.line_ids_installation_kit.filtered(lambda l: l.ups_index == 11)
+ rec.line_ids_installation_kit_ups12 = rec.line_ids_installation_kit.filtered(lambda l: l.ups_index == 12)
+ rec.line_ids_installation_kit_ups13 = rec.line_ids_installation_kit.filtered(lambda l: l.ups_index == 13)
+ rec.line_ids_installation_kit_ups14 = rec.line_ids_installation_kit.filtered(lambda l: l.ups_index == 14)
+ rec.line_ids_installation_kit_ups15 = rec.line_ids_installation_kit.filtered(lambda l: l.ups_index == 15)
rec.line_ids_miscellaneous_ups1 = rec.line_ids_miscellaneous.filtered(lambda l: l.ups_index == 1)
rec.line_ids_miscellaneous_ups2 = rec.line_ids_miscellaneous.filtered(lambda l: l.ups_index == 2)
@@ -337,6 +401,11 @@ class Battery_Installation_Requirement(models.Model):
rec.line_ids_miscellaneous_ups8 = rec.line_ids_miscellaneous.filtered(lambda l: l.ups_index == 8)
rec.line_ids_miscellaneous_ups9 = rec.line_ids_miscellaneous.filtered(lambda l: l.ups_index == 9)
rec.line_ids_miscellaneous_ups10 = rec.line_ids_miscellaneous.filtered(lambda l: l.ups_index == 10)
+ rec.line_ids_miscellaneous_ups11 = rec.line_ids_miscellaneous.filtered(lambda l: l.ups_index == 11)
+ rec.line_ids_miscellaneous_ups12 = rec.line_ids_miscellaneous.filtered(lambda l: l.ups_index == 12)
+ rec.line_ids_miscellaneous_ups13 = rec.line_ids_miscellaneous.filtered(lambda l: l.ups_index == 13)
+ rec.line_ids_miscellaneous_ups14 = rec.line_ids_miscellaneous.filtered(lambda l: l.ups_index == 14)
+ rec.line_ids_miscellaneous_ups15 = rec.line_ids_miscellaneous.filtered(lambda l: l.ups_index == 15)
@api.model
@@ -362,10 +431,14 @@ class Battery_Installation_Requirement(models.Model):
self.communication_type = self.proposal_id.communication_type
self.specific_requirements = self.proposal_id.specific_requirements
multiplier = max(1, math.ceil(self.proposal_id.number_of_batteries / 20))
- self.engineers_nos = 1
- self.no_of_days = multiplier
- packing_calculation = 1200
- packing_kg_12V=100
+ if self.proposal_id.number_of_batteries < 100:
+ self.engineers_nos = 1
+ self.no_of_days = multiplier
+ else:
+ self.engineers_nos = 2
+ self.no_of_days = multiplier / 2
+ packing_calculation = 1200
+ packing_kg_12V = 100 * self.proposal_id.number_of_ups
direction = self.proposal_id.direction
if direction == "North":
forwarding_calculation = 20 * packing_kg_12V
@@ -375,8 +448,9 @@ class Battery_Installation_Requirement(models.Model):
forwarding_calculation = 23 * packing_kg_12V
else:
forwarding_calculation = 13 * packing_kg_12V
- base_amount = 0.20 * (packing_calculation + forwarding_calculation)
- self.packing_and_forwarding = base_amount * 0.18
+ base_amount = 1.20 * (packing_calculation + forwarding_calculation)
+ self.packing_and_forwarding = base_amount * 1.18
+
# Clear all lines
self.line_ids_fg = [(5, 0, 0)]
self.line_ids_sfg = [(5, 0, 0)]
@@ -424,6 +498,7 @@ class Battery_Installation_Requirement(models.Model):
material_lines.append((0, 0, {
'component_id': material_record.id,
'uom': material_record.uom,
+ 'singet_set_qty':single_set_qty,
'unit_price': material_record.unit_price,
'description': material_record.description,
'ups_index': ups_index,
@@ -778,7 +853,7 @@ class sos_proposal_Material_installationkit(models.Model):
def _compute_set_wise(self):
for record in self:
record.quantity = record.singet_set_qty * record.total_set
- @api.depends('unit_price','quantity')
+ @api.depends('unit_price','quantity','production_cost')
def _compute_total_price(self):
for record in self:
if record.production_cost:
@@ -1153,6 +1228,176 @@ class sos_spare_material10(models.Model):
_name = 'sos_proposal_line_spare_ups10'
_description = 'Spare Material 10'
+ ref_id = fields.Many2one('sos_proposal_boq', ondelete="cascade")
+ component_id = fields.Many2one('sos_material', string="Material Name", required=True)
+ uom = fields.Selection([('meters', 'Meters'),('Nos', 'Nos'),('coils', 'Coils'), ('litre', 'litre'), ('kg', 'Kilogram'), ('Packs', 'Packs')], default="Nos",string="Uom")
+ currency_id = fields.Many2one('res.currency', string='Currency')
+ material_code = fields.Char(related="component_id.material_code",string="Material Code")
+ material_name = fields.Char(related="component_id.part_no",string="Material Name")
+ singet_set_qty = fields.Integer(string="Single Set Quantity",default=1)
+ total_set = fields.Integer(string="Total Set",default=1)
+ quantity = fields.Integer(string="Quantity",compute="_compute_set_wise",readonly=False)
+ unit_price = fields.Monetary(store=True,currency_field='currency_id',string="Unit Price",related="component_id.unit_price")
+ total_price = fields.Monetary(string="Total",compute="_compute_total_price")
+ description = fields.Char(string="Description")
+ production_cost = fields.Boolean(string="Include in Costing",default=True)
+ ups_index = fields.Integer(string="UPS Index",store=True)
+ @api.onchange('component_id')
+ def _onchange_component_id(self):
+ for record in self:
+ if record.component_id:
+ record.unit_price = record.component_id.unit_price
+ @api.depends('singet_set_qty','total_set')
+ def _compute_set_wise(self):
+ for record in self:
+ record.quantity = record.singet_set_qty * record.total_set
+ @api.depends('unit_price', 'quantity', 'production_cost')
+ def _compute_total_price(self):
+ for record in self:
+ if record.production_cost:
+ record.total_price = record.unit_price * record.quantity
+ else:
+ record.total_price = 0.00
+class sos_spare_material11(models.Model):
+ _name = 'sos_proposal_line_spare_ups11'
+ _description = 'Spare Material 11'
+
+ ref_id = fields.Many2one('sos_proposal_boq', ondelete="cascade")
+ component_id = fields.Many2one('sos_material', string="Material Name", required=True)
+ uom = fields.Selection([('meters', 'Meters'),('Nos', 'Nos'),('coils', 'Coils'), ('litre', 'litre'), ('kg', 'Kilogram'), ('Packs', 'Packs')], default="Nos",string="Uom")
+ currency_id = fields.Many2one('res.currency', string='Currency')
+ material_code = fields.Char(related="component_id.material_code",string="Material Code")
+ material_name = fields.Char(related="component_id.part_no",string="Material Name")
+ singet_set_qty = fields.Integer(string="Single Set Quantity",default=1)
+ total_set = fields.Integer(string="Total Set",default=1)
+ quantity = fields.Integer(string="Quantity",compute="_compute_set_wise",readonly=False)
+ unit_price = fields.Monetary(store=True,currency_field='currency_id',string="Unit Price",related="component_id.unit_price")
+ total_price = fields.Monetary(string="Total",compute="_compute_total_price")
+ description = fields.Char(string="Description")
+ production_cost = fields.Boolean(string="Include in Costing",default=True)
+ ups_index = fields.Integer(string="UPS Index",store=True)
+ @api.onchange('component_id')
+ def _onchange_component_id(self):
+ for record in self:
+ if record.component_id:
+ record.unit_price = record.component_id.unit_price
+ @api.depends('singet_set_qty','total_set')
+ def _compute_set_wise(self):
+ for record in self:
+ record.quantity = record.singet_set_qty * record.total_set
+ @api.depends('unit_price', 'quantity', 'production_cost')
+ def _compute_total_price(self):
+ for record in self:
+ if record.production_cost:
+ record.total_price = record.unit_price * record.quantity
+ else:
+ record.total_price = 0.00
+class sos_spare_material12(models.Model):
+ _name = 'sos_proposal_line_spare_ups12'
+ _description = 'Spare Material 12'
+
+ ref_id = fields.Many2one('sos_proposal_boq', ondelete="cascade")
+ component_id = fields.Many2one('sos_material', string="Material Name", required=True)
+ uom = fields.Selection([('meters', 'Meters'),('Nos', 'Nos'),('coils', 'Coils'), ('litre', 'litre'), ('kg', 'Kilogram'), ('Packs', 'Packs')], default="Nos",string="Uom")
+ currency_id = fields.Many2one('res.currency', string='Currency')
+ material_code = fields.Char(related="component_id.material_code",string="Material Code")
+ material_name = fields.Char(related="component_id.part_no",string="Material Name")
+ singet_set_qty = fields.Integer(string="Single Set Quantity",default=1)
+ total_set = fields.Integer(string="Total Set",default=1)
+ quantity = fields.Integer(string="Quantity",compute="_compute_set_wise",readonly=False)
+ unit_price = fields.Monetary(store=True,currency_field='currency_id',string="Unit Price",related="component_id.unit_price")
+ total_price = fields.Monetary(string="Total",compute="_compute_total_price")
+ description = fields.Char(string="Description")
+ production_cost = fields.Boolean(string="Include in Costing",default=True)
+ ups_index = fields.Integer(string="UPS Index",store=True)
+ @api.onchange('component_id')
+ def _onchange_component_id(self):
+ for record in self:
+ if record.component_id:
+ record.unit_price = record.component_id.unit_price
+ @api.depends('singet_set_qty','total_set')
+ def _compute_set_wise(self):
+ for record in self:
+ record.quantity = record.singet_set_qty * record.total_set
+ @api.depends('unit_price', 'quantity', 'production_cost')
+ def _compute_total_price(self):
+ for record in self:
+ if record.production_cost:
+ record.total_price = record.unit_price * record.quantity
+ else:
+ record.total_price = 0.00
+class sos_spare_material13(models.Model):
+ _name = 'sos_proposal_line_spare_ups13'
+ _description = 'Spare Material 13'
+
+ ref_id = fields.Many2one('sos_proposal_boq', ondelete="cascade")
+ component_id = fields.Many2one('sos_material', string="Material Name", required=True)
+ uom = fields.Selection([('meters', 'Meters'),('Nos', 'Nos'),('coils', 'Coils'), ('litre', 'litre'), ('kg', 'Kilogram'), ('Packs', 'Packs')], default="Nos",string="Uom")
+ currency_id = fields.Many2one('res.currency', string='Currency')
+ material_code = fields.Char(related="component_id.material_code",string="Material Code")
+ material_name = fields.Char(related="component_id.part_no",string="Material Name")
+ singet_set_qty = fields.Integer(string="Single Set Quantity",default=1)
+ total_set = fields.Integer(string="Total Set",default=1)
+ quantity = fields.Integer(string="Quantity",compute="_compute_set_wise",readonly=False)
+ unit_price = fields.Monetary(store=True,currency_field='currency_id',string="Unit Price",related="component_id.unit_price")
+ total_price = fields.Monetary(string="Total",compute="_compute_total_price")
+ description = fields.Char(string="Description")
+ production_cost = fields.Boolean(string="Include in Costing",default=True)
+ ups_index = fields.Integer(string="UPS Index",store=True)
+ @api.onchange('component_id')
+ def _onchange_component_id(self):
+ for record in self:
+ if record.component_id:
+ record.unit_price = record.component_id.unit_price
+ @api.depends('singet_set_qty','total_set')
+ def _compute_set_wise(self):
+ for record in self:
+ record.quantity = record.singet_set_qty * record.total_set
+ @api.depends('unit_price', 'quantity', 'production_cost')
+ def _compute_total_price(self):
+ for record in self:
+ if record.production_cost:
+ record.total_price = record.unit_price * record.quantity
+ else:
+ record.total_price = 0.00
+class sos_spare_material14(models.Model):
+ _name = 'sos_proposal_line_spare_ups14'
+ _description = 'Spare Material 14'
+
+ ref_id = fields.Many2one('sos_proposal_boq', ondelete="cascade")
+ component_id = fields.Many2one('sos_material', string="Material Name", required=True)
+ uom = fields.Selection([('meters', 'Meters'),('Nos', 'Nos'),('coils', 'Coils'), ('litre', 'litre'), ('kg', 'Kilogram'), ('Packs', 'Packs')], default="Nos",string="Uom")
+ currency_id = fields.Many2one('res.currency', string='Currency')
+ material_code = fields.Char(related="component_id.material_code",string="Material Code")
+ material_name = fields.Char(related="component_id.part_no",string="Material Name")
+ singet_set_qty = fields.Integer(string="Single Set Quantity",default=1)
+ total_set = fields.Integer(string="Total Set",default=1)
+ quantity = fields.Integer(string="Quantity",compute="_compute_set_wise",readonly=False)
+ unit_price = fields.Monetary(store=True,currency_field='currency_id',string="Unit Price",related="component_id.unit_price")
+ total_price = fields.Monetary(string="Total",compute="_compute_total_price")
+ description = fields.Char(string="Description")
+ production_cost = fields.Boolean(string="Include in Costing",default=True)
+ ups_index = fields.Integer(string="UPS Index",store=True)
+ @api.onchange('component_id')
+ def _onchange_component_id(self):
+ for record in self:
+ if record.component_id:
+ record.unit_price = record.component_id.unit_price
+ @api.depends('singet_set_qty','total_set')
+ def _compute_set_wise(self):
+ for record in self:
+ record.quantity = record.singet_set_qty * record.total_set
+ @api.depends('unit_price', 'quantity', 'production_cost')
+ def _compute_total_price(self):
+ for record in self:
+ if record.production_cost:
+ record.total_price = record.unit_price * record.quantity
+ else:
+ record.total_price = 0.00
+class sos_spare_material15(models.Model):
+ _name = 'sos_proposal_line_spare_ups15'
+ _description = 'Spare Material 15'
+
ref_id = fields.Many2one('sos_proposal_boq', ondelete="cascade")
component_id = fields.Many2one('sos_material', string="Material Name", required=True)
uom = fields.Selection([('meters', 'Meters'),('Nos', 'Nos'),('coils', 'Coils'), ('litre', 'litre'), ('kg', 'Kilogram'), ('Packs', 'Packs')], default="Nos",string="Uom")
diff --git a/sos_sales/models/sos_proposal_builder.py b/sos_sales/models/sos_proposal_builder.py
index 7f5620b..ab345ed 100755
--- a/sos_sales/models/sos_proposal_builder.py
+++ b/sos_sales/models/sos_proposal_builder.py
@@ -18,7 +18,7 @@ class SOS_Proposal_Builder(models.Model):
string='Currency',
default=lambda self: self.env['res.currency'].search([('name', '=', 'INR')], limit=1).id or False
)
- total_cost = fields.Monetary(string="Total Cost", currency_field='currency_id')
+ total_cost = fields.Float(string="Total Cost", compute='_compute_total_cost', store=True)
buffer_in_percentage = fields.Integer(string="Buffer(%)",default=0,store=True)
total_with_buffer = fields.Monetary(string="Total with Buffer", compute='_compute_total_with_buffer', currency_field='currency_id', store=True)
#Proposal builder
@@ -197,6 +197,32 @@ class SOS_Proposal_Builder(models.Model):
for rec in self:
buffer_percent = rec.buffer_in_percentage or 0.0
rec.total_with_buffer = rec.total_cost * (1 + buffer_percent / 100.0)
+ @api.depends('proposal_id')
+ def _compute_total_cost(self):
+ for rec in self:
+ boq = self.env['sos_proposal_boq'].search(
+ [('proposal_id', '=', rec.proposal_id.id)],
+ limit=1, order='id desc'
+ )
+ rec.total_cost = boq.final_cost if boq else 0.0
+ @api.model
+ def create(self, vals):
+ if vals.get('proposal_id') and not vals.get('total_cost'):
+ boq = self.env['sos_proposal_boq'].search(
+ [('proposal_id', '=', vals['proposal_id'])], limit=1, order='id desc'
+ )
+ if boq:
+ vals['total_cost'] = boq.final_cost
+ return super().create(vals)
+
+ def write(self, vals):
+ if vals.get('proposal_id') and not vals.get('total_cost'):
+ boq = self.env['sos_proposal_boq'].search(
+ [('proposal_id', '=', vals['proposal_id'])], limit=1, order='id desc'
+ )
+ if boq:
+ vals['total_cost'] = boq.final_cost
+ return super().write(vals)
class SOS_Technical_Diagrams(models.Model):
_name = 'sos_proposal_diagrams'
diff --git a/sos_sales/models/sos_proposal_customer_requirement.py b/sos_sales/models/sos_proposal_customer_requirement.py
index 3d04d42..b921509 100755
--- a/sos_sales/models/sos_proposal_customer_requirement.py
+++ b/sos_sales/models/sos_proposal_customer_requirement.py
@@ -147,8 +147,8 @@ class Battery_Installation_Requirement(models.Model):
@api.onchange('number_of_ups')
def _onchange_number_of_ups(self):
if self.number_of_ups is not None:
- if self.number_of_ups > 10:
- raise ValidationError("Number of UPS cannot be more than 10.")
+ if self.number_of_ups > 15:
+ raise ValidationError("Number of UPS cannot be more than 15.")
lines = [(5, 0, 0)] # clear all
for i in range(self.number_of_ups):
diff --git a/sos_sales/models/sos_sales_achievement_report.py b/sos_sales/models/sos_sales_achievement_report.py
index f1f225c..ff9fdb3 100755
--- a/sos_sales/models/sos_sales_achievement_report.py
+++ b/sos_sales/models/sos_sales_achievement_report.py
@@ -1,8 +1,9 @@
from odoo import models, fields, api
-from datetime import date
+from datetime import date, timedelta,datetime
import re
import calendar
+from odoo.exceptions import UserError
class SOS_Sales_Achievement_Report(models.Model):
@@ -20,13 +21,13 @@ class SOS_Sales_Achievement_Report(models.Model):
planned_target_april = fields.Float(string="Planned For April")
actual_target_april = fields.Float(string="Actual For April")
billed_target_april = fields.Float(string="Billed For April")
- yet_to_billed_target_april = fields.Float(string="Yet to Billed For April")
+ yet_to_billed_target_april = fields.Float(string="Yet to Billed For April",compute="_compute_yet_to_billed_april")
collected_target_april = fields.Float(string="Collected For April")
achievement_percentage_april = fields.Char(string="Achievement Percentage For April", compute="_compute_achievement_percentage", store=True)
planned_target_may = fields.Float(string="Planned For May")
actual_target_may = fields.Float(string="Actual For May")
billed_target_may = fields.Float(string="Billed For May")
- yet_to_billed_target_may = fields.Float(string="Yet to Billed For May")
+ yet_to_billed_target_may = fields.Float(string="Yet to Billed For May",compute="_compute_yet_to_billed_targets")
collected_target_may = fields.Float(string="Collected For May")
achievement_percentage_may = fields.Char(string="Achievement Percentage For May", compute="_compute_achievement_percentage", store=True)
planned_target_june = fields.Float(string="Planned For June")
@@ -108,7 +109,50 @@ class SOS_Sales_Achievement_Report(models.Model):
ytd_billed = fields.Float(string="YTD Billed", store=True,compute="_compute_ytd_billed")
ytd_collected = fields.Float(string="YTD Collected", store=True,compute="_compute_ytd_collected")
line_ids = fields.One2many('sos_sales_achievement_report_brief', 'ref_id',copy=True)
-
+ billing_collection_line_ids = fields.One2many(
+ 'sos_billing_collection',
+ 'ref_id',
+ string="Billing Collection Lines"
+ )
+ @api.depends('opening_balance', 'actual_target_april', 'billed_target_april')
+ def _compute_yet_to_billed_april(self):
+ for rec in self:
+ rec.yet_to_billed_target_april = (rec.opening_balance or 0.0) + \
+ (rec.actual_target_april or 0.0) - \
+ (rec.billed_target_april or 0.0)
+
+ @api.depends(
+ 'yet_to_billed_target_april', # link to April
+ 'actual_target_may', 'billed_target_may',
+ 'actual_target_june', 'billed_target_june',
+ 'actual_target_july', 'billed_target_july',
+ 'actual_target_august', 'billed_target_august',
+ 'actual_target_september', 'billed_target_september',
+ 'actual_target_october', 'billed_target_october',
+ 'actual_target_november', 'billed_target_november',
+ 'actual_target_december', 'billed_target_december',
+ 'actual_target_january', 'billed_target_january',
+ 'actual_target_february', 'billed_target_february',
+ 'actual_target_march', 'billed_target_march',
+ )
+ def _compute_yet_to_billed_targets(self):
+ month_order = [
+ 'may', 'june', 'july', 'august', 'september',
+ 'october', 'november', 'december',
+ 'january', 'february', 'march'
+ ]
+
+ for rec in self:
+ prev_yet_to_bill = rec.yet_to_billed_target_april or 0.0
+ for month in month_order:
+ actual = getattr(rec, f'actual_target_{month}', 0.0) or 0.0
+ billed = getattr(rec, f'billed_target_{month}', 0.0) or 0.0
+ result = prev_yet_to_bill + actual - billed
+ setattr(rec, f'yet_to_billed_target_{month}', result)
+ prev_yet_to_bill = result
+
+
+
@api.depends(
'yet_to_billed_target_april', 'yet_to_billed_target_may', 'yet_to_billed_target_june',
'yet_to_billed_target_july', 'yet_to_billed_target_august', 'yet_to_billed_target_september',
@@ -137,41 +181,52 @@ class SOS_Sales_Achievement_Report(models.Model):
def action_view_billed_lines(self):
self.ensure_one()
- # Extract the month from context
- month = int(self.env.context.get('month', 0))
- if not month:
- month = 4 # fallback to April
-
- # You must extract year (hardcoded or dynamic based on financial year logic)
- # Assuming your main model has `financial_year = 'FY 2025-2026'`
- match = re.search(r'FY\s*(\d{4})', self.financial_year or '')
- if match:
- year = int(match.group(1))
+ month = int(self.env.context.get('month', 4)) # default April
+ action = self.env.context.get('action', 'Collected')
+ if action == 'Billed':
+ view_id = self.env.ref('sos_sales.view_sos_billed_collection_wizard_form_billed').id
else:
- year = date.today().year # fallback
+ view_id = self.env.ref('sos_sales.view_sos_billed_collection_wizard_form_collected').id
- # Get month boundaries
+ # Extract year from financial_year string like 'FY 2025-2026'
+ match = re.search(r'FY\s*(\d{4})', self.financial_year or '')
+ year = int(match.group(1)) if match else date.today().year
+
+ # Get month date range
last_day = calendar.monthrange(year, month)[1]
- from_date = f'{year}-{month:02d}-01'
- to_date = f'{year}-{month:02d}-{last_day:02d}'
- # Fetch Billed lines
+ from_date = date(year, month, 1)
+ to_date = date(year, month, last_day)
+
+ # Use the actual related record's value (not the related field)
+ sales_person_id = self.sales_person.id or self.ref('sales_person').id
+
+ # If sales_person is still None, raise a warning
+ if not sales_person_id:
+ raise UserError("Sales person is missing in this record.")
+
+ # Search filtered billing lines
billed_lines = self.env['sos_billing_collection'].search([
- ('sales_person', '=', self.sales_person.id),
- ('action_status', '=', self.env.context.get('action')),
+ ('sales_person', '=', sales_person_id),
+ ('action_status', '=', action),
('date_of_action', '>=', from_date),
('date_of_action', '<=', to_date),
])
+ wizard = self.env['sos_billed_collection_wizard'].create({
+ 'main_parent_id': self.id,
+ 'billed_line_ids': [(6, 0, billed_lines.ids)],
+ 'action_status': action,
+ })
+
return {
'name': f"{self.env.context.get('action')} Items",
'type': 'ir.actions.act_window',
'res_model': 'sos_billed_collection_wizard',
'view_mode': 'form',
+ 'view_id': view_id,
'target': 'new',
- 'context': {
- 'default_billed_line_ids': [(6, 0, billed_lines.ids)],
- 'default_parent_id': self.id,
- }
+ 'res_id': wizard.id,
}
+
def action_view_brief_lines(self):
self.ensure_one()
domain = [('ref_id', '=', self.id)]
@@ -204,6 +259,7 @@ class SOS_Sales_Achievement_Report(models.Model):
}
}
+
@api.depends(
'collected_target_april', 'collected_target_may', 'collected_target_june',
'collected_target_july', 'collected_target_august', 'collected_target_september',
@@ -500,13 +556,107 @@ class SOS_Sales_Achievement_Report_Brief(models.Model):
_order = 'action_date desc'
ref_id = fields.Many2one('sos_sales_achievement_report', string="Financial Year", ondelete="cascade")
customer_name = fields.Many2one('sos_customers',string="Customer Name", required=True)
- action_date = fields.Date(string="QP No")
+ action_date = fields.Date(string="Date")
currency_id = fields.Many2one(
'res.currency',
string='Currency',
default=lambda self: self.env['res.currency'].search([('name', '=', 'INR')], limit=1).id or False
)
- proposal_value = fields.Monetary(currency_field='currency_id',string="Proposal Value(In Lakhs)")
+ proposal_value = fields.Monetary(currency_field='currency_id',string="PO Received(In Lakhs)")
+ billed_date = fields.Date(string="Billed Date")
+ billed_amount = fields.Monetary(currency_field='currency_id',string="Billed Value(In Lakhs)")
+ def open_line_form(self):
+ self.ensure_one()
+ return {
+ 'type': 'ir.actions.act_window',
+ 'name': 'Edit Line',
+ 'res_model': self._name,
+ 'res_id': self.id,
+ 'view_mode': 'form',
+ 'target': 'new', # Opens as a popup
+ }
+ def _get_financial_year(self,current_date):
+ start_year = current_date.year if current_date.month >= 4 else current_date.year - 1
+ end_year = start_year + 1
+ return f"FY {start_year}-{end_year}"
+
+ def write(self, vals):
+ for rec in self:
+ # Previous values
+ old_billed_date = rec.billed_date
+ old_billed_amount = rec.billed_amount
+ report = rec.ref_id
+ # New values
+ new_billed_date = vals.get('billed_date', old_billed_date)
+ new_billed_amount = vals.get('billed_amount', old_billed_amount)
+
+ if isinstance(new_billed_date, str):
+ new_billed_date = datetime.strptime(new_billed_date, '%Y-%m-%d').date()
+
+ # Adjust billed target
+ if report and old_billed_date and new_billed_date:
+ old_month_billed = old_billed_date.strftime('%B').lower()
+ new_month_billed = new_billed_date.strftime('%B').lower()
+ if old_month_billed != new_month_billed or old_billed_amount != new_billed_amount:
+ old_field_billed = f"billed_target_{old_month_billed}"
+ new_field_billed = f"billed_target_{new_month_billed}"
+ if hasattr(report, old_field_billed):
+ report.write({
+ old_field_billed: max((getattr(report, old_field_billed, 0.0) or 0.0) - old_billed_amount, 0.0)
+ })
+ if hasattr(report, new_field_billed):
+ report.write({
+ new_field_billed: (getattr(report, new_field_billed, 0.0) or 0.0) + new_billed_amount
+ })
+ # Optionally create billing collection entry (if needed)
+ if new_billed_amount > 0:
+ self.env['sos_billing_collection'].create({
+ 'ref_id': report.id,
+ 'customer_name': vals.get('customer_name', rec.customer_name.id),
+ 'sales_person': report.sales_person.id,
+ 'action_status': 'Billed',
+ 'date_of_action': new_billed_date,
+ 'value': new_billed_amount
+ })
+
+ return super(SOS_Sales_Achievement_Report_Brief, self).write(vals)
+
+ @api.model
+ def create(self, vals):
+ action_date = vals.get('action_date')
+ billed_date = vals.get('billed_date')
+ customer_name = vals.get('customer_name')
+
+ if isinstance(action_date, str):
+ action_date = datetime.strptime(action_date, '%Y-%m-%d').date()
+ if isinstance(billed_date, str):
+ billed_date = datetime.strptime(billed_date, '%Y-%m-%d').date()
+
+ month_name = action_date.strftime('%B').lower()
+ value = vals.get('proposal_value', 0.0)
+ actual_field = f"actual_target_{month_name}"
+
+ ref_id = vals.get('ref_id')
+ if ref_id:
+ report = self.env['sos_sales_achievement_report'].browse(ref_id)
+ sales_person = report.sales_person
+ if sales_person and hasattr(report, actual_field):
+ current_value = getattr(report, actual_field, 0.0) or 0.0
+ report.write({actual_field: current_value + value})
+
+ # Instead of also summing billed_value here, just create billing line
+ if billed_date:
+ self.env['sos_billing_collection'].create({
+ 'ref_id': report.id,
+ 'customer_name': customer_name,
+ 'sales_person': report.sales_person.id,
+ 'action_status': 'Billed',
+ 'date_of_action': billed_date,
+ 'value': vals.get('billed_amount', 0.0)
+ })
+
+ return super(SOS_Sales_Achievement_Report_Brief, self).create(vals)
+
class SOS_Sales_Achievement_Report_Brief_Wizard(models.TransientModel):
_name = 'sos_sales_achievement_report_brief_wizard'
@@ -520,13 +670,144 @@ class SOS_Sales_Achievement_Report_Brief_Wizard(models.TransientModel):
string="Brief Lines",
readonly=True
)
+
+
+ def action_add_new_brief_line(self):
+ self.ensure_one()
+ return {
+ 'type': 'ir.actions.act_window',
+ 'name': 'Add Brief Line',
+ 'res_model': 'sos_sales_achievement_report_brief',
+ 'view_mode': 'form',
+ 'target': 'new',
+ 'context': {
+ 'default_ref_id': self.parent_id.id,
+ 'default_currency_id': self.env.ref('base.INR').id,
+ }
+ }
class SosBilledCollectionWizard(models.TransientModel):
_name = 'sos_billed_collection_wizard'
_description = 'Billed Collection Lines Wizard'
- parent_id = fields.Many2one('sos_billing_collection') # adjust accordingly
+ main_parent_id = fields.Many2one('sos_sales_achievement_report', string="Sales Achievement Report")
billed_line_ids = fields.Many2many(
'sos_billing_collection',
string="Billed Lines",
readonly=True,
- )
\ No newline at end of file
+ domain="[('ref_id', '=', main_parent_id)]"
+ )
+ action_status = fields.Selection([
+ ('Billed', 'Billed'),
+ ('Collected', 'Collected')
+ ], string='Action Status', readonly=True)
+ @api.model
+ def default_get(self, fields_list):
+ res = super(SosBilledCollectionWizard, self).default_get(fields_list)
+ main_parent_id = self.env.context.get('active_id')
+ if main_parent_id and self.env.context.get('active_model') == 'sos_sales_achievement_report':
+ res['main_parent_id'] = main_parent_id
+ # Populate billed_line_ids with related sos_billing_collection records
+ billing_lines = self.env['sos_billing_collection'].search([('ref_id', '=', main_parent_id)])
+ res['billed_line_ids'] = [(6, 0, billing_lines.ids)]
+ return res
+
+ def action_add_new_collection_line(self):
+ self.ensure_one()
+ if not self.main_parent_id:
+ raise UserError("No Sales Achievement Report found.")
+ if self.action_status == "Collected":
+ form_title="Collection"
+ view_xml_id="view_sos_billing_collection_minimal_form_collection"
+ else:
+ form_title="Billed"
+ view_xml_id="view_sos_billing_collection_minimal_form_billed"
+ return {
+ 'type': 'ir.actions.act_window',
+ 'name': f'Add {form_title}',
+ 'res_model': 'sos_billing_collection',
+ 'view_mode': 'form',
+ 'view_id': self.env.ref(f'sos_sales.{view_xml_id}').id,
+
+ 'target': 'new',
+ 'context': {
+ 'default_ref_id': self.main_parent_id.id,
+ 'default_action_status': self.action_status,
+ 'default_sales_person': self.main_parent_id.sales_person.id if self.main_parent_id.sales_person else False,
+ }
+ }
+class SosBillingCollection(models.Model):
+ _name = 'sos_billing_collection'
+ _description = 'Billing & Collection Details'
+
+ ref_id = fields.Many2one('sos_sales_achievement_report', ondelete="cascade", required=True)
+ customer_name = fields.Many2one('sos_customers', string="Customer Name")
+ sales_person = fields.Many2one(
+ 'res.users',
+ string='Sales Executive',
+ related="ref_id.sales_person",
+ store=True
+ )
+ action_status = fields.Selection([
+ ('Billed', 'Billed'),
+ ('Collected', 'Collected')
+ ], string='Action')
+ date_of_action = fields.Date(string="Date")
+ currency_id = fields.Many2one(
+ 'res.currency',
+ string='Currency',
+ default=lambda self: self.env['res.currency'].search([('name', '=', 'INR')], limit=1).id or False
+ )
+ value = fields.Monetary(currency_field='currency_id', string="Value (In Lakhs)")
+ customer_name = fields.Many2one('sos_customers',string="Customer Name")
+ po_no = fields.Char(string="PO No")
+ invoice_no = fields.Char(string="Invoice No")
+ products = fields.Selection(
+ [
+ ('BHMS 1.2V', 'BHMS 1.2V'),
+ ('BHMS 2V', 'BHMS 2V'),
+ ('BHMS 12V', 'BHMS 12V'),
+ ('BHMS 48V', 'BHMS 48V'),
+ ('BMS-HV', 'BMS-HV'),
+ ('BMS-LV 100A', 'BMS-LV 100A'),
+ ('BMS-LV 40A', 'BMS-LV 40A'),
+ ('SBMS 55A', 'SBMS 55A'),
+ ('MC 250W', 'MC 250W'),
+ ('HeartTarang', 'HeartTarang')
+ ],
+ string="Products")
+ quantity = fields.Integer(string="Quantity")
+ def _get_financial_year(self, current_date):
+ start_year = current_date.year if current_date.month >= 4 else current_date.year - 1
+ end_year = start_year + 1
+ return f"FY {start_year}-{end_year}"
+
+ @api.model
+ def create(self, vals):
+ if not vals.get('ref_id'):
+ raise UserError("Cannot create record without a valid Sales Achievement Report.")
+
+ action_date = vals.get('date_of_action')
+ action_status = vals.get('action_status').lower()
+ if isinstance(action_date, str):
+ action_date = datetime.strptime(action_date, '%Y-%m-%d').date()
+
+ fy = self._get_financial_year(action_date) if action_date else False
+ month_name = action_date.strftime('%B').lower() if action_date else False
+ final_value = vals.get('value', 0.0)
+ actual_field = f"{action_status}_target_{month_name}" if month_name else False
+
+ record = super(SosBillingCollection, self).create(vals)
+
+ # Update the related sos_sales_achievement_report by adding value
+ if vals.get('ref_id') and month_name:
+ report = self.env['sos_sales_achievement_report'].search([
+ ('id', '=', vals.get('ref_id')),
+ ('financial_year', '=', fy),
+ ])
+ if report and hasattr(report, actual_field):
+ current_value = getattr(report, actual_field, 0.0) or 0.0
+ new_value = current_value + final_value
+ report.write({actual_field: new_value})
+
+ return record
+
diff --git a/sos_sales/models/sos_sales_action_plan.py b/sos_sales/models/sos_sales_action_plan.py
index d883847..4772bb8 100755
--- a/sos_sales/models/sos_sales_action_plan.py
+++ b/sos_sales/models/sos_sales_action_plan.py
@@ -24,6 +24,14 @@ class sos_sales_action_plan(models.Model):
string="End Customer/Quote No",
domain="[('customer_name', '=', customer_name), ('quote_no', '!=', False)]"
)
+ ce_product_type = fields.Selection(
+ [
+ ('Sales', 'Sales'),
+ ('Service', 'Service'),
+ ('Spare', 'Spare'),
+ ('Cloud', 'Cloud')
+ ],
+ string="Service Type")
product = fields.Selection(
[
('BHMS 1.2V', 'BHMS 1.2V'),
@@ -80,7 +88,7 @@ class sos_sales_action_plan(models.Model):
('Engaged to Negotiation', 'Engaged to Negotiation'),
('Negotiation to Order', 'Negotiation to Order')
],
- string="Action Category",requierd=True)
+ string="Action Category",required=True)
action_plan = fields.Text(string="Action Plan")
result = fields.Text(string="Result")
status = fields.Selection(
@@ -101,9 +109,15 @@ class sos_sales_action_plan(models.Model):
], string='State', default='draft', readonly=True)
def _get_customer_domain(self):
- if self.env.user.has_group('sos_inventory.sos_management_user'):
+ if (
+ self.env.user.has_group('sos_inventory.sos_management_user') or
+ self.env.user.has_group('sos_inventory.sos_ce_head') or
+ self.env.user.has_group('sos_inventory.sos_ce_user')
+ ):
return [] # no filter, show all
return [('responsible', '=', self.env.uid)]
+
+
@api.onchange('quote_no_selector')
def _onchange_quote_no_selector(self):
if self.quote_no_selector:
@@ -436,21 +450,23 @@ class sos_sales_action_plan(models.Model):
}
}
else:
- casediary_record = self.env['sos_case_diary'].create({
+ vals = {
'customer_name': self.customer_name.id,
- 'end_customer_name':self.end_customer_name,
+ 'end_customer_name': self.end_customer_name,
'customer_city': self.location,
'sales_person': self.sales_executive.id,
'products': self.product,
'proposal_value': self.value,
'status': self.status,
- 'quote_no':self.quote_no,
- 'quantity':self.quantity,
- 'po_no':self.po_no,
- 'po_copy':self.po_copy,
- 'po_copy_filename':self.po_copy_filename
-
- })
+ 'quote_no': self.quote_no,
+ 'quantity': self.quantity,
+ 'po_no': self.po_no,
+ 'po_copy': self.po_copy,
+ 'po_copy_filename': self.po_copy_filename
+ }
+ if self.ce_product_type:
+ vals['ce_product_type'] = self.ce_product_type
+ casediary_record = self.env['sos_case_diary'].create(vals)
self.env['sos_case_diary_line'].create({
'ref_id': casediary_record.id,
'action_plan_date': self.date,
diff --git a/sos_sales/report/sos_proposal_report_view.xml b/sos_sales/report/sos_proposal_report_view.xml
index 91e9356..c902d37 100755
--- a/sos_sales/report/sos_proposal_report_view.xml
+++ b/sos_sales/report/sos_proposal_report_view.xml
@@ -35,24 +35,24 @@
-
- 1. INTRODUCTION
- ABOUT SOSALEY
+
+ 1. INTRODUCTION
+ ABOUT SOSALEY
- 2. PROPOSAL OBJECTIVE
+ 2. PROPOSAL OBJECTIVE
- 3. PROPOSAL OVERVIEW
+ 3. PROPOSAL OVERVIEW
- 4. ARCHITECTURE
+ 4. ARCHITECTURE
- PARAMETERS MONITORED
+ PARAMETERS MONITORED
- 5. QUOTATION
+ 5. QUOTATION
| Customer Details |
| Billing Address | |
@@ -74,9 +74,9 @@
- 6. PAYMENT TERMS
+ 6. PAYMENT TERMS
- 7. TERMS AND CONDITIONS
+ 7. TERMS AND CONDITIONS
diff --git a/sos_sales/security/ir.model.access.csv b/sos_sales/security/ir.model.access.csv
index ff410dd..0f4ffdb 100755
--- a/sos_sales/security/ir.model.access.csv
+++ b/sos_sales/security/ir.model.access.csv
@@ -51,9 +51,15 @@ access_sos_proposal_line_spare_ups7,sos_proposal_line_spare_ups7 access,model_so
access_sos_proposal_line_spare_ups8,sos_proposal_line_spare_ups8 access,model_sos_proposal_line_spare_ups8,base.group_user,1,1,1,1
access_sos_proposal_line_spare_ups9,sos_proposal_line_spare_ups9 access,model_sos_proposal_line_spare_ups9,base.group_user,1,1,1,1
access_sos_proposal_line_spare_ups10,sos_proposal_line_spare_ups10 access,model_sos_proposal_line_spare_ups10,base.group_user,1,1,1,1
+access_sos_proposal_line_spare_ups11,sos_proposal_line_spare_ups11 access,model_sos_proposal_line_spare_ups11,base.group_user,1,1,1,1
+access_sos_proposal_line_spare_ups12,sos_proposal_line_spare_ups12 access,model_sos_proposal_line_spare_ups12,base.group_user,1,1,1,1
+access_sos_proposal_line_spare_ups13,sos_proposal_line_spare_ups13 access,model_sos_proposal_line_spare_ups13,base.group_user,1,1,1,1
+access_sos_proposal_line_spare_ups14,sos_proposal_line_spare_ups14 access,model_sos_proposal_line_spare_ups14,base.group_user,1,1,1,1
+access_sos_proposal_line_spare_ups15,sos_proposal_line_spare_ups15 access,model_sos_proposal_line_spare_ups15,base.group_user,1,1,1,1
access_sos_sales_achievement_report_brief,sos_sales_achievement_report_brief access,model_sos_sales_achievement_report_brief,base.group_user,1,1,1,1
access_sos_sales_achievement_report_brief_wizard,sos_sales_achievement_report_brief_wizard access,model_sos_sales_achievement_report_brief_wizard,base.group_user,1,1,1,1
access_sos_billing_collection,sos_billing_collection access,model_sos_billing_collection,base.group_user,1,1,1,1
access_sos_billed_collection_wizard,sos_billed_collection_wizard access,model_sos_billed_collection_wizard,base.group_user,1,1,1,1
+access_sos_business_performance_wizard,sos_business_performance_wizard access,model_sos_business_performance_wizard,base.group_user,1,1,1,1
diff --git a/sos_sales/security/record_rules.xml b/sos_sales/security/record_rules.xml
index 53faec2..77969c5 100755
--- a/sos_sales/security/record_rules.xml
+++ b/sos_sales/security/record_rules.xml
@@ -68,6 +68,17 @@
+
+ Sales Action Plan: CE Head Reads CE User Records
+
+
+
+
+
+
+
+
+
Sos sales action plan: All Records - Read Access
@@ -89,7 +100,8 @@
[(1, '=', 1)]
@@ -127,7 +139,16 @@
-
+
+ Case Diary: CE Head Reads CE User Records
+
+
+
+
+
+
+
+
Sos Case Diary: All Records - Read Access
diff --git a/sos_sales/views/menu.xml b/sos_sales/views/menu.xml
index 402d5cc..335ec9f 100755
--- a/sos_sales/views/menu.xml
+++ b/sos_sales/views/menu.xml
@@ -1,7 +1,7 @@
-
+
\ No newline at end of file
diff --git a/sos_sales/views/sos_case_diary_view.xml b/sos_sales/views/sos_case_diary_view.xml
index c18591f..15c0924 100755
--- a/sos_sales/views/sos_case_diary_view.xml
+++ b/sos_sales/views/sos_case_diary_view.xml
@@ -164,36 +164,7 @@
-
-
-
-
-
Billed
-
-
-
-
-
-
-
-
-
-
-
-
Collected
-
-
-
-
-
-
-
-
-
-
-
+
@@ -310,7 +281,7 @@
-
+