增加选项及日期显示优化
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
v-model="dateModel"
|
||||
:input-id="inputId"
|
||||
class="localized-date-picker"
|
||||
date-format="yy-mm-dd"
|
||||
:date-format="dateFormat"
|
||||
:placeholder="placeholder"
|
||||
:input-class="inputClass"
|
||||
:pt="datePickerPt"
|
||||
@@ -36,6 +36,7 @@ const props = withDefaults(defineProps<{
|
||||
name?: string
|
||||
testId: string
|
||||
panelTestId?: string
|
||||
dateFormat?: string
|
||||
placeholder?: string
|
||||
inputClass?: string | Record<string, boolean>
|
||||
invalid?: boolean
|
||||
@@ -46,6 +47,7 @@ const props = withDefaults(defineProps<{
|
||||
}>(), {
|
||||
name: undefined,
|
||||
panelTestId: undefined,
|
||||
dateFormat: 'yy-mm-dd',
|
||||
placeholder: '',
|
||||
inputClass: undefined,
|
||||
invalid: false,
|
||||
|
||||
@@ -412,7 +412,7 @@ export default {
|
||||
peoplePerRoom: 'People per room',
|
||||
arrival: 'Arrival',
|
||||
departure: 'Departure',
|
||||
datePlaceholder: 'yyyy-MM-dd',
|
||||
datePlaceholder: 'dd/MM/yyyy',
|
||||
roomType: 'Room type',
|
||||
roomTypePlaceholder: 'e.g. RM2/RM3',
|
||||
titleColumn: 'Title',
|
||||
@@ -441,7 +441,7 @@ export default {
|
||||
nonNegativeInteger: 'Must be 0 or a positive integer.',
|
||||
arrivalRequired: 'Enter arrival date.',
|
||||
departureRequired: 'Enter departure date.',
|
||||
dateFormat: 'Date format must be yyyy-MM-dd.',
|
||||
dateFormat: 'Date format must be dd/MM/yyyy.',
|
||||
departureAfterArrival: 'Departure must be later than arrival.',
|
||||
roomTypeRequired: 'Enter room type.',
|
||||
paymentTypeRequired: 'Select payment type.',
|
||||
|
||||
@@ -412,7 +412,7 @@ export default {
|
||||
peoplePerRoom: 'จำนวนคนต่อห้อง',
|
||||
arrival: 'Arrival',
|
||||
departure: 'Departure',
|
||||
datePlaceholder: 'yyyy-MM-dd',
|
||||
datePlaceholder: 'dd/MM/yyyy',
|
||||
roomType: 'Room Type',
|
||||
roomTypePlaceholder: 'เช่น RM2/RM3',
|
||||
titleColumn: 'Title',
|
||||
@@ -441,7 +441,7 @@ export default {
|
||||
nonNegativeInteger: 'ต้องเป็น 0 หรือจำนวนเต็มบวก',
|
||||
arrivalRequired: 'กรุณากรอก Arrival',
|
||||
departureRequired: 'กรุณากรอก Departure',
|
||||
dateFormat: 'รูปแบบวันที่ต้องเป็น yyyy-MM-dd',
|
||||
dateFormat: 'รูปแบบวันที่ต้องเป็น dd/MM/yyyy',
|
||||
departureAfterArrival: 'Departure ต้องอยู่หลัง Arrival',
|
||||
roomTypeRequired: 'กรุณากรอก Room Type',
|
||||
paymentTypeRequired: 'กรุณาเลือก Payment Type',
|
||||
|
||||
@@ -411,7 +411,7 @@ export default {
|
||||
peoplePerRoom: '每间房人数',
|
||||
arrival: '入住日期',
|
||||
departure: '离店日期',
|
||||
datePlaceholder: 'yyyy-MM-dd',
|
||||
datePlaceholder: 'dd/MM/yyyy',
|
||||
roomType: '房型',
|
||||
roomTypePlaceholder: '例如 RM2/RM3',
|
||||
titleColumn: '称谓(Title)',
|
||||
@@ -440,7 +440,7 @@ export default {
|
||||
nonNegativeInteger: '必须是 0 或正整数。',
|
||||
arrivalRequired: '请填写入住日期。',
|
||||
departureRequired: '请填写离店日期。',
|
||||
dateFormat: '日期格式必须为 yyyy-MM-dd。',
|
||||
dateFormat: '日期格式必须为 dd/MM/yyyy。',
|
||||
departureAfterArrival: '离店日期必须晚于入住日期。',
|
||||
roomTypeRequired: '请填写房型。',
|
||||
paymentTypeRequired: '请选择付款方式。',
|
||||
|
||||
@@ -182,8 +182,16 @@ const menuLabelKeys: Record<string, string> = {
|
||||
SYSTEM_RESERVATION_CATALOGS: 'nav.systemReservationCatalogs',
|
||||
}
|
||||
|
||||
const navItems = computed(() => authStore.visibleMenus)
|
||||
const brandTarget = computed(() => resolveAuthenticatedEntryPath(authStore.firstMenuPath))
|
||||
const defaultSidebarMenuCodes = new Set([
|
||||
'RESERVATION_MANUAL_INVOICE',
|
||||
'RESERVATION_INVOICES',
|
||||
'RESERVATION_ROOMING_LIST',
|
||||
'RESERVATION_ROOMING_LISTS',
|
||||
'SYSTEM_SETTINGS',
|
||||
])
|
||||
|
||||
const navItems = computed(() => authStore.visibleMenus.filter((menu) => defaultSidebarMenuCodes.has(menu.menu_code)))
|
||||
const brandTarget = computed(() => resolveAuthenticatedEntryPath(navItems.value[0]?.route_path ?? authStore.firstMenuPath))
|
||||
const currentUserLabel = computed(() => authStore.user?.display_name || authStore.user?.username || '-')
|
||||
const currentHotelLabel = computed(() => {
|
||||
const hotel = authStore.selectedHotel
|
||||
|
||||
@@ -573,6 +573,99 @@ describe('ReservationManualInvoiceView', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('prefills TEIGER TRAVEL directory data and submits its stable recipient codes', async () => {
|
||||
vi.mocked(service.generateManualReservationInvoice).mockResolvedValue(createResult())
|
||||
const wrapper = mountView()
|
||||
|
||||
await wrapper.find('[data-testid="recipient-company-code"]').setValue('TEIGER_TRAVEL')
|
||||
|
||||
expect((wrapper.find('[data-testid="recipient-contact-id"]').element as HTMLSelectElement).value).toBe(
|
||||
'TEIGER_TRAVEL_KHUN_NUTKRITTA_UPAMAI',
|
||||
)
|
||||
expect((wrapper.find('[data-testid="recipient-company-input"]').element as HTMLInputElement).value).toBe(
|
||||
'TEIGER TRAVEL GROUP CO.,LTD',
|
||||
)
|
||||
expect((wrapper.find('[data-testid="recipient-attention-input"]').element as HTMLInputElement).value).toBe(
|
||||
'Khun Nutkritta Upamai (Kwan)',
|
||||
)
|
||||
expect((wrapper.find('[data-testid="recipient-address"]').element as HTMLTextAreaElement).value).toBe(
|
||||
'20,22 Soi Rinrada,Srinakarin, Pattanakarn,Bangkok,TH, 10250',
|
||||
)
|
||||
expect((wrapper.find('[data-testid="recipient-telephone"]').element as HTMLInputElement).value).toBe(
|
||||
'063-516-8294',
|
||||
)
|
||||
expect((wrapper.find('[data-testid="recipient-email"]').element as HTMLInputElement).value).toBe(
|
||||
'nutkritta.upamai@gmail.com',
|
||||
)
|
||||
|
||||
await fillMinimumInvoiceForm(wrapper)
|
||||
await wrapper.find('[data-testid="manual-invoice-submit"]').trigger('click')
|
||||
await flushPromises()
|
||||
|
||||
expect(service.generateManualReservationInvoice).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
invoice_payload: expect.objectContaining({
|
||||
recipient: expect.objectContaining({
|
||||
company_code: 'TEIGER_TRAVEL',
|
||||
contact_id: 'TEIGER_TRAVEL_KHUN_NUTKRITTA_UPAMAI',
|
||||
company: 'TEIGER TRAVEL GROUP CO.,LTD',
|
||||
attention: 'Khun Nutkritta Upamai (Kwan)',
|
||||
address: '20,22 Soi Rinrada,Srinakarin, Pattanakarn,Bangkok,TH, 10250',
|
||||
telephone: '063-516-8294',
|
||||
email: 'nutkritta.upamai@gmail.com',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('prefills CLOVER HOLIDAY directory data and submits its stable recipient codes', async () => {
|
||||
vi.mocked(service.generateManualReservationInvoice).mockResolvedValue(createResult())
|
||||
const wrapper = mountView()
|
||||
|
||||
await wrapper.find('[data-testid="recipient-company-code"]').setValue('CLOVER_HOLIDAY')
|
||||
|
||||
expect((wrapper.find('[data-testid="recipient-contact-id"]').element as HTMLSelectElement).value).toBe(
|
||||
'CLOVER_HOLIDAY_KHUN_JANE',
|
||||
)
|
||||
expect((wrapper.find('[data-testid="recipient-company-input"]').element as HTMLInputElement).value).toBe(
|
||||
'CLOVER HOLIDAY',
|
||||
)
|
||||
expect((wrapper.find('[data-testid="recipient-attention-input"]').element as HTMLInputElement).value).toBe(
|
||||
'KHUN JANE',
|
||||
)
|
||||
expect((wrapper.find('[data-testid="recipient-address"]').element as HTMLTextAreaElement).value).toBe(
|
||||
'290 SOI Lat Phrao 94 (Panjamit), Phlapphla Subdistrict, 555,555/1 Moo 1, Na Jomtien, Sattahip',
|
||||
)
|
||||
expect((wrapper.find('[data-testid="recipient-telephone"]').element as HTMLInputElement).value).toBe(
|
||||
'083-1470352',
|
||||
)
|
||||
expect((wrapper.find('[data-testid="recipient-email"]').element as HTMLInputElement).value).toBe(
|
||||
'longtai.maleemalee@gmail.com',
|
||||
)
|
||||
|
||||
await fillMinimumInvoiceForm(wrapper)
|
||||
await wrapper.find('[data-testid="manual-invoice-submit"]').trigger('click')
|
||||
await flushPromises()
|
||||
|
||||
expect(service.generateManualReservationInvoice).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
invoice_payload: expect.objectContaining({
|
||||
recipient: expect.objectContaining({
|
||||
company_code: 'CLOVER_HOLIDAY',
|
||||
contact_id: 'CLOVER_HOLIDAY_KHUN_JANE',
|
||||
company: 'CLOVER HOLIDAY',
|
||||
attention: 'KHUN JANE',
|
||||
address:
|
||||
'290 SOI Lat Phrao 94 (Panjamit), Phlapphla Subdistrict, 555,555/1 Moo 1, Na Jomtien, Sattahip',
|
||||
telephone: '083-1470352',
|
||||
email: 'longtai.maleemalee@gmail.com',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('downloads the generated PDF through a browser blob URL', async () => {
|
||||
vi.mocked(service.generateManualReservationInvoice).mockResolvedValue(createResult())
|
||||
const fetchMock = vi.fn().mockResolvedValue({
|
||||
|
||||
@@ -194,21 +194,29 @@ describe('ReservationAppShell', () => {
|
||||
expect(wrapper.text()).toContain('Order list')
|
||||
})
|
||||
|
||||
it('renders navigation from backend menus and hides Debug EML when the menu is absent', async () => {
|
||||
const wrapper = await mountShell()
|
||||
it('limits the default sidebar to manual invoice, rooming list, and system settings', async () => {
|
||||
const wrapper = await mountShell([
|
||||
createReservationOrdersMenu(),
|
||||
createReservationTasksMenu(),
|
||||
createDebugMenu(),
|
||||
createManualInvoiceMenu(),
|
||||
createRoomingListMenu(),
|
||||
createSystemMenu(),
|
||||
])
|
||||
|
||||
expect(wrapper.find('a[href="/reservation/orders"]').exists()).toBe(true)
|
||||
expect(wrapper.find('a[href="/reservation/tasks"]').exists()).toBe(true)
|
||||
expect(wrapper.text()).toContain('预订事项')
|
||||
expect(wrapper.text()).not.toContain('任务队列')
|
||||
expect(wrapper.find('a[href="/debug/eml-superagent"]').exists()).toBe(false)
|
||||
expect(wrapper.find('nav a[href="/reservation/invoices/new"]').exists()).toBe(true)
|
||||
expect(wrapper.find('nav a[href="/reservation/rooming-lists/new"]').exists()).toBe(true)
|
||||
expect(wrapper.find('nav a[href="/system"]').exists()).toBe(true)
|
||||
expect(wrapper.find('nav a[href="/reservation/orders"]').exists()).toBe(false)
|
||||
expect(wrapper.find('nav a[href="/reservation/tasks"]').exists()).toBe(false)
|
||||
expect(wrapper.find('nav a[href="/debug/eml-superagent"]').exists()).toBe(false)
|
||||
expect(wrapper.findAll('nav a')).toHaveLength(3)
|
||||
})
|
||||
|
||||
it('shows Debug EML only when backend menus include it', async () => {
|
||||
it('hides Debug EML from the default sidebar even when backend menus include it', async () => {
|
||||
const wrapper = await mountShell([createReservationOrdersMenu(), createDebugMenu()])
|
||||
|
||||
expect(wrapper.find('a[href="/debug/eml-superagent"]').exists()).toBe(true)
|
||||
expect(wrapper.text()).toContain('Debug EML')
|
||||
expect(wrapper.find('nav a[href="/debug/eml-superagent"]').exists()).toBe(false)
|
||||
})
|
||||
|
||||
it('shows Manual Invoice only when backend menus include it', async () => {
|
||||
|
||||
@@ -157,6 +157,8 @@ describe('ReservationRoomingListGenerationView', () => {
|
||||
|
||||
expect(wrapper.find('[data-testid="rooming-list-arrival"]').exists()).toBe(true)
|
||||
expect(wrapper.find('[data-testid="rooming-list-departure"]').exists()).toBe(true)
|
||||
expect(wrapper.find('[data-testid="rooming-list-arrival"]').attributes('placeholder')).toBe('dd/MM/yyyy')
|
||||
expect(wrapper.find('[data-testid="rooming-list-departure"]').attributes('placeholder')).toBe('dd/MM/yyyy')
|
||||
expect(wrapper.find('[data-testid="rooming-list-room-type"]').attributes('placeholder')).toBe('例如 RM2/RM3')
|
||||
expect(wrapper.find('[data-testid="rooming-list-title"]').exists()).toBe(false)
|
||||
expect(wrapper.find('[data-testid="rooming-list-rate-code"]').exists()).toBe(false)
|
||||
@@ -247,8 +249,8 @@ describe('ReservationRoomingListGenerationView', () => {
|
||||
})
|
||||
const wrapper = mountView()
|
||||
await fillRequiredFields(wrapper)
|
||||
await wrapper.find('[data-testid="rooming-list-arrival"]').setValue('2026-05-10')
|
||||
await wrapper.find('[data-testid="rooming-list-departure"]').setValue('2026-05-16')
|
||||
await wrapper.find('[data-testid="rooming-list-arrival"]').setValue('10/05/2026')
|
||||
await wrapper.find('[data-testid="rooming-list-departure"]').setValue('16/05/2026')
|
||||
|
||||
await wrapper.find('[data-testid="rooming-list-submit"]').trigger('click')
|
||||
await flushPromises()
|
||||
@@ -264,8 +266,8 @@ describe('ReservationRoomingListGenerationView', () => {
|
||||
it('blocks submit when optional manual stay dates are incomplete or out of order', async () => {
|
||||
const wrapper = mountView()
|
||||
await fillRequiredFields(wrapper)
|
||||
await wrapper.find('[data-testid="rooming-list-arrival"]').setValue('2026-05-16')
|
||||
await wrapper.find('[data-testid="rooming-list-departure"]').setValue('2026-05-10')
|
||||
await wrapper.find('[data-testid="rooming-list-arrival"]').setValue('16/05/2026')
|
||||
await wrapper.find('[data-testid="rooming-list-departure"]').setValue('10/05/2026')
|
||||
|
||||
await wrapper.find('[data-testid="rooming-list-submit"]').trigger('click')
|
||||
await flushPromises()
|
||||
@@ -357,7 +359,7 @@ describe('ReservationRoomingListGenerationView', () => {
|
||||
|
||||
await wrapper.find('[data-testid="rooming-list-submit"]').trigger('click')
|
||||
await flushPromises()
|
||||
await wrapper.find('[data-testid="rooming-list-arrival"]').setValue('2026-05-10')
|
||||
await wrapper.find('[data-testid="rooming-list-arrival"]').setValue('10/05/2026')
|
||||
await flushPromises()
|
||||
|
||||
expect(wrapper.text()).not.toContain('Enter arrival date.')
|
||||
@@ -365,7 +367,7 @@ describe('ReservationRoomingListGenerationView', () => {
|
||||
expect(wrapper.find('[data-testid="rooming-list-arrival"]').classes()).not.toContain('is-invalid')
|
||||
expect(wrapper.find('[data-testid="rooming-list-departure"]').classes()).toContain('is-invalid')
|
||||
|
||||
await wrapper.find('[data-testid="rooming-list-departure"]').setValue('2026-05-16')
|
||||
await wrapper.find('[data-testid="rooming-list-departure"]').setValue('16/05/2026')
|
||||
await flushPromises()
|
||||
|
||||
expect(wrapper.text()).not.toContain('Enter departure date.')
|
||||
|
||||
@@ -883,6 +883,32 @@ const recipientCompanies: RecipientCompanySeed[] = [
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
company_code: 'TEIGER_TRAVEL',
|
||||
company: 'TEIGER TRAVEL GROUP CO.,LTD',
|
||||
contacts: [
|
||||
{
|
||||
contact_id: 'TEIGER_TRAVEL_KHUN_NUTKRITTA_UPAMAI',
|
||||
attention: 'Khun Nutkritta Upamai (Kwan)',
|
||||
address: '20,22 Soi Rinrada,Srinakarin, Pattanakarn,Bangkok,TH, 10250',
|
||||
telephone: '063-516-8294',
|
||||
email: 'nutkritta.upamai@gmail.com',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
company_code: 'CLOVER_HOLIDAY',
|
||||
company: 'CLOVER HOLIDAY',
|
||||
contacts: [
|
||||
{
|
||||
contact_id: 'CLOVER_HOLIDAY_KHUN_JANE',
|
||||
attention: 'KHUN JANE',
|
||||
address: '290 SOI Lat Phrao 94 (Panjamit), Phlapphla Subdistrict, 555,555/1 Moo 1, Na Jomtien, Sattahip',
|
||||
telephone: '083-1470352',
|
||||
email: 'longtai.maleemalee@gmail.com',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
company_code: 'HANATOUR',
|
||||
company: 'HANATOUR TD CO., LTD.',
|
||||
|
||||
@@ -128,6 +128,7 @@
|
||||
v-model="form.arrival"
|
||||
input-id="rooming-list-arrival-input"
|
||||
test-id="rooming-list-arrival"
|
||||
date-format="dd/mm/yy"
|
||||
:placeholder="t('roomingList.datePlaceholder')"
|
||||
:input-class="validationClass('arrival')"
|
||||
:invalid="Boolean(fieldError('arrival'))"
|
||||
@@ -153,6 +154,7 @@
|
||||
v-model="form.departure"
|
||||
input-id="rooming-list-departure-input"
|
||||
test-id="rooming-list-departure"
|
||||
date-format="dd/mm/yy"
|
||||
:placeholder="t('roomingList.datePlaceholder')"
|
||||
:input-class="validationClass('departure')"
|
||||
:invalid="Boolean(fieldError('departure'))"
|
||||
|
||||
Reference in New Issue
Block a user