Skip to content

Commit 30ba16c

Browse files
Update: latest changes
1 parent de6d0db commit 30ba16c

File tree

6 files changed

+381
-55
lines changed

6 files changed

+381
-55
lines changed

.devcontainer/devcontainer.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
{
22
"features": {
33
"ghcr.io/devcontainers/features/docker-outside-of-docker:1": {}
4+
},
5+
"customizations": {
6+
"vscode": {
7+
"extensions": [
8+
"tht13.python"
9+
]
10+
}
411
}
512
}

.vscode/launch.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
55
"version": "0.2.0",
66
"configurations": [
7+
8+
79
{
810
"name": "Python Debugger: Current File",
911
"type": "debugpy",

HTTP Controller

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ _logger = logging.getLogger(__name__)
55

66
CUSTOMER_FIELD = 'customer_id'
77
INTERNAL_USAGE = 'internal'
8+
INVENTORY_PAGE_LIMIT = 100 # Limit for scalability
89

910
class MyController(http.Controller):
11+
<<<<<<< HEAD
1012
"""
1113
Controller for managing inventory-related HTTP requests.
1214
"""
@@ -24,6 +26,102 @@ class MyController(http.Controller):
2426
('lot_id', 'in', serials.ids),
2527
('location_id.usage', '=', INTERNAL_USAGE)
2628
])
29+
=======
30+
def _get_current_user(self):
31+
"""
32+
Helper to get the current Odoo user.
33+
Returns:
34+
res.users recordset
35+
"""
36+
return http.request.env.user
37+
38+
def _get_current_partner(self):
39+
"""
40+
Helper to get the current user's partner.
41+
Returns:
42+
res.partner recordset
43+
"""
44+
return self._get_current_user().partner_id
45+
46+
def _handle_message(self, error=None, success=None):
47+
"""
48+
Modularize error and success message handling.
49+
Args:
50+
error (str): Error message.
51+
success (str): Success message.
52+
Returns:
53+
dict: Message context for rendering.
54+
"""
55+
if error:
56+
return {'message': error, 'message_type': 'error'}
57+
if success:
58+
return {'message': success, 'message_type': 'success'}
59+
return {'message': '', 'message_type': ''}
60+
61+
def _get_partner_inventory(self, partner, limit=INVENTORY_PAGE_LIMIT):
62+
"""
63+
Retrieve inventory for a given partner, paginated for scalability.
64+
Args:
65+
partner: res.partner recordset.
66+
limit (int): Max number of records to return.
67+
Returns:
68+
stock.quant recordset.
69+
Example:
70+
>>> self._get_partner_inventory(partner)
71+
stock.quant(1, 2, 3)
72+
Edge Cases:
73+
- Partner has no inventory: returns empty recordset.
74+
- Large inventory: returns only up to 'limit' records.
75+
"""
76+
try:
77+
serials = http.request.env['stock.production.lot'].search([(CUSTOMER_FIELD, '=', partner.id)])
78+
if not serials:
79+
_logger.info("No serials found for partner %s", partner.id)
80+
return http.request.env['stock.quant'].browse([])
81+
quants = http.request.env['stock.quant'].search([
82+
('lot_id', 'in', serials.ids),
83+
('location_id.usage', '=', INTERNAL_USAGE)
84+
], limit=limit)
85+
_logger.info("Fetched %d inventory items for partner %s", len(quants), partner.id)
86+
return quants
87+
except Exception as e:
88+
_logger.error("Database error fetching inventory for partner %s: %s", partner.id, e)
89+
return http.request.env['stock.quant'].browse([])
90+
91+
def _parse_item_ids(self, form):
92+
"""
93+
Safely parse item_ids from form data.
94+
Args:
95+
form: werkzeug.form object.
96+
Returns:
97+
List of item_ids as integers, or None if parsing fails.
98+
Example:
99+
>>> self._parse_item_ids(form)
100+
[1, 2, 3]
101+
Edge Cases:
102+
- No item_ids: returns None.
103+
- Non-integer values: returns None.
104+
- Duplicates: returns unique integers.
105+
"""
106+
item_ids = form.getlist('item_ids')
107+
if not item_ids:
108+
_logger.warning("No item_ids provided in the form.")
109+
return None
110+
try:
111+
# Validate: only allow positive integers, remove duplicates
112+
parsed_ids = []
113+
for id_str in item_ids:
114+
id_int = int(id_str)
115+
if id_int <= 0:
116+
raise ValueError("Item ID must be positive.")
117+
parsed_ids.append(id_int)
118+
unique_ids = list(set(parsed_ids))
119+
_logger.info("Parsed item_ids: %s", unique_ids)
120+
return unique_ids
121+
except Exception as e:
122+
_logger.error("Error parsing item_ids: %s. Data: %s", e, item_ids)
123+
return None
124+
>>>>>>> 46620db (Update: latest changes)
27125

28126
def _parse_item_ids(self, form):
29127
"""
@@ -41,6 +139,7 @@ class MyController(http.Controller):
41139
@http.route('/my/inventory/request_pickup', type='http', auth='user', methods=['POST'], csrf=True)
42140
def request_pickup(self, **kw):
43141
"""
142+
<<<<<<< HEAD
44143
Handle inventory pickup requests for logged-in users.
45144
"""
46145
user = http.request.env.user
@@ -56,10 +155,40 @@ class MyController(http.Controller):
56155
_logger.info('Pickup requested for items %s by partner ID %s', item_ids, partner.id)
57156
# ...existing pickup processing code should be placed here...
58157
return http.request.redirect('/my/inventory?success=pickup_requested')
158+
=======
159+
Handle pickup requests for inventory items.
160+
Enhanced logging, error handling, and user feedback.
161+
"""
162+
user = self._get_current_user()
163+
partner = self._get_current_partner()
164+
_logger.info('Pickup request initiated by user %s (ID: %s), partner %s', user.login, user.id, partner.id)
165+
item_ids = self._parse_item_ids(http.request.httprequest.form)
166+
if item_ids is None:
167+
error_msg = (
168+
"Invalid item selection. Please select valid inventory items. "
169+
"If the problem persists, contact support."
170+
)
171+
_logger.warning("User %s (ID: %s) provided invalid item_ids: %s", user.login, user.id, http.request.httprequest.form.getlist('item_ids'))
172+
return http.request.redirect('/my/inventory?error=invalid_item_ids')
173+
try:
174+
# ...existing code for processing pickup...
175+
_logger.info('Pickup requested for items %s by partner %s', item_ids, partner.id)
176+
# On success:
177+
success_msg = "Pickup request submitted successfully for selected items."
178+
return http.request.redirect('/my/inventory?success=pickup_requested')
179+
except Exception as e:
180+
_logger.error("Error processing pickup request for user %s: %s", user.login, e)
181+
error_msg = (
182+
"An error occurred while processing your pickup request. "
183+
"Please try again later or contact support."
184+
)
185+
return http.request.redirect('/my/inventory?error=pickup_failed')
186+
>>>>>>> 46620db (Update: latest changes)
59187

60188
@http.route('/my/inventory', type='http', auth='user', website=True, methods=['GET'], csrf=True)
61189
def my_inventory(self, **kw):
62190
"""
191+
<<<<<<< HEAD
63192
Display inventory relevant to the logged-in user.
64193
"""
65194
user = http.request.env.user
@@ -77,11 +206,41 @@ class MyController(http.Controller):
77206
elif success:
78207
message = 'Your pickup request has been submitted.'
79208

209+
=======
210+
Render the inventory page for the current user.
211+
Enhanced with pagination, user-friendly messages, and tooltips.
212+
"""
213+
user = self._get_current_user()
214+
partner = self._get_current_partner()
215+
error = http.request.params.get('error')
216+
success = http.request.params.get('success')
217+
message_ctx = self._handle_message(
218+
error="Invalid item selection. Please select valid inventory items." if error == 'invalid_item_ids' else (
219+
"An error occurred while processing your pickup request. Please try again later." if error == 'pickup_failed' else None
220+
),
221+
success="Pickup request submitted successfully!" if success == 'pickup_requested' else None
222+
)
223+
quants = self._get_partner_inventory(partner)
224+
if not quants:
225+
message_ctx['message'] = 'No inventory items found. If you believe this is an error, please contact support.'
226+
message_ctx['message_type'] = 'info'
227+
# Add tooltips/explanations for inventory actions
228+
inventory_tooltips = {
229+
'pickup': 'Request pickup for selected inventory items. Only available for items in internal locations.'
230+
}
231+
>>>>>>> 46620db (Update: latest changes)
80232
return http.request.render(
81233
'records_management.inventory_template',
82234
{
83235
'quants': quants,
236+
<<<<<<< HEAD
84237
'message': message
238+
=======
239+
'message': message_ctx['message'],
240+
'message_type': message_ctx['message_type'],
241+
'tooltips': inventory_tooltips,
242+
'page_limit': INVENTORY_PAGE_LIMIT,
243+
>>>>>>> 46620db (Update: latest changes)
85244
}
86245
)
87246

docker-compose.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ services:
1818
- ./records_management:/mnt/extra-addons/records_management
1919
environment:
2020
- HOST=db
21-
- USER=odoo
21+
- USER=
22+
2223
- PASSWORD=odoo
2324
restart: always
Lines changed: 80 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,93 @@
1-
from odoo import fields, models
1+
from odoo import fields, models, api
2+
from odoo.exceptions import ValidationError
23

34
class PickupRequest(models.Model):
5+
"""
6+
PickupRequest Model
7+
8+
This model represents a request for picking up items by a customer.
9+
It manages the customer, requested items, request date, and the workflow state.
10+
Includes validation to ensure data integrity and business logic for state transitions.
11+
"""
412
_name = 'pickup.request'
513
_description = 'Pickup Request'
614

7-
customer_id = fields.Many2one('res.partner', string='Customer', required=True)
8-
request_date = fields.Date(string='Request Date', default=fields.Date.today)
15+
customer_id = fields.Many2one(
16+
'res.partner',
17+
string='Customer',
18+
required=True,
19+
help="The customer requesting the pickup."
20+
)
21+
request_date = fields.Date(
22+
string='Request Date',
23+
default=fields.Date.today,
24+
required=True,
25+
help="The date when the pickup is requested. Cannot be in the past."
26+
)
927
state = fields.Selection([
1028
('draft', 'Draft'),
1129
('confirmed', 'Confirmed'),
1230
('done', 'Done')
13-
], default='draft', string='Status')
31+
], default='draft', string='Status', help="Current status of the pickup request.")
1432
item_ids = fields.Many2many(
1533
'stock.production.lot',
1634
string='Items',
17-
domain="[('customer_id', '=', customer_id)]"
35+
domain="[('customer_id', '=', customer_id)]",
36+
help="Items to be picked up. Only items belonging to the selected customer are allowed."
1837
)
38+
39+
@api.constrains('request_date')
40+
def _check_request_date(self):
41+
"""
42+
Ensure the request date is not in the past.
43+
"""
44+
for rec in self:
45+
if rec.request_date and rec.request_date < fields.Date.today():
46+
raise ValidationError("The request date cannot be in the past.")
47+
48+
@api.constrains('item_ids', 'customer_id')
49+
def _check_item_customer(self):
50+
"""
51+
Ensure all selected items belong to the selected customer.
52+
"""
53+
for rec in self:
54+
if rec.customer_id and rec.item_ids:
55+
invalid_items = rec.item_ids.filtered(lambda l: l.customer_id != rec.customer_id)
56+
if invalid_items:
57+
raise ValidationError("All items must belong to the selected customer.")
58+
59+
def action_confirm(self):
60+
"""
61+
Confirm the pickup request, moving it to the 'confirmed' state.
62+
"""
63+
for rec in self:
64+
rec.state = 'confirmed'
65+
66+
def action_done(self):
67+
"""
68+
Mark the pickup request as done, moving it to the 'done' state.
69+
"""
70+
for rec in self:
71+
rec.state = 'done'
72+
73+
# Security rules should be defined in the corresponding XML security files.
74+
# Example: records_management/security/ir.model.access.csv
75+
76+
# Optimize domain filtering for item_ids
77+
@api.onchange('customer_id')
78+
def _onchange_customer_id(self):
79+
"""
80+
Dynamically filter items based on the selected customer.
81+
"""
82+
if self.customer_id:
83+
return {
84+
'domain': {
85+
'item_ids': [('customer_id', '=', self.customer_id.id)]
86+
}
87+
}
88+
else:
89+
return {
90+
'domain': {
91+
'item_ids': []
92+
}
93+
}

0 commit comments

Comments
 (0)