feat: make server owner of tabs
This commit is contained in:
@@ -162,6 +162,9 @@ class RemoteSyncState extends Table {
|
||||
|
||||
TextColumn get serverCursor => text().nullable()();
|
||||
|
||||
/// Where the feed of settlements made anywhere in the venue left off.
|
||||
TextColumn get settlementsCursor => text().nullable()();
|
||||
|
||||
DateTimeColumn get lastSuccessfulSyncAt => dateTime().nullable()();
|
||||
|
||||
DateTimeColumn get lastAttemptedSyncAt => dateTime().nullable()();
|
||||
@@ -204,6 +207,26 @@ class TabEvents extends Table {
|
||||
Set<Column> get primaryKey => {id};
|
||||
}
|
||||
|
||||
/// Outcomes of the stock adjustments sent by the dashboard, waiting to be
|
||||
/// reported. A row also records that its adjustment was handled, so it is
|
||||
/// kept for a while after the server acknowledges it: the same adjustment
|
||||
/// can still arrive in a response computed before the server saw the outcome.
|
||||
@DataClassName('StockAdjustmentResultRow')
|
||||
class StockAdjustmentResults extends Table {
|
||||
/// The adjustment's id from the server.
|
||||
TextColumn get id => text()();
|
||||
|
||||
/// The outcome as JSON, exactly as it is sent.
|
||||
TextColumn get payload => text()();
|
||||
|
||||
BoolColumn get acknowledged => boolean().withDefault(const Constant(false))();
|
||||
|
||||
DateTimeColumn get createdAt => dateTime()();
|
||||
|
||||
@override
|
||||
Set<Column> get primaryKey => {id};
|
||||
}
|
||||
|
||||
@DriftDatabase(
|
||||
tables: [
|
||||
Products,
|
||||
@@ -216,13 +239,14 @@ class TabEvents extends Table {
|
||||
RemoteSyncState,
|
||||
ProcessedRemoteCommands,
|
||||
TabEvents,
|
||||
StockAdjustmentResults,
|
||||
],
|
||||
)
|
||||
class AppDatabase extends _$AppDatabase {
|
||||
AppDatabase([QueryExecutor? executor]) : super(executor ?? _openConnection());
|
||||
|
||||
@override
|
||||
int get schemaVersion => 15;
|
||||
int get schemaVersion => 17;
|
||||
|
||||
@override
|
||||
MigrationStrategy get migration {
|
||||
@@ -325,6 +349,19 @@ class AppDatabase extends _$AppDatabase {
|
||||
if (from < 15) {
|
||||
await migrator.createTable(tabEvents);
|
||||
}
|
||||
|
||||
if (from < 16) {
|
||||
await migrator.createTable(stockAdjustmentResults);
|
||||
}
|
||||
|
||||
if (from < 17 &&
|
||||
await hasTableColumn('remote_sync_state', 'device_id') &&
|
||||
!await hasTableColumn('remote_sync_state', 'settlements_cursor')) {
|
||||
await migrator.addColumn(
|
||||
remoteSyncState,
|
||||
remoteSyncState.settlementsCursor,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3060,6 +3060,18 @@ class $RemoteSyncStateTable extends RemoteSyncState
|
||||
type: DriftSqlType.string,
|
||||
requiredDuringInsert: false,
|
||||
);
|
||||
static const VerificationMeta _settlementsCursorMeta = const VerificationMeta(
|
||||
'settlementsCursor',
|
||||
);
|
||||
@override
|
||||
late final GeneratedColumn<String> settlementsCursor =
|
||||
GeneratedColumn<String>(
|
||||
'settlements_cursor',
|
||||
aliasedName,
|
||||
true,
|
||||
type: DriftSqlType.string,
|
||||
requiredDuringInsert: false,
|
||||
);
|
||||
static const VerificationMeta _lastSuccessfulSyncAtMeta =
|
||||
const VerificationMeta('lastSuccessfulSyncAt');
|
||||
@override
|
||||
@@ -3098,6 +3110,7 @@ class $RemoteSyncStateTable extends RemoteSyncState
|
||||
List<GeneratedColumn> get $columns => [
|
||||
deviceId,
|
||||
serverCursor,
|
||||
settlementsCursor,
|
||||
lastSuccessfulSyncAt,
|
||||
lastAttemptedSyncAt,
|
||||
consecutiveFailureCount,
|
||||
@@ -3131,6 +3144,15 @@ class $RemoteSyncStateTable extends RemoteSyncState
|
||||
),
|
||||
);
|
||||
}
|
||||
if (data.containsKey('settlements_cursor')) {
|
||||
context.handle(
|
||||
_settlementsCursorMeta,
|
||||
settlementsCursor.isAcceptableOrUnknown(
|
||||
data['settlements_cursor']!,
|
||||
_settlementsCursorMeta,
|
||||
),
|
||||
);
|
||||
}
|
||||
if (data.containsKey('last_successful_sync_at')) {
|
||||
context.handle(
|
||||
_lastSuccessfulSyncAtMeta,
|
||||
@@ -3175,6 +3197,10 @@ class $RemoteSyncStateTable extends RemoteSyncState
|
||||
DriftSqlType.string,
|
||||
data['${effectivePrefix}server_cursor'],
|
||||
),
|
||||
settlementsCursor: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.string,
|
||||
data['${effectivePrefix}settlements_cursor'],
|
||||
),
|
||||
lastSuccessfulSyncAt: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.dateTime,
|
||||
data['${effectivePrefix}last_successful_sync_at'],
|
||||
@@ -3200,12 +3226,16 @@ class RemoteSyncStateRow extends DataClass
|
||||
implements Insertable<RemoteSyncStateRow> {
|
||||
final String deviceId;
|
||||
final String? serverCursor;
|
||||
|
||||
/// Where the feed of settlements made anywhere in the venue left off.
|
||||
final String? settlementsCursor;
|
||||
final DateTime? lastSuccessfulSyncAt;
|
||||
final DateTime? lastAttemptedSyncAt;
|
||||
final int consecutiveFailureCount;
|
||||
const RemoteSyncStateRow({
|
||||
required this.deviceId,
|
||||
this.serverCursor,
|
||||
this.settlementsCursor,
|
||||
this.lastSuccessfulSyncAt,
|
||||
this.lastAttemptedSyncAt,
|
||||
required this.consecutiveFailureCount,
|
||||
@@ -3217,6 +3247,9 @@ class RemoteSyncStateRow extends DataClass
|
||||
if (!nullToAbsent || serverCursor != null) {
|
||||
map['server_cursor'] = Variable<String>(serverCursor);
|
||||
}
|
||||
if (!nullToAbsent || settlementsCursor != null) {
|
||||
map['settlements_cursor'] = Variable<String>(settlementsCursor);
|
||||
}
|
||||
if (!nullToAbsent || lastSuccessfulSyncAt != null) {
|
||||
map['last_successful_sync_at'] = Variable<DateTime>(lastSuccessfulSyncAt);
|
||||
}
|
||||
@@ -3233,6 +3266,9 @@ class RemoteSyncStateRow extends DataClass
|
||||
serverCursor: serverCursor == null && nullToAbsent
|
||||
? const Value.absent()
|
||||
: Value(serverCursor),
|
||||
settlementsCursor: settlementsCursor == null && nullToAbsent
|
||||
? const Value.absent()
|
||||
: Value(settlementsCursor),
|
||||
lastSuccessfulSyncAt: lastSuccessfulSyncAt == null && nullToAbsent
|
||||
? const Value.absent()
|
||||
: Value(lastSuccessfulSyncAt),
|
||||
@@ -3251,6 +3287,9 @@ class RemoteSyncStateRow extends DataClass
|
||||
return RemoteSyncStateRow(
|
||||
deviceId: serializer.fromJson<String>(json['deviceId']),
|
||||
serverCursor: serializer.fromJson<String?>(json['serverCursor']),
|
||||
settlementsCursor: serializer.fromJson<String?>(
|
||||
json['settlementsCursor'],
|
||||
),
|
||||
lastSuccessfulSyncAt: serializer.fromJson<DateTime?>(
|
||||
json['lastSuccessfulSyncAt'],
|
||||
),
|
||||
@@ -3268,6 +3307,7 @@ class RemoteSyncStateRow extends DataClass
|
||||
return <String, dynamic>{
|
||||
'deviceId': serializer.toJson<String>(deviceId),
|
||||
'serverCursor': serializer.toJson<String?>(serverCursor),
|
||||
'settlementsCursor': serializer.toJson<String?>(settlementsCursor),
|
||||
'lastSuccessfulSyncAt': serializer.toJson<DateTime?>(
|
||||
lastSuccessfulSyncAt,
|
||||
),
|
||||
@@ -3281,12 +3321,16 @@ class RemoteSyncStateRow extends DataClass
|
||||
RemoteSyncStateRow copyWith({
|
||||
String? deviceId,
|
||||
Value<String?> serverCursor = const Value.absent(),
|
||||
Value<String?> settlementsCursor = const Value.absent(),
|
||||
Value<DateTime?> lastSuccessfulSyncAt = const Value.absent(),
|
||||
Value<DateTime?> lastAttemptedSyncAt = const Value.absent(),
|
||||
int? consecutiveFailureCount,
|
||||
}) => RemoteSyncStateRow(
|
||||
deviceId: deviceId ?? this.deviceId,
|
||||
serverCursor: serverCursor.present ? serverCursor.value : this.serverCursor,
|
||||
settlementsCursor: settlementsCursor.present
|
||||
? settlementsCursor.value
|
||||
: this.settlementsCursor,
|
||||
lastSuccessfulSyncAt: lastSuccessfulSyncAt.present
|
||||
? lastSuccessfulSyncAt.value
|
||||
: this.lastSuccessfulSyncAt,
|
||||
@@ -3302,6 +3346,9 @@ class RemoteSyncStateRow extends DataClass
|
||||
serverCursor: data.serverCursor.present
|
||||
? data.serverCursor.value
|
||||
: this.serverCursor,
|
||||
settlementsCursor: data.settlementsCursor.present
|
||||
? data.settlementsCursor.value
|
||||
: this.settlementsCursor,
|
||||
lastSuccessfulSyncAt: data.lastSuccessfulSyncAt.present
|
||||
? data.lastSuccessfulSyncAt.value
|
||||
: this.lastSuccessfulSyncAt,
|
||||
@@ -3319,6 +3366,7 @@ class RemoteSyncStateRow extends DataClass
|
||||
return (StringBuffer('RemoteSyncStateRow(')
|
||||
..write('deviceId: $deviceId, ')
|
||||
..write('serverCursor: $serverCursor, ')
|
||||
..write('settlementsCursor: $settlementsCursor, ')
|
||||
..write('lastSuccessfulSyncAt: $lastSuccessfulSyncAt, ')
|
||||
..write('lastAttemptedSyncAt: $lastAttemptedSyncAt, ')
|
||||
..write('consecutiveFailureCount: $consecutiveFailureCount')
|
||||
@@ -3330,6 +3378,7 @@ class RemoteSyncStateRow extends DataClass
|
||||
int get hashCode => Object.hash(
|
||||
deviceId,
|
||||
serverCursor,
|
||||
settlementsCursor,
|
||||
lastSuccessfulSyncAt,
|
||||
lastAttemptedSyncAt,
|
||||
consecutiveFailureCount,
|
||||
@@ -3340,6 +3389,7 @@ class RemoteSyncStateRow extends DataClass
|
||||
(other is RemoteSyncStateRow &&
|
||||
other.deviceId == this.deviceId &&
|
||||
other.serverCursor == this.serverCursor &&
|
||||
other.settlementsCursor == this.settlementsCursor &&
|
||||
other.lastSuccessfulSyncAt == this.lastSuccessfulSyncAt &&
|
||||
other.lastAttemptedSyncAt == this.lastAttemptedSyncAt &&
|
||||
other.consecutiveFailureCount == this.consecutiveFailureCount);
|
||||
@@ -3348,6 +3398,7 @@ class RemoteSyncStateRow extends DataClass
|
||||
class RemoteSyncStateCompanion extends UpdateCompanion<RemoteSyncStateRow> {
|
||||
final Value<String> deviceId;
|
||||
final Value<String?> serverCursor;
|
||||
final Value<String?> settlementsCursor;
|
||||
final Value<DateTime?> lastSuccessfulSyncAt;
|
||||
final Value<DateTime?> lastAttemptedSyncAt;
|
||||
final Value<int> consecutiveFailureCount;
|
||||
@@ -3355,6 +3406,7 @@ class RemoteSyncStateCompanion extends UpdateCompanion<RemoteSyncStateRow> {
|
||||
const RemoteSyncStateCompanion({
|
||||
this.deviceId = const Value.absent(),
|
||||
this.serverCursor = const Value.absent(),
|
||||
this.settlementsCursor = const Value.absent(),
|
||||
this.lastSuccessfulSyncAt = const Value.absent(),
|
||||
this.lastAttemptedSyncAt = const Value.absent(),
|
||||
this.consecutiveFailureCount = const Value.absent(),
|
||||
@@ -3363,6 +3415,7 @@ class RemoteSyncStateCompanion extends UpdateCompanion<RemoteSyncStateRow> {
|
||||
RemoteSyncStateCompanion.insert({
|
||||
required String deviceId,
|
||||
this.serverCursor = const Value.absent(),
|
||||
this.settlementsCursor = const Value.absent(),
|
||||
this.lastSuccessfulSyncAt = const Value.absent(),
|
||||
this.lastAttemptedSyncAt = const Value.absent(),
|
||||
this.consecutiveFailureCount = const Value.absent(),
|
||||
@@ -3371,6 +3424,7 @@ class RemoteSyncStateCompanion extends UpdateCompanion<RemoteSyncStateRow> {
|
||||
static Insertable<RemoteSyncStateRow> custom({
|
||||
Expression<String>? deviceId,
|
||||
Expression<String>? serverCursor,
|
||||
Expression<String>? settlementsCursor,
|
||||
Expression<DateTime>? lastSuccessfulSyncAt,
|
||||
Expression<DateTime>? lastAttemptedSyncAt,
|
||||
Expression<int>? consecutiveFailureCount,
|
||||
@@ -3379,6 +3433,7 @@ class RemoteSyncStateCompanion extends UpdateCompanion<RemoteSyncStateRow> {
|
||||
return RawValuesInsertable({
|
||||
if (deviceId != null) 'device_id': deviceId,
|
||||
if (serverCursor != null) 'server_cursor': serverCursor,
|
||||
if (settlementsCursor != null) 'settlements_cursor': settlementsCursor,
|
||||
if (lastSuccessfulSyncAt != null)
|
||||
'last_successful_sync_at': lastSuccessfulSyncAt,
|
||||
if (lastAttemptedSyncAt != null)
|
||||
@@ -3392,6 +3447,7 @@ class RemoteSyncStateCompanion extends UpdateCompanion<RemoteSyncStateRow> {
|
||||
RemoteSyncStateCompanion copyWith({
|
||||
Value<String>? deviceId,
|
||||
Value<String?>? serverCursor,
|
||||
Value<String?>? settlementsCursor,
|
||||
Value<DateTime?>? lastSuccessfulSyncAt,
|
||||
Value<DateTime?>? lastAttemptedSyncAt,
|
||||
Value<int>? consecutiveFailureCount,
|
||||
@@ -3400,6 +3456,7 @@ class RemoteSyncStateCompanion extends UpdateCompanion<RemoteSyncStateRow> {
|
||||
return RemoteSyncStateCompanion(
|
||||
deviceId: deviceId ?? this.deviceId,
|
||||
serverCursor: serverCursor ?? this.serverCursor,
|
||||
settlementsCursor: settlementsCursor ?? this.settlementsCursor,
|
||||
lastSuccessfulSyncAt: lastSuccessfulSyncAt ?? this.lastSuccessfulSyncAt,
|
||||
lastAttemptedSyncAt: lastAttemptedSyncAt ?? this.lastAttemptedSyncAt,
|
||||
consecutiveFailureCount:
|
||||
@@ -3417,6 +3474,9 @@ class RemoteSyncStateCompanion extends UpdateCompanion<RemoteSyncStateRow> {
|
||||
if (serverCursor.present) {
|
||||
map['server_cursor'] = Variable<String>(serverCursor.value);
|
||||
}
|
||||
if (settlementsCursor.present) {
|
||||
map['settlements_cursor'] = Variable<String>(settlementsCursor.value);
|
||||
}
|
||||
if (lastSuccessfulSyncAt.present) {
|
||||
map['last_successful_sync_at'] = Variable<DateTime>(
|
||||
lastSuccessfulSyncAt.value,
|
||||
@@ -3443,6 +3503,7 @@ class RemoteSyncStateCompanion extends UpdateCompanion<RemoteSyncStateRow> {
|
||||
return (StringBuffer('RemoteSyncStateCompanion(')
|
||||
..write('deviceId: $deviceId, ')
|
||||
..write('serverCursor: $serverCursor, ')
|
||||
..write('settlementsCursor: $settlementsCursor, ')
|
||||
..write('lastSuccessfulSyncAt: $lastSuccessfulSyncAt, ')
|
||||
..write('lastAttemptedSyncAt: $lastAttemptedSyncAt, ')
|
||||
..write('consecutiveFailureCount: $consecutiveFailureCount, ')
|
||||
@@ -4060,6 +4121,332 @@ class TabEventsCompanion extends UpdateCompanion<TabEventRow> {
|
||||
}
|
||||
}
|
||||
|
||||
class $StockAdjustmentResultsTable extends StockAdjustmentResults
|
||||
with TableInfo<$StockAdjustmentResultsTable, StockAdjustmentResultRow> {
|
||||
@override
|
||||
final GeneratedDatabase attachedDatabase;
|
||||
final String? _alias;
|
||||
$StockAdjustmentResultsTable(this.attachedDatabase, [this._alias]);
|
||||
static const VerificationMeta _idMeta = const VerificationMeta('id');
|
||||
@override
|
||||
late final GeneratedColumn<String> id = GeneratedColumn<String>(
|
||||
'id',
|
||||
aliasedName,
|
||||
false,
|
||||
type: DriftSqlType.string,
|
||||
requiredDuringInsert: true,
|
||||
);
|
||||
static const VerificationMeta _payloadMeta = const VerificationMeta(
|
||||
'payload',
|
||||
);
|
||||
@override
|
||||
late final GeneratedColumn<String> payload = GeneratedColumn<String>(
|
||||
'payload',
|
||||
aliasedName,
|
||||
false,
|
||||
type: DriftSqlType.string,
|
||||
requiredDuringInsert: true,
|
||||
);
|
||||
static const VerificationMeta _acknowledgedMeta = const VerificationMeta(
|
||||
'acknowledged',
|
||||
);
|
||||
@override
|
||||
late final GeneratedColumn<bool> acknowledged = GeneratedColumn<bool>(
|
||||
'acknowledged',
|
||||
aliasedName,
|
||||
false,
|
||||
type: DriftSqlType.bool,
|
||||
requiredDuringInsert: false,
|
||||
defaultConstraints: GeneratedColumn.constraintIsAlways(
|
||||
'CHECK ("acknowledged" IN (0, 1))',
|
||||
),
|
||||
defaultValue: const Constant(false),
|
||||
);
|
||||
static const VerificationMeta _createdAtMeta = const VerificationMeta(
|
||||
'createdAt',
|
||||
);
|
||||
@override
|
||||
late final GeneratedColumn<DateTime> createdAt = GeneratedColumn<DateTime>(
|
||||
'created_at',
|
||||
aliasedName,
|
||||
false,
|
||||
type: DriftSqlType.dateTime,
|
||||
requiredDuringInsert: true,
|
||||
);
|
||||
@override
|
||||
List<GeneratedColumn> get $columns => [id, payload, acknowledged, createdAt];
|
||||
@override
|
||||
String get aliasedName => _alias ?? actualTableName;
|
||||
@override
|
||||
String get actualTableName => $name;
|
||||
static const String $name = 'stock_adjustment_results';
|
||||
@override
|
||||
VerificationContext validateIntegrity(
|
||||
Insertable<StockAdjustmentResultRow> instance, {
|
||||
bool isInserting = false,
|
||||
}) {
|
||||
final context = VerificationContext();
|
||||
final data = instance.toColumns(true);
|
||||
if (data.containsKey('id')) {
|
||||
context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta));
|
||||
} else if (isInserting) {
|
||||
context.missing(_idMeta);
|
||||
}
|
||||
if (data.containsKey('payload')) {
|
||||
context.handle(
|
||||
_payloadMeta,
|
||||
payload.isAcceptableOrUnknown(data['payload']!, _payloadMeta),
|
||||
);
|
||||
} else if (isInserting) {
|
||||
context.missing(_payloadMeta);
|
||||
}
|
||||
if (data.containsKey('acknowledged')) {
|
||||
context.handle(
|
||||
_acknowledgedMeta,
|
||||
acknowledged.isAcceptableOrUnknown(
|
||||
data['acknowledged']!,
|
||||
_acknowledgedMeta,
|
||||
),
|
||||
);
|
||||
}
|
||||
if (data.containsKey('created_at')) {
|
||||
context.handle(
|
||||
_createdAtMeta,
|
||||
createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta),
|
||||
);
|
||||
} else if (isInserting) {
|
||||
context.missing(_createdAtMeta);
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
@override
|
||||
Set<GeneratedColumn> get $primaryKey => {id};
|
||||
@override
|
||||
StockAdjustmentResultRow map(
|
||||
Map<String, dynamic> data, {
|
||||
String? tablePrefix,
|
||||
}) {
|
||||
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
|
||||
return StockAdjustmentResultRow(
|
||||
id: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.string,
|
||||
data['${effectivePrefix}id'],
|
||||
)!,
|
||||
payload: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.string,
|
||||
data['${effectivePrefix}payload'],
|
||||
)!,
|
||||
acknowledged: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.bool,
|
||||
data['${effectivePrefix}acknowledged'],
|
||||
)!,
|
||||
createdAt: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.dateTime,
|
||||
data['${effectivePrefix}created_at'],
|
||||
)!,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
$StockAdjustmentResultsTable createAlias(String alias) {
|
||||
return $StockAdjustmentResultsTable(attachedDatabase, alias);
|
||||
}
|
||||
}
|
||||
|
||||
class StockAdjustmentResultRow extends DataClass
|
||||
implements Insertable<StockAdjustmentResultRow> {
|
||||
/// The adjustment's id from the server.
|
||||
final String id;
|
||||
|
||||
/// The outcome as JSON, exactly as it is sent.
|
||||
final String payload;
|
||||
final bool acknowledged;
|
||||
final DateTime createdAt;
|
||||
const StockAdjustmentResultRow({
|
||||
required this.id,
|
||||
required this.payload,
|
||||
required this.acknowledged,
|
||||
required this.createdAt,
|
||||
});
|
||||
@override
|
||||
Map<String, Expression> toColumns(bool nullToAbsent) {
|
||||
final map = <String, Expression>{};
|
||||
map['id'] = Variable<String>(id);
|
||||
map['payload'] = Variable<String>(payload);
|
||||
map['acknowledged'] = Variable<bool>(acknowledged);
|
||||
map['created_at'] = Variable<DateTime>(createdAt);
|
||||
return map;
|
||||
}
|
||||
|
||||
StockAdjustmentResultsCompanion toCompanion(bool nullToAbsent) {
|
||||
return StockAdjustmentResultsCompanion(
|
||||
id: Value(id),
|
||||
payload: Value(payload),
|
||||
acknowledged: Value(acknowledged),
|
||||
createdAt: Value(createdAt),
|
||||
);
|
||||
}
|
||||
|
||||
factory StockAdjustmentResultRow.fromJson(
|
||||
Map<String, dynamic> json, {
|
||||
ValueSerializer? serializer,
|
||||
}) {
|
||||
serializer ??= driftRuntimeOptions.defaultSerializer;
|
||||
return StockAdjustmentResultRow(
|
||||
id: serializer.fromJson<String>(json['id']),
|
||||
payload: serializer.fromJson<String>(json['payload']),
|
||||
acknowledged: serializer.fromJson<bool>(json['acknowledged']),
|
||||
createdAt: serializer.fromJson<DateTime>(json['createdAt']),
|
||||
);
|
||||
}
|
||||
@override
|
||||
Map<String, dynamic> toJson({ValueSerializer? serializer}) {
|
||||
serializer ??= driftRuntimeOptions.defaultSerializer;
|
||||
return <String, dynamic>{
|
||||
'id': serializer.toJson<String>(id),
|
||||
'payload': serializer.toJson<String>(payload),
|
||||
'acknowledged': serializer.toJson<bool>(acknowledged),
|
||||
'createdAt': serializer.toJson<DateTime>(createdAt),
|
||||
};
|
||||
}
|
||||
|
||||
StockAdjustmentResultRow copyWith({
|
||||
String? id,
|
||||
String? payload,
|
||||
bool? acknowledged,
|
||||
DateTime? createdAt,
|
||||
}) => StockAdjustmentResultRow(
|
||||
id: id ?? this.id,
|
||||
payload: payload ?? this.payload,
|
||||
acknowledged: acknowledged ?? this.acknowledged,
|
||||
createdAt: createdAt ?? this.createdAt,
|
||||
);
|
||||
StockAdjustmentResultRow copyWithCompanion(
|
||||
StockAdjustmentResultsCompanion data,
|
||||
) {
|
||||
return StockAdjustmentResultRow(
|
||||
id: data.id.present ? data.id.value : this.id,
|
||||
payload: data.payload.present ? data.payload.value : this.payload,
|
||||
acknowledged: data.acknowledged.present
|
||||
? data.acknowledged.value
|
||||
: this.acknowledged,
|
||||
createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return (StringBuffer('StockAdjustmentResultRow(')
|
||||
..write('id: $id, ')
|
||||
..write('payload: $payload, ')
|
||||
..write('acknowledged: $acknowledged, ')
|
||||
..write('createdAt: $createdAt')
|
||||
..write(')'))
|
||||
.toString();
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(id, payload, acknowledged, createdAt);
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
(other is StockAdjustmentResultRow &&
|
||||
other.id == this.id &&
|
||||
other.payload == this.payload &&
|
||||
other.acknowledged == this.acknowledged &&
|
||||
other.createdAt == this.createdAt);
|
||||
}
|
||||
|
||||
class StockAdjustmentResultsCompanion
|
||||
extends UpdateCompanion<StockAdjustmentResultRow> {
|
||||
final Value<String> id;
|
||||
final Value<String> payload;
|
||||
final Value<bool> acknowledged;
|
||||
final Value<DateTime> createdAt;
|
||||
final Value<int> rowid;
|
||||
const StockAdjustmentResultsCompanion({
|
||||
this.id = const Value.absent(),
|
||||
this.payload = const Value.absent(),
|
||||
this.acknowledged = const Value.absent(),
|
||||
this.createdAt = const Value.absent(),
|
||||
this.rowid = const Value.absent(),
|
||||
});
|
||||
StockAdjustmentResultsCompanion.insert({
|
||||
required String id,
|
||||
required String payload,
|
||||
this.acknowledged = const Value.absent(),
|
||||
required DateTime createdAt,
|
||||
this.rowid = const Value.absent(),
|
||||
}) : id = Value(id),
|
||||
payload = Value(payload),
|
||||
createdAt = Value(createdAt);
|
||||
static Insertable<StockAdjustmentResultRow> custom({
|
||||
Expression<String>? id,
|
||||
Expression<String>? payload,
|
||||
Expression<bool>? acknowledged,
|
||||
Expression<DateTime>? createdAt,
|
||||
Expression<int>? rowid,
|
||||
}) {
|
||||
return RawValuesInsertable({
|
||||
if (id != null) 'id': id,
|
||||
if (payload != null) 'payload': payload,
|
||||
if (acknowledged != null) 'acknowledged': acknowledged,
|
||||
if (createdAt != null) 'created_at': createdAt,
|
||||
if (rowid != null) 'rowid': rowid,
|
||||
});
|
||||
}
|
||||
|
||||
StockAdjustmentResultsCompanion copyWith({
|
||||
Value<String>? id,
|
||||
Value<String>? payload,
|
||||
Value<bool>? acknowledged,
|
||||
Value<DateTime>? createdAt,
|
||||
Value<int>? rowid,
|
||||
}) {
|
||||
return StockAdjustmentResultsCompanion(
|
||||
id: id ?? this.id,
|
||||
payload: payload ?? this.payload,
|
||||
acknowledged: acknowledged ?? this.acknowledged,
|
||||
createdAt: createdAt ?? this.createdAt,
|
||||
rowid: rowid ?? this.rowid,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, Expression> toColumns(bool nullToAbsent) {
|
||||
final map = <String, Expression>{};
|
||||
if (id.present) {
|
||||
map['id'] = Variable<String>(id.value);
|
||||
}
|
||||
if (payload.present) {
|
||||
map['payload'] = Variable<String>(payload.value);
|
||||
}
|
||||
if (acknowledged.present) {
|
||||
map['acknowledged'] = Variable<bool>(acknowledged.value);
|
||||
}
|
||||
if (createdAt.present) {
|
||||
map['created_at'] = Variable<DateTime>(createdAt.value);
|
||||
}
|
||||
if (rowid.present) {
|
||||
map['rowid'] = Variable<int>(rowid.value);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return (StringBuffer('StockAdjustmentResultsCompanion(')
|
||||
..write('id: $id, ')
|
||||
..write('payload: $payload, ')
|
||||
..write('acknowledged: $acknowledged, ')
|
||||
..write('createdAt: $createdAt, ')
|
||||
..write('rowid: $rowid')
|
||||
..write(')'))
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _$AppDatabase extends GeneratedDatabase {
|
||||
_$AppDatabase(QueryExecutor e) : super(e);
|
||||
$AppDatabaseManager get managers => $AppDatabaseManager(this);
|
||||
@@ -4080,6 +4467,8 @@ abstract class _$AppDatabase extends GeneratedDatabase {
|
||||
late final $ProcessedRemoteCommandsTable processedRemoteCommands =
|
||||
$ProcessedRemoteCommandsTable(this);
|
||||
late final $TabEventsTable tabEvents = $TabEventsTable(this);
|
||||
late final $StockAdjustmentResultsTable stockAdjustmentResults =
|
||||
$StockAdjustmentResultsTable(this);
|
||||
@override
|
||||
Iterable<TableInfo<Table, Object?>> get allTables =>
|
||||
allSchemaEntities.whereType<TableInfo<Table, Object?>>();
|
||||
@@ -4095,6 +4484,7 @@ abstract class _$AppDatabase extends GeneratedDatabase {
|
||||
remoteSyncState,
|
||||
processedRemoteCommands,
|
||||
tabEvents,
|
||||
stockAdjustmentResults,
|
||||
];
|
||||
@override
|
||||
StreamQueryUpdateRules get streamUpdateRules => const StreamQueryUpdateRules([
|
||||
@@ -6355,6 +6745,7 @@ typedef $$RemoteSyncStateTableCreateCompanionBuilder =
|
||||
RemoteSyncStateCompanion Function({
|
||||
required String deviceId,
|
||||
Value<String?> serverCursor,
|
||||
Value<String?> settlementsCursor,
|
||||
Value<DateTime?> lastSuccessfulSyncAt,
|
||||
Value<DateTime?> lastAttemptedSyncAt,
|
||||
Value<int> consecutiveFailureCount,
|
||||
@@ -6364,6 +6755,7 @@ typedef $$RemoteSyncStateTableUpdateCompanionBuilder =
|
||||
RemoteSyncStateCompanion Function({
|
||||
Value<String> deviceId,
|
||||
Value<String?> serverCursor,
|
||||
Value<String?> settlementsCursor,
|
||||
Value<DateTime?> lastSuccessfulSyncAt,
|
||||
Value<DateTime?> lastAttemptedSyncAt,
|
||||
Value<int> consecutiveFailureCount,
|
||||
@@ -6389,6 +6781,11 @@ class $$RemoteSyncStateTableFilterComposer
|
||||
builder: (column) => ColumnFilters(column),
|
||||
);
|
||||
|
||||
ColumnFilters<String> get settlementsCursor => $composableBuilder(
|
||||
column: $table.settlementsCursor,
|
||||
builder: (column) => ColumnFilters(column),
|
||||
);
|
||||
|
||||
ColumnFilters<DateTime> get lastSuccessfulSyncAt => $composableBuilder(
|
||||
column: $table.lastSuccessfulSyncAt,
|
||||
builder: (column) => ColumnFilters(column),
|
||||
@@ -6424,6 +6821,11 @@ class $$RemoteSyncStateTableOrderingComposer
|
||||
builder: (column) => ColumnOrderings(column),
|
||||
);
|
||||
|
||||
ColumnOrderings<String> get settlementsCursor => $composableBuilder(
|
||||
column: $table.settlementsCursor,
|
||||
builder: (column) => ColumnOrderings(column),
|
||||
);
|
||||
|
||||
ColumnOrderings<DateTime> get lastSuccessfulSyncAt => $composableBuilder(
|
||||
column: $table.lastSuccessfulSyncAt,
|
||||
builder: (column) => ColumnOrderings(column),
|
||||
@@ -6457,6 +6859,11 @@ class $$RemoteSyncStateTableAnnotationComposer
|
||||
builder: (column) => column,
|
||||
);
|
||||
|
||||
GeneratedColumn<String> get settlementsCursor => $composableBuilder(
|
||||
column: $table.settlementsCursor,
|
||||
builder: (column) => column,
|
||||
);
|
||||
|
||||
GeneratedColumn<DateTime> get lastSuccessfulSyncAt => $composableBuilder(
|
||||
column: $table.lastSuccessfulSyncAt,
|
||||
builder: (column) => column,
|
||||
@@ -6512,6 +6919,7 @@ class $$RemoteSyncStateTableTableManager
|
||||
({
|
||||
Value<String> deviceId = const Value.absent(),
|
||||
Value<String?> serverCursor = const Value.absent(),
|
||||
Value<String?> settlementsCursor = const Value.absent(),
|
||||
Value<DateTime?> lastSuccessfulSyncAt = const Value.absent(),
|
||||
Value<DateTime?> lastAttemptedSyncAt = const Value.absent(),
|
||||
Value<int> consecutiveFailureCount = const Value.absent(),
|
||||
@@ -6519,6 +6927,7 @@ class $$RemoteSyncStateTableTableManager
|
||||
}) => RemoteSyncStateCompanion(
|
||||
deviceId: deviceId,
|
||||
serverCursor: serverCursor,
|
||||
settlementsCursor: settlementsCursor,
|
||||
lastSuccessfulSyncAt: lastSuccessfulSyncAt,
|
||||
lastAttemptedSyncAt: lastAttemptedSyncAt,
|
||||
consecutiveFailureCount: consecutiveFailureCount,
|
||||
@@ -6528,6 +6937,7 @@ class $$RemoteSyncStateTableTableManager
|
||||
({
|
||||
required String deviceId,
|
||||
Value<String?> serverCursor = const Value.absent(),
|
||||
Value<String?> settlementsCursor = const Value.absent(),
|
||||
Value<DateTime?> lastSuccessfulSyncAt = const Value.absent(),
|
||||
Value<DateTime?> lastAttemptedSyncAt = const Value.absent(),
|
||||
Value<int> consecutiveFailureCount = const Value.absent(),
|
||||
@@ -6535,6 +6945,7 @@ class $$RemoteSyncStateTableTableManager
|
||||
}) => RemoteSyncStateCompanion.insert(
|
||||
deviceId: deviceId,
|
||||
serverCursor: serverCursor,
|
||||
settlementsCursor: settlementsCursor,
|
||||
lastSuccessfulSyncAt: lastSuccessfulSyncAt,
|
||||
lastAttemptedSyncAt: lastAttemptedSyncAt,
|
||||
consecutiveFailureCount: consecutiveFailureCount,
|
||||
@@ -6936,6 +7347,208 @@ typedef $$TabEventsTableProcessedTableManager =
|
||||
TabEventRow,
|
||||
PrefetchHooks Function()
|
||||
>;
|
||||
typedef $$StockAdjustmentResultsTableCreateCompanionBuilder =
|
||||
StockAdjustmentResultsCompanion Function({
|
||||
required String id,
|
||||
required String payload,
|
||||
Value<bool> acknowledged,
|
||||
required DateTime createdAt,
|
||||
Value<int> rowid,
|
||||
});
|
||||
typedef $$StockAdjustmentResultsTableUpdateCompanionBuilder =
|
||||
StockAdjustmentResultsCompanion Function({
|
||||
Value<String> id,
|
||||
Value<String> payload,
|
||||
Value<bool> acknowledged,
|
||||
Value<DateTime> createdAt,
|
||||
Value<int> rowid,
|
||||
});
|
||||
|
||||
class $$StockAdjustmentResultsTableFilterComposer
|
||||
extends Composer<_$AppDatabase, $StockAdjustmentResultsTable> {
|
||||
$$StockAdjustmentResultsTableFilterComposer({
|
||||
required super.$db,
|
||||
required super.$table,
|
||||
super.joinBuilder,
|
||||
super.$addJoinBuilderToRootComposer,
|
||||
super.$removeJoinBuilderFromRootComposer,
|
||||
});
|
||||
ColumnFilters<String> get id => $composableBuilder(
|
||||
column: $table.id,
|
||||
builder: (column) => ColumnFilters(column),
|
||||
);
|
||||
|
||||
ColumnFilters<String> get payload => $composableBuilder(
|
||||
column: $table.payload,
|
||||
builder: (column) => ColumnFilters(column),
|
||||
);
|
||||
|
||||
ColumnFilters<bool> get acknowledged => $composableBuilder(
|
||||
column: $table.acknowledged,
|
||||
builder: (column) => ColumnFilters(column),
|
||||
);
|
||||
|
||||
ColumnFilters<DateTime> get createdAt => $composableBuilder(
|
||||
column: $table.createdAt,
|
||||
builder: (column) => ColumnFilters(column),
|
||||
);
|
||||
}
|
||||
|
||||
class $$StockAdjustmentResultsTableOrderingComposer
|
||||
extends Composer<_$AppDatabase, $StockAdjustmentResultsTable> {
|
||||
$$StockAdjustmentResultsTableOrderingComposer({
|
||||
required super.$db,
|
||||
required super.$table,
|
||||
super.joinBuilder,
|
||||
super.$addJoinBuilderToRootComposer,
|
||||
super.$removeJoinBuilderFromRootComposer,
|
||||
});
|
||||
ColumnOrderings<String> get id => $composableBuilder(
|
||||
column: $table.id,
|
||||
builder: (column) => ColumnOrderings(column),
|
||||
);
|
||||
|
||||
ColumnOrderings<String> get payload => $composableBuilder(
|
||||
column: $table.payload,
|
||||
builder: (column) => ColumnOrderings(column),
|
||||
);
|
||||
|
||||
ColumnOrderings<bool> get acknowledged => $composableBuilder(
|
||||
column: $table.acknowledged,
|
||||
builder: (column) => ColumnOrderings(column),
|
||||
);
|
||||
|
||||
ColumnOrderings<DateTime> get createdAt => $composableBuilder(
|
||||
column: $table.createdAt,
|
||||
builder: (column) => ColumnOrderings(column),
|
||||
);
|
||||
}
|
||||
|
||||
class $$StockAdjustmentResultsTableAnnotationComposer
|
||||
extends Composer<_$AppDatabase, $StockAdjustmentResultsTable> {
|
||||
$$StockAdjustmentResultsTableAnnotationComposer({
|
||||
required super.$db,
|
||||
required super.$table,
|
||||
super.joinBuilder,
|
||||
super.$addJoinBuilderToRootComposer,
|
||||
super.$removeJoinBuilderFromRootComposer,
|
||||
});
|
||||
GeneratedColumn<String> get id =>
|
||||
$composableBuilder(column: $table.id, builder: (column) => column);
|
||||
|
||||
GeneratedColumn<String> get payload =>
|
||||
$composableBuilder(column: $table.payload, builder: (column) => column);
|
||||
|
||||
GeneratedColumn<bool> get acknowledged => $composableBuilder(
|
||||
column: $table.acknowledged,
|
||||
builder: (column) => column,
|
||||
);
|
||||
|
||||
GeneratedColumn<DateTime> get createdAt =>
|
||||
$composableBuilder(column: $table.createdAt, builder: (column) => column);
|
||||
}
|
||||
|
||||
class $$StockAdjustmentResultsTableTableManager
|
||||
extends
|
||||
RootTableManager<
|
||||
_$AppDatabase,
|
||||
$StockAdjustmentResultsTable,
|
||||
StockAdjustmentResultRow,
|
||||
$$StockAdjustmentResultsTableFilterComposer,
|
||||
$$StockAdjustmentResultsTableOrderingComposer,
|
||||
$$StockAdjustmentResultsTableAnnotationComposer,
|
||||
$$StockAdjustmentResultsTableCreateCompanionBuilder,
|
||||
$$StockAdjustmentResultsTableUpdateCompanionBuilder,
|
||||
(
|
||||
StockAdjustmentResultRow,
|
||||
BaseReferences<
|
||||
_$AppDatabase,
|
||||
$StockAdjustmentResultsTable,
|
||||
StockAdjustmentResultRow
|
||||
>,
|
||||
),
|
||||
StockAdjustmentResultRow,
|
||||
PrefetchHooks Function()
|
||||
> {
|
||||
$$StockAdjustmentResultsTableTableManager(
|
||||
_$AppDatabase db,
|
||||
$StockAdjustmentResultsTable table,
|
||||
) : super(
|
||||
TableManagerState(
|
||||
db: db,
|
||||
table: table,
|
||||
createFilteringComposer: () =>
|
||||
$$StockAdjustmentResultsTableFilterComposer(
|
||||
$db: db,
|
||||
$table: table,
|
||||
),
|
||||
createOrderingComposer: () =>
|
||||
$$StockAdjustmentResultsTableOrderingComposer(
|
||||
$db: db,
|
||||
$table: table,
|
||||
),
|
||||
createComputedFieldComposer: () =>
|
||||
$$StockAdjustmentResultsTableAnnotationComposer(
|
||||
$db: db,
|
||||
$table: table,
|
||||
),
|
||||
updateCompanionCallback:
|
||||
({
|
||||
Value<String> id = const Value.absent(),
|
||||
Value<String> payload = const Value.absent(),
|
||||
Value<bool> acknowledged = const Value.absent(),
|
||||
Value<DateTime> createdAt = const Value.absent(),
|
||||
Value<int> rowid = const Value.absent(),
|
||||
}) => StockAdjustmentResultsCompanion(
|
||||
id: id,
|
||||
payload: payload,
|
||||
acknowledged: acknowledged,
|
||||
createdAt: createdAt,
|
||||
rowid: rowid,
|
||||
),
|
||||
createCompanionCallback:
|
||||
({
|
||||
required String id,
|
||||
required String payload,
|
||||
Value<bool> acknowledged = const Value.absent(),
|
||||
required DateTime createdAt,
|
||||
Value<int> rowid = const Value.absent(),
|
||||
}) => StockAdjustmentResultsCompanion.insert(
|
||||
id: id,
|
||||
payload: payload,
|
||||
acknowledged: acknowledged,
|
||||
createdAt: createdAt,
|
||||
rowid: rowid,
|
||||
),
|
||||
withReferenceMapper: (p0) => p0
|
||||
.map((e) => (e.readTable(table), BaseReferences(db, table, e)))
|
||||
.toList(),
|
||||
prefetchHooksCallback: null,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
typedef $$StockAdjustmentResultsTableProcessedTableManager =
|
||||
ProcessedTableManager<
|
||||
_$AppDatabase,
|
||||
$StockAdjustmentResultsTable,
|
||||
StockAdjustmentResultRow,
|
||||
$$StockAdjustmentResultsTableFilterComposer,
|
||||
$$StockAdjustmentResultsTableOrderingComposer,
|
||||
$$StockAdjustmentResultsTableAnnotationComposer,
|
||||
$$StockAdjustmentResultsTableCreateCompanionBuilder,
|
||||
$$StockAdjustmentResultsTableUpdateCompanionBuilder,
|
||||
(
|
||||
StockAdjustmentResultRow,
|
||||
BaseReferences<
|
||||
_$AppDatabase,
|
||||
$StockAdjustmentResultsTable,
|
||||
StockAdjustmentResultRow
|
||||
>,
|
||||
),
|
||||
StockAdjustmentResultRow,
|
||||
PrefetchHooks Function()
|
||||
>;
|
||||
|
||||
class $AppDatabaseManager {
|
||||
final _$AppDatabase _db;
|
||||
@@ -6963,4 +7576,9 @@ class $AppDatabaseManager {
|
||||
);
|
||||
$$TabEventsTableTableManager get tabEvents =>
|
||||
$$TabEventsTableTableManager(_db, _db.tabEvents);
|
||||
$$StockAdjustmentResultsTableTableManager get stockAdjustmentResults =>
|
||||
$$StockAdjustmentResultsTableTableManager(
|
||||
_db,
|
||||
_db.stockAdjustmentResults,
|
||||
);
|
||||
}
|
||||
|
||||
+116
-34
@@ -1,14 +1,11 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:drift/drift.dart';
|
||||
|
||||
import '../models/remote_command.dart';
|
||||
import '../models/tab_event.dart';
|
||||
import 'app_database.dart';
|
||||
|
||||
class InsufficientStockException implements Exception {
|
||||
const InsufficientStockException();
|
||||
|
||||
@override
|
||||
String toString() => 'Not enough stock';
|
||||
}
|
||||
|
||||
class ProductNotFoundException implements Exception {
|
||||
final String productId;
|
||||
|
||||
@@ -20,44 +17,129 @@ class ProductNotFoundException implements Exception {
|
||||
|
||||
/// Atomic stock updates shared by the product and tab services, so they can
|
||||
/// run inside either one's transaction.
|
||||
///
|
||||
/// Stock may go below zero: the bar keeps serving when the count was off.
|
||||
extension StockQueries on AppDatabase {
|
||||
/// Products with infinite stock are only checked for existence.
|
||||
Future<void> decreaseStock(String productId, int amount) async {
|
||||
_validateStockAmount(amount);
|
||||
|
||||
final updatedRows =
|
||||
await (update(products)..where(
|
||||
(row) =>
|
||||
row.id.equals(productId) &
|
||||
row.infiniteStock.not() &
|
||||
row.stockQuantity.isBiggerOrEqualValue(amount),
|
||||
))
|
||||
.write(
|
||||
ProductsCompanion.custom(
|
||||
stockQuantity: products.stockQuantity - Variable(amount),
|
||||
),
|
||||
);
|
||||
|
||||
if (updatedRows == 1) return;
|
||||
|
||||
final product = await _findProduct(productId);
|
||||
if (!product.infiniteStock) throw const InsufficientStockException();
|
||||
if (await changeTrackedStock(productId, -amount) == 0) {
|
||||
await _findProduct(productId);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> increaseStock(String productId, int amount) async {
|
||||
_validateStockAmount(amount);
|
||||
|
||||
final updatedRows =
|
||||
await (update(products)..where(
|
||||
(row) => row.id.equals(productId) & row.infiniteStock.not(),
|
||||
))
|
||||
.write(
|
||||
ProductsCompanion.custom(
|
||||
stockQuantity: products.stockQuantity + Variable(amount),
|
||||
),
|
||||
);
|
||||
if (await changeTrackedStock(productId, amount) == 0) {
|
||||
await _findProduct(productId);
|
||||
}
|
||||
}
|
||||
|
||||
if (updatedRows != 1) await _findProduct(productId);
|
||||
/// Adds [delta] to the stock of [productId] when its stock is tracked.
|
||||
/// Returns the number of products changed: 0 when the product has infinite
|
||||
/// stock or no longer exists, which is not an error.
|
||||
Future<int> changeTrackedStock(String productId, int delta) {
|
||||
return (update(products)
|
||||
..where((row) => row.id.equals(productId) & row.infiniteStock.not()))
|
||||
.write(
|
||||
ProductsCompanion.custom(
|
||||
stockQuantity: products.stockQuantity + Variable(delta),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Queues a `stock_set` event, so a stock count or tracking change made on
|
||||
/// this tablet reaches the server. Call it inside the transaction that
|
||||
/// writes the stock.
|
||||
Future<void> queueStockSet(TabEvent event) async {
|
||||
await into(tabEvents).insert(
|
||||
TabEventsCompanion.insert(
|
||||
id: event.id,
|
||||
payload: jsonEncode(event.payload),
|
||||
createdAt: DateTime.now(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Replaces the local stock of the products in [serverStock] with the
|
||||
/// server's, then replays the outbox events the server hasn't applied yet
|
||||
/// on top, the way the server will. The events the server acknowledged
|
||||
/// must have left the outbox first: they are in [serverStock] already.
|
||||
///
|
||||
/// Products the server doesn't know keep their local stock.
|
||||
Future<void> rebaseStock(List<ServerStock> serverStock) {
|
||||
return transaction(() async {
|
||||
final stock = {
|
||||
for (final entry in serverStock)
|
||||
entry.productId: (stock: entry.stock, countedAt: entry.countedAt),
|
||||
};
|
||||
|
||||
// Read here, so changes made while the sync was in flight are included
|
||||
final outbox =
|
||||
await (select(tabEvents)..orderBy([
|
||||
(row) => OrderingTerm.asc(row.createdAt),
|
||||
(row) => OrderingTerm.asc(const CustomExpression<int>('rowid')),
|
||||
]))
|
||||
.get();
|
||||
for (final row in outbox) {
|
||||
final event = jsonDecode(row.payload) as Map<String, dynamic>;
|
||||
final productId = event['productId'] as String?;
|
||||
final current = stock[productId];
|
||||
if (productId == null || current == null) continue;
|
||||
|
||||
final occurredAt = DateTime.tryParse(
|
||||
event['occurredAt'] as String? ?? '',
|
||||
);
|
||||
if (occurredAt == null) continue;
|
||||
final countedAt = current.countedAt;
|
||||
|
||||
switch (TabEventType.fromValue(event['type'] as String? ?? '')) {
|
||||
// An older count than the server's is outdated
|
||||
case TabEventType.stockSet:
|
||||
if (countedAt != null && occurredAt.isBefore(countedAt)) continue;
|
||||
stock[productId] = (
|
||||
stock: event['quantity'] as int?,
|
||||
countedAt: occurredAt,
|
||||
);
|
||||
// A sale before the count is already in the count
|
||||
case TabEventType.lineAdded || TabEventType.lineRemoved
|
||||
when current.stock != null &&
|
||||
(countedAt == null || occurredAt.isAfter(countedAt)):
|
||||
final quantity = event['quantity'] as int? ?? 0;
|
||||
final sign = event['type'] == TabEventType.lineAdded.value ? -1 : 1;
|
||||
stock[productId] = (
|
||||
stock: current.stock! + sign * quantity,
|
||||
countedAt: countedAt,
|
||||
);
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
for (final product in await select(products).get()) {
|
||||
final rebased = stock[product.id];
|
||||
if (rebased == null) continue;
|
||||
|
||||
final infinite = rebased.stock == null;
|
||||
// An untracked product keeps its last count for when tracking resumes
|
||||
final quantity = rebased.stock ?? product.stockQuantity;
|
||||
if (product.infiniteStock == infinite &&
|
||||
product.stockQuantity == quantity) {
|
||||
continue;
|
||||
}
|
||||
|
||||
await (update(
|
||||
products,
|
||||
)..where((row) => row.id.equals(product.id))).write(
|
||||
ProductsCompanion(
|
||||
stockQuantity: Value(quantity),
|
||||
infiniteStock: Value(infinite),
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<ProductRow> _findProduct(String productId) async {
|
||||
|
||||
Reference in New Issue
Block a user