// Actual UI price saving against a synthetic API, without live business writes. const test = require('node:test'); const assert = require('node:assert/strict'); const {harness,response} = require('./helpers/arr_ui_harness.cjs'); const fixture=(price=null)=>({case_id:'fixture-case',case_status:'open',revision:1, completed_items:price===null?0:1,total_items:1, items:[{item_id:1,company_key:'Fixture Company',rate_code:'FIXTURE',effective_rate_amount:100, real_price:price,affected_records:1,affected_rooms:1,affected_room_nights:2,candidate_prices:[]}], pagination:{offset:0,limit:50,total:1}}); test('zero is a valid explicit price while blank and negative prices remain invalid',()=>{ const h=harness(()=>{throw new Error('unexpected request');}); for(const value of [0,'0',' 0 ','1500']) assert.equal(h.strictReviewPrice(value),true); for(const value of [null,undefined,'',' ','-1',-1,'0.00','1.5','abc']) assert.equal(h.strictReviewPrice(value),false); assert.equal(h.reviewPriceInput(0),'0'); assert.equal(h.reviewPriceInput('0'),'0'); assert.equal(h.reviewPriceInput(null),''); }); test('zero is rendered as a saved value and permits continuation when all items are saved',()=>{ const h=harness(()=>{throw new Error('unexpected request');}); h.renderDailyPriceReview(fixture(0)); const html=h.element('#daily-price-review-body').innerHTML; assert.match(html,/value="0"/); assert.match(html,/daily-price-review-row is-complete/); assert.match(html,/daily_review.saved/); assert.equal(h.element('#daily-price-review-finalize').disabled,false); h.renderDailyPriceReview(fixture(null)); assert.match(h.element('#daily-price-review-body').innerHTML,/value=""/); assert.match(h.element('#daily-price-review-body').innerHTML,/daily_review.price_placeholder/); assert.equal(h.element('#daily-price-review-body').innerHTML.includes('placeholder="0"'),false); assert.equal(h.element('#daily-price-review-finalize').disabled,true); }); test('explicit zero is saved as zero and survives reloading without becoming empty',async()=>{ let current=fixture(); const h=harness((url,options)=>{ if(options.method==='PATCH') { assert.equal(url,'/api/jobs/fixture-job/review/items/1'); const decision=JSON.parse(options.body); assert.deepEqual(decision,{case_id:'fixture-case',revision:1,real_price:'0'}); current={...fixture('0'),revision:2}; return response(200,{revision:2}); } if(url.startsWith('/api/jobs/fixture-job/review?')) return response(200,current); if(url.startsWith('/api/jobs?')) return response(200,[]); throw new Error('unexpected request '+url); }); h.state.dailyPriceReview=fixture(); h.state.dailyPriceReviewJobId='fixture-job'; const input={value:'0',focus(){}}; const row={dataset:{dailyPriceReviewItemId:'1'},querySelector:()=>input}; await h.saveDailyPriceReviewItem({closest:()=>row}); assert.equal(h.calls.filter(c=>c.method==='PATCH').length,1); assert.equal(h.state.dailyPriceReview.items[0].real_price,'0'); assert.equal(h.state.dailyPriceReview.completed_items,1); assert.equal(h.dailyPriceReviewCanFinalize(h.state.dailyPriceReview),true); assert.equal(h.element('#daily-price-review-finalize').disabled,false); assert.match(h.element('#daily-price-review-body').innerHTML,/value="0"/); await h.loadDailyPriceReview(); assert.equal(h.state.dailyPriceReview.items[0].real_price,'0'); assert.equal(h.calls.filter(c=>c.method==='PATCH').length,1,'reload must not submit another decision'); }); test('an empty input cannot be silently saved as zero',async()=>{ const h=harness(()=>{throw new Error('unexpected request');}); h.state.dailyPriceReview=fixture(); h.state.dailyPriceReviewJobId='fixture-job'; const input={value:'',focus(){}}; const row={dataset:{dailyPriceReviewItemId:'1'},querySelector:()=>input}; await h.saveDailyPriceReviewItem({closest:()=>row}); assert.equal(h.calls.length,0); assert.equal(h.state.dailyPriceReview.items[0].real_price,null); assert.equal(h.dailyPriceReviewCanFinalize(h.state.dailyPriceReview),false); });