vanitha commited on
Commit
20f9031
·
1 Parent(s): 80b51d1

feat: implement global meta schema for credit module with audit information

Browse files
app/credit_debit_notes/controllers/router.py CHANGED
@@ -5,9 +5,11 @@ Complete invoice adjustment lifecycle endpoints
5
  import logging
6
  from typing import List, Dict, Any, AsyncGenerator
7
  from uuid import UUID
 
8
  from fastapi import APIRouter, Depends, HTTPException, status, Query
9
  from sqlalchemy.ext.asyncio import AsyncSession
10
 
 
11
  from app.sql import async_session
12
  from app.dependencies.auth import get_current_user, TokenUser
13
  from app.credit_debit_notes.services.service import CreditDebitNoteService
@@ -77,12 +79,13 @@ async def create_adjustment_note(
77
  "errors": error_messages
78
  }
79
  )
80
-
81
  return AdjustmentNoteCreateResponse(
82
  success=True,
83
  message="Draft adjustment note created successfully",
84
  note_id=note.note_id,
85
- note_no=note.note_no
 
86
  )
87
 
88
  except HTTPException:
@@ -95,7 +98,7 @@ async def create_adjustment_note(
95
  )
96
 
97
 
98
- @router.get("/{note_id}", response_model=AdjustmentNoteDetailRead)
99
  async def get_adjustment_note(
100
  note_id: UUID,
101
  include_logs: bool = Query(True, description="Include status logs"),
@@ -152,6 +155,9 @@ async def get_adjustment_note(
152
  "rejected_at": note.rejected_at,
153
  "applied_by": note.applied_by,
154
  "applied_at": note.applied_at,
 
 
 
155
  "status_logs": []
156
  }
157
 
@@ -169,9 +175,10 @@ async def get_adjustment_note(
169
  }
170
  for log in note.status_logs
171
  ]
172
-
173
- return AdjustmentNoteDetailRead(**response_data)
174
-
 
175
  except HTTPException:
176
  raise
177
  except Exception as e:
@@ -182,7 +189,7 @@ async def get_adjustment_note(
182
  )
183
 
184
 
185
- @router.put("/{note_id}", response_model=AdjustmentNoteActionResponse)
186
  async def update_adjustment_note(
187
  note_id: UUID,
188
  note_data: AdjustmentNoteUpdate,
@@ -217,12 +224,18 @@ async def update_adjustment_note(
217
  "errors": error_messages
218
  }
219
  )
220
-
 
 
 
 
 
221
  return AdjustmentNoteActionResponse(
222
  success=True,
223
  message="Adjustment note updated successfully",
224
  note_id=note.note_id,
225
- new_status=note.status
 
226
  )
227
 
228
  except HTTPException:
@@ -268,42 +281,49 @@ async def list_adjustment_notes(
268
  db=db,
269
  filter_request=filter_request
270
  )
271
-
272
- notes_data = [
273
- AdjustmentNoteRead(
274
- note_id=note.note_id,
275
- note_no=note.note_no,
276
- invoice_id=note.invoice_id,
277
- supplier_id=note.supplier_id,
278
- buyer_id=note.buyer_id,
279
- category_code=note.category_code,
280
- reason_code=note.reason_code,
281
- note_type=note.note_type,
282
- note_date=note.note_date,
283
- original_amount=note.original_amount,
284
- revised_amount=note.revised_amount,
285
- adjustment_amount=note.adjustment_amount,
286
- taxable_amount=note.taxable_amount,
287
- original_tax_rate=note.original_tax_rate,
288
- correct_tax_rate=note.correct_tax_rate,
289
- tax_adjustment_amt=note.tax_adjustment_amt,
290
- description=note.description,
291
- remarks=note.remarks,
292
- status=note.status,
293
- created_by=note.created_by,
294
- created_at=note.created_at,
295
- updated_at=note.updated_at,
296
- submitted_by=note.submitted_by,
297
- submitted_at=note.submitted_at,
298
- approved_by=note.approved_by,
299
- approved_at=note.approved_at,
300
- rejected_by=note.rejected_by,
301
- rejected_at=note.rejected_at,
302
- applied_by=note.applied_by,
303
- applied_at=note.applied_at
304
- )
305
- for note in notes
306
- ]
 
 
 
 
 
 
 
307
  page = (filter_request.skip // filter_request.limit) + 1 if filter_request.limit else 1
308
 
309
  return {
@@ -364,14 +384,15 @@ async def perform_note_action(
364
  "errors": error_messages
365
  }
366
  )
367
-
368
  message = ACTION_MESSAGES.get(action_request.action, "Action completed successfully")
369
 
370
  return AdjustmentNoteActionResponse(
371
  success=True,
372
  message=message,
373
  note_id=note.note_id,
374
- new_status=note.status
 
375
  )
376
 
377
  except HTTPException:
 
5
  import logging
6
  from typing import List, Dict, Any, AsyncGenerator
7
  from uuid import UUID
8
+ from attrs import inspect
9
  from fastapi import APIRouter, Depends, HTTPException, status, Query
10
  from sqlalchemy.ext.asyncio import AsyncSession
11
 
12
+ from app.core.utils import format_meta_field
13
  from app.sql import async_session
14
  from app.dependencies.auth import get_current_user, TokenUser
15
  from app.credit_debit_notes.services.service import CreditDebitNoteService
 
79
  "errors": error_messages
80
  }
81
  )
82
+ formated_data = format_meta_field(note)
83
  return AdjustmentNoteCreateResponse(
84
  success=True,
85
  message="Draft adjustment note created successfully",
86
  note_id=note.note_id,
87
+ note_no=note.note_no,
88
+ data=formated_data
89
  )
90
 
91
  except HTTPException:
 
98
  )
99
 
100
 
101
+ @router.get("/{note_id}", response_model=dict)
102
  async def get_adjustment_note(
103
  note_id: UUID,
104
  include_logs: bool = Query(True, description="Include status logs"),
 
155
  "rejected_at": note.rejected_at,
156
  "applied_by": note.applied_by,
157
  "applied_at": note.applied_at,
158
+ "created_by_username": note.created_by_username,
159
+ "updated_by_username": note.updated_by_username,
160
+ "updated_by": note.updated_by,
161
  "status_logs": []
162
  }
163
 
 
175
  }
176
  for log in note.status_logs
177
  ]
178
+ response = AdjustmentNoteDetailRead(**response_data)
179
+ formated_data = format_meta_field(response.model_dump())
180
+ return formated_data
181
+
182
  except HTTPException:
183
  raise
184
  except Exception as e:
 
189
  )
190
 
191
 
192
+ @router.put("/{note_id}")
193
  async def update_adjustment_note(
194
  note_id: UUID,
195
  note_data: AdjustmentNoteUpdate,
 
224
  "errors": error_messages
225
  }
226
  )
227
+ note_dict = {
228
+ column.key: getattr(note, column.key)
229
+ for column in inspect(note).mapper.column_attrs
230
+ }
231
+
232
+ formated_data = format_meta_field(note_dict)
233
  return AdjustmentNoteActionResponse(
234
  success=True,
235
  message="Adjustment note updated successfully",
236
  note_id=note.note_id,
237
+ new_status=note.status,
238
+ data=formated_data
239
  )
240
 
241
  except HTTPException:
 
281
  db=db,
282
  filter_request=filter_request
283
  )
284
+
285
+ notes_data = []
286
+
287
+ for note in notes:
288
+ # Convert ORM object to dict
289
+ note_dict = {
290
+ "note_id": note.note_id,
291
+ "note_no": note.note_no,
292
+ "invoice_id": note.invoice_id,
293
+ "supplier_id": note.supplier_id,
294
+ "buyer_id": note.buyer_id,
295
+ "category_code": note.category_code,
296
+ "reason_code": note.reason_code,
297
+ "note_type": note.note_type,
298
+ "note_date": note.note_date,
299
+ "original_amount": note.original_amount,
300
+ "revised_amount": note.revised_amount,
301
+ "adjustment_amount": note.adjustment_amount,
302
+ "taxable_amount": note.taxable_amount,
303
+ "original_tax_rate": note.original_tax_rate,
304
+ "correct_tax_rate": note.correct_tax_rate,
305
+ "tax_adjustment_amt": note.tax_adjustment_amt,
306
+ "description": note.description,
307
+ "remarks": note.remarks,
308
+ "status": note.status,
309
+ "created_by": note.created_by,
310
+ "created_by_username": note.created_by_username,
311
+ "created_at": note.created_at,
312
+ "updated_by": note.updated_by,
313
+ "updated_by_username": note.updated_by_username,
314
+ "updated_at": note.updated_at,
315
+ "submitted_by": note.submitted_by,
316
+ "submitted_at": note.submitted_at,
317
+ "approved_by": note.approved_by,
318
+ "approved_at": note.approved_at,
319
+ "rejected_by": note.rejected_by,
320
+ "rejected_at": note.rejected_at,
321
+ "applied_by": note.applied_by,
322
+ "applied_at": note.applied_at,
323
+ }
324
+ formatted_note = format_meta_field(note_dict)
325
+ notes_data.append(formatted_note)
326
+
327
  page = (filter_request.skip // filter_request.limit) + 1 if filter_request.limit else 1
328
 
329
  return {
 
384
  "errors": error_messages
385
  }
386
  )
387
+ formatted_data = format_meta_field(note.__dict__)
388
  message = ACTION_MESSAGES.get(action_request.action, "Action completed successfully")
389
 
390
  return AdjustmentNoteActionResponse(
391
  success=True,
392
  message=message,
393
  note_id=note.note_id,
394
+ new_status=note.status,
395
+ data=formatted_data
396
  )
397
 
398
  except HTTPException:
app/credit_debit_notes/models/model.py CHANGED
@@ -75,6 +75,8 @@ class ScmInvoiceAdjustmentNote(Base):
75
  updated_at = Column(DateTime(timezone=True), nullable=False, server_default=func.now(), onupdate=func.now())
76
 
77
  # Approval tracking
 
 
78
  submitted_by = Column(String(64), nullable=True)
79
  submitted_at = Column(DateTime(timezone=True), nullable=True)
80
  approved_by = Column(String(64), nullable=True)
 
75
  updated_at = Column(DateTime(timezone=True), nullable=False, server_default=func.now(), onupdate=func.now())
76
 
77
  # Approval tracking
78
+ updated_by = Column(String(64), nullable=True)
79
+ updated_by_username = Column(String(64), nullable=True)
80
  submitted_by = Column(String(64), nullable=True)
81
  submitted_at = Column(DateTime(timezone=True), nullable=True)
82
  approved_by = Column(String(64), nullable=True)
app/credit_debit_notes/schemas/schema.py CHANGED
@@ -179,8 +179,12 @@ class AdjustmentNoteDetailRead(BaseModel):
179
  rejected_by: Optional[str]
180
  rejected_at: Optional[datetime]
181
  applied_by: Optional[str]
 
 
 
182
  applied_at: Optional[datetime]
183
  status_logs: Optional[List[AdjustmentNoteStatusLogRead]] = None
 
184
 
185
  class Config:
186
  from_attributes = True
@@ -238,6 +242,7 @@ class AdjustmentNoteCreateResponse(BaseModel):
238
  message: str
239
  note_id: UUID
240
  note_no: str
 
241
 
242
  class Config:
243
  json_schema_extra = {
@@ -256,6 +261,7 @@ class AdjustmentNoteActionResponse(BaseModel):
256
  message: str
257
  note_id: UUID
258
  new_status: str
 
259
 
260
  class Config:
261
  json_schema_extra = {
@@ -333,4 +339,4 @@ class ReasonsByCategoryResponse(BaseModel):
333
  {"code": "OVERCHARGE", "description": "Overcharge Correction"}
334
  ]
335
  }
336
- }
 
179
  rejected_by: Optional[str]
180
  rejected_at: Optional[datetime]
181
  applied_by: Optional[str]
182
+ updated_by: Optional[str]
183
+ updated_by_username: Optional[str]
184
+ created_by_username: Optional[str]
185
  applied_at: Optional[datetime]
186
  status_logs: Optional[List[AdjustmentNoteStatusLogRead]] = None
187
+ meta: Optional[Dict[str, Any]] = None # For any additional metadata
188
 
189
  class Config:
190
  from_attributes = True
 
242
  message: str
243
  note_id: UUID
244
  note_no: str
245
+ data:dict
246
 
247
  class Config:
248
  json_schema_extra = {
 
261
  message: str
262
  note_id: UUID
263
  new_status: str
264
+ data:dict
265
 
266
  class Config:
267
  json_schema_extra = {
 
339
  {"code": "OVERCHARGE", "description": "Overcharge Correction"}
340
  ]
341
  }
342
+ }
app/credit_debit_notes/services/service.py CHANGED
@@ -260,7 +260,7 @@ class CreditDebitNoteService:
260
  return None, errors
261
 
262
  note.updated_at = datetime.now()
263
-
264
  await db.commit()
265
  await db.refresh(note)
266
 
 
260
  return None, errors
261
 
262
  note.updated_at = datetime.now()
263
+ note.updated_by = updated_by
264
  await db.commit()
265
  await db.refresh(note)
266