Skip to content

Case 39: Property fee waiver eligibility

This case corresponds to Scenario 39 in USE-CASES.md (Requirement 49: property-fee / parking-fee waiver eligibility), in the property & community domain. The rule script owns the one-vote veto reason codes, the waiver percentages for the two fee types, and the guaranteed-area policy; the Service owns veto short-circuiting, guaranteed-area capping, waiver amount calculation, and the parking-space limit, outputting each household’s eligibility verdict, waiver percentages, counted area, itemized amounts, and review notes.

Owner waiver applications are carried by PropertyWaiverApplication: beyond the household ID and owner information, the core is the household type householdType — SUBSISTENCE (subsistence/extreme hardship), MARTYR (martyr-family preferential care), DISABLED (severe disability), LOW_INCOME (near-subsistence), ELDERLY_ALONE (elderly living alone), ORDINARY (ordinary owner); property data includes the titled area, resident family size, per-capita floor area, the property-fee unit price, and the number of months applied for; parking data includes the number of registered spaces and the monthly fee per space; compliance flags include whether the unit is vacant, rented out, illegally constructed, the number of historical arrears months, and whether there are safety violations such as e-bike fly-wire charging or bringing e-bikes into elevators or homes.

Eligibility starts with four one-vote vetoes: renting out (waivers are for owner-occupiers only), illegal construction, arrears over 6 months, and parking safety violations — hitting any of them immediately returns a reason code (RENTING / ILLEGAL / OVERDUE / SAFETY) without entering waiver calculation. After passing the vetoes, the percentage is set by household type: subsistence, martyr-family, and severely-disabled households get 100% off both property and parking fees; near-subsistence households get 60% off property and 50% off parking; elderly living alone get 50% off property; ordinary owners with a vacant unit pay property fees at 70% (i.e., a 30% waiver) and get no parking waiver.

The guaranteed-area rule prevents “large units enjoying high percentages”: subsistence, martyr-family, severely-disabled, and elderly-living-alone households use a 30 ㎡ per-capita guarantee line, near-subsistence households use 20 ㎡; if per-capita floor area does not exceed the line, the whole unit is counted, otherwise the counted area is capped at “guarantee line × family size” and the excess is not waived; ordinary vacant units do not use the guarantee line and are waived on the whole unit. Parking waivers are strictly limited to 1 registered space — additional spaces in multi-space households are paid normally. The decision result PropertyWaiverDecision outputs the eligibility verdict, veto reason code, counted area, the two waiver percentages and amounts, the total waiver, and review notes.

  • Livelihood policies are rigid: waiver percentages and guaranteed-area standards come from government documents; renting out, illegal construction, arrears, and safety violations must be one-vote vetoes — looseness in manual calculation leaks subsidies, while over-strictness invites complaints.
  • Property and parking fees use two different percentage sets: the same household type has different percentages for the two fees (e.g., near-subsistence 60% vs. 50%), and the parking waiver covers only 1 space — combined calculation is extremely error-prone.
  • Guaranteed-area capping is complex: first compare per-capita area against the guarantee line, then compute the capped area from family size, and take the smaller of titled area and capped area as the billing base.
  • The vacant-unit policy is easily abused: ordinary owners with vacant units only get a 30% property waiver and no parking waiver; this must not be confused with the 100% waivers for hardship groups.
  • Amount precision and audit trail requirements are high: waiver amounts involve government subsidies and owners’ vital interests; they must be exact to the cent, with explainable notes such as “how many square meters in excess are not waived” and “remaining spaces paid normally”.
  • Policy standards vary widely by region and change often: guarantee-area lines (30/20 ㎡) and waiver percentages differ across cities; once hard-coded into the billing system, cross-project reuse is difficult.
Component File Responsibility
Input model model/PropertyWaiverApplication.java Household type, property and parking data, unit price and months, plus the four compliance flags
Decision model model/PropertyWaiverDecision.java Eligibility verdict, veto reason code, counted area, both percentages and amounts, total, notes
Rule interface PropertyWaiverRules.java 4 rule methods: veto reason, property percentage, parking percentage, guaranteed area
Rule script resources/demo/property-waiver.xml Veto conditions, household-type percentage table, per-capita guarantee lines all externalized
Orchestration service service/PropertyWaiverService.java Veto short circuit, area-cap selection, amount calculation, parking-space limit, note assembly
Tests PropertyWaiverServiceTest.java 6 household shapes, including a looped comparison across the four veto types
Rule method Inputs (@Param) Policy Returns
hardRejectReason renting, illegal, overdueMonths, safetyViolation Renting returns RENTING; illegal construction ILLEGAL; arrears over 6 months OVERDUE; safety violation SAFETY; otherwise PASS String reason code
propertyWaiverPercent householdType, vacant Subsistence/martyr/severely-disabled 100%; near-subsistence 60%; elderly living alone 50%; ordinary vacant unit 30%; otherwise 0 int percentage
parkingWaiverPercent householdType Subsistence/martyr/severely-disabled 100% (limited to 1 space); near-subsistence 50%; otherwise 0 int percentage
qualifiedArea size, perCapita, family, householdType Guarantee lines: subsistence/martyr/severely-disabled/elderly-alone 30 ㎡ per capita, near-subsistence 20 ㎡ (default 15 ㎡); under the line counts the whole unit, over the line caps at guarantee line × family size; the smaller value is taken double counted area

Place the following files into the JQuick-Java test project by package structure: the two models into src/test/java/com/github/paohaijiao/demo/model/, the rule interface and test into src/test/java/com/github/paohaijiao/demo/, the Service into demo/service/, and the XML into src/test/resources/demo/. Then run:

Terminal window
mvn test -Dtest=PropertyWaiverServiceTest
package com.github.paohaijiao.demo.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 业主物业费/停车费减免申请要素
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class PropertyWaiverApplication {
/**
* 户号
*/
private String householdId;
private String ownerName;
/**
* 家庭类型:SUBSISTENCE 低保特困 / MARTYR 烈属优抚 / DISABLED 重度残疾 /
* LOW_INCOME 低保边缘 / ELDERLY_ALONE 独居老人 / ORDINARY 普通业主
*/
private String householdType;
/**
* 产权面积(㎡)
*/
private double propertySize;
/**
* 家庭常住人口数
*/
private int familySize;
/**
* 人均建筑面积(㎡)
*/
private double perCapitaArea;
/**
* 物业费单价(元/㎡/月)
*/
private double monthlyFeePerSqm;
/**
* 申请减免月数
*/
private int monthsApplied;
/**
* 登记车位数
*/
private int parkingSpaceCount;
/**
* 单个车位月停车费
*/
private double monthlyParkingFee;
/**
* 房屋是否空置(未入住毛坯/长期空置,普通业主按 70% 缴纳)
*/
private boolean vacant;
/**
* 是否出租(减免仅限自住)
*/
private boolean rentingOut;
/**
* 是否存在违法建设
*/
private boolean illegalConstruction;
/**
* 历史欠费月数
*/
private int overdueMonths;
/**
* 是否存在电动车飞线充电/进梯入户等安全违规
*/
private boolean parkingSafetyViolation;
}
package com.github.paohaijiao.demo.model;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
/**
* 物业/停车费减免决策
*/
@Data
public class PropertyWaiverDecision {
/**
* 户号
*/
private String householdId;
/**
* 是否通过资格审核
*/
private boolean eligible;
/**
* 否决原因码:RENTING 出租 / ILLEGAL 违建 / OVERDUE 长期欠费 / SAFETY 安全违规
*/
private String rejectReason;
/**
* 计入减免的建筑面积(㎡,超出保障面积标准部分不免)
*/
private double qualifiedArea;
/**
* 物业费减免比例(百分比)
*/
private int propertyWaiverPercent;
/**
* 停车费减免比例(百分比)
*/
private int parkingWaiverPercent;
/**
* 物业费减免金额
*/
private double propertyWaiverAmount;
/**
* 停车费减免金额
*/
private double parkingWaiverAmount;
/**
* 减免合计
*/
private double totalWaiver;
/**
* 审核说明
*/
private List<String> notes = new ArrayList<String>();
public void addNote(String note) {
this.notes.add(note);
}
}
package com.github.paohaijiao.demo;
import com.github.paohaijiao.xml.param.Param;
/**
* 场景 39:物业/停车费减免资格、比例与保障面积规则
*/
public interface PropertyWaiverRules {
/**
* 一票否决原因码:出租/违建/欠费超 6 个月/安全违规,其余 PASS
*/
String hardRejectReason(@Param("renting") boolean renting,
@Param("illegal") boolean illegal,
@Param("overdueMonths") int overdueMonths,
@Param("safetyViolation") boolean safetyViolation);
/**
* 物业费减免比例:低保特困/烈属/重残 100%、边缘困难 60%、独居老人 50%、
* 普通空置房 30%,其余 0
*/
int propertyWaiverPercent(@Param("householdType") String householdType,
@Param("vacant") boolean vacant);
/**
* 停车费减免比例:低保特困/烈属/重残 100%(限 1 个车位)、边缘困难 50%,其余 0
*/
int parkingWaiverPercent(@Param("householdType") String householdType);
/**
* 计入减免的面积:低保特困/烈属/重残/独居老人按人均 30 ㎡保障线、
* 边缘困难按人均 20 ㎡,超出部分按家庭人口封顶;未超标按整套
*/
double qualifiedArea(@Param("size") double size,
@Param("perCapita") double perCapita,
@Param("family") int family,
@Param("householdType") String householdType);
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE javas PUBLIC "-//PAOHAIJIAO//DTD API JAVA 1.0//EN"
"classpath:paohaijiao/dtd/Jquick-java.dtd">
<javas namespace="com.github.paohaijiao.demo.PropertyWaiverRules">
<java name="hardRejectReason" returnClass="java.lang.String">
<![CDATA[
str def hardRejectReason(boolean:renting, boolean:illegal, int:overdueMonths, boolean:safetyViolation) {
if (renting) {
return "RENTING";
} else if (illegal) {
return "ILLEGAL";
} else if (overdueMonths > 6) {
return "OVERDUE";
} else if (safetyViolation) {
return "SAFETY";
} else {
return "PASS";
}
}
]]>
</java>
<java name="propertyWaiverPercent" returnClass="int">
<![CDATA[
int def propertyWaiverPercent(str:householdType, boolean:vacant) {
if (householdType == "SUBSISTENCE") {
return 100;
} else if (householdType == "MARTYR") {
return 100;
} else if (householdType == "DISABLED") {
return 100;
} else if (householdType == "LOW_INCOME") {
return 60;
} else if (householdType == "ELDERLY_ALONE") {
return 50;
} else if (vacant) {
return 30;
} else {
return 0;
}
}
]]>
</java>
<java name="parkingWaiverPercent" returnClass="int">
<![CDATA[
int def parkingWaiverPercent(str:householdType) {
if (householdType == "SUBSISTENCE") {
return 100;
} else if (householdType == "MARTYR") {
return 100;
} else if (householdType == "DISABLED") {
return 100;
} else if (householdType == "LOW_INCOME") {
return 50;
} else {
return 0;
}
}
]]>
</java>
<java name="qualifiedArea" returnClass="double">
<![CDATA[
double def qualifiedArea(double:size, double:perCapita, int:family, str:householdType) {
double cap = 15.0;
if (householdType == "SUBSISTENCE") {
cap = 30.0;
} else if (householdType == "MARTYR") {
cap = 30.0;
} else if (householdType == "DISABLED") {
cap = 30.0;
} else if (householdType == "ELDERLY_ALONE") {
cap = 30.0;
} else if (householdType == "LOW_INCOME") {
cap = 20.0;
}
if (perCapita <= cap) {
return size;
}
double limit = cap * family;
if (size < limit) {
return size;
} else {
return limit;
}
}
]]>
</java>
</javas>
package com.github.paohaijiao.demo.service;
import com.github.paohaijiao.demo.PropertyWaiverRules;
import com.github.paohaijiao.demo.model.PropertyWaiverApplication;
import com.github.paohaijiao.demo.model.PropertyWaiverDecision;
import java.math.BigDecimal;
import java.math.RoundingMode;
/**
* 场景 39:物业费/停车费减免资格判定
*/
public class PropertyWaiverService {
private final PropertyWaiverRules rules;
public PropertyWaiverService(PropertyWaiverRules rules) {
this.rules = rules;
}
public PropertyWaiverDecision review(PropertyWaiverApplication a) {
PropertyWaiverDecision d = new PropertyWaiverDecision();
d.setHouseholdId(a.getHouseholdId());
String reject = rules.hardRejectReason(a.isRentingOut(), a.isIllegalConstruction(),
a.getOverdueMonths(), a.isParkingSafetyViolation());
if (!"PASS".equals(reject)) {
d.setEligible(false);
d.setRejectReason(reject);
d.setQualifiedArea(0.0);
return d;
}
d.setEligible(true);
int propertyPercent = rules.propertyWaiverPercent(a.getHouseholdType(), a.isVacant());
int parkingPercent = rules.parkingWaiverPercent(a.getHouseholdType());
d.setPropertyWaiverPercent(propertyPercent);
d.setParkingWaiverPercent(parkingPercent);
double area;
if ("ORDINARY".equals(a.getHouseholdType()) && a.isVacant()) {
area = a.getPropertySize();
} else {
area = rules.qualifiedArea(a.getPropertySize(), a.getPerCapitaArea(),
a.getFamilySize(), a.getHouseholdType());
}
d.setQualifiedArea(money(area));
if (area < a.getPropertySize()) {
d.addNote("人均建筑面积超出保障标准,超出的 "
+ money(a.getPropertySize() - area) + " ㎡ 部分不予减免");
}
double propertyAmount = money(area * a.getMonthlyFeePerSqm()
* a.getMonthsApplied() * propertyPercent / 100.0);
d.setPropertyWaiverAmount(propertyAmount);
double parkingAmount = 0.0;
if (a.getParkingSpaceCount() >= 1 && parkingPercent > 0) {
parkingAmount = money(a.getMonthlyParkingFee() * a.getMonthsApplied()
* parkingPercent / 100.0);
if (a.getParkingSpaceCount() > 1) {
d.addNote("停车费减免仅限 1 个登记车位,其余 "
+ (a.getParkingSpaceCount() - 1) + " 个车位正常缴纳");
}
}
d.setParkingWaiverAmount(parkingAmount);
d.setTotalWaiver(money(propertyAmount + parkingAmount));
if ("ORDINARY".equals(a.getHouseholdType()) && a.isVacant()) {
d.addNote("空置房物业费按应缴标准的 70% 收取(减免 30%),停车费不免");
}
return d;
}
private double money(double v) {
return BigDecimal.valueOf(v).setScale(2, RoundingMode.HALF_UP).doubleValue();
}
}
package com.github.paohaijiao.demo;
import com.github.paohaijiao.demo.model.PropertyWaiverApplication;
import com.github.paohaijiao.demo.model.PropertyWaiverDecision;
import com.github.paohaijiao.demo.service.PropertyWaiverService;
import com.github.paohaijiao.xml.JQuickJava;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
/**
* 场景 39:物业费/停车费减免资格判定
* <p>
* 6 组形态:低保户物业停车全免、烈属全免无车位、低保边缘比例减免与车位限制、
* 空置房 70% 缴纳、四类一票否决、独居老人超保障面积封顶。
*/
public class PropertyWaiverServiceTest {
private PropertyWaiverService service;
@Before
public void setUp() {
PropertyWaiverRules rules = JQuickJava.create()
.createApi(PropertyWaiverRules.class, "demo/property-waiver.xml");
this.service = new PropertyWaiverService(rules);
}
/**
* 低保户 80 ㎡ 四口之家、6 个月、1 车位 → 物业停车全免 3000
*/
@Test
public void shouldFullyWaiveForSubsistenceHousehold() {
PropertyWaiverApplication a = base("H001", "SUBSISTENCE");
a.setPropertySize(80);
a.setFamilySize(4);
a.setPerCapitaArea(20);
a.setMonthlyFeePerSqm(2.5);
a.setMonthsApplied(6);
a.setParkingSpaceCount(1);
a.setMonthlyParkingFee(300);
PropertyWaiverDecision d = service.review(a);
Assert.assertTrue(d.isEligible());
Assert.assertEquals(80.0, d.getQualifiedArea(), 0.0001);
Assert.assertEquals(100, d.getPropertyWaiverPercent());
Assert.assertEquals(100, d.getParkingWaiverPercent());
Assert.assertEquals(1200.0, d.getPropertyWaiverAmount(), 0.0001);
Assert.assertEquals(1800.0, d.getParkingWaiverAmount(), 0.0001);
Assert.assertEquals(3000.0, d.getTotalWaiver(), 0.0001);
}
/**
* 烈属 90 ㎡ 人均 30 ㎡ 不超标、全年、无车位 → 全免 2160
*/
@Test
public void shouldFullyWaivePropertyForMartyrWithoutParking() {
PropertyWaiverApplication a = base("H002", "MARTYR");
a.setPropertySize(90);
a.setFamilySize(3);
a.setPerCapitaArea(30);
a.setMonthlyFeePerSqm(2);
a.setMonthsApplied(12);
a.setParkingSpaceCount(0);
a.setMonthlyParkingFee(0);
PropertyWaiverDecision d = service.review(a);
Assert.assertTrue(d.isEligible());
Assert.assertEquals(90.0, d.getQualifiedArea(), 0.0001);
Assert.assertEquals(2160.0, d.getPropertyWaiverAmount(), 0.0001);
Assert.assertEquals(0.0, d.getParkingWaiverAmount(), 0.0001);
Assert.assertEquals(2160.0, d.getTotalWaiver(), 0.0001);
}
/**
* 低保边缘 70 ㎡ 不超标、2 车位 → 物业 60%、1 个车位停车 50%
*/
@Test
public void shouldPartiallyWaiveForLowIncomeAndLimitOneParkingSpace() {
PropertyWaiverApplication a = base("H003", "LOW_INCOME");
a.setPropertySize(70);
a.setFamilySize(4);
a.setPerCapitaArea(17.5);
a.setMonthlyFeePerSqm(2);
a.setMonthsApplied(12);
a.setParkingSpaceCount(2);
a.setMonthlyParkingFee(200);
PropertyWaiverDecision d = service.review(a);
Assert.assertEquals(60, d.getPropertyWaiverPercent());
Assert.assertEquals(70.0, d.getQualifiedArea(), 0.0001);
Assert.assertEquals(1008.0, d.getPropertyWaiverAmount(), 0.0001);
Assert.assertEquals(1200.0, d.getParkingWaiverAmount(), 0.0001);
Assert.assertEquals(2208.0, d.getTotalWaiver(), 0.0001);
Assert.assertTrue(join(d).contains("停车费减免仅限 1 个登记车位"));
}
/**
* 普通业主 100 ㎡ 空置 6 个月 → 按整套减免 30%,停车不免
*/
@Test
public void shouldWaiveThirtyPercentForVacantApartment() {
PropertyWaiverApplication a = base("H004", "ORDINARY");
a.setPropertySize(100);
a.setFamilySize(3);
a.setPerCapitaArea(33.33);
a.setMonthlyFeePerSqm(2);
a.setMonthsApplied(6);
a.setParkingSpaceCount(1);
a.setMonthlyParkingFee(200);
a.setVacant(true);
PropertyWaiverDecision d = service.review(a);
Assert.assertTrue(d.isEligible());
Assert.assertEquals(100.0, d.getQualifiedArea(), 0.0001);
Assert.assertEquals(30, d.getPropertyWaiverPercent());
Assert.assertEquals(360.0, d.getPropertyWaiverAmount(), 0.0001);
Assert.assertEquals(0.0, d.getParkingWaiverAmount(), 0.0001);
Assert.assertTrue(join(d).contains("空置房物业费按应缴标准的 70% 收取"));
}
/**
* 出租、违建、欠费 8 个月、飞线充电 → 四类一票否决
*/
@Test
public void shouldHardRejectFourVetoConditions() {
String[][] cases = {
{"LOW_INCOME", "RENTING"}, {"SUBSISTENCE", "ILLEGAL"},
{"MARTYR", "OVERDUE"}, {"DISABLED", "SAFETY"}
};
for (String[] c : cases) {
PropertyWaiverApplication a = base("H005", c[0]);
a.setPropertySize(60);
a.setFamilySize(2);
a.setPerCapitaArea(30);
a.setMonthlyFeePerSqm(2);
a.setMonthsApplied(12);
if ("RENTING".equals(c[1])) {
a.setRentingOut(true);
} else if ("ILLEGAL".equals(c[1])) {
a.setIllegalConstruction(true);
} else if ("OVERDUE".equals(c[1])) {
a.setOverdueMonths(8);
} else {
a.setParkingSafetyViolation(true);
}
PropertyWaiverDecision d = service.review(a);
Assert.assertFalse(c[0] + " 应被否决", d.isEligible());
Assert.assertEquals(c[0] + " 否决原因码不符", c[1], d.getRejectReason());
Assert.assertEquals(0.0, d.getTotalWaiver(), 0.0001);
}
}
/**
* 独居老人 60 ㎡(人均超 30 ㎡保障线)→ 仅 30 ㎡享 50% 减免
*/
@Test
public void shouldCapElderlyWaiverAtSecurityAreaStandard() {
PropertyWaiverApplication a = base("H006", "ELDERLY_ALONE");
a.setPropertySize(60);
a.setFamilySize(1);
a.setPerCapitaArea(60);
a.setMonthlyFeePerSqm(2);
a.setMonthsApplied(12);
a.setParkingSpaceCount(0);
PropertyWaiverDecision d = service.review(a);
Assert.assertEquals(50, d.getPropertyWaiverPercent());
Assert.assertEquals(30.0, d.getQualifiedArea(), 0.0001);
Assert.assertEquals(360.0, d.getPropertyWaiverAmount(), 0.0001);
Assert.assertTrue(join(d).contains("超出的 30.0 ㎡ 部分不予减免"));
}
private PropertyWaiverApplication base(String id, String type) {
PropertyWaiverApplication a = new PropertyWaiverApplication();
a.setHouseholdId(id);
a.setOwnerName("测试业主");
a.setHouseholdType(type);
a.setPropertySize(0);
a.setFamilySize(1);
a.setPerCapitaArea(0);
a.setMonthlyFeePerSqm(0);
a.setMonthsApplied(0);
a.setParkingSpaceCount(0);
a.setMonthlyParkingFee(0);
a.setVacant(false);
a.setRentingOut(false);
a.setIllegalConstruction(false);
a.setOverdueMonths(0);
a.setParkingSafetyViolation(false);
return a;
}
private String join(PropertyWaiverDecision d) {
StringBuilder sb = new StringBuilder();
for (String n : d.getNotes()) {
sb.append(n).append(';');
}
return sb.toString();
}
}

Run at the project root:

Terminal window
mvn test -Dtest=PropertyWaiverServiceTest

The tests cover 6 household shapes (the one-vote veto cases loop over 4 household types inside a single test method); the assertions correspond one-to-one with the source code:

Test method Business shape Key assertions
shouldFullyWaiveForSubsistenceHousehold Subsistence household, 80 ㎡ family of four (per-capita 20, under the line), 6 months, 1 parking space Eligible; counted area 80; property/parking both 100%; property waiver 1200 (80×2.5×6), parking 1800 (300×6), total 3000
shouldFullyWaivePropertyForMartyrWithoutParking Martyr family, 90 ㎡ at per-capita 30 exactly at the line, full year, no parking space Counted area 90; property waiver 2160 (90×2×12); parking 0; total 2160
shouldPartiallyWaiveForLowIncomeAndLimitOneParkingSpace Near-subsistence, 70 ㎡ (per-capita 17.5, under the 20 line), 2 parking spaces Property 60%, counted 70 ㎡, waiver 1008; parking waived 50% on 1 space = 1200; total 2208; note contains “停车费减免仅限 1 个登记车位”
shouldWaiveThirtyPercentForVacantApartment Ordinary owner, 100 ㎡ vacant for 6 months, 1 parking space Whole unit counted (100 ㎡); property 30% waiver = 360 (100×2×6×30%); parking 0; note contains “空置房物业费按应缴标准的 70% 收取”
shouldHardRejectFourVetoConditions Near-subsistence renting out, subsistence with illegal construction, martyr family 8 months in arrears, severely-disabled with fly-wire charging All four ineligible; veto codes RENTING/ILLEGAL/OVERDUE/SAFETY respectively; total waiver 0 in all cases
shouldCapElderlyWaiverAtSecurityAreaStandard Elderly living alone, 60 ㎡ single person (per-capita 60 exceeds the 30 ㎡ guarantee line) Property 50%; counted area capped at 30 ㎡; waiver 360 (30×2×12×50%); note contains “超出的 30.0 ㎡ 部分不予减免”
  • One-vote vetoes front-loaded with short circuit: once hardRejectReason returns a reason code, the Service ends the flow immediately with counted area and amounts all zeroed; the veto priority (renting → illegal construction → arrears → safety) is expressed as a positive-condition sequence in the script, clear and auditable.
  • One method, one policy: the four rule methods — veto reason, property percentage, parking percentage, guaranteed area — are fully independent; the property and parking percentage tables are maintained separately, so adjusting one never harms the other.
  • Service-side capping and fixed-amount backstops: the guaranteed-area rule returns the area for “whole unit under the line, capped at guarantee line × family size over the line”, while the Service compares it against the titled area and generates the shortfall note; vacant ordinary units take the whole-unit special case; the parking waiver is limited to 1 space on the Java side.
  • Amounts exact to the cent: all amounts go through BigDecimal + HALF_UP to two decimals; property, parking, and total uniformly use the money() path, so livelihood subsidy amounts carry no floating-point residue.
  • Externalized rules with hot update: the household-type percentage table (100/60/50/30), the 6-month arrears flag line, and the 30/20 ㎡ per-capita guarantee lines all live in the XML — taking a project to another city only requires swapping the threshold table to reuse the whole skeleton.
  • Proxy reuse, ASM, Java 8 / domestic-JDK friendly: the proxy is created once in @Before and shared by all 6 shapes; the engine is based on dynamic proxies and ASM with no heavy dependencies, and can embed into property-billing systems on JDK 8 and domestic (Xinchuang) JDKs.