Skip to content

Commit 2d488fe

Browse files
author
John75SunCity
committed
feat: Add shredding bin products and quote detail template for customer portal
1 parent b1f05d3 commit 2d488fe

File tree

3 files changed

+350
-3
lines changed

3 files changed

+350
-3
lines changed

records_management/controllers/portal.py

Lines changed: 106 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6031,6 +6031,7 @@ def portal_quotes(self, **kw):
60316031
# Shredding bin pricing
60326032
shredding_prices = []
60336033
bin_sizes = [
6034+
('box', 'Shred Box (Standard Box)'),
60346035
('23', '23 Gallon Shredinator'),
60356036
('32g', '32 Gallon Bin'),
60366037
('32c', '32 Gallon Console'),
@@ -6039,7 +6040,7 @@ def portal_quotes(self, **kw):
60396040
]
60406041

60416042
# Default base prices
6042-
base_shred_prices = {'23': 20.0, '32g': 35.0, '32c': 30.0, '64': 65.0, '96': 95.0}
6043+
base_shred_prices = {'box': 10.0, '23': 20.0, '32g': 35.0, '32c': 30.0, '64': 65.0, '96': 95.0}
60436044

60446045
for bin_code, bin_name in bin_sizes:
60456046
# Check for negotiated rate
@@ -6180,6 +6181,7 @@ def generate_quote(self, **post):
61806181
if line_type == 'shredding' and bin_size:
61816182
# Get shredding bin product
61826183
xmlid_map = {
6184+
'box': 'records_management.product_shred_box',
61836185
'23': 'records_management.product_shredding_bin_23',
61846186
'32g': 'records_management.product_shredding_bin_32g',
61856187
'32c': 'records_management.product_shredding_bin_32c',
@@ -6203,7 +6205,7 @@ def generate_quote(self, **post):
62036205
else:
62046206
price = base_shred_prices.get(bin_size, 35.0)
62056207

6206-
bin_names = {'23': '23 Gallon', '32g': '32 Gallon Bin', '32c': '32 Gallon Console', '64': '64 Gallon', '96': '96 Gallon'}
6208+
bin_names = {'box': 'Shred Box', '23': '23 Gallon', '32g': '32 Gallon Bin', '32c': '32 Gallon Console', '64': '64 Gallon', '96': '96 Gallon'}
62076209
description = '%s Shredding Service' % bin_names.get(bin_size, 'Shredding')
62086210

62096211
elif line_type == 'pickup':
@@ -6250,14 +6252,115 @@ def generate_quote(self, **post):
62506252

62516253
i += 1
62526254

6255+
# Log the quote creation
6256+
order.message_post(
6257+
body=_("Quote created via customer portal"),
6258+
message_type='notification',
6259+
)
6260+
6261+
# Redirect to quote detail page where they can review, download PDF, or submit
6262+
return request.redirect('/my/quotes/%s?new=1' % order.id)
6263+
6264+
@http.route(['/my/quotes/<int:order_id>'], type='http', auth='user', website=True)
6265+
def portal_quote_detail(self, order_id, **kw):
6266+
"""Display quote detail with options to download PDF or submit for service."""
6267+
values = self._prepare_portal_layout_values()
6268+
partner = request.env.user.partner_id.commercial_partner_id
6269+
6270+
# Get the quote
6271+
order = request.env['sale.order'].sudo().search([
6272+
('id', '=', order_id),
6273+
('partner_id', 'child_of', partner.id),
6274+
], limit=1)
6275+
6276+
if not order:
6277+
return request.redirect('/my/quotes?error=not_found')
6278+
6279+
values.update({
6280+
'order': order,
6281+
'is_new': kw.get('new') == '1',
6282+
'submitted': kw.get('submitted') == '1',
6283+
'partner': partner,
6284+
'currency_symbol': request.env.company.currency_id.symbol or '$',
6285+
})
6286+
6287+
return request.render('records_management.portal_quote_detail', values)
6288+
6289+
@http.route(['/my/quotes/<int:order_id>/pdf'], type='http', auth='user', website=True)
6290+
def portal_quote_pdf(self, order_id, **kw):
6291+
"""Download quote as PDF."""
6292+
partner = request.env.user.partner_id.commercial_partner_id
6293+
6294+
# Get the quote
6295+
order = request.env['sale.order'].sudo().search([
6296+
('id', '=', order_id),
6297+
('partner_id', 'child_of', partner.id),
6298+
], limit=1)
6299+
6300+
if not order:
6301+
return request.redirect('/my/quotes?error=not_found')
6302+
62536303
# Generate PDF
62546304
report = request.env.ref('sale.action_report_saleorder')
62556305
pdf_content, _ = report._render_qweb_pdf([order.id])
62566306
return request.make_response(pdf_content, headers=[
62576307
('Content-Type', 'application/pdf'),
6258-
('Content-Disposition', f'attachment; filename="Quote_{order.name}.pdf"')
6308+
('Content-Disposition', 'attachment; filename="Quote_%s.pdf"' % order.name)
62596309
])
62606310

6311+
@http.route(['/my/quotes/<int:order_id>/submit'], type='http', auth='user', website=True, methods=['POST'], csrf=True)
6312+
def portal_quote_submit(self, order_id, **post):
6313+
"""Submit quote for service - confirms the sale order."""
6314+
partner = request.env.user.partner_id.commercial_partner_id
6315+
6316+
# Get the quote
6317+
order = request.env['sale.order'].sudo().search([
6318+
('id', '=', order_id),
6319+
('partner_id', 'child_of', partner.id),
6320+
('state', '=', 'draft'),
6321+
], limit=1)
6322+
6323+
if not order:
6324+
return request.redirect('/my/quotes?error=not_found')
6325+
6326+
# Add notes if provided
6327+
if post.get('notes'):
6328+
order.note = post['notes']
6329+
6330+
# Add requested date if provided
6331+
if post.get('requested_date'):
6332+
order.commitment_date = post['requested_date']
6333+
6334+
# Confirm the order
6335+
order.action_confirm()
6336+
6337+
# Log the submission
6338+
order.message_post(
6339+
body=_("Quote submitted for service via customer portal"),
6340+
message_type='notification',
6341+
)
6342+
6343+
# Create a portal request for tracking
6344+
portal_request_vals = {
6345+
'name': _('Service Request from Quote %s') % order.name,
6346+
'partner_id': partner.id,
6347+
'request_type': 'other',
6348+
'state': 'submitted',
6349+
'created_via_portal': True,
6350+
'description': _('Services requested via portal quote generator. See Sale Order %s for details.') % order.name,
6351+
}
6352+
6353+
# Link to department if available
6354+
if hasattr(partner, 'department_id') and partner.department_id:
6355+
portal_request_vals['department_id'] = partner.department_id.id
6356+
6357+
try:
6358+
request.env['portal.request'].sudo().create(portal_request_vals)
6359+
except Exception:
6360+
pass # Don't fail if portal.request has issues
6361+
6362+
return request.redirect('/my/quotes/%s?submitted=1' % order.id)
6363+
62616364
@http.route(['/my/inventory/temp'], type='http', auth="user", website=True)
62626365
def portal_inventory_temp(self, page=1, sortby=None, filterby=None, search=None, **kw):
62636366
"""Temp inventory tab - backend-style list view"""

records_management/data/products_data.xml

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,68 @@
3838
<field name="description">NAID-compliant secure destruction per container</field>
3939
</record>
4040

41+
<!-- Shredding Bin Products - Specific Sizes -->
42+
<record id="product_shredding_bin_23" model="product.product">
43+
<field name="name">23 Gallon Shredinator Bin Service</field>
44+
<field name="type">service</field>
45+
<field name="categ_id" ref="product_category_paper_scrap"/>
46+
<field name="default_code">SHRED-23</field>
47+
<field name="list_price">20.00</field>
48+
<field name="standard_price">12.00</field>
49+
<field name="description">23 Gallon Shredinator desk-side bin - compact secure shredding</field>
50+
</record>
51+
52+
<record id="product_shredding_bin_32g" model="product.product">
53+
<field name="name">32 Gallon Shredding Bin Service</field>
54+
<field name="type">service</field>
55+
<field name="categ_id" ref="product_category_paper_scrap"/>
56+
<field name="default_code">SHRED-32G</field>
57+
<field name="list_price">35.00</field>
58+
<field name="standard_price">20.00</field>
59+
<field name="description">32 Gallon standard shredding bin - office floor placement</field>
60+
</record>
61+
62+
<record id="product_shredding_bin_32c" model="product.product">
63+
<field name="name">32 Gallon Console Bin Service</field>
64+
<field name="type">service</field>
65+
<field name="categ_id" ref="product_category_paper_scrap"/>
66+
<field name="default_code">SHRED-32C</field>
67+
<field name="list_price">30.00</field>
68+
<field name="standard_price">18.00</field>
69+
<field name="description">32 Gallon console bin - slim profile for reception areas</field>
70+
</record>
71+
72+
<record id="product_shredding_bin_64" model="product.product">
73+
<field name="name">64 Gallon Shredding Bin Service</field>
74+
<field name="type">service</field>
75+
<field name="categ_id" ref="product_category_paper_scrap"/>
76+
<field name="default_code">SHRED-64</field>
77+
<field name="list_price">65.00</field>
78+
<field name="standard_price">38.00</field>
79+
<field name="description">64 Gallon large capacity shredding bin - high volume areas</field>
80+
</record>
81+
82+
<record id="product_shredding_bin_96" model="product.product">
83+
<field name="name">96 Gallon Shredding Bin Service</field>
84+
<field name="type">service</field>
85+
<field name="categ_id" ref="product_category_paper_scrap"/>
86+
<field name="default_code">SHRED-96</field>
87+
<field name="list_price">95.00</field>
88+
<field name="standard_price">55.00</field>
89+
<field name="description">96 Gallon extra-large shredding bin - warehouse/bulk document areas</field>
90+
</record>
91+
92+
<!-- Shred Box - Customer-Supplied Standard Box -->
93+
<record id="product_shred_box" model="product.product">
94+
<field name="name">Shred Box (Standard Box)</field>
95+
<field name="type">service</field>
96+
<field name="categ_id" ref="product_category_paper_scrap"/>
97+
<field name="default_code">SHRED-BOX</field>
98+
<field name="list_price">10.00</field>
99+
<field name="standard_price">5.00</field>
100+
<field name="description">Secure shredding of customer-supplied standard cardboard box of documents (banker box size)</field>
101+
</record>
102+
41103
<!-- Business Integration Configuration Parameters -->
42104
<record id="container_service_integration" model="ir.config_parameter">
43105
<field name="key">records_management.container_service_integration</field>

0 commit comments

Comments
 (0)