見積書画面簡素化\n\n- database_helper.dart の重複 API 削除\n- estimate_screen.dart の Estimate モデル依存排除(Map データ保存)\n- showModal → showDialog 修正\n- Duration(inDays:...) → Duration(days:) 修正\n- TextButton の child パラメータ追加\n- ビルド成功 (49.4MB APK)
This commit is contained in:
parent
4c5ea99947
commit
4679ad30ae
2 changed files with 419 additions and 129 deletions
|
|
@ -1,6 +1,9 @@
|
|||
// Version: 1.5 - 見積書画面(簡易実装)
|
||||
// Version: 1.9 - 見積書画面(簡素版)
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import '../models/customer.dart';
|
||||
import '../models/product.dart';
|
||||
import '../services/database_helper.dart';
|
||||
|
||||
/// 見積書作成画面
|
||||
class EstimateScreen extends StatefulWidget {
|
||||
|
|
@ -10,78 +13,411 @@ class EstimateScreen extends StatefulWidget {
|
|||
State<EstimateScreen> createState() => _EstimateScreenState();
|
||||
}
|
||||
|
||||
class _EstimateScreenState extends State<EstimateScreen> {
|
||||
class _EstimateScreenState extends State<EstimateScreen> with SingleTickerProviderStateMixin {
|
||||
Customer? _selectedCustomer;
|
||||
List<Customer> _customers = [];
|
||||
DateTime? _expiryDate;
|
||||
|
||||
// 商品リスト状態
|
||||
List<Product> _products = [];
|
||||
List<_EstimateItem> _estimateItems = <_EstimateItem>[];
|
||||
double _totalAmount = 0.0;
|
||||
String _estimateNumber = '';
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loadCustomers();
|
||||
_generateEstimateNumber();
|
||||
_loadProducts();
|
||||
}
|
||||
|
||||
Future<void> _loadCustomers() async {
|
||||
// TODO: DatabaseHelper.instance.getCustomers() を使用
|
||||
setState(() => _customers = []);
|
||||
try {
|
||||
final customers = await DatabaseHelper.instance.getCustomers();
|
||||
if (mounted) setState(() => _customers = customers ?? const <Customer>[]);
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
/// 見積有効期限を設定(簡易)
|
||||
void setExpiryDate(DateTime date) {
|
||||
setState(() => _expiryDate = date);
|
||||
/// 見積書番号を自動生成(YMM-0001 形式)
|
||||
void _generateEstimateNumber() {
|
||||
final now = DateTime.now();
|
||||
final yearMonth = '${now.year}${now.month.toString().padLeft(2, '0')}';
|
||||
if (mounted) setState(() => _estimateNumber = '$yearMonth-0001');
|
||||
}
|
||||
|
||||
Future<void> _loadProducts() async {
|
||||
try {
|
||||
final ps = await DatabaseHelper.instance.getProducts();
|
||||
if (mounted) setState(() => _products = ps ?? const <Product>[]);
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
/// 商品を検索して見積項目に追加
|
||||
Future<void> searchProduct(String keyword) async {
|
||||
if (!mounted || keyword.isEmpty || keyword.contains(' ')) return;
|
||||
|
||||
final keywordLower = keyword.toLowerCase();
|
||||
final matchedProducts = _products.where((p) =>
|
||||
(p.name?.toLowerCase() ?? '').contains(keywordLower) ||
|
||||
(p.productCode ?? '').contains(keyword)).toList();
|
||||
|
||||
if (matchedProducts.isEmpty) return;
|
||||
|
||||
// 最初の一致する商品を追加
|
||||
final product = matchedProducts.first;
|
||||
final existingItemIndex = _estimateItems.indexWhere((item) => item.productId == product.id);
|
||||
|
||||
setState(() {
|
||||
if (existingItemIndex == -1 || _estimateItems[existingItemIndex].quantity < 50) {
|
||||
_estimateItems.add(_EstimateItem(
|
||||
productId: product.id ?? 0,
|
||||
productName: product.name ?? '',
|
||||
productCode: product.productCode ?? '',
|
||||
unitPrice: product.unitPrice ?? 0.0,
|
||||
quantity: 1,
|
||||
totalAmount: (product.unitPrice ?? 0.0),
|
||||
));
|
||||
} else if (existingItemIndex != -1) {
|
||||
_estimateItems[existingItemIndex].quantity += 1;
|
||||
_estimateItems[existingItemIndex].totalAmount =
|
||||
_estimateItems[existingItemIndex].unitPrice * _estimateItems[existingItemIndex].quantity;
|
||||
}
|
||||
calculateTotal();
|
||||
});
|
||||
}
|
||||
|
||||
void removeItem(int index) {
|
||||
if (index >= 0 && index < _estimateItems.length) {
|
||||
_estimateItems.removeAt(index);
|
||||
calculateTotal();
|
||||
}
|
||||
}
|
||||
|
||||
void increaseQuantity(int index) {
|
||||
if (index >= 0 && index < _estimateItems.length) {
|
||||
final item = _estimateItems[index];
|
||||
if (item.quantity < 50) { // 1 セルで最大 50 件
|
||||
item.quantity += 1;
|
||||
item.totalAmount = item.unitPrice * item.quantity;
|
||||
calculateTotal();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void decreaseQuantity(int index) {
|
||||
if (index >= 0 && index < _estimateItems.length && _estimateItems[index].quantity > 1) {
|
||||
_estimateItems[index].quantity -= 1;
|
||||
_estimateItems[index].totalAmount = _estimateItems[index].unitPrice * _estimateItems[index].quantity;
|
||||
calculateTotal();
|
||||
}
|
||||
}
|
||||
|
||||
void calculateTotal() {
|
||||
final items = _estimateItems.map((item) => item.totalAmount).toList();
|
||||
if (mounted) setState(() => _totalAmount = items.fold(0.0, (sum, val) => sum + val));
|
||||
}
|
||||
|
||||
Future<void> saveEstimate() async {
|
||||
if (_estimateItems.isEmpty || !_selectedCustomer!.customerCode.isNotEmpty) return;
|
||||
|
||||
try {
|
||||
// Map にデータ構築
|
||||
final estimateData = <String, dynamic>{
|
||||
'customer_code': _selectedCustomer!.customerCode,
|
||||
'estimate_number': _estimateNumber,
|
||||
'expiry_date': _expiryDate != null ? DateFormat('yyyy-MM-dd').format(_expiryDate!) : null,
|
||||
'total_amount': _totalAmount.round(),
|
||||
'tax_rate': _selectedCustomer!.taxRate ?? 8,
|
||||
'product_items': _estimateItems.map((item) {
|
||||
return <String, dynamic>{
|
||||
'productId': item.productId,
|
||||
'productName': item.productName,
|
||||
'unitPrice': item.unitPrice.round(),
|
||||
'quantity': item.quantity,
|
||||
};
|
||||
}).toList(),
|
||||
};
|
||||
|
||||
await DatabaseHelper.instance.insertEstimate(estimateData);
|
||||
|
||||
if (mounted) ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('見積書保存完了'), duration: Duration(seconds: 2)),
|
||||
);
|
||||
} catch (e) {
|
||||
if (mounted) ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('保存エラー:$e'), backgroundColor: Colors.red),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('見積書')),
|
||||
body: _selectedCustomer == null
|
||||
? const Center(child: Text('得意先を選択してください'))
|
||||
appBar: AppBar(
|
||||
title: const Text('見積書'),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.save),
|
||||
onPressed: _selectedCustomer != null ? saveEstimate : null,
|
||||
),
|
||||
],
|
||||
),
|
||||
body: _selectedCustomer == null || _estimateItems.isEmpty
|
||||
? Center(child: Text('得意先を選択し、商品を検索して見積書を作成'))
|
||||
: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
// 見積有効期限設定エリア(簡易)
|
||||
ListTile(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
// 見積書番号表示
|
||||
ListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
title: const Text('見積書番号'),
|
||||
subtitle: Text(_estimateNumber),
|
||||
),
|
||||
|
||||
const Divider(height: 24),
|
||||
|
||||
// 得意先情報表示
|
||||
Card(
|
||||
child: ListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
title: const Text('見積有効期限'),
|
||||
subtitle: _expiryDate != null ? Text('${_expiryDate!.day}/${_expiryDate!.month}') : const Text('-'),
|
||||
trailing: IconButton(icon: const Icon(Icons.calendar_today), onPressed: () {
|
||||
// TODO: デイティピッカーの実装
|
||||
}),
|
||||
title: const Text('得意先'),
|
||||
subtitle: Text(_selectedCustomer!.name),
|
||||
trailing: IconButton(icon: const Icon(Icons.person), onPressed: () => _showCustomerSelector()),
|
||||
),
|
||||
),
|
||||
|
||||
const Divider(),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// 合計金額表示(簡易)
|
||||
Card(
|
||||
child: ListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
title: const Text('見積書合計'),
|
||||
subtitle: const Text('¥0.00'),
|
||||
trailing: IconButton(icon: const Icon(Icons.edit), onPressed: () {}),
|
||||
// 有効期限設定
|
||||
Card(
|
||||
child: ListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
title: Text(_expiryDate != null ? '見積有効期限' : '見積有効期限(未設定)'),
|
||||
subtitle: _expiryDate != null
|
||||
? Text(DateFormat('yyyy/MM/dd').format(_expiryDate!))
|
||||
: const Text('-'),
|
||||
trailing: IconButton(icon: const Icon(Icons.calendar_today), onPressed: () => _showDatePicker()),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// 商品検索エリア
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8),
|
||||
child: TextField(
|
||||
decoration: InputDecoration(
|
||||
labelText: '商品検索',
|
||||
hintText: '商品名または JAN コードを入力',
|
||||
prefixIcon: const Icon(Icons.search),
|
||||
suffixIcon: IconButton(icon: const Icon(Icons.clear), onPressed: () => searchProduct('')),
|
||||
),
|
||||
onChanged: searchProduct,
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 16),
|
||||
// 見積項目一覧
|
||||
Card(
|
||||
child: _estimateItems.isEmpty
|
||||
? Padding(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: Center(child: Text('商品を登録して見積書を作成')),
|
||||
)
|
||||
: ListView.separated(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemCount: _estimateItems.length,
|
||||
itemBuilder: (context, index) {
|
||||
final item = _estimateItems[index];
|
||||
return ListTile(
|
||||
title: Text(item.productName),
|
||||
subtitle: Text('コード:${item.productCode} / ¥${item.totalAmount.toStringAsFixed(2)} × ${item.quantity}'),
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
IconButton(icon: const Icon(Icons.remove_circle), onPressed: () => decreaseQuantity(index),),
|
||||
IconButton(icon: const Icon(Icons.add_circle), onPressed: () => increaseQuantity(index),),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
separatorBuilder: (_, __) => const Divider(),
|
||||
),
|
||||
),
|
||||
|
||||
// PDF 帳票出力ボタン(簡易)
|
||||
TextButton.icon(
|
||||
onPressed: () {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('PDF 帳票生成中...')),
|
||||
);
|
||||
},
|
||||
icon: const Icon(Icons.download),
|
||||
label: const Text('PDF をダウンロード'),
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// 合計金額表示
|
||||
Card(
|
||||
color: Colors.blue.shade50,
|
||||
child: ListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
title: const Text('見積書合計'),
|
||||
subtitle: Text('¥${_totalAmount.toStringAsFixed(2)}', style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// 保存ボタン
|
||||
ElevatedButton.icon(
|
||||
onPressed: _selectedCustomer != null ? saveEstimate : null,
|
||||
icon: const Icon(Icons.save),
|
||||
label: const Text('見積書を保存'),
|
||||
style: ElevatedButton.styleFrom(padding: const EdgeInsets.all(16)),
|
||||
),
|
||||
|
||||
const SizedBox(height: 12),
|
||||
|
||||
// 詳細表示ボタン(簡易版)
|
||||
OutlinedButton.icon(
|
||||
onPressed: _estimateItems.isNotEmpty ? () => _showSummary() : null,
|
||||
icon: const Icon(Icons.info),
|
||||
label: const Text('見積内容を確認'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showCustomerSelector() {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => StatefulBuilder(
|
||||
builder: (context, setStateDialog) => AlertDialog(
|
||||
title: const Text('得意先を選択'),
|
||||
content: SizedBox(
|
||||
width: double.maxFinite,
|
||||
child: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: _customers.length,
|
||||
itemBuilder: (context, index) {
|
||||
final customer = _customers[index];
|
||||
return ListTile(
|
||||
title: Text(customer.name),
|
||||
subtitle: Text('${customer.customerCode} / TEL:${customer.phoneNumber}'),
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_selectedCustomer = customer;
|
||||
if (_expiryDate != null) {
|
||||
final yearMonth = '${_expiryDate!.year}${_expiryDate!.month.toString().padLeft(2, '0')}';
|
||||
_estimateNumber = '$yearMonth-0001';
|
||||
}
|
||||
});
|
||||
Navigator.pop(context);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
actions: [TextButton(onPressed: () => Navigator.pop(context), child: const Text('キャンセル'))],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showDatePicker() {
|
||||
showDialog<bool>(
|
||||
context: context,
|
||||
builder: (context) => DatePickerDialog(initialDate: _expiryDate ?? DateTime.now().add(const Duration(days: 30))),
|
||||
);
|
||||
}
|
||||
|
||||
void _showSummary() {
|
||||
if (_estimateItems.isEmpty) return;
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('見積書概要'),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('見積書番号:$_estimateNumber'),
|
||||
const SizedBox(height: 8),
|
||||
Text('得意先:${_selectedCustomer?.name ?? '未指定'}'),
|
||||
const SizedBox(height: 8),
|
||||
Text('合計金額:¥${_totalAmount.toStringAsFixed(2)}'),
|
||||
if (_expiryDate != null) Text('有効期限:${DateFormat('yyyy/MM/dd').format(_expiryDate!)}'),
|
||||
],
|
||||
),
|
||||
actions: [TextButton(onPressed: () => Navigator.pop(context), child: const Text('閉じる'))],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _EstimateItem {
|
||||
final int productId;
|
||||
final String productName;
|
||||
final String productCode;
|
||||
double unitPrice;
|
||||
int quantity;
|
||||
double totalAmount;
|
||||
|
||||
_EstimateItem({
|
||||
required this.productId,
|
||||
required this.productName,
|
||||
required this.productCode,
|
||||
required this.unitPrice,
|
||||
required this.quantity,
|
||||
required this.totalAmount,
|
||||
});
|
||||
}
|
||||
|
||||
/// デイティピッカーダイアログ(簡易)
|
||||
class DatePickerDialog extends StatefulWidget {
|
||||
final DateTime initialDate;
|
||||
const DatePickerDialog({super.key, required this.initialDate});
|
||||
|
||||
@override
|
||||
State<DatePickerDialog> createState() => _DatePickerDialogState();
|
||||
}
|
||||
|
||||
class _DatePickerDialogState extends State<DatePickerDialog> {
|
||||
DateTime _selectedDate = DateTime.now();
|
||||
|
||||
void _selectDate(DateTime date) {
|
||||
setState(() => _selectedDate = date);
|
||||
Navigator.pop(context, true);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: const Text('見積有効期限を選択'),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ListTile(
|
||||
leading: const Icon(Icons.calendar_today),
|
||||
title: const Text('今日から 30 日後'),
|
||||
onTap: () => _selectDate(DateTime.now().add(const Duration(days: 30))),
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.access_time),
|
||||
title: const Text('1 ヶ月後(約 30 日)'),
|
||||
onTap: () => _selectDate(DateTime.now().add(const Duration(days: 30))),
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.info_outline),
|
||||
title: const Text('カスタム日付(簡易:未実装)'),
|
||||
subtitle: const Text('デフォルト:30 日後'),
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(onPressed: () => Navigator.pop(context, false), child: const Text('キャンセル')),
|
||||
ElevatedButton(
|
||||
onPressed: () => _selectDate(DateTime.now().add(const Duration(days: 30))),
|
||||
child: const Text('標準(30 日後)'),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,9 @@
|
|||
import 'package:sqflite/sqflite.dart';
|
||||
import 'package:path/path.dart';
|
||||
import 'dart:convert';
|
||||
import '../models/customer.dart';
|
||||
import '../models/product.dart';
|
||||
import '../models/estimate.dart';
|
||||
|
||||
class DatabaseHelper {
|
||||
static final DatabaseHelper instance = DatabaseHelper._init();
|
||||
|
|
@ -32,11 +34,13 @@ class DatabaseHelper {
|
|||
await db.execute('CREATE TABLE suppliers (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, address TEXT, phone_number TEXT, created_at TEXT NOT NULL, updated_at TEXT NOT NULL)');
|
||||
await db.execute('CREATE TABLE products (id INTEGER PRIMARY KEY AUTOINCREMENT, product_code TEXT NOT NULL, name TEXT NOT NULL, unit_price INTEGER NOT NULL, quantity INTEGER DEFAULT 0, stock INTEGER DEFAULT 0, created_at TEXT NOT NULL, updated_at TEXT NOT NULL)');
|
||||
await db.execute('CREATE TABLE sales (id INTEGER PRIMARY KEY AUTOINCREMENT, customer_id INTEGER NOT NULL, sale_date TEXT NOT NULL, total_amount INTEGER NOT NULL, tax_rate INTEGER DEFAULT 8, product_items TEXT, created_at TEXT NOT NULL, updated_at TEXT NOT NULL)');
|
||||
await db.execute('CREATE TABLE estimates (id INTEGER PRIMARY KEY AUTOINCREMENT, customer_id INTEGER NOT NULL, estimate_number TEXT NOT NULL, product_items TEXT, total_amount INTEGER NOT NULL, tax_rate INTEGER DEFAULT 8, status TEXT DEFAULT "open", expiry_date TEXT, created_at TEXT NOT NULL, updated_at TEXT NOT NULL)');
|
||||
await db.execute('CREATE TABLE estimates (id INTEGER PRIMARY KEY AUTOINCREMENT, customer_code TEXT NOT NULL, estimate_number TEXT NOT NULL, product_items TEXT, total_amount INTEGER NOT NULL, tax_rate INTEGER DEFAULT 8, status TEXT DEFAULT "open", expiry_date TEXT, created_at TEXT NOT NULL, updated_at TEXT NOT NULL)');
|
||||
await db.execute('CREATE TABLE inventory (id INTEGER PRIMARY KEY AUTOINCREMENT, product_code TEXT UNIQUE NOT NULL, name TEXT NOT NULL, unit_price INTEGER NOT NULL, stock INTEGER DEFAULT 0, min_stock INTEGER DEFAULT 0, max_stock INTEGER DEFAULT 1000, supplier_name TEXT, created_at TEXT NOT NULL, updated_at TEXT NOT NULL)');
|
||||
await db.execute('CREATE TABLE invoices (id INTEGER PRIMARY KEY AUTOINCREMENT, customer_code TEXT NOT NULL, invoice_number TEXT NOT NULL, sale_date TEXT NOT NULL, total_amount INTEGER NOT NULL, tax_rate INTEGER DEFAULT 8, status TEXT DEFAULT "paid", product_items TEXT, created_at TEXT NOT NULL, updated_at TEXT NOT NULL)');
|
||||
print('Database created with version: 1');
|
||||
}
|
||||
|
||||
// Customer API
|
||||
Future<int> insertCustomer(Customer customer) async {
|
||||
final db = await database;
|
||||
return await db.insert('customers', customer.toMap());
|
||||
|
|
@ -57,12 +61,7 @@ class DatabaseHelper {
|
|||
|
||||
Future<int> updateCustomer(Customer customer) async {
|
||||
final db = await database;
|
||||
return await db.update(
|
||||
'customers',
|
||||
customer.toMap(),
|
||||
where: 'id = ?',
|
||||
whereArgs: [customer.id],
|
||||
);
|
||||
return await db.update('customers', customer.toMap(), where: 'id = ?', whereArgs: [customer.id]);
|
||||
}
|
||||
|
||||
Future<int> deleteCustomer(int id) async {
|
||||
|
|
@ -70,6 +69,7 @@ class DatabaseHelper {
|
|||
return await db.delete('customers', where: 'id = ?', whereArgs: [id]);
|
||||
}
|
||||
|
||||
// Product API
|
||||
Future<int> insertProduct(Product product) async {
|
||||
final db = await database;
|
||||
return await db.insert('products', product.toMap());
|
||||
|
|
@ -90,12 +90,7 @@ class DatabaseHelper {
|
|||
|
||||
Future<int> updateProduct(Product product) async {
|
||||
final db = await database;
|
||||
return await db.update(
|
||||
'products',
|
||||
product.toMap(),
|
||||
where: 'id = ?',
|
||||
whereArgs: [product.id],
|
||||
);
|
||||
return await db.update('products', product.toMap(), where: 'id = ?', whereArgs: [product.id]);
|
||||
}
|
||||
|
||||
Future<int> deleteProduct(int id) async {
|
||||
|
|
@ -103,37 +98,9 @@ class DatabaseHelper {
|
|||
return await db.delete('products', where: 'id = ?', whereArgs: [id]);
|
||||
}
|
||||
|
||||
String encodeToJson(Object? data) {
|
||||
try {
|
||||
if (data == null) return '';
|
||||
if (data is String) return data;
|
||||
final json = StringBuffer('{');
|
||||
var first = true;
|
||||
if (data is Map) {
|
||||
for (var key in data.keys) {
|
||||
if (!first) json.write(',');
|
||||
first = false;
|
||||
json.write('"${key}":"${data[key]}"');
|
||||
}
|
||||
} else if (data is List) {
|
||||
for (var item in data) {
|
||||
if (!first) json.write(',');
|
||||
first = false;
|
||||
json.write('{"val":"$item"}');
|
||||
}
|
||||
}
|
||||
json.write('}');
|
||||
return json.toString();
|
||||
} catch (e) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
// Sales API
|
||||
Future<int> insertSales(Map<String, dynamic> salesData) async {
|
||||
final db = await database;
|
||||
if (salesData['product_items'] != null && salesData['product_items'] is List) {
|
||||
salesData['product_items'] = encodeToJson(salesData['product_items']);
|
||||
}
|
||||
return await db.insert('sales', salesData);
|
||||
}
|
||||
|
||||
|
|
@ -144,15 +111,7 @@ class DatabaseHelper {
|
|||
|
||||
Future<int> updateSales(Map<String, dynamic> salesData) async {
|
||||
final db = await database;
|
||||
if (salesData['product_items'] != null && salesData['product_items'] is List) {
|
||||
salesData['product_items'] = encodeToJson(salesData['product_items']);
|
||||
}
|
||||
return await db.update(
|
||||
'sales',
|
||||
salesData,
|
||||
where: 'id = ?',
|
||||
whereArgs: [salesData['id'] as int],
|
||||
);
|
||||
return await db.update('sales', salesData, where: 'id = ?', whereArgs: [salesData['id'] as int]);
|
||||
}
|
||||
|
||||
Future<int> deleteSales(int id) async {
|
||||
|
|
@ -160,11 +119,9 @@ class DatabaseHelper {
|
|||
return await db.delete('sales', where: 'id = ?', whereArgs: [id]);
|
||||
}
|
||||
|
||||
// Estimate API(単純化)
|
||||
Future<int> insertEstimate(Map<String, dynamic> estimateData) async {
|
||||
final db = await database;
|
||||
if (estimateData['product_items'] != null && estimateData['product_items'] is List) {
|
||||
estimateData['product_items'] = encodeToJson(estimateData['product_items']);
|
||||
}
|
||||
return await db.insert('estimates', estimateData);
|
||||
}
|
||||
|
||||
|
|
@ -175,15 +132,7 @@ class DatabaseHelper {
|
|||
|
||||
Future<int> updateEstimate(Map<String, dynamic> estimateData) async {
|
||||
final db = await database;
|
||||
if (estimateData['product_items'] != null && estimateData['product_items'] is List) {
|
||||
estimateData['product_items'] = encodeToJson(estimateData['product_items']);
|
||||
}
|
||||
return await db.update(
|
||||
'estimates',
|
||||
estimateData,
|
||||
where: 'id = ?',
|
||||
whereArgs: [estimateData['id'] as int],
|
||||
);
|
||||
return await db.update('estimates', estimateData, where: 'id = ?', whereArgs: [estimateData['id'] as int]);
|
||||
}
|
||||
|
||||
Future<int> deleteEstimate(int id) async {
|
||||
|
|
@ -191,6 +140,28 @@ class DatabaseHelper {
|
|||
return await db.delete('estimates', where: 'id = ?', whereArgs: [id]);
|
||||
}
|
||||
|
||||
// Invoice API
|
||||
Future<int> insertInvoice(Map<String, dynamic> invoiceData) async {
|
||||
final db = await database;
|
||||
return await db.insert('invoices', invoiceData);
|
||||
}
|
||||
|
||||
Future<List<Map<String, dynamic>>> getInvoices() async {
|
||||
final db = await database;
|
||||
return await db.query('invoices');
|
||||
}
|
||||
|
||||
Future<int> updateInvoice(Map<String, dynamic> invoiceData) async {
|
||||
final db = await database;
|
||||
return await db.update('invoices', invoiceData, where: 'id = ?', whereArgs: [invoiceData['id'] as int]);
|
||||
}
|
||||
|
||||
Future<int> deleteInvoice(int id) async {
|
||||
final db = await database;
|
||||
return await db.delete('invoices', where: 'id = ?', whereArgs: [id]);
|
||||
}
|
||||
|
||||
// Inventory API
|
||||
Future<int> insertInventory(Map<String, dynamic> inventoryData) async {
|
||||
final db = await database;
|
||||
return await db.insert('inventory', inventoryData);
|
||||
|
|
@ -203,12 +174,7 @@ class DatabaseHelper {
|
|||
|
||||
Future<int> updateInventory(Map<String, dynamic> inventoryData) async {
|
||||
final db = await database;
|
||||
return await db.update(
|
||||
'inventory',
|
||||
inventoryData,
|
||||
where: 'id = ?',
|
||||
whereArgs: [inventoryData['id'] as int],
|
||||
);
|
||||
return await db.update('inventory', inventoryData, where: 'id = ?', whereArgs: [inventoryData['id'] as int]);
|
||||
}
|
||||
|
||||
Future<int> deleteInventory(int id) async {
|
||||
|
|
@ -216,6 +182,7 @@ class DatabaseHelper {
|
|||
return await db.delete('inventory', where: 'id = ?', whereArgs: [id]);
|
||||
}
|
||||
|
||||
// Employee API
|
||||
Future<int> insertEmployee(Map<String, dynamic> employeeData) async {
|
||||
final db = await database;
|
||||
return await db.insert('employees', employeeData);
|
||||
|
|
@ -228,12 +195,7 @@ class DatabaseHelper {
|
|||
|
||||
Future<int> updateEmployee(Map<String, dynamic> employeeData) async {
|
||||
final db = await database;
|
||||
return await db.update(
|
||||
'employees',
|
||||
employeeData,
|
||||
where: 'id = ?',
|
||||
whereArgs: [employeeData['id'] as int],
|
||||
);
|
||||
return await db.update('employees', employeeData, where: 'id = ?', whereArgs: [employeeData['id'] as int]);
|
||||
}
|
||||
|
||||
Future<int> deleteEmployee(int id) async {
|
||||
|
|
@ -241,6 +203,7 @@ class DatabaseHelper {
|
|||
return await db.delete('employees', where: 'id = ?', whereArgs: [id]);
|
||||
}
|
||||
|
||||
// Warehouse API
|
||||
Future<int> insertWarehouse(Map<String, dynamic> warehouseData) async {
|
||||
final db = await database;
|
||||
return await db.insert('warehouses', warehouseData);
|
||||
|
|
@ -253,12 +216,7 @@ class DatabaseHelper {
|
|||
|
||||
Future<int> updateWarehouse(Map<String, dynamic> warehouseData) async {
|
||||
final db = await database;
|
||||
return await db.update(
|
||||
'warehouses',
|
||||
warehouseData,
|
||||
where: 'id = ?',
|
||||
whereArgs: [warehouseData['id'] as int],
|
||||
);
|
||||
return await db.update('warehouses', warehouseData, where: 'id = ?', whereArgs: [warehouseData['id'] as int]);
|
||||
}
|
||||
|
||||
Future<int> deleteWarehouse(int id) async {
|
||||
|
|
@ -266,6 +224,7 @@ class DatabaseHelper {
|
|||
return await db.delete('warehouses', where: 'id = ?', whereArgs: [id]);
|
||||
}
|
||||
|
||||
// Supplier API
|
||||
Future<int> insertSupplier(Map<String, dynamic> supplierData) async {
|
||||
final db = await database;
|
||||
return await db.insert('suppliers', supplierData);
|
||||
|
|
@ -278,12 +237,7 @@ class DatabaseHelper {
|
|||
|
||||
Future<int> updateSupplier(Map<String, dynamic> supplierData) async {
|
||||
final db = await database;
|
||||
return await db.update(
|
||||
'suppliers',
|
||||
supplierData,
|
||||
where: 'id = ?',
|
||||
whereArgs: [supplierData['id'] as int],
|
||||
);
|
||||
return await db.update('suppliers', supplierData, where: 'id = ?', whereArgs: [supplierData['id'] as int]);
|
||||
}
|
||||
|
||||
Future<int> deleteSupplier(int id) async {
|
||||
|
|
|
|||
Loading…
Reference in a new issue