Files
QuantEngineByItz/oms-wms-erp/src/components/fields/domain/TaxIDField/TaxIDField.vue
T
kjh2064 b34b0dd7d6
Validators (Pushes and Pull Requests) / UI & Storage Validation (pull_request) Failing after 12s
Validators (Pushes and Pull Requests) / Database & Schema Validation (pull_request) Successful in 13s
Validators (Pushes and Pull Requests) / Core Validators & Database Setup (pull_request) Failing after 28s
Validators (Pushes and Pull Requests) / WBS & Audit Validations (pull_request) Has been skipped
Validators (Pushes and Pull Requests) / .NET Contracts (pull_request) Has been skipped
Validators (Pushes and Pull Requests) / Calibration & Performance (pull_request) Has been skipped
Validators (Pushes and Pull Requests) / Operational Report & Decision Packet (pull_request) Has been skipped
Validators (Pushes and Pull Requests) / CI Workflow Lint (pull_request) Failing after 10s
Validators (Pushes and Pull Requests) / Security & Secrets (pull_request) Successful in 12s
Validators (Pushes and Pull Requests) / Notify PR Results (pull_request) Successful in 2s
Frontend CI Pipeline / ci-frontend-8-steps (pull_request) Failing after 2m50s
Add OMS WMS ERP platform
2026-07-27 00:45:39 +09:00

396 lines
9.6 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="tax-id-field">
<div class="tax-id-group">
<!-- Country Selection -->
<div class="form-group">
<label class="form-label">
Country
<span class="text-danger">*</span>
</label>
<select
v-model="taxId.country"
class="form-control"
:class="{ 'is-invalid': countryError }"
@blur="validateCountry"
>
<option value="">-- Select Country --</option>
<option value="KR">South Korea (사업자등록번호)</option>
<option value="US">United States (EIN)</option>
<option value="JP">Japan (法人番号)</option>
<option value="CN">China (统一社会信用代码)</option>
<option value="SG">Singapore (UEN)</option>
<option value="TW">Taiwan (統一編號)</option>
</select>
<div v-if="countryError" class="invalid-feedback d-block">
{{ countryError }}
</div>
</div>
<!-- Tax ID Number -->
<div class="form-group col-full">
<label class="form-label">
{{ getFieldLabel() }}
<span class="text-danger">*</span>
</label>
<input
v-model="taxId.taxIdNumber"
type="text"
class="form-control"
:class="{ 'is-invalid': taxIdError }"
:placeholder="getPlaceholder()"
@blur="validateTaxId"
/>
<small class="text-muted d-block mt-1">
{{ getFormatHint() }}
</small>
<div v-if="taxIdError" class="invalid-feedback d-block">
{{ taxIdError }}
</div>
</div>
<!-- Company/Individual Type (optional) -->
<div class="form-group">
<label class="form-label">Type</label>
<select v-model="taxId.entityType" class="form-control">
<option value="">-- Select Type --</option>
<option value="INDIVIDUAL">Individual</option>
<option value="BUSINESS">Business</option>
<option value="CORPORATION">Corporation</option>
<option value="PARTNERSHIP">Partnership</option>
</select>
</div>
<!-- Verification Status (optional) -->
<div class="form-group">
<label class="form-label">Verification Status</label>
<select v-model="taxId.verificationStatus" class="form-control">
<option value="">-- Not Verified --</option>
<option value="PENDING">Pending</option>
<option value="VERIFIED">Verified</option>
<option value="FAILED">Failed</option>
</select>
</div>
</div>
<!-- Tax ID Summary -->
<div v-if="isComplete" class="tax-id-summary mt-2">
<small class="text-muted">
📋 <strong>{{ getCountryName(taxId.country) }}</strong>
{{ formatTaxId() }}
<span v-if="taxId.verificationStatus" class="badge" :class="getStatusBadgeClass()">
{{ taxId.verificationStatus }}
</span>
</small>
</div>
<!-- Validation Status -->
<div v-if="hasErrors" class="alert alert-danger mt-2">
Please complete all required fields
</div>
<div v-if="isComplete && !hasErrors" class="alert alert-success mt-2">
Tax ID information is valid
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
interface TaxID {
country: string
taxIdNumber: string
entityType?: string
verificationStatus?: string
}
const props = defineProps<{
modelValue: TaxID | null
}>()
const emit = defineEmits<{
'update:modelValue': [value: TaxID | null]
}>()
// State
const taxId = ref<TaxID>({
country: props.modelValue?.country || '',
taxIdNumber: props.modelValue?.taxIdNumber || '',
entityType: props.modelValue?.entityType || '',
verificationStatus: props.modelValue?.verificationStatus || ''
})
const countryError = ref<string | null>(null)
const taxIdError = ref<string | null>(null)
// Computed
const isComplete = computed(() => {
return (
taxId.value.country.length > 0 &&
taxId.value.taxIdNumber.trim().length > 0 &&
!hasErrors.value
)
})
const hasErrors = computed(() => {
return countryError.value !== null || taxIdError.value !== null
})
// Methods
const validateCountry = () => {
countryError.value = null
if (!taxId.value.country) {
countryError.value = 'Please select a country'
return
}
emitUpdate()
}
const validateTaxId = () => {
taxIdError.value = null
if (!taxId.value.taxIdNumber.trim()) {
taxIdError.value = 'Tax ID is required'
return
}
// Validate based on country
const country = taxId.value.country
const value = taxId.value.taxIdNumber.trim()
if (country === 'KR') {
// Korean: 사업자등록번호 (10 digits with optional hyphens: XXX-XX-XXXXX)
if (!/^(\d{3}-\d{2}-\d{5}|\d{10})$/.test(value)) {
taxIdError.value = 'Korean Tax ID format: XXX-XX-XXXXX or 10 digits'
return
}
} else if (country === 'US') {
// US: EIN (9 digits with optional hyphen: XX-XXXXXXX)
if (!/^(\d{2}-\d{7}|\d{9})$/.test(value)) {
taxIdError.value = 'US EIN format: XX-XXXXXXX or 9 digits'
return
}
} else if (country === 'JP') {
// Japan: 12 digits (XXXXXXXXXXXX)
if (!/^\d{12,13}$/.test(value)) {
taxIdError.value = 'Japan Tax ID format: 12-13 digits'
return
}
} else if (country === 'CN') {
// China: 18 digits (统一社会信用代码)
if (!/^\d{18}$/.test(value)) {
taxIdError.value = 'China Tax ID format: 18 digits'
return
}
} else if (country === 'SG') {
// Singapore: UEN (9 digits with optional hyphen: XXXXXXXXX or XXX-XXXXXX)
if (!/^(\d{9}|\d{3}-\d{6})$/.test(value)) {
taxIdError.value = 'Singapore UEN format: 9 digits or XXX-XXXXXX'
return
}
} else if (country === 'TW') {
// Taiwan: 8 digits (統一編號)
if (!/^\d{8}$/.test(value)) {
taxIdError.value = 'Taiwan Tax ID format: 8 digits'
return
}
}
emitUpdate()
}
const emitUpdate = () => {
if (isComplete.value) {
emit('update:modelValue', { ...taxId.value })
}
}
const getFieldLabel = (): string => {
const labels: Record<string, string> = {
KR: 'Business Registration Number (사업자등록번호)',
US: 'Employer Identification Number (EIN)',
JP: 'Corporate Number (法人番号)',
CN: 'Unified Social Credit Code (统一社会信用代码)',
SG: 'Unique Entity Number (UEN)',
TW: 'Uniform Number (統一編號)'
}
return labels[taxId.value.country] || 'Tax ID Number'
}
const getPlaceholder = (): string => {
const placeholders: Record<string, string> = {
KR: 'e.g., 123-45-67890',
US: 'e.g., 12-3456789',
JP: 'e.g., 1234567890123',
CN: 'e.g., 123456789012345678',
SG: 'e.g., 123456789 or 123-456789',
TW: 'e.g., 12345678'
}
return placeholders[taxId.value.country] || ''
}
const getFormatHint = (): string => {
const hints: Record<string, string> = {
KR: 'Format: XXX-XX-XXXXX (10 digits)',
US: 'Format: XX-XXXXXXX (9 digits)',
JP: 'Format: 12-13 digits',
CN: 'Format: 18 digits',
SG: 'Format: 9 digits or XXX-XXXXXX',
TW: 'Format: 8 digits'
}
return hints[taxId.value.country] || 'Format: country-specific'
}
const getCountryName = (code: string): string => {
const countries: Record<string, string> = {
KR: 'South Korea',
US: 'United States',
JP: 'Japan',
CN: 'China',
SG: 'Singapore',
TW: 'Taiwan'
}
return countries[code] || 'Country'
}
const formatTaxId = (): string => {
const value = taxId.value.taxIdNumber
const country = taxId.value.country
// Format with country indicator
return `${getCountryName(country)}: ${value}`
}
const getStatusBadgeClass = (): string => {
const status = taxId.value.verificationStatus
if (status === 'VERIFIED') return 'bg-success'
if (status === 'PENDING') return 'bg-warning'
if (status === 'FAILED') return 'bg-danger'
return 'bg-secondary'
}
</script>
<style scoped>
.tax-id-field {
margin-bottom: 1.5rem;
}
.tax-id-group {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1rem;
padding: 1rem;
border: 1px solid #dee2e6;
border-radius: 4px;
background-color: #f8f9fa;
}
.col-full {
grid-column: 1 / -1;
}
.form-group {
display: flex;
flex-direction: column;
}
.form-label {
font-weight: 500;
margin-bottom: 0.5rem;
font-size: 0.875rem;
}
.form-control {
border-radius: 4px;
border: 1px solid #dee2e6;
padding: 0.5rem 0.75rem;
font-size: 0.875rem;
font-family: inherit;
}
.form-control:focus {
border-color: #80bdff;
outline: 0;
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
}
.form-control.is-invalid {
border-color: #dc3545;
}
.text-danger {
color: #dc3545;
margin-left: 0.25rem;
}
.text-muted {
color: #6c757d;
font-size: 0.875rem;
}
.invalid-feedback {
color: #dc3545;
font-size: 0.875rem;
margin-top: 0.25rem;
display: block;
}
.d-block {
display: block;
}
.mt-1 {
margin-top: 0.25rem;
}
.mt-2 {
margin-top: 0.5rem;
}
.tax-id-summary {
padding: 0.75rem;
background-color: #e7f3ff;
border-left: 3px solid #0d6efd;
border-radius: 4px;
font-size: 0.875rem;
line-height: 1.6;
}
.badge {
font-size: 0.7rem;
padding: 0.25rem 0.5rem;
margin-left: 0.5rem;
vertical-align: middle;
}
.alert {
padding: 0.75rem 1rem;
border-radius: 4px;
font-size: 0.875rem;
border: 1px solid transparent;
margin-bottom: 1rem;
}
.alert-danger {
background-color: #f8d7da;
color: #721c24;
border-color: #f5c6cb;
}
.alert-success {
background-color: #d4edda;
color: #155724;
border-color: #c3e6cb;
}
small {
display: block;
}
strong {
font-weight: 600;
}
</style>