MukeshKapoor25 commited on
Commit
8730c05
·
1 Parent(s): 17fe554

fix(bidder, distributor, project): change EmployeeId type to string and update related schemas

Browse files
app/controllers/projects.py CHANGED
@@ -3,7 +3,7 @@ from sqlalchemy.orm import Session
3
  from app.db.session import get_db
4
  from app.services.project_service import ProjectService
5
  from app.schemas.project import ProjectCreate, ProjectOut
6
- from app.schemas.project_detail import ProjectDetailOut, ProjectCustomerOut
7
  from app.schemas.paginated_response import PaginatedResponse
8
  from typing import List, Optional
9
 
@@ -36,9 +36,12 @@ def list_projects(
36
  page_size=page_size
37
  )
38
 
39
- @router.get("/{project_no}", response_model=ProjectDetailOut)
40
  def get_project(project_no: int, db: Session = Depends(get_db)):
41
- """Get a specific project by ProjectNo with full detailed information"""
 
 
 
42
  service = ProjectService(db)
43
  return service.get_detailed(project_no)
44
 
 
3
  from app.db.session import get_db
4
  from app.services.project_service import ProjectService
5
  from app.schemas.project import ProjectCreate, ProjectOut
6
+ from app.schemas.project_detail import ProjectDetailOut, ProjectCustomerOut, ProjectDetailNoCustomersOut
7
  from app.schemas.paginated_response import PaginatedResponse
8
  from typing import List, Optional
9
 
 
36
  page_size=page_size
37
  )
38
 
39
+ @router.get("/{project_no}", response_model=ProjectDetailNoCustomersOut)
40
  def get_project(project_no: int, db: Session = Depends(get_db)):
41
+ """Get a specific project by ProjectNo without customers collection
42
+
43
+ For customer data, use `/api/v1/projects/{project_no}/customers` instead.
44
+ """
45
  service = ProjectService(db)
46
  return service.get_detailed(project_no)
47
 
app/db/models/bidder.py CHANGED
@@ -28,4 +28,4 @@ class Bidder(Base):
28
  InvoiceDate = Column(DateTime, nullable=True)
29
  LessPayment = Column(String(50), nullable=True)
30
  Enabled = Column(Boolean, nullable=False, default=True)
31
- EmployeeId = Column(Integer, nullable=True)
 
28
  InvoiceDate = Column(DateTime, nullable=True)
29
  LessPayment = Column(String(50), nullable=True)
30
  Enabled = Column(Boolean, nullable=False, default=True)
31
+ EmployeeId = Column(String(5), nullable=True)
app/db/models/distributor.py CHANGED
@@ -23,4 +23,4 @@ class Distributor(Base):
23
  DateCreated = Column(DateTime, nullable=True)
24
  DateModified = Column(DateTime, nullable=True)
25
  Enabled = Column(Boolean, nullable=False, default=True)
26
- EmployeeId = Column(Integer, nullable=True)
 
23
  DateCreated = Column(DateTime, nullable=True)
24
  DateModified = Column(DateTime, nullable=True)
25
  Enabled = Column(Boolean, nullable=False, default=True)
26
+ EmployeeId = Column(String(5), nullable=True)
app/db/repositories/bidder_repo.py CHANGED
@@ -68,36 +68,39 @@ class BidderRepository:
68
  """
69
  try:
70
  with self.db.get_bind().connect() as conn:
71
- bidders_query = text(f"""
 
72
  SELECT
73
- ProjNo as proj_no,
74
- CustId as cust_id,
75
- Quote as quote,
76
- Contact as contact,
77
- Phone as phone,
78
- Notes as notes,
79
- DateLastContact as date_last_contact,
80
- DateFollowup as date_followup,
81
- [Primary] as is_primary,
82
- CustType as cust_type,
83
- EmailAddress as email_address,
84
- Id,
85
- Fax as fax,
86
- OrderNr as order_nr,
87
- CustomerPO as customer_po,
88
- ShipDate as ship_date,
89
- DeliverDate as deliver_date,
90
- ReplacementCost as replacement_cost,
91
- QuoteDate as quote_date,
92
- InvoiceDate as invoice_date,
93
- LessPayment as less_payment,
94
- Enabled as enabled,
95
- EmployeeId as employee_id
96
- FROM Bidders
97
- WHERE ProjNo = :project_no
98
- ORDER BY [Primary] DESC, Id
 
 
99
  OFFSET :offset ROWS FETCH NEXT :limit ROWS ONLY
100
- """,
101
  )
102
 
103
  offset = max(0, (page - 1) * page_size)
@@ -117,34 +120,38 @@ class BidderRepository:
117
  """
118
  try:
119
  with self.db.get_bind().connect() as conn:
120
- bidder_query = text("""
 
121
  SELECT
122
- ProjNo as proj_no,
123
- CustId as cust_id,
124
- Quote as quote,
125
- Contact as contact,
126
- Phone as phone,
127
- Notes as notes,
128
- DateLastContact as date_last_contact,
129
- DateFollowup as date_followup,
130
- [Primary] as is_primary,
131
- CustType as cust_type,
132
- EmailAddress as email_address,
133
- Id,
134
- Fax as fax,
135
- OrderNr as order_nr,
136
- CustomerPO as customer_po,
137
- ShipDate as ship_date,
138
- DeliverDate as deliver_date,
139
- ReplacementCost as replacement_cost,
140
- QuoteDate as quote_date,
141
- InvoiceDate as invoice_date,
142
- LessPayment as less_payment,
143
- Enabled as enabled,
144
- EmployeeId as employee_id
145
- FROM Bidders
146
- WHERE ProjNo = :project_no AND CustId = :customer_id
147
- """)
 
 
 
148
 
149
  result = conn.execute(bidder_query, {"project_no": project_no, "customer_id": customer_id})
150
  row = result.fetchone()
@@ -163,36 +170,40 @@ class BidderRepository:
163
  """
164
  try:
165
  with self.db.get_bind().connect() as conn:
166
- bidders_query = text("""
 
167
  SELECT
168
- ProjNo as proj_no,
169
- CustId as cust_id,
170
- Quote as quote,
171
- Contact as contact,
172
- Phone as phone,
173
- Notes as notes,
174
- DateLastContact as date_last_contact,
175
- DateFollowup as date_followup,
176
- [Primary] as is_primary,
177
- CustType as cust_type,
178
- EmailAddress as email_address,
179
- Id,
180
- Fax as fax,
181
- OrderNr as order_nr,
182
- CustomerPO as customer_po,
183
- ShipDate as ship_date,
184
- DeliverDate as deliver_date,
185
- ReplacementCost as replacement_cost,
186
- QuoteDate as quote_date,
187
- InvoiceDate as invoice_date,
188
- LessPayment as less_payment,
189
- Enabled as enabled,
190
- EmployeeId as employee_id
191
- FROM Bidders
192
- WHERE CustId = :customer_id
193
- ORDER BY ProjNo DESC, Id
 
 
194
  OFFSET :offset ROWS FETCH NEXT :limit ROWS ONLY
195
- """)
 
196
 
197
  offset = max(0, (page - 1) * page_size)
198
  limit = max(1, min(page_size, 1000))
@@ -486,6 +497,12 @@ class BidderRepository:
486
  return int(value)
487
  except (ValueError, TypeError):
488
  return None
 
 
 
 
 
 
489
 
490
  return BidderOut(
491
  id=bidder_data.get('Id'),
@@ -510,5 +527,5 @@ class BidderRepository:
510
  invoice_date=bidder_data.get('InvoiceDate'),
511
  less_payment=safe_float_conversion(bidder_data.get('LessPayment')),
512
  enabled=bidder_data.get('Enabled'),
513
- employee_id=safe_int_conversion(bidder_data.get('EmployeeId'))
514
  )
 
68
  """
69
  try:
70
  with self.db.get_bind().connect() as conn:
71
+ bidders_query = text(
72
+ """
73
  SELECT
74
+ b.ProjNo as proj_no,
75
+ b.CustId as cust_id,
76
+ c.CompanyName as customer_name,
77
+ b.Quote as quote,
78
+ b.Contact as contact,
79
+ b.Phone as phone,
80
+ b.Notes as notes,
81
+ b.DateLastContact as date_last_contact,
82
+ b.DateFollowup as date_followup,
83
+ b.[Primary] as is_primary,
84
+ b.CustType as cust_type,
85
+ b.EmailAddress as email_address,
86
+ b.Id,
87
+ b.Fax as fax,
88
+ b.OrderNr as order_nr,
89
+ b.CustomerPO as customer_po,
90
+ b.ShipDate as ship_date,
91
+ b.DeliverDate as deliver_date,
92
+ b.ReplacementCost as replacement_cost,
93
+ b.QuoteDate as quote_date,
94
+ b.InvoiceDate as invoice_date,
95
+ b.LessPayment as less_payment,
96
+ b.Enabled as enabled,
97
+ b.EmployeeId as employee_id
98
+ FROM Bidders b
99
+ LEFT JOIN Customers c ON b.CustId = CAST(c.CustomerID AS nvarchar)
100
+ WHERE b.ProjNo = :project_no
101
+ ORDER BY b.[Primary] DESC, b.Id
102
  OFFSET :offset ROWS FETCH NEXT :limit ROWS ONLY
103
+ """
104
  )
105
 
106
  offset = max(0, (page - 1) * page_size)
 
120
  """
121
  try:
122
  with self.db.get_bind().connect() as conn:
123
+ bidder_query = text(
124
+ """
125
  SELECT
126
+ b.ProjNo as proj_no,
127
+ b.CustId as cust_id,
128
+ c.CompanyName as customer_name,
129
+ b.Quote as quote,
130
+ b.Contact as contact,
131
+ b.Phone as phone,
132
+ b.Notes as notes,
133
+ b.DateLastContact as date_last_contact,
134
+ b.DateFollowup as date_followup,
135
+ b.[Primary] as is_primary,
136
+ b.CustType as cust_type,
137
+ b.EmailAddress as email_address,
138
+ b.Id,
139
+ b.Fax as fax,
140
+ b.OrderNr as order_nr,
141
+ b.CustomerPO as customer_po,
142
+ b.ShipDate as ship_date,
143
+ b.DeliverDate as deliver_date,
144
+ b.ReplacementCost as replacement_cost,
145
+ b.QuoteDate as quote_date,
146
+ b.InvoiceDate as invoice_date,
147
+ b.LessPayment as less_payment,
148
+ b.Enabled as enabled,
149
+ b.EmployeeId as employee_id
150
+ FROM Bidders b
151
+ LEFT JOIN Customers c ON b.CustId = CAST(c.CustomerID AS nvarchar)
152
+ WHERE b.ProjNo = :project_no AND b.CustId = :customer_id
153
+ """
154
+ )
155
 
156
  result = conn.execute(bidder_query, {"project_no": project_no, "customer_id": customer_id})
157
  row = result.fetchone()
 
170
  """
171
  try:
172
  with self.db.get_bind().connect() as conn:
173
+ bidders_query = text(
174
+ """
175
  SELECT
176
+ b.ProjNo as proj_no,
177
+ b.CustId as cust_id,
178
+ c.CompanyName as customer_name,
179
+ b.Quote as quote,
180
+ b.Contact as contact,
181
+ b.Phone as phone,
182
+ b.Notes as notes,
183
+ b.DateLastContact as date_last_contact,
184
+ b.DateFollowup as date_followup,
185
+ b.[Primary] as is_primary,
186
+ b.CustType as cust_type,
187
+ b.EmailAddress as email_address,
188
+ b.Id,
189
+ b.Fax as fax,
190
+ b.OrderNr as order_nr,
191
+ b.CustomerPO as customer_po,
192
+ b.ShipDate as ship_date,
193
+ b.DeliverDate as deliver_date,
194
+ b.ReplacementCost as replacement_cost,
195
+ b.QuoteDate as quote_date,
196
+ b.InvoiceDate as invoice_date,
197
+ b.LessPayment as less_payment,
198
+ b.Enabled as enabled,
199
+ b.EmployeeId as employee_id
200
+ FROM Bidders b
201
+ LEFT JOIN Customers c ON b.CustId = CAST(c.CustomerID AS nvarchar)
202
+ WHERE b.CustId = :customer_id
203
+ ORDER BY b.ProjNo DESC, b.Id
204
  OFFSET :offset ROWS FETCH NEXT :limit ROWS ONLY
205
+ """
206
+ )
207
 
208
  offset = max(0, (page - 1) * page_size)
209
  limit = max(1, min(page_size, 1000))
 
497
  return int(value)
498
  except (ValueError, TypeError):
499
  return None
500
+
501
+ # Helper function to safely convert to string, handling None values
502
+ def safe_string_conversion(value):
503
+ if value is None or value == 'None':
504
+ return None
505
+ return str(value).strip() if value else None
506
 
507
  return BidderOut(
508
  id=bidder_data.get('Id'),
 
527
  invoice_date=bidder_data.get('InvoiceDate'),
528
  less_payment=safe_float_conversion(bidder_data.get('LessPayment')),
529
  enabled=bidder_data.get('Enabled'),
530
+ employee_id=safe_string_conversion(bidder_data.get('EmployeeId'))
531
  )
app/schemas/bidder.py CHANGED
@@ -24,7 +24,7 @@ class BidderCreate(BaseModel):
24
  invoice_date: Optional[datetime] = Field(None, description="Invoice date")
25
  less_payment: Optional[float] = Field(None, description="Less payment")
26
  enabled: Optional[bool] = Field(True, description="Enabled status")
27
- employee_id: Optional[int] = Field(None, description="Employee ID")
28
 
29
  class BidderOut(BaseModel):
30
  id: int = Field(..., description="Bidder ID")
@@ -49,7 +49,7 @@ class BidderOut(BaseModel):
49
  invoice_date: Optional[datetime] = Field(None, description="Invoice date")
50
  less_payment: Optional[float] = Field(None, description="Less payment")
51
  enabled: Optional[bool] = Field(None, description="Enabled status")
52
- employee_id: Optional[int] = Field(None, description="Employee ID")
53
 
54
  class Config:
55
  from_attributes = True # Updated for Pydantic v2
 
24
  invoice_date: Optional[datetime] = Field(None, description="Invoice date")
25
  less_payment: Optional[float] = Field(None, description="Less payment")
26
  enabled: Optional[bool] = Field(True, description="Enabled status")
27
+ employee_id: Optional[str] = Field(None, description="Employee ID")
28
 
29
  class BidderOut(BaseModel):
30
  id: int = Field(..., description="Bidder ID")
 
49
  invoice_date: Optional[datetime] = Field(None, description="Invoice date")
50
  less_payment: Optional[float] = Field(None, description="Less payment")
51
  enabled: Optional[bool] = Field(None, description="Enabled status")
52
+ employee_id: Optional[str] = Field(None, description="Employee ID")
53
 
54
  class Config:
55
  from_attributes = True # Updated for Pydantic v2
app/schemas/distributor.py CHANGED
@@ -19,7 +19,7 @@ class DistributorCreate(BaseModel):
19
  status: Optional[str] = Field("active", description="Distributor status")
20
  notes: Optional[str] = Field(None, description="Additional notes")
21
  enabled: Optional[bool] = Field(True, description="Enabled status")
22
- employee_id: Optional[int] = Field(None, description="Employee ID")
23
 
24
  class DistributorOut(BaseModel):
25
  id: int = Field(..., description="Distributor ID")
@@ -41,7 +41,7 @@ class DistributorOut(BaseModel):
41
  date_created: Optional[datetime] = Field(None, description="Date created")
42
  date_modified: Optional[datetime] = Field(None, description="Date modified")
43
  enabled: Optional[bool] = Field(None, description="Enabled status")
44
- employee_id: Optional[int] = Field(None, description="Employee ID")
45
 
46
  class Config:
47
  from_attributes = True # Updated for Pydantic v2
 
19
  status: Optional[str] = Field("active", description="Distributor status")
20
  notes: Optional[str] = Field(None, description="Additional notes")
21
  enabled: Optional[bool] = Field(True, description="Enabled status")
22
+ employee_id: Optional[str] = Field(None, description="Employee ID")
23
 
24
  class DistributorOut(BaseModel):
25
  id: int = Field(..., description="Distributor ID")
 
41
  date_created: Optional[datetime] = Field(None, description="Date created")
42
  date_modified: Optional[datetime] = Field(None, description="Date modified")
43
  enabled: Optional[bool] = Field(None, description="Enabled status")
44
+ employee_id: Optional[str] = Field(None, description="Employee ID")
45
 
46
  class Config:
47
  from_attributes = True # Updated for Pydantic v2
app/schemas/project_detail.py CHANGED
@@ -39,6 +39,7 @@ class BidderNoteOut(BaseModel):
39
  class ProjectCustomerOut(BaseModel):
40
  proj_no: int = Field(0, description="Project number")
41
  cust_id: str = Field(..., description="Customer ID")
 
42
  quote: Optional[Decimal] = Field(None, description="Quote amount")
43
  contact: Optional[str] = Field(None, description="Contact person")
44
  phone: Optional[str] = Field(None, description="Phone number")
@@ -159,6 +160,97 @@ class ProjectDetailOut(BaseModel):
159
  customers: List[ProjectCustomerOut] = Field(default_factory=list, description="Project customers")
160
  project_notes: List[ProjectNoteDetailOut] = Field(default_factory=list, alias="projectNotes", description="Project notes")
161
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
162
  class Config:
163
  allow_population_by_field_name = True
164
  populate_by_name = True
 
39
  class ProjectCustomerOut(BaseModel):
40
  proj_no: int = Field(0, description="Project number")
41
  cust_id: str = Field(..., description="Customer ID")
42
+ customer_name: Optional[str] = Field(None, description="Customer company name")
43
  quote: Optional[Decimal] = Field(None, description="Quote amount")
44
  contact: Optional[str] = Field(None, description="Contact person")
45
  phone: Optional[str] = Field(None, description="Phone number")
 
160
  customers: List[ProjectCustomerOut] = Field(default_factory=list, description="Project customers")
161
  project_notes: List[ProjectNoteDetailOut] = Field(default_factory=list, alias="projectNotes", description="Project notes")
162
 
163
+ class Config:
164
+ allow_population_by_field_name = True
165
+ populate_by_name = True
166
+
167
+
168
+ class ProjectDetailNoCustomersOut(BaseModel):
169
+ """Project detail schema without the customers collection.
170
+
171
+ Use this for endpoints where we don't want to include the potentially
172
+ large customers payload. Keeps project_notes for context.
173
+ """
174
+
175
+ # Basic project fields (using camelCase to match legacy API)
176
+ project_no: int = Field(..., alias="projectNo", description="Project number")
177
+ project_name: str = Field(..., alias="projectName", description="Project name")
178
+ project_location: Optional[str] = Field(None, alias="projectLocation", description="Project location")
179
+ project_type: Optional[str] = Field(None, alias="projectType", description="Project type")
180
+ bid_date: Optional[datetime] = Field(None, alias="bidDate", description="Bid date")
181
+ start_date: Optional[datetime] = Field(None, alias="startDate", description="Start date")
182
+ is_awarded: bool = Field(False, alias="isAwarded", description="Awarded status")
183
+ install: bool = Field(False, description="Install option")
184
+ notes: Optional[str] = Field(None, description="Project notes")
185
+ barrier_size: Optional[str] = Field(None, alias="barrierSize", description="Barrier size")
186
+ lease_term: Optional[str] = Field(None, alias="leaseTerm", description="Lease term")
187
+ purchase_option: bool = Field(False, alias="purchaseOption", description="Purchase option")
188
+ lead_source: Optional[str] = Field(None, alias="leadSource", description="Lead source")
189
+ rep: Optional[str] = Field(None, description="Representative")
190
+ engineer_company_id: Optional[str] = Field(None, alias="engineerCompanyId", description="Engineer company ID")
191
+ engineer_notes: Optional[str] = Field(None, alias="engineerNotes", description="Engineer notes")
192
+ status: Optional[int] = Field(None, description="Project status")
193
+
194
+ # Billing information
195
+ bill_name: Optional[str] = Field(None, alias="billName", description="Billing name")
196
+ bill_address1: Optional[str] = Field(None, alias="billAddress1", description="Billing address 1")
197
+ bill_address2: Optional[str] = Field(None, alias="billAddress2", description="Billing address 2")
198
+ bill_city: Optional[str] = Field(None, alias="billCity", description="Billing city")
199
+ bill_state: Optional[str] = Field(None, alias="billState", description="Billing state")
200
+ bill_zip: Optional[str] = Field(None, alias="billZip", description="Billing ZIP")
201
+ bill_email: Optional[str] = Field(None, alias="billEmail", description="Billing email")
202
+ bill_phone: Optional[str] = Field(None, alias="billPhone", description="Billing phone")
203
+
204
+ # Shipping information
205
+ ship_name: Optional[str] = Field(None, alias="shipName", description="Shipping name")
206
+ ship_address1: Optional[str] = Field(None, alias="shipAddress1", description="Shipping address 1")
207
+ ship_address2: Optional[str] = Field(None, alias="shipAddress2", description="Shipping address 2")
208
+ ship_city: Optional[str] = Field(None, alias="shipCity", description="Shipping city")
209
+ ship_state: Optional[str] = Field(None, alias="shipState", description="Shipping state")
210
+ ship_zip: Optional[str] = Field(None, alias="shipZip", description="Shipping ZIP")
211
+ ship_email: Optional[str] = Field(None, alias="shipEmail", description="Shipping email")
212
+ ship_phone: Optional[str] = Field(None, alias="shipPhone", description="Shipping phone")
213
+ ship_office_phone: Optional[str] = Field(None, alias="shipOfficePhone", description="Shipping office phone")
214
+
215
+ # Additional fields from legacy API
216
+ fas_dam: bool = Field(False, alias="fasDam", description="FAS DAM flag")
217
+ engineer_company: Optional[str] = Field(None, alias="engineerCompany", description="Engineer company")
218
+ customer_type_id: Optional[int] = Field(None, alias="customertTypeId", description="Customer type ID")
219
+ acct_payable: Optional[str] = Field(None, alias="acctPayable", description="Account payable")
220
+ payment_term_id: Optional[int] = Field(None, alias="paymentTermId", description="Payment term ID")
221
+ payment_note: Optional[str] = Field(None, alias="paymentNote", description="Payment note")
222
+ rental_price_id: Optional[int] = Field(None, alias="rentalPriceId", description="Rental price ID")
223
+ purchase_price_id: Optional[int] = Field(None, alias="purchasePriceId", description="Purchase price ID")
224
+ est_ship_date_id: Optional[int] = Field(None, alias="estShipDateId", description="Estimated ship date ID")
225
+ fob_id: Optional[int] = Field(None, alias="fobId", description="FOB ID")
226
+ expedite_fee: Optional[Decimal] = Field(None, alias="expediteFee", description="Expedite fee")
227
+ est_freight_id: Optional[int] = Field(None, alias="estFreightId", description="Estimated freight ID")
228
+ est_freight_fee: Optional[Decimal] = Field(None, alias="estFreightFee", description="Estimated freight fee")
229
+ tax_rate: Optional[Decimal] = Field(None, alias="taxRate", description="Tax rate")
230
+ weekly_charge: Optional[Decimal] = Field(None, alias="weeklyCharge", description="Weekly charge")
231
+ crew_members: Optional[int] = Field(None, alias="crewMembers", description="Crew members")
232
+ tack_hoes: Optional[int] = Field(None, alias="tackHoes", description="Tack hoes")
233
+ water_pump: Optional[str] = Field(None, alias="waterPump", description="Water pump")
234
+ water_pump2: Optional[str] = Field(None, alias="waterPump2", description="Water pump 2")
235
+ est_installation_time: Optional[int] = Field(None, alias="estInstalationTime", description="Estimated installation time") # Note: legacy has typo
236
+ repair_kits: Optional[int] = Field(None, alias="repairKits", description="Repair kits")
237
+ installation_advisor: Optional[str] = Field(None, alias="installationAdvisor", description="Installation advisor")
238
+ employee_id: Optional[str] = Field(None, alias="employeeId", description="Employee ID")
239
+ install_date: Optional[datetime] = Field(None, alias="installDate", description="Install date")
240
+ commission: Optional[Decimal] = Field(None, description="Commission")
241
+ advisor_id: Optional[str] = Field(None, alias="advisorId", description="Advisor ID")
242
+ pipes: Optional[int] = Field(None, description="Pipes")
243
+ timpers: Optional[int] = Field(None, description="Timpers")
244
+ ship_via: Optional[str] = Field(None, alias="shipVia", description="Ship via")
245
+ valid_for: Optional[str] = Field(None, alias="validFor", description="Valid for")
246
+ same_bill_address: bool = Field(False, alias="sameBillAddress", description="Same billing address")
247
+ order_number: Optional[str] = Field(None, alias="orderNumber", description="Order number")
248
+ order_status: Optional[str] = Field(None, alias="orderStatus", description="Order status")
249
+ is_international: Optional[bool] = Field(None, alias="isInternational", description="International flag")
250
+
251
+ # Only keep project notes; omit customers
252
+ project_notes: List[ProjectNoteDetailOut] = Field(default_factory=list, alias="projectNotes", description="Project notes")
253
+
254
  class Config:
255
  allow_population_by_field_name = True
256
  populate_by_name = True
app/services/project_service.py CHANGED
@@ -210,6 +210,7 @@ class ProjectService:
210
  customer = ProjectCustomerOut(
211
  proj_no=bidder_data.get('proj_no', 0),
212
  cust_id=str(bidder_data.get('cust_id', '')),
 
213
  quote=bidder_data.get('quote'),
214
  contact=bidder_data.get('contact'),
215
  phone=bidder_data.get('phone'),
@@ -324,6 +325,7 @@ class ProjectService:
324
  customer = ProjectCustomerOut(
325
  proj_no=bidder_data.get('proj_no', 0),
326
  cust_id=str(bidder_data.get('cust_id', '')),
 
327
  quote=bidder_data.get('quote'),
328
  contact=bidder_data.get('contact'),
329
  phone=bidder_data.get('phone'),
@@ -889,6 +891,7 @@ class ProjectService:
889
  customer = ProjectCustomerOut(
890
  proj_no=bidder_data.get('proj_no', 0),
891
  cust_id=str(bidder_data.get('cust_id', '')),
 
892
  quote=bidder_data.get('quote'),
893
  contact=bidder_data.get('contact'),
894
  phone=bidder_data.get('phone'),
 
210
  customer = ProjectCustomerOut(
211
  proj_no=bidder_data.get('proj_no', 0),
212
  cust_id=str(bidder_data.get('cust_id', '')),
213
+ customer_name=bidder_data.get('customer_name'),
214
  quote=bidder_data.get('quote'),
215
  contact=bidder_data.get('contact'),
216
  phone=bidder_data.get('phone'),
 
325
  customer = ProjectCustomerOut(
326
  proj_no=bidder_data.get('proj_no', 0),
327
  cust_id=str(bidder_data.get('cust_id', '')),
328
+ customer_name=bidder_data.get('customer_name'),
329
  quote=bidder_data.get('quote'),
330
  contact=bidder_data.get('contact'),
331
  phone=bidder_data.get('phone'),
 
891
  customer = ProjectCustomerOut(
892
  proj_no=bidder_data.get('proj_no', 0),
893
  cust_id=str(bidder_data.get('cust_id', '')),
894
+ customer_name=bidder_data.get('customer_name'),
895
  quote=bidder_data.get('quote'),
896
  contact=bidder_data.get('contact'),
897
  phone=bidder_data.get('phone'),