woocommerce_inventory/mobile_app/lib/models/product.dart

416 lines
12 KiB
Dart

enum ProductType {
simple,
variable,
grouped,
external,
digital, // specific logic might be needed, often just 'simple' + virtual/downloadable properties
downloadable, // usually property of simple/variable
virtual, // usually property of simple/variable
subscription,
variableSubscription, // 'variable-subscription'
bundle,
composite,
bookable,
unknown,
}
abstract class Product {
final int id;
final String name;
final ProductType type;
final String status; // publish, private, draft, pending, future
final String? sku;
final String? price;
final String? regularPrice;
final String? salePrice;
final String stockStatus; // instock, outofstock, onbackorder
final bool manageStock;
final int? stockQuantity;
final List<ProductImage> images;
final String description;
final String shortDescription;
final List<dynamic> metaData;
Product({
required this.id,
required this.name,
required this.type,
required this.status,
this.sku,
this.price,
this.regularPrice,
this.salePrice,
required this.stockStatus,
this.manageStock = false,
this.stockQuantity,
this.images = const [],
this.description = '',
this.shortDescription = '',
this.metaData = const [],
});
factory Product.fromJson(Map<String, dynamic> json) {
final typeStr = json['type'] as String? ?? 'simple';
final type = _parseType(typeStr);
switch (type) {
case ProductType.variable:
return VariableProduct.fromJson(json, type);
case ProductType.grouped:
return GroupedProduct.fromJson(json, type);
case ProductType.external:
return ExternalProduct.fromJson(json, type);
// Add other specific classes as needed.
// For now, map others to SimpleProduct or a specific generic fallback if they don't have unique fields we critically need right now.
default:
// 'simple', 'booking', 'subscription', etc. unless they get their own class
return SimpleProduct.fromJson(json, type);
}
}
static ProductType _parseType(String type) {
switch (type) {
case 'simple': return ProductType.simple;
case 'variable': return ProductType.variable;
case 'grouped': return ProductType.grouped;
case 'external': return ProductType.external;
case 'subscription': return ProductType.subscription;
case 'variable-subscription': return ProductType.variableSubscription;
case 'bundle': return ProductType.bundle;
case 'composite': return ProductType.composite;
case 'booking': return ProductType.bookable;
default: return ProductType.unknown;
}
}
Map<String, dynamic> toJson() {
return {
'id': id,
'name': name,
'type': _typeToString(type),
'status': status,
'sku': sku,
'regular_price': regularPrice,
'sale_price': salePrice,
'stock_status': stockStatus,
'manage_stock': manageStock,
'stock_quantity': stockQuantity,
'images': images.map((i) => i.toJson()).toList(),
'description': description,
'short_description': shortDescription,
'meta_data': metaData,
};
}
String _typeToString(ProductType t) {
// simplified reverse mapping
switch (t) {
case ProductType.simple: return 'simple';
case ProductType.variable: return 'variable';
case ProductType.grouped: return 'grouped';
case ProductType.external: return 'external';
case ProductType.subscription: return 'subscription';
case ProductType.variableSubscription: return 'variable-subscription';
case ProductType.bundle: return 'bundle';
case ProductType.composite: return 'composite';
case ProductType.bookable: return 'booking';
default: return 'simple';
}
}
}
class ProductImage {
final int id;
final String src;
final String name;
final String alt;
ProductImage({
required this.id,
required this.src,
required this.name,
required this.alt,
});
factory ProductImage.fromJson(Map<String, dynamic> json) {
return ProductImage(
id: json['id'] ?? 0,
src: json['src'] ?? '',
name: json['name'] ?? '',
alt: json['alt'] ?? '',
);
}
Map<String, dynamic> toJson() => {
'id': id,
'src': src,
'name': name,
'alt': alt,
};
}
class SimpleProduct extends Product {
final bool virtual;
final bool downloadable;
SimpleProduct({
required super.id,
required super.name,
required super.type,
required super.status,
super.sku,
super.price,
super.regularPrice,
super.salePrice,
required super.stockStatus,
super.manageStock,
super.stockQuantity,
super.images,
super.description,
super.shortDescription,
super.metaData,
this.virtual = false,
this.downloadable = false,
});
factory SimpleProduct.fromJson(Map<String, dynamic> json, ProductType type) {
return SimpleProduct(
id: json['id'],
name: json['name'] ?? '',
type: type,
status: json['status'] ?? 'publish',
sku: json['sku'],
price: json['price']?.toString(), // API returns string or number? Usually string in WC
regularPrice: json['regular_price']?.toString(),
salePrice: json['sale_price']?.toString(),
stockStatus: json['stock_status'] ?? 'instock',
manageStock: json['manage_stock'] ?? false,
stockQuantity: json['stock_quantity'],
images: (json['images'] as List?)?.map((e) => ProductImage.fromJson(e)).toList() ?? [],
description: json['description'] ?? '',
shortDescription: json['short_description'] ?? '',
metaData: List.from(json['meta_data'] ?? []),
virtual: json['virtual'] ?? false,
downloadable: json['downloadable'] ?? false,
);
}
@override
Map<String, dynamic> toJson() {
final m = super.toJson();
m['virtual'] = virtual;
m['downloadable'] = downloadable;
return m;
}
}
class VariableProduct extends Product {
final List<int> variations;
VariableProduct({
required super.id,
required super.name,
required super.type,
required super.status,
super.sku,
super.price,
super.regularPrice,
super.salePrice,
required super.stockStatus,
super.manageStock,
super.stockQuantity,
super.images,
super.description,
super.shortDescription,
super.metaData,
this.variations = const [],
});
factory VariableProduct.fromJson(Map<String, dynamic> json, ProductType type) {
return VariableProduct(
id: json['id'],
name: json['name'] ?? '',
type: type,
status: json['status'] ?? 'publish',
sku: json['sku'],
price: json['price']?.toString(),
regularPrice: json['regular_price']?.toString(),
salePrice: json['sale_price']?.toString(),
stockStatus: json['stock_status'] ?? 'instock',
manageStock: json['manage_stock'] ?? false,
stockQuantity: json['stock_quantity'],
images: (json['images'] as List?)?.map((e) => ProductImage.fromJson(e)).toList() ?? [],
description: json['description'] ?? '',
shortDescription: json['short_description'] ?? '',
metaData: List.from(json['meta_data'] ?? []),
variations: (json['variations'] as List?)?.cast<int>() ?? [],
);
}
@override
Map<String, dynamic> toJson() {
final m = super.toJson();
m['variations'] = variations;
return m;
}
}
class Variation {
final int id;
final String sku;
final String? price;
final String? regularPrice;
final String? salePrice;
final String stockStatus;
final bool manageStock;
final int? stockQuantity;
final List<Map<String, dynamic>> attributes; // e.g. [{id: 0, name: 'Color', option: 'Blue'}]
final ProductImage? image;
Variation({
required this.id,
this.sku = '',
this.price,
this.regularPrice,
this.salePrice,
this.stockStatus = 'instock',
this.manageStock = false,
this.stockQuantity,
this.attributes = const [],
this.image,
});
factory Variation.fromJson(Map<String, dynamic> json) {
return Variation(
id: json['id'],
sku: json['sku'] ?? '',
price: json['price']?.toString(),
regularPrice: json['regular_price']?.toString(),
salePrice: json['sale_price']?.toString(),
stockStatus: json['stock_status'] ?? 'instock',
manageStock: json['manage_stock'] ?? false,
stockQuantity: json['stock_quantity'],
attributes: List<Map<String, dynamic>>.from(json['attributes'] ?? []),
image: json['image'] != null ? ProductImage.fromJson(json['image']) : null,
);
}
Map<String, dynamic> toJson() => {
'id': id,
'sku': sku,
'regular_price': regularPrice,
'sale_price': salePrice,
'stock_status': stockStatus,
'manage_stock': manageStock,
'stock_quantity': stockQuantity,
'attributes': attributes,
'image': image?.toJson(),
};
}
class GroupedProduct extends Product {
final List<int> groupedProducts;
GroupedProduct({
required super.id,
required super.name,
required super.type,
required super.status,
super.sku,
super.price,
super.regularPrice,
super.salePrice,
required super.stockStatus,
super.manageStock,
super.stockQuantity,
super.images,
super.description,
super.shortDescription,
super.metaData,
this.groupedProducts = const [],
});
factory GroupedProduct.fromJson(Map<String, dynamic> json, ProductType type) {
return GroupedProduct(
id: json['id'],
name: json['name'] ?? '',
type: type,
status: json['status'] ?? 'publish',
sku: json['sku'],
price: json['price']?.toString(),
regularPrice: json['regular_price']?.toString(),
salePrice: json['sale_price']?.toString(),
stockStatus: json['stock_status'] ?? 'instock',
manageStock: json['manage_stock'] ?? false,
stockQuantity: json['stock_quantity'],
images: (json['images'] as List?)?.map((e) => ProductImage.fromJson(e)).toList() ?? [],
description: json['description'] ?? '',
shortDescription: json['short_description'] ?? '',
metaData: List.from(json['meta_data'] ?? []),
groupedProducts: (json['grouped_products'] as List?)?.cast<int>() ?? [],
);
}
@override
Map<String, dynamic> toJson() {
final m = super.toJson();
m['grouped_products'] = groupedProducts;
return m;
}
}
class ExternalProduct extends Product {
final String externalUrl;
final String buttonText;
ExternalProduct({
required super.id,
required super.name,
required super.type,
required super.status,
super.sku,
super.price,
super.regularPrice,
super.salePrice,
required super.stockStatus,
super.manageStock,
super.stockQuantity,
super.images,
super.description,
super.shortDescription,
super.metaData,
this.externalUrl = '',
this.buttonText = '',
});
factory ExternalProduct.fromJson(Map<String, dynamic> json, ProductType type) {
return ExternalProduct(
id: json['id'],
name: json['name'] ?? '',
type: type,
status: json['status'] ?? 'publish',
sku: json['sku'],
price: json['price']?.toString(),
regularPrice: json['regular_price']?.toString(),
salePrice: json['sale_price']?.toString(),
stockStatus: json['stock_status'] ?? 'instock',
manageStock: json['manage_stock'] ?? false,
stockQuantity: json['stock_quantity'],
images: (json['images'] as List?)?.map((e) => ProductImage.fromJson(e)).toList() ?? [],
description: json['description'] ?? '',
shortDescription: json['short_description'] ?? '',
metaData: List.from(json['meta_data'] ?? []),
externalUrl: json['external_url'] ?? '',
buttonText: json['button_text'] ?? '',
);
}
@override
Map<String, dynamic> toJson() {
final m = super.toJson();
m['external_url'] = externalUrl;
m['button_text'] = buttonText;
return m;
}
}