240 lines
8.5 KiB
Dart
240 lines
8.5 KiB
Dart
import 'package:freezed_annotation/freezed_annotation.dart';
|
|
|
|
part 'purchase_order_model.freezed.dart';
|
|
part 'purchase_order_model.g.dart';
|
|
|
|
String _idFromJson(Object? value) => value?.toString() ?? '';
|
|
|
|
int? _intFromJsonNullable(Object? value) {
|
|
if (value == null) return null;
|
|
if (value is int) return value;
|
|
if (value is num) return value.toInt();
|
|
return int.tryParse(value.toString());
|
|
}
|
|
|
|
double? _doubleFromJsonNullable(Object? value) {
|
|
if (value == null) return null;
|
|
if (value is double) return value;
|
|
if (value is num) return value.toDouble();
|
|
return double.tryParse(value.toString());
|
|
}
|
|
|
|
DateTime? _dateFromJsonNullable(Object? value) {
|
|
if (value == null) return null;
|
|
if (value is DateTime) return value;
|
|
return DateTime.tryParse(value.toString());
|
|
}
|
|
|
|
Object? _readNestedName(Map<dynamic, dynamic> json, String flatKey, String nestedKey) {
|
|
final flat = json[flatKey];
|
|
if (flat is String && flat.isNotEmpty) return flat;
|
|
final nested = json[nestedKey];
|
|
if (nested is Map) {
|
|
return nested['name'] ?? nested['vendor_name'];
|
|
}
|
|
return null;
|
|
}
|
|
|
|
Object? _readVendorName(Map<dynamic, dynamic> json, String key) =>
|
|
_readNestedName(json, 'vendor_name', 'vendor');
|
|
|
|
Object? _readPlantName(Map<dynamic, dynamic> json, String key) =>
|
|
_readNestedName(json, 'plant_name', 'plant');
|
|
|
|
Object? _readWarehouseName(Map<dynamic, dynamic> json, String key) =>
|
|
_readNestedName(json, 'warehouse_name', 'warehouse');
|
|
|
|
Object? _readItemName(Map<dynamic, dynamic> json, String key) {
|
|
final flat = json['item_name'];
|
|
if (flat is String && flat.isNotEmpty) return flat;
|
|
final nested = json['item'];
|
|
if (nested is Map) return nested['name'];
|
|
return null;
|
|
}
|
|
|
|
Object? _readItemCode(Map<dynamic, dynamic> json, String key) {
|
|
final flat = json['item_code'];
|
|
if (flat is String && flat.isNotEmpty) return flat;
|
|
final nested = json['item'];
|
|
if (nested is Map) return nested['code'];
|
|
return null;
|
|
}
|
|
|
|
Object? _readUomName(Map<dynamic, dynamic> json, String key) {
|
|
final flat = json['uom_name'];
|
|
if (flat is String && flat.isNotEmpty) return flat;
|
|
final nested = json['uom'];
|
|
if (nested is Map) return nested['name'];
|
|
return null;
|
|
}
|
|
|
|
Object? _readPoNumber(Map<dynamic, dynamic> json, String key) {
|
|
final poNumber = json['po_number'];
|
|
if (poNumber != null && poNumber.toString().trim().isNotEmpty) {
|
|
return poNumber.toString();
|
|
}
|
|
final poNo = json['po_no'];
|
|
if (poNo != null && poNo.toString().trim().isNotEmpty) {
|
|
return poNo.toString();
|
|
}
|
|
return null;
|
|
}
|
|
|
|
Object? _readGrandTotal(Map<dynamic, dynamic> json, String key) =>
|
|
_doubleFromJsonNullable(json['grand_total'] ?? json['total_amount']);
|
|
|
|
Object? _readTaxTotal(Map<dynamic, dynamic> json, String key) =>
|
|
_doubleFromJsonNullable(json['tax_total'] ?? json['tax_amount']);
|
|
|
|
Object? _readSubTotal(Map<dynamic, dynamic> json, String key) =>
|
|
_doubleFromJsonNullable(json['sub_total'] ?? json['taxable_amount']);
|
|
|
|
@freezed
|
|
class PurchaseOrderModel with _$PurchaseOrderModel {
|
|
const PurchaseOrderModel._();
|
|
|
|
const factory PurchaseOrderModel({
|
|
@JsonKey(fromJson: _idFromJson) required String id,
|
|
@JsonKey(name: 'po_number', readValue: _readPoNumber) String? poNo,
|
|
@JsonKey(name: 'po_date', fromJson: _dateFromJsonNullable) DateTime? poDate,
|
|
@JsonKey(name: 'po_type') String? poType,
|
|
@Default('DRAFT') String status,
|
|
@JsonKey(name: 'vendor_id', fromJson: _intFromJsonNullable) int? vendorId,
|
|
@JsonKey(name: 'vendor_name', readValue: _readVendorName) String? vendorName,
|
|
@JsonKey(name: 'plant_id', fromJson: _intFromJsonNullable) int? plantId,
|
|
@JsonKey(name: 'plant_name', readValue: _readPlantName) String? plantName,
|
|
@JsonKey(name: 'warehouse_id', fromJson: _intFromJsonNullable) int? warehouseId,
|
|
@JsonKey(name: 'warehouse_name', readValue: _readWarehouseName) String? warehouseName,
|
|
@JsonKey(name: 'brand_id', fromJson: _intFromJsonNullable) int? brandId,
|
|
@JsonKey(name: 'payment_term_id', fromJson: _intFromJsonNullable) int? paymentTermId,
|
|
@JsonKey(name: 'delivery_term_id', fromJson: _intFromJsonNullable) int? deliveryTermId,
|
|
@JsonKey(name: 'expected_delivery_date', fromJson: _dateFromJsonNullable)
|
|
DateTime? expectedDeliveryDate,
|
|
@JsonKey(name: 'discount_amount', fromJson: _doubleFromJsonNullable)
|
|
double? discountAmount,
|
|
@JsonKey(name: 'freight_charges', fromJson: _doubleFromJsonNullable)
|
|
double? freightCharges,
|
|
@JsonKey(name: 'other_charges', fromJson: _doubleFromJsonNullable)
|
|
double? otherCharges,
|
|
@JsonKey(name: 'sub_total', readValue: _readSubTotal)
|
|
double? taxableAmount,
|
|
@JsonKey(name: 'tax_total', readValue: _readTaxTotal)
|
|
double? taxAmount,
|
|
@JsonKey(name: 'grand_total', readValue: _readGrandTotal)
|
|
double? totalAmount,
|
|
@JsonKey(name: 'terms_and_conditions') String? termsAndConditions,
|
|
String? remarks,
|
|
@JsonKey(name: 'revision_no', fromJson: _intFromJsonNullable) int? revisionNo,
|
|
@JsonKey(name: 'created_at', fromJson: _dateFromJsonNullable) DateTime? createdAt,
|
|
@JsonKey(name: 'updated_at', fromJson: _dateFromJsonNullable) DateTime? updatedAt,
|
|
@Default([]) List<PurchaseOrderItemModel> items,
|
|
}) = _PurchaseOrderModel;
|
|
|
|
factory PurchaseOrderModel.fromJson(Map<String, dynamic> json) =>
|
|
_$PurchaseOrderModelFromJson(json);
|
|
|
|
bool get canEdit =>
|
|
status.toUpperCase() == 'DRAFT' || status.toUpperCase() == 'REJECTED';
|
|
|
|
bool get canDelete => canEdit;
|
|
|
|
bool get canSubmit => status.toUpperCase() == 'DRAFT';
|
|
|
|
bool get canApprove {
|
|
final s = status.toUpperCase();
|
|
return s == 'SUBMITTED' || s == 'PENDING_APPROVAL' || s == 'PENDING';
|
|
}
|
|
|
|
bool get canReject => canApprove;
|
|
|
|
bool get canAmend => status.toUpperCase() == 'APPROVED';
|
|
|
|
bool get canCancel {
|
|
final s = status.toUpperCase();
|
|
return s != 'CANCELLED' && s != 'DRAFT';
|
|
}
|
|
}
|
|
|
|
@freezed
|
|
class PurchaseOrderItemModel with _$PurchaseOrderItemModel {
|
|
const factory PurchaseOrderItemModel({
|
|
@JsonKey(fromJson: _idFromJson) required String id,
|
|
@JsonKey(name: 'po_id', fromJson: _idFromJson) String? poId,
|
|
@JsonKey(name: 'item_id', fromJson: _intFromJsonNullable) int? itemId,
|
|
@JsonKey(name: 'item_code', readValue: _readItemCode) String? itemCode,
|
|
@JsonKey(name: 'item_name', readValue: _readItemName) String? itemName,
|
|
@JsonKey(name: 'line_no', fromJson: _intFromJsonNullable) int? lineNo,
|
|
@JsonKey(name: 'ordered_qty', fromJson: _doubleFromJsonNullable)
|
|
double? orderedQty,
|
|
@JsonKey(name: 'received_qty', fromJson: _doubleFromJsonNullable)
|
|
double? receivedQty,
|
|
@JsonKey(name: 'uom_id', fromJson: _intFromJsonNullable) int? uomId,
|
|
@JsonKey(name: 'uom_name', readValue: _readUomName) String? uomName,
|
|
@JsonKey(fromJson: _doubleFromJsonNullable) double? rate,
|
|
@JsonKey(name: 'discount_pct', fromJson: _doubleFromJsonNullable)
|
|
double? discountPct,
|
|
@JsonKey(name: 'discount_amount', fromJson: _doubleFromJsonNullable)
|
|
double? discountAmount,
|
|
@JsonKey(name: 'gst_rate_id', fromJson: _intFromJsonNullable) int? gstRateId,
|
|
@JsonKey(name: 'hsn_code_id', fromJson: _intFromJsonNullable) int? hsnCodeId,
|
|
@JsonKey(name: 'line_amount', fromJson: _doubleFromJsonNullable)
|
|
double? lineAmount,
|
|
String? remarks,
|
|
}) = _PurchaseOrderItemModel;
|
|
|
|
factory PurchaseOrderItemModel.fromJson(Map<String, dynamic> json) =>
|
|
_$PurchaseOrderItemModelFromJson(json);
|
|
}
|
|
|
|
@freezed
|
|
class PurchaseOrderListQuery with _$PurchaseOrderListQuery {
|
|
const factory PurchaseOrderListQuery({
|
|
@Default(1) int page,
|
|
@Default(20) int limit,
|
|
String? search,
|
|
String? status,
|
|
String? poType,
|
|
int? vendorId,
|
|
int? plantId,
|
|
String? dateFrom,
|
|
String? dateTo,
|
|
}) = _PurchaseOrderListQuery;
|
|
}
|
|
|
|
const poTypeOptions = [
|
|
('RAW_MATERIAL', 'Raw Material'),
|
|
('PACKING_MATERIAL', 'Packing Material'),
|
|
('ASSET_CAPITAL', 'Asset / Capital'),
|
|
('SERVICE', 'Service'),
|
|
('GENERAL', 'General'),
|
|
];
|
|
|
|
const poStatusOptions = [
|
|
('DRAFT', 'Draft'),
|
|
('SUBMITTED', 'Submitted'),
|
|
('PENDING_APPROVAL', 'Pending Approval'),
|
|
('APPROVED', 'Approved'),
|
|
('REJECTED', 'Rejected'),
|
|
('CANCELLED', 'Cancelled'),
|
|
('PARTIALLY_RECEIVED', 'Partially Received'),
|
|
('FULLY_RECEIVED', 'Fully Received'),
|
|
];
|
|
|
|
String poTypeLabel(String? value) {
|
|
if (value == null) return '—';
|
|
return poTypeOptions
|
|
.where((e) => e.$1 == value)
|
|
.map((e) => e.$2)
|
|
.firstOrNull ??
|
|
value;
|
|
}
|
|
|
|
String poStatusLabel(String? value) {
|
|
if (value == null) return '—';
|
|
return poStatusOptions
|
|
.where((e) => e.$1 == value)
|
|
.map((e) => e.$2)
|
|
.firstOrNull ??
|
|
value.replaceAll('_', ' ');
|
|
}
|