feat: implement I-9 and W-4 tax form handling with dedicated use cases and mappers
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
import 'package:core_localization/core_localization.dart' as core_localization;
|
import 'package:core_localization/core_localization.dart' as core_localization;
|
||||||
import 'package:design_system/design_system.dart';
|
import 'package:design_system/design_system.dart';
|
||||||
|
import 'package:firebase_core/firebase_core.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||||
@@ -7,7 +8,6 @@ import 'package:flutter_modular/flutter_modular.dart';
|
|||||||
import 'package:staff_authentication/staff_authentication.dart'
|
import 'package:staff_authentication/staff_authentication.dart'
|
||||||
as staff_authentication;
|
as staff_authentication;
|
||||||
import 'package:staff_main/staff_main.dart' as staff_main;
|
import 'package:staff_main/staff_main.dart' as staff_main;
|
||||||
import 'package:firebase_core/firebase_core.dart';
|
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|||||||
@@ -15,18 +15,36 @@ class TaxFormAdapter {
|
|||||||
DateTime? createdAt,
|
DateTime? createdAt,
|
||||||
DateTime? updatedAt,
|
DateTime? updatedAt,
|
||||||
}) {
|
}) {
|
||||||
return TaxForm(
|
final TaxFormType formType = _stringToType(type);
|
||||||
id: id,
|
final TaxFormStatus formStatus = _stringToStatus(status);
|
||||||
type: _stringToType(type),
|
final Map<String, dynamic> formDetails =
|
||||||
title: title,
|
formData is Map ? Map<String, dynamic>.from(formData as Map) : <String, dynamic>{};
|
||||||
subtitle: subtitle,
|
|
||||||
description: description,
|
if (formType == TaxFormType.i9) {
|
||||||
status: _stringToStatus(status),
|
return I9TaxForm(
|
||||||
staffId: staffId,
|
id: id,
|
||||||
formData: formData is Map ? Map<String, dynamic>.from(formData) : null,
|
title: title,
|
||||||
createdAt: createdAt,
|
subtitle: subtitle,
|
||||||
updatedAt: updatedAt,
|
description: description,
|
||||||
);
|
status: formStatus,
|
||||||
|
staffId: staffId,
|
||||||
|
formData: formDetails,
|
||||||
|
createdAt: createdAt,
|
||||||
|
updatedAt: updatedAt,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return W4TaxForm(
|
||||||
|
id: id,
|
||||||
|
title: title,
|
||||||
|
subtitle: subtitle,
|
||||||
|
description: description,
|
||||||
|
status: formStatus,
|
||||||
|
staffId: staffId,
|
||||||
|
formData: formDetails,
|
||||||
|
createdAt: createdAt,
|
||||||
|
updatedAt: updatedAt,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static TaxFormType _stringToType(String? value) {
|
static TaxFormType _stringToType(String? value) {
|
||||||
|
|||||||
@@ -4,27 +4,26 @@ enum TaxFormType { i9, w4 }
|
|||||||
|
|
||||||
enum TaxFormStatus { notStarted, inProgress, submitted, approved, rejected }
|
enum TaxFormStatus { notStarted, inProgress, submitted, approved, rejected }
|
||||||
|
|
||||||
class TaxForm extends Equatable {
|
abstract class TaxForm extends Equatable {
|
||||||
final String id;
|
final String id;
|
||||||
final TaxFormType type;
|
TaxFormType get type;
|
||||||
final String title;
|
final String title;
|
||||||
final String? subtitle;
|
final String? subtitle;
|
||||||
final String? description;
|
final String? description;
|
||||||
final TaxFormStatus status;
|
final TaxFormStatus status;
|
||||||
final String? staffId;
|
final String? staffId;
|
||||||
final Map<String, dynamic>? formData;
|
final Map<String, dynamic> formData;
|
||||||
final DateTime? createdAt;
|
final DateTime? createdAt;
|
||||||
final DateTime? updatedAt;
|
final DateTime? updatedAt;
|
||||||
|
|
||||||
const TaxForm({
|
const TaxForm({
|
||||||
required this.id,
|
required this.id,
|
||||||
required this.type,
|
|
||||||
required this.title,
|
required this.title,
|
||||||
this.subtitle,
|
this.subtitle,
|
||||||
this.description,
|
this.description,
|
||||||
this.status = TaxFormStatus.notStarted,
|
this.status = TaxFormStatus.notStarted,
|
||||||
this.staffId,
|
this.staffId,
|
||||||
this.formData,
|
this.formData = const {},
|
||||||
this.createdAt,
|
this.createdAt,
|
||||||
this.updatedAt,
|
this.updatedAt,
|
||||||
});
|
});
|
||||||
@@ -43,3 +42,37 @@ class TaxForm extends Equatable {
|
|||||||
updatedAt,
|
updatedAt,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class I9TaxForm extends TaxForm {
|
||||||
|
const I9TaxForm({
|
||||||
|
required super.id,
|
||||||
|
required super.title,
|
||||||
|
super.subtitle,
|
||||||
|
super.description,
|
||||||
|
super.status,
|
||||||
|
super.staffId,
|
||||||
|
super.formData,
|
||||||
|
super.createdAt,
|
||||||
|
super.updatedAt,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
TaxFormType get type => TaxFormType.i9;
|
||||||
|
}
|
||||||
|
|
||||||
|
class W4TaxForm extends TaxForm {
|
||||||
|
const W4TaxForm({
|
||||||
|
required super.id,
|
||||||
|
required super.title,
|
||||||
|
super.subtitle,
|
||||||
|
super.description,
|
||||||
|
super.status,
|
||||||
|
super.staffId,
|
||||||
|
super.formData,
|
||||||
|
super.createdAt,
|
||||||
|
super.updatedAt,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
TaxFormType get type => TaxFormType.w4;
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
import 'package:krow_data_connect/krow_data_connect.dart' as dc;
|
||||||
|
import 'package:krow_domain/krow_domain.dart';
|
||||||
|
|
||||||
|
class TaxFormMapper {
|
||||||
|
static TaxForm fromDataConnect(dc.GetTaxFormsByStaffIdTaxForms form) {
|
||||||
|
// Construct the legacy map for the entity
|
||||||
|
final Map<String, dynamic> formData = {
|
||||||
|
'firstName': form.firstName,
|
||||||
|
'lastName': form.lastName,
|
||||||
|
'middleInitial': form.mInitial,
|
||||||
|
'otherLastNames': form.oLastName,
|
||||||
|
'dob': form.dob?.toDateTime().toIso8601String(),
|
||||||
|
'ssn': form.socialSN.toString(),
|
||||||
|
'email': form.email,
|
||||||
|
'phone': form.phone,
|
||||||
|
'address': form.address,
|
||||||
|
'aptNumber': form.apt,
|
||||||
|
'city': form.city,
|
||||||
|
'state': form.state,
|
||||||
|
'zipCode': form.zipCode,
|
||||||
|
|
||||||
|
// I-9 Fields
|
||||||
|
'citizenshipStatus': form.citizen?.stringValue,
|
||||||
|
'uscisNumber': form.uscis,
|
||||||
|
'passportNumber': form.passportNumber,
|
||||||
|
'countryIssuance': form.countryIssue,
|
||||||
|
'preparerUsed': form.prepartorOrTranslator,
|
||||||
|
|
||||||
|
// W-4 Fields
|
||||||
|
'filingStatus': form.marital?.stringValue,
|
||||||
|
'multipleJobs': form.multipleJob,
|
||||||
|
'qualifyingChildren': form.childrens,
|
||||||
|
'otherDependents': form.otherDeps,
|
||||||
|
'otherIncome': form.otherInconme?.toString(),
|
||||||
|
'deductions': form.deductions?.toString(),
|
||||||
|
'extraWithholding': form.extraWithholding?.toString(),
|
||||||
|
|
||||||
|
'signature': form.signature,
|
||||||
|
};
|
||||||
|
|
||||||
|
String title = '';
|
||||||
|
String subtitle = '';
|
||||||
|
String description = '';
|
||||||
|
|
||||||
|
if (form.formType == dc.TaxFormType.I9) {
|
||||||
|
title = 'Form I-9';
|
||||||
|
subtitle = 'Employment Eligibility Verification';
|
||||||
|
description = 'Required for all new hires to verify identity.';
|
||||||
|
} else {
|
||||||
|
title = 'Form W-4';
|
||||||
|
subtitle = 'Employee\'s Withholding Certificate';
|
||||||
|
description = 'Determines federal income tax withholding.';
|
||||||
|
}
|
||||||
|
|
||||||
|
return TaxFormAdapter.fromPrimitives(
|
||||||
|
id: form.id,
|
||||||
|
type: form.formType.stringValue,
|
||||||
|
title: title,
|
||||||
|
subtitle: subtitle,
|
||||||
|
description: description,
|
||||||
|
status: form.status.stringValue,
|
||||||
|
staffId: form.staffId,
|
||||||
|
formData: formData,
|
||||||
|
updatedAt: form.updatedAt?.toDateTime(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ import 'package:krow_data_connect/krow_data_connect.dart' as dc;
|
|||||||
import 'package:krow_domain/krow_domain.dart';
|
import 'package:krow_domain/krow_domain.dart';
|
||||||
|
|
||||||
import '../../domain/repositories/tax_forms_repository.dart';
|
import '../../domain/repositories/tax_forms_repository.dart';
|
||||||
|
import '../mappers/tax_form_mapper.dart';
|
||||||
|
|
||||||
class TaxFormsRepositoryImpl implements TaxFormsRepository {
|
class TaxFormsRepositoryImpl implements TaxFormsRepository {
|
||||||
TaxFormsRepositoryImpl({
|
TaxFormsRepositoryImpl({
|
||||||
@@ -37,7 +38,7 @@ class TaxFormsRepositoryImpl implements TaxFormsRepository {
|
|||||||
result =
|
result =
|
||||||
await dataConnect.getTaxFormsByStaffId(staffId: staffId).execute();
|
await dataConnect.getTaxFormsByStaffId(staffId: staffId).execute();
|
||||||
|
|
||||||
final List<TaxForm> forms = result.data.taxForms.map((dc.GetTaxFormsByStaffIdTaxForms e) => _mapToEntity(e)).toList();
|
final List<TaxForm> forms = result.data.taxForms.map(TaxFormMapper.fromDataConnect).toList();
|
||||||
|
|
||||||
// Check if required forms exist, create if not.
|
// Check if required forms exist, create if not.
|
||||||
final Set<TaxFormType> typesPresent = forms.map((TaxForm f) => f.type).toSet();
|
final Set<TaxFormType> typesPresent = forms.map((TaxForm f) => f.type).toSet();
|
||||||
@@ -56,7 +57,7 @@ class TaxFormsRepositoryImpl implements TaxFormsRepository {
|
|||||||
final QueryResult<dc.GetTaxFormsByStaffIdData, dc.GetTaxFormsByStaffIdVariables>
|
final QueryResult<dc.GetTaxFormsByStaffIdData, dc.GetTaxFormsByStaffIdVariables>
|
||||||
result2 =
|
result2 =
|
||||||
await dataConnect.getTaxFormsByStaffId(staffId: staffId).execute();
|
await dataConnect.getTaxFormsByStaffId(staffId: staffId).execute();
|
||||||
return result2.data.taxForms.map((dc.GetTaxFormsByStaffIdTaxForms e) => _mapToEntity(e)).toList();
|
return result2.data.taxForms.map(TaxFormMapper.fromDataConnect).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
return forms;
|
return forms;
|
||||||
@@ -78,155 +79,111 @@ class TaxFormsRepositoryImpl implements TaxFormsRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> submitForm(TaxFormType type, Map<String, dynamic> data) async {
|
Future<void> updateI9Form(I9TaxForm form) async {
|
||||||
final String staffId = _getStaffId();
|
final Map<String, dynamic> data = form.formData;
|
||||||
final QueryResult<dc.GetTaxFormsByStaffIdData, dc.GetTaxFormsByStaffIdVariables>
|
|
||||||
result =
|
|
||||||
await dataConnect.getTaxFormsByStaffId(staffId: staffId).execute();
|
|
||||||
final String targetTypeString = TaxFormAdapter.typeToString(type);
|
|
||||||
|
|
||||||
final dc.GetTaxFormsByStaffIdTaxForms form =
|
|
||||||
result.data.taxForms.firstWhere(
|
|
||||||
(dc.GetTaxFormsByStaffIdTaxForms e) =>
|
|
||||||
e.formType.stringValue == targetTypeString,
|
|
||||||
orElse: () => throw Exception('Form not found for submission'),
|
|
||||||
);
|
|
||||||
|
|
||||||
final builder = dataConnect.updateTaxForm(id: form.id);
|
final builder = dataConnect.updateTaxForm(id: form.id);
|
||||||
|
_mapCommonFields(builder, data);
|
||||||
|
_mapI9Fields(builder, data);
|
||||||
|
await builder.execute();
|
||||||
|
}
|
||||||
|
|
||||||
// Map input fields to DataConnect variables
|
@override
|
||||||
if (data.containsKey('firstName')) {
|
Future<void> submitI9Form(I9TaxForm form) async {
|
||||||
builder.firstName(data['firstName'] as String);
|
final Map<String, dynamic> data = form.formData;
|
||||||
}
|
final builder = dataConnect.updateTaxForm(id: form.id);
|
||||||
if (data.containsKey('lastName')) {
|
_mapCommonFields(builder, data);
|
||||||
builder.lastName(data['lastName'] as String);
|
_mapI9Fields(builder, data);
|
||||||
}
|
|
||||||
if (data.containsKey('middleInitial')) {
|
|
||||||
builder.mInitial(data['middleInitial'] as String);
|
|
||||||
}
|
|
||||||
if (data.containsKey('otherLastNames')) {
|
|
||||||
builder.oLastName(data['otherLastNames'] as String);
|
|
||||||
}
|
|
||||||
if (data.containsKey('ssn') && data['ssn'] != null) {
|
|
||||||
builder.socialSN(int.tryParse(data['ssn'].toString()) ?? 0);
|
|
||||||
}
|
|
||||||
if (data.containsKey('email')) {
|
|
||||||
builder.email(data['email'] as String);
|
|
||||||
}
|
|
||||||
if (data.containsKey('phone')) {
|
|
||||||
builder.phone(data['phone'] as String);
|
|
||||||
}
|
|
||||||
if (data.containsKey('address')) {
|
|
||||||
builder.address(data['address'] as String);
|
|
||||||
}
|
|
||||||
if (data.containsKey('aptNumber')) {
|
|
||||||
builder.apt(data['aptNumber'] as String);
|
|
||||||
}
|
|
||||||
if (data.containsKey('city')) {
|
|
||||||
builder.city(data['city'] as String);
|
|
||||||
}
|
|
||||||
if (data.containsKey('state')) {
|
|
||||||
builder.state(data['state'] as String);
|
|
||||||
}
|
|
||||||
if (data.containsKey('zipCode')) {
|
|
||||||
builder.zipCode(data['zipCode'] as String);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Citizenship / Marital / Bool fields would go here.
|
|
||||||
// For now, mapping the core identity fields visible in the form logic.
|
|
||||||
// Assuming UI keys match these:
|
|
||||||
if (data.containsKey('citizenshipStatus')) {
|
|
||||||
// Need mapping for enum
|
|
||||||
}
|
|
||||||
|
|
||||||
await builder.status(dc.TaxFormStatus.SUBMITTED).execute();
|
await builder.status(dc.TaxFormStatus.SUBMITTED).execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> updateFormStatus(TaxFormType type, TaxFormStatus status) async {
|
Future<void> updateW4Form(W4TaxForm form) async {
|
||||||
final String staffId = _getStaffId();
|
final Map<String, dynamic> data = form.formData;
|
||||||
final QueryResult<dc.GetTaxFormsByStaffIdData, dc.GetTaxFormsByStaffIdVariables>
|
final builder = dataConnect.updateTaxForm(id: form.id);
|
||||||
result =
|
_mapCommonFields(builder, data);
|
||||||
await dataConnect.getTaxFormsByStaffId(staffId: staffId).execute();
|
_mapW4Fields(builder, data);
|
||||||
final String targetTypeString = TaxFormAdapter.typeToString(type);
|
await builder.execute();
|
||||||
|
|
||||||
final dc.GetTaxFormsByStaffIdTaxForms form =
|
|
||||||
result.data.taxForms.firstWhere(
|
|
||||||
(dc.GetTaxFormsByStaffIdTaxForms e) =>
|
|
||||||
e.formType.stringValue == targetTypeString,
|
|
||||||
orElse: () => throw Exception('Form not found for update'),
|
|
||||||
);
|
|
||||||
|
|
||||||
await dataConnect
|
|
||||||
.updateTaxForm(
|
|
||||||
id: form.id,
|
|
||||||
)
|
|
||||||
.status(dc.TaxFormStatus.values
|
|
||||||
.byName(TaxFormAdapter.statusToString(status)))
|
|
||||||
.execute();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TaxForm _mapToEntity(dc.GetTaxFormsByStaffIdTaxForms form) {
|
@override
|
||||||
// Construct the legacy map for the entity
|
Future<void> submitW4Form(W4TaxForm form) async {
|
||||||
final Map<String, dynamic> formData = {
|
final Map<String, dynamic> data = form.formData;
|
||||||
'firstName': form.firstName,
|
final builder = dataConnect.updateTaxForm(id: form.id);
|
||||||
'lastName': form.lastName,
|
_mapCommonFields(builder, data);
|
||||||
'middleInitial': form.mInitial,
|
_mapW4Fields(builder, data);
|
||||||
'otherLastNames': form.oLastName,
|
await builder.status(dc.TaxFormStatus.SUBMITTED).execute();
|
||||||
'dob': form.dob.toString(),
|
}
|
||||||
'ssn': form.socialSN.toString(),
|
|
||||||
'email': form.email,
|
|
||||||
'phone': form.phone,
|
|
||||||
'address': form.address,
|
|
||||||
'aptNumber': form.apt,
|
|
||||||
'city': form.city,
|
|
||||||
'state': form.state,
|
|
||||||
'zipCode': form.zipCode,
|
|
||||||
|
|
||||||
// I-9 Fields
|
void _mapCommonFields(dc.UpdateTaxFormVariablesBuilder builder, Map<String, dynamic> data) {
|
||||||
'citizenshipStatus': form.citizen,
|
if (data.containsKey('firstName')) builder.firstName(data['firstName'] as String?);
|
||||||
'uscisNumber': form.uscis,
|
if (data.containsKey('lastName')) builder.lastName(data['lastName'] as String?);
|
||||||
'passportNumber': form.passportNumber,
|
if (data.containsKey('middleInitial')) builder.mInitial(data['middleInitial'] as String?);
|
||||||
'countryIssuance': form.countryIssue,
|
if (data.containsKey('otherLastNames')) builder.oLastName(data['otherLastNames'] as String?);
|
||||||
'preparerUsed': form.prepartorOrTranslator,
|
if (data.containsKey('ssn') && data['ssn']?.toString().isNotEmpty == true) {
|
||||||
|
builder.socialSN(int.tryParse(data['ssn'].toString().replaceAll(RegExp(r'\D'), '')) ?? 0);
|
||||||
// W-4 Fields
|
|
||||||
'filingStatus': form.marital,
|
|
||||||
'multipleJobs': form.multipleJob,
|
|
||||||
'qualifyingChildren': form.childrens,
|
|
||||||
'otherDependents': form.otherDeps,
|
|
||||||
'otherIncome': form.otherInconme.toString(), // Note backend typo
|
|
||||||
'deductions': form.deductions.toString(),
|
|
||||||
'extraWithholding': form.extraWithholding.toString(),
|
|
||||||
|
|
||||||
'signature': form.signature,
|
|
||||||
};
|
|
||||||
|
|
||||||
String title = '';
|
|
||||||
String subtitle = '';
|
|
||||||
String description = '';
|
|
||||||
|
|
||||||
if (form.formType == dc.TaxFormType.I9) {
|
|
||||||
title = 'Form I-9';
|
|
||||||
subtitle = 'Employment Eligibility Verification';
|
|
||||||
description = 'Required for all new hires to verify identity.';
|
|
||||||
} else {
|
|
||||||
title = 'Form W-4';
|
|
||||||
subtitle = 'Employee\'s Withholding Certificate';
|
|
||||||
description = 'Determines federal income tax withholding.';
|
|
||||||
}
|
}
|
||||||
|
if (data.containsKey('email')) builder.email(data['email'] as String?);
|
||||||
|
if (data.containsKey('phone')) builder.phone(data['phone'] as String?);
|
||||||
|
if (data.containsKey('address')) builder.address(data['address'] as String?);
|
||||||
|
if (data.containsKey('aptNumber')) builder.apt(data['aptNumber'] as String?);
|
||||||
|
if (data.containsKey('city')) builder.city(data['city'] as String?);
|
||||||
|
if (data.containsKey('state')) builder.state(data['state'] as String?);
|
||||||
|
if (data.containsKey('zipCode')) builder.zipCode(data['zipCode'] as String?);
|
||||||
|
}
|
||||||
|
|
||||||
return TaxFormAdapter.fromPrimitives(
|
void _mapI9Fields(dc.UpdateTaxFormVariablesBuilder builder, Map<String, dynamic> data) {
|
||||||
id: form.id,
|
if (data.containsKey('citizenshipStatus')) {
|
||||||
type: form.formType.stringValue,
|
final String status = data['citizenshipStatus'] as String;
|
||||||
title: title,
|
// Map string to enum if possible, or handle otherwise.
|
||||||
subtitle: subtitle,
|
// Generated enum: CITIZEN, NONCITIZEN_NATIONAL, PERMANENT_RESIDENT, ALIEN_AUTHORIZED
|
||||||
description: description,
|
try {
|
||||||
status: form.status.stringValue,
|
builder.citizen(dc.CitizenshipStatus.values.byName(status.toUpperCase()));
|
||||||
staffId: form.staffId,
|
} catch (_) {}
|
||||||
formData: formData,
|
}
|
||||||
updatedAt: form.updatedAt?.toDateTime(),
|
if (data.containsKey('uscisNumber')) builder.uscis(data['uscisNumber'] as String?);
|
||||||
);
|
if (data.containsKey('passportNumber')) builder.passportNumber(data['passportNumber'] as String?);
|
||||||
|
if (data.containsKey('countryIssuance')) builder.countryIssue(data['countryIssuance'] as String?);
|
||||||
|
if (data.containsKey('preparerUsed')) builder.prepartorOrTranslator(data['preparerUsed'] as bool?);
|
||||||
|
if (data.containsKey('signature')) builder.signature(data['signature'] as String?);
|
||||||
|
// Note: admissionNumber not in builder based on file read
|
||||||
|
}
|
||||||
|
|
||||||
|
void _mapW4Fields(dc.UpdateTaxFormVariablesBuilder builder, Map<String, dynamic> data) {
|
||||||
|
if (data.containsKey('cityStateZip')) {
|
||||||
|
final String csz = data['cityStateZip'] as String;
|
||||||
|
// Extremely basic split: City, State Zip
|
||||||
|
final List<String> parts = csz.split(',');
|
||||||
|
if (parts.length >= 2) {
|
||||||
|
builder.city(parts[0].trim());
|
||||||
|
final String stateZip = parts[1].trim();
|
||||||
|
final List<String> szParts = stateZip.split(' ');
|
||||||
|
if (szParts.isNotEmpty) builder.state(szParts[0]);
|
||||||
|
if (szParts.length > 1) builder.zipCode(szParts.last);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (data.containsKey('filingStatus')) {
|
||||||
|
// MARITIAL_STATUS_SINGLE, MARITIAL_STATUS_MARRIED, MARITIAL_STATUS_HEAD
|
||||||
|
try {
|
||||||
|
final String status = data['filingStatus'] as String;
|
||||||
|
// Simple mapping assumptions:
|
||||||
|
if (status.contains('single')) builder.marital(dc.MaritalStatus.SINGLE);
|
||||||
|
else if (status.contains('married')) builder.marital(dc.MaritalStatus.MARRIED);
|
||||||
|
else if (status.contains('head')) builder.marital(dc.MaritalStatus.HEAD);
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
|
if (data.containsKey('multipleJobs')) builder.multipleJob(data['multipleJobs'] as bool?);
|
||||||
|
if (data.containsKey('qualifyingChildren')) builder.childrens(data['qualifyingChildren'] as int?);
|
||||||
|
if (data.containsKey('otherDependents')) builder.otherDeps(data['otherDependents'] as int?);
|
||||||
|
if (data.containsKey('otherIncome')) {
|
||||||
|
builder.otherInconme(double.tryParse(data['otherIncome'].toString()));
|
||||||
|
}
|
||||||
|
if (data.containsKey('deductions')) {
|
||||||
|
builder.deductions(double.tryParse(data['deductions'].toString()));
|
||||||
|
}
|
||||||
|
if (data.containsKey('extraWithholding')) {
|
||||||
|
builder.extraWithholding(double.tryParse(data['extraWithholding'].toString()));
|
||||||
|
}
|
||||||
|
if (data.containsKey('signature')) builder.signature(data['signature'] as String?);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import 'package:krow_domain/krow_domain.dart';
|
|||||||
|
|
||||||
abstract class TaxFormsRepository {
|
abstract class TaxFormsRepository {
|
||||||
Future<List<TaxForm>> getTaxForms();
|
Future<List<TaxForm>> getTaxForms();
|
||||||
Future<void> submitForm(TaxFormType type, Map<String, dynamic> data);
|
Future<void> updateI9Form(I9TaxForm form);
|
||||||
Future<void> updateFormStatus(TaxFormType type, TaxFormStatus status);
|
Future<void> submitI9Form(I9TaxForm form);
|
||||||
|
Future<void> updateW4Form(W4TaxForm form);
|
||||||
|
Future<void> submitW4Form(W4TaxForm form);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import 'package:krow_domain/krow_domain.dart';
|
||||||
|
import '../repositories/tax_forms_repository.dart';
|
||||||
|
|
||||||
|
class SaveI9FormUseCase {
|
||||||
|
final TaxFormsRepository _repository;
|
||||||
|
|
||||||
|
SaveI9FormUseCase(this._repository);
|
||||||
|
|
||||||
|
Future<void> call(I9TaxForm form) async {
|
||||||
|
return _repository.updateI9Form(form);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import 'package:krow_domain/krow_domain.dart';
|
||||||
|
import '../repositories/tax_forms_repository.dart';
|
||||||
|
|
||||||
|
class SaveW4FormUseCase {
|
||||||
|
final TaxFormsRepository _repository;
|
||||||
|
|
||||||
|
SaveW4FormUseCase(this._repository);
|
||||||
|
|
||||||
|
Future<void> call(W4TaxForm form) async {
|
||||||
|
return _repository.updateW4Form(form);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import 'package:krow_domain/krow_domain.dart';
|
||||||
|
import '../repositories/tax_forms_repository.dart';
|
||||||
|
|
||||||
|
class SubmitI9FormUseCase {
|
||||||
|
final TaxFormsRepository _repository;
|
||||||
|
|
||||||
|
SubmitI9FormUseCase(this._repository);
|
||||||
|
|
||||||
|
Future<void> call(I9TaxForm form) async {
|
||||||
|
return _repository.submitI9Form(form);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
import 'package:krow_domain/krow_domain.dart';
|
|
||||||
import '../repositories/tax_forms_repository.dart';
|
|
||||||
|
|
||||||
class SubmitTaxFormUseCase {
|
|
||||||
final TaxFormsRepository _repository;
|
|
||||||
|
|
||||||
SubmitTaxFormUseCase(this._repository);
|
|
||||||
|
|
||||||
Future<void> call(TaxFormType type, Map<String, dynamic> data) async {
|
|
||||||
return _repository.submitForm(type, data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import 'package:krow_domain/krow_domain.dart';
|
||||||
|
import '../repositories/tax_forms_repository.dart';
|
||||||
|
|
||||||
|
class SubmitW4FormUseCase {
|
||||||
|
final TaxFormsRepository _repository;
|
||||||
|
|
||||||
|
SubmitW4FormUseCase(this._repository);
|
||||||
|
|
||||||
|
Future<void> call(W4TaxForm form) async {
|
||||||
|
return _repository.submitW4Form(form);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,13 +1,15 @@
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:krow_domain/krow_domain.dart';
|
import 'package:krow_domain/krow_domain.dart';
|
||||||
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
import '../../../domain/usecases/submit_tax_form_usecase.dart';
|
import '../../../domain/usecases/submit_i9_form_usecase.dart';
|
||||||
import 'form_i9_state.dart';
|
import 'form_i9_state.dart';
|
||||||
|
|
||||||
class FormI9Cubit extends Cubit<FormI9State> {
|
class FormI9Cubit extends Cubit<FormI9State> {
|
||||||
final SubmitTaxFormUseCase _submitTaxFormUseCase;
|
final SubmitI9FormUseCase _submitI9FormUseCase;
|
||||||
|
String _formId = '';
|
||||||
|
|
||||||
FormI9Cubit(this._submitTaxFormUseCase) : super(const FormI9State());
|
FormI9Cubit(this._submitI9FormUseCase) : super(const FormI9State());
|
||||||
|
|
||||||
void initialize(TaxForm? form) {
|
void initialize(TaxForm? form) {
|
||||||
if (form == null || form.formData.isEmpty) {
|
if (form == null || form.formData.isEmpty) {
|
||||||
@@ -16,6 +18,7 @@ class FormI9Cubit extends Cubit<FormI9State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final Map<String, dynamic> data = form.formData;
|
final Map<String, dynamic> data = form.formData;
|
||||||
|
_formId = form.id;
|
||||||
emit(FormI9State(
|
emit(FormI9State(
|
||||||
firstName: data['firstName'] as String? ?? '',
|
firstName: data['firstName'] as String? ?? '',
|
||||||
lastName: data['lastName'] as String? ?? '',
|
lastName: data['lastName'] as String? ?? '',
|
||||||
@@ -83,18 +86,36 @@ class FormI9Cubit extends Cubit<FormI9State> {
|
|||||||
Future<void> submit() async {
|
Future<void> submit() async {
|
||||||
emit(state.copyWith(status: FormI9Status.submitting));
|
emit(state.copyWith(status: FormI9Status.submitting));
|
||||||
try {
|
try {
|
||||||
await _submitTaxFormUseCase(
|
final Map<String, dynamic> formData = {
|
||||||
TaxFormType.i9,
|
'firstName': state.firstName,
|
||||||
<String, dynamic>{
|
'lastName': state.lastName,
|
||||||
'firstName': state.firstName,
|
'middleInitial': state.middleInitial,
|
||||||
'lastName': state.lastName,
|
'otherLastNames': state.otherLastNames,
|
||||||
'middleInitial': state.middleInitial,
|
'dob': state.dob,
|
||||||
'citizenshipStatus': state.citizenshipStatus,
|
'ssn': state.ssn,
|
||||||
'ssn': state.ssn,
|
'email': state.email,
|
||||||
'signature': state.signature,
|
'phone': state.phone,
|
||||||
// ... add other fields as needed for backend
|
'address': state.address,
|
||||||
},
|
'aptNumber': state.aptNumber,
|
||||||
|
'city': state.city,
|
||||||
|
'state': state.state,
|
||||||
|
'zipCode': state.zipCode,
|
||||||
|
'citizenshipStatus': state.citizenshipStatus,
|
||||||
|
'uscisNumber': state.uscisNumber,
|
||||||
|
'admissionNumber': state.admissionNumber,
|
||||||
|
'passportNumber': state.passportNumber,
|
||||||
|
'countryIssuance': state.countryIssuance,
|
||||||
|
'preparerUsed': state.preparerUsed,
|
||||||
|
'signature': state.signature,
|
||||||
|
};
|
||||||
|
|
||||||
|
final I9TaxForm form = I9TaxForm(
|
||||||
|
id: _formId.isNotEmpty ? _formId : const Uuid().v4(),
|
||||||
|
title: 'Form I-9',
|
||||||
|
formData: formData,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await _submitI9FormUseCase(form);
|
||||||
emit(state.copyWith(status: FormI9Status.success));
|
emit(state.copyWith(status: FormI9Status.success));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:krow_domain/krow_domain.dart';
|
import 'package:krow_domain/krow_domain.dart';
|
||||||
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
import '../../../domain/usecases/submit_tax_form_usecase.dart';
|
import '../../../domain/usecases/submit_w4_form_usecase.dart';
|
||||||
import 'form_w4_state.dart';
|
import 'form_w4_state.dart';
|
||||||
|
|
||||||
class FormW4Cubit extends Cubit<FormW4State> {
|
class FormW4Cubit extends Cubit<FormW4State> {
|
||||||
final SubmitTaxFormUseCase _submitTaxFormUseCase;
|
final SubmitW4FormUseCase _submitW4FormUseCase;
|
||||||
|
String _formId = '';
|
||||||
|
|
||||||
FormW4Cubit(this._submitTaxFormUseCase) : super(const FormW4State());
|
FormW4Cubit(this._submitW4FormUseCase) : super(const FormW4State());
|
||||||
|
|
||||||
void initialize(TaxForm? form) {
|
void initialize(TaxForm? form) {
|
||||||
if (form == null || form.formData.isEmpty) {
|
if (form == null || form.formData.isEmpty) {
|
||||||
@@ -16,6 +18,7 @@ class FormW4Cubit extends Cubit<FormW4State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final Map<String, dynamic> data = form.formData;
|
final Map<String, dynamic> data = form.formData;
|
||||||
|
_formId = form.id;
|
||||||
|
|
||||||
// Combine address parts if needed, or take existing
|
// Combine address parts if needed, or take existing
|
||||||
final String city = data['city'] as String? ?? '';
|
final String city = data['city'] as String? ?? '';
|
||||||
@@ -76,18 +79,29 @@ class FormW4Cubit extends Cubit<FormW4State> {
|
|||||||
Future<void> submit() async {
|
Future<void> submit() async {
|
||||||
emit(state.copyWith(status: FormW4Status.submitting));
|
emit(state.copyWith(status: FormW4Status.submitting));
|
||||||
try {
|
try {
|
||||||
await _submitTaxFormUseCase(
|
final Map<String, dynamic> formData = {
|
||||||
TaxFormType.w4,
|
'firstName': state.firstName,
|
||||||
<String, dynamic>{
|
'lastName': state.lastName,
|
||||||
'firstName': state.firstName,
|
'ssn': state.ssn,
|
||||||
'lastName': state.lastName,
|
'address': state.address,
|
||||||
'ssn': state.ssn,
|
'cityStateZip': state.cityStateZip, // Note: Repository should split this if needed.
|
||||||
'filingStatus': state.filingStatus,
|
'filingStatus': state.filingStatus,
|
||||||
'multipleJobs': state.multipleJobs,
|
'multipleJobs': state.multipleJobs,
|
||||||
'signature': state.signature,
|
'qualifyingChildren': state.qualifyingChildren,
|
||||||
// ... add other fields as needed
|
'otherDependents': state.otherDependents,
|
||||||
},
|
'otherIncome': state.otherIncome,
|
||||||
|
'deductions': state.deductions,
|
||||||
|
'extraWithholding': state.extraWithholding,
|
||||||
|
'signature': state.signature,
|
||||||
|
};
|
||||||
|
|
||||||
|
final W4TaxForm form = W4TaxForm(
|
||||||
|
id: _formId.isNotEmpty ? _formId : const Uuid().v4(),
|
||||||
|
title: 'Form W-4',
|
||||||
|
formData: formData,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await _submitW4FormUseCase(form);
|
||||||
emit(state.copyWith(status: FormW4Status.success));
|
emit(state.copyWith(status: FormW4Status.success));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
|
|||||||
@@ -140,13 +140,13 @@ class TaxFormsPage extends StatelessWidget {
|
|||||||
|
|
||||||
Widget _buildFormCard(TaxForm form) {
|
Widget _buildFormCard(TaxForm form) {
|
||||||
// Helper to get icon based on type (could be in entity or a mapper)
|
// Helper to get icon based on type (could be in entity or a mapper)
|
||||||
final String icon = form.type == TaxFormType.i9 ? '🛂' : '📋';
|
final String icon = form is I9TaxForm ? '🛂' : '📋';
|
||||||
|
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (form.type == TaxFormType.i9) {
|
if (form is I9TaxForm) {
|
||||||
Modular.to.pushNamed('i9', arguments: form);
|
Modular.to.pushNamed('i9', arguments: form);
|
||||||
} else if (form.type == TaxFormType.w4) {
|
} else if (form is W4TaxForm) {
|
||||||
Modular.to.pushNamed('w4', arguments: form);
|
Modular.to.pushNamed('w4', arguments: form);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ import 'package:krow_domain/krow_domain.dart';
|
|||||||
import 'data/repositories/tax_forms_repository_impl.dart';
|
import 'data/repositories/tax_forms_repository_impl.dart';
|
||||||
import 'domain/repositories/tax_forms_repository.dart';
|
import 'domain/repositories/tax_forms_repository.dart';
|
||||||
import 'domain/usecases/get_tax_forms_usecase.dart';
|
import 'domain/usecases/get_tax_forms_usecase.dart';
|
||||||
import 'domain/usecases/submit_tax_form_usecase.dart';
|
import 'domain/usecases/submit_i9_form_usecase.dart';
|
||||||
|
import 'domain/usecases/submit_w4_form_usecase.dart';
|
||||||
import 'presentation/blocs/i9/form_i9_cubit.dart';
|
import 'presentation/blocs/i9/form_i9_cubit.dart';
|
||||||
import 'presentation/blocs/tax_forms/tax_forms_cubit.dart';
|
import 'presentation/blocs/tax_forms/tax_forms_cubit.dart';
|
||||||
import 'presentation/blocs/w4/form_w4_cubit.dart';
|
import 'presentation/blocs/w4/form_w4_cubit.dart';
|
||||||
@@ -25,7 +26,8 @@ class StaffTaxFormsModule extends Module {
|
|||||||
|
|
||||||
// Use Cases
|
// Use Cases
|
||||||
i.addLazySingleton(GetTaxFormsUseCase.new);
|
i.addLazySingleton(GetTaxFormsUseCase.new);
|
||||||
i.addLazySingleton(SubmitTaxFormUseCase.new);
|
i.addLazySingleton(SubmitI9FormUseCase.new);
|
||||||
|
i.addLazySingleton(SubmitW4FormUseCase.new);
|
||||||
|
|
||||||
// Blocs
|
// Blocs
|
||||||
i.addLazySingleton(TaxFormsCubit.new);
|
i.addLazySingleton(TaxFormsCubit.new);
|
||||||
|
|||||||
Reference in New Issue
Block a user