            lines=data['lines'];gross={r['id']:D(r['unit_price'])*r['qty'] for r in lines};total=sum(gross.values());discount_cents=int(D(data['discount'])*100);raw={k:D(discount_cents)*v/total for k,v in gross.items()};allocated={k:int(v) for k,v in raw.items()};remainder=discount_cents-sum(allocated.values())
            for key in sorted(raw,key=lambda k:(-(raw[k]-int(raw[k])),k))[:remainder]:allocated[key]+=1
            rows=[];refund=0
            for line in lines:
                base,extra=divmod(allocated[line['id']],line['qty'])
                for ordinal in range(1,line['qty']+1):
                    off=base+(ordinal<=extra);net=int(D(line['unit_price'])*100)-off;returned=line['id']==data['return']['line'] and ordinal<=output['refund_units']
                    if returned:refund+=net
                    rows.append({'line':line['id'],'unit':ordinal,'original_cents':int(D(line['unit_price'])*100),'discount_cents':off,'net_cents':net,'returned':returned})
            paid=int(total*100)-discount_cents;remaining=sum(r['net_cents'] for r in rows if not r['returned'])
            check('优惠总额分位守恒',sum(r['discount_cents'] for r in rows)==discount_cents);check('行分摊A67分B33分',allocated=={'A':67,'B':33});check('退款加剩余货款仍等于实付',refund+remaining==paid);check('部分退货金额9.66元',refund==966);cout('unit-refund-ledger.csv',rows);result={'line_discount_cents':allocated,'refund':money(D(refund)/100),'remaining_paid':money(D(remaining)/100),'original_paid':money(D(paid)/100),'rounding_note':'先分摊优惠到商品行，再把行优惠分摊到各件；相同余数按ID/件序升序。'}
