Skip to content

Case 31: Incoming material acceptance

This case corresponds to scenario 31 of JQuick-Java USE-CASES (requirement 36: dynamic adaptation of incoming material acceptance standards), in the manufacturing MES domain. The decision performs defect-rate banding and multi-dimensional quality scoring on IQC inspection batches, and outputs a conclusion of accept-to-stock, conditional acceptance, or full-batch rejection together with disposition requirements.

At the raw-material intake stage of a discrete manufacturer, IQC inspectors sample each batch and record its attributes. The input model MaterialInspection carries the batch number, material code, material category (ELECTRONIC / METAL / CHEMICAL / PLASTIC), sample size, and three levels of defect counts: critical defects (zero tolerance for safety/function), major defects, and minor defects. It also records whether a warranty certificate/COA is attached, whether appearance passes, whether critical dimensions pass, whether RoHS/environmental compliance passes, and the supplier grade A/B/C.

Acceptance runs in two stages. The first stage is the zero-tolerance hard gate: whenever a critical defect appears, the warranty certificate/COA is missing, or environmental compliance fails, the Service directly rules full-batch rejection, sets the composite quality score to 0, marks hardReject, and skips scoring, while producing per-item dispositions such as supplier accountability and freezing in-transit inventory. The second stage is six-dimension quality scoring: major defect rate 30 points, minor defect rate 20 points, warranty documents 15 points, appearance 15 points, dimensions 10 points, environmental compliance 10 points, totaling 100; the script inspectionLevel maps the total to ACCEPT, CONDITIONAL, or REJECT along the 90/70 acceptance lines.

The decision result is carried by MaterialInspectionDecision: batch number, composite quality score, disposition action, hard-reject flag, plus two lists — score details scoreDetails and dispositions dispositions. For grade-C suppliers, tightened acceptance is enforced on the Java side — whenever the script’s preliminary verdict is not ACCEPT (i.e. the quality score is below 90), the batch is escalated to full return. Conditionally accepted batches carry auditable rectification requirements such as 100% sorting, 8D corrective-action reports, and technical sign-off.

  • Acceptance thresholds change frequently with material category and supplier: AQL defect-rate lines differ for electronic, metal, and chemical materials, and tightened policies must follow supplier re-grading; hardcoding them in Java forces a release every time.
  • Multi-dimensional inspection results are hard to tune independently: the weights and banding lines of the six dimensions — defect rate, warranty documents, appearance, dimensions, environmental compliance — are coupled in one long if-else chain, so changing one threshold requires regressing the whole acceptance logic.
  • Zero-tolerance rules are mixed with scoring rules: critical defects, missing certificates, and RoHS non-compliance must be rejected unconditionally; if buried at the end of scoring branches, they can be masked by high scores, creating a risk of releasing quality accidents.
  • Disposition results are hard to audit: actions such as return, sorting, 8D rectification, and technical sign-off lack structured records, leaving no per-batch basis for supplier quality claims or external system audits.
  • Legacy MES and IT-innovation environments cannot host heavy platforms: shop-floor systems often run on JDK 8 or even older domestic JDKs, and cannot carry heavyweight rule platforms that require separate service processes and databases.

This case uses the standard JQuick-Java six-piece set: input model, decision result model, Rules interface, XML rule script, Service orchestration, and JUnit tests. All code is verified on Oracle JDK 1.8.

Layer Responsibility Files
Domain model Carries IQC sampling attributes and batch acceptance decisions (score details, dispositions) MaterialInspection.java, MaterialInspectionDecision.java
Rule layer Six scoring dimensions and the preliminary quality-score verdict; AQL ratio lines and acceptance lines all externalized IncomingMaterialRules.java, incoming-material.xml
Orchestration layer Zero-tolerance hard veto up front, six-dimension aggregation, tightened acceptance for grade-C suppliers, disposition assembly IncomingMaterialService.java
Tests 6 real-world incoming batch profiles IncomingMaterialServiceTest.java
Rule method Criteria Score/output
scoreMajor Full score when major defects = 0; defect count and sample size are compared by cross-multiplication: ratio below 1% yields 20, below 3% yields 10, otherwise 0 0-30
scoreMinor Full score when minor defects = 0; ratio below 3% yields 14, below 5% yields 7, otherwise 0 0-20
scoreCertificate 15 when warranty certificate/COA is attached, otherwise 0 0-15
scoreAppearance 15 when appearance inspection passes, otherwise 0 0-15
scoreDimension 10 when critical dimensions pass, otherwise 0 0-10
scoreEnvironmental 10 when RoHS/environmental compliance passes, otherwise 0 0-10
inspectionLevel Total score ≥ 90 ACCEPT; ≥ 70 CONDITIONAL; otherwise REJECT ACCEPT/CONDITIONAL/REJECT

Place the two model classes under src/test/java/com/github/paohaijiao/demo/model/, the rules interface under demo/, the Service under demo/service/, the XML script under src/test/resources/demo/, and the test class under demo/, keeping the package names exactly as in the source. Run mvn test -Dtest=IncomingMaterialServiceTest from the project root to reproduce all 6 cases.

package com.github.paohaijiao.demo.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 来料检验(IQC)抽检结果要素
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MaterialInspection {
/**
* 来料批次号
*/
private String batchNo;
/**
* 物料编码
*/
private String materialCode;
/**
* 物料类别:ELECTRONIC 电子件 / METAL 金属件 / CHEMICAL 化工 / PLASTIC 塑胶
*/
private String materialCategory;
/**
* 抽检样本数
*/
private int sampleSize;
/**
* 致命缺陷数(安全/功能零容忍)
*/
private int criticalDefects;
/**
* 严重缺陷数
*/
private int majorDefects;
/**
* 轻微缺陷数
*/
private int minorDefects;
/**
* 是否随附质保书/COA
*/
private boolean certificateProvided;
/**
* 外观检验是否合格
*/
private boolean appearancePass;
/**
* 关键尺寸检验是否合格
*/
private boolean dimensionPass;
/**
* RoHS/环保合规是否通过
*/
private boolean environmentalCompliant;
/**
* 供应商评级:A / B / C
*/
private String supplierGrade;
}
package com.github.paohaijiao.demo.model;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
/**
* 来料批次接收决策
*/
@Data
public class MaterialInspectionDecision {
/**
* 批次号
*/
private String batchNo;
/**
* 综合质量分(硬拒收时为 0)
*/
private int totalScore;
/**
* 处置动作:ACCEPT 接收入库 / CONDITIONAL 让步接收或挑选 / REJECT 整批拒收
*/
private String action;
/**
* 是否命中零容忍硬规则
*/
private boolean hardReject;
/**
* 评分明细
*/
private List<String> scoreDetails = new ArrayList<String>();
/**
* 处置说明与整改要求
*/
private List<String> dispositions = new ArrayList<String>();
public void addDetail(String detail) {
this.scoreDetails.add(detail);
}
public void addDisposition(String disposition) {
this.dispositions.add(disposition);
}
}
package com.github.paohaijiao.demo;
import com.github.paohaijiao.xml.param.Param;
/**
* 场景 31:来料检验缺陷率分档与单据/外观尺寸评分
*/
public interface IncomingMaterialRules {
/**
* 严重缺陷率得分(0-30):0 个满分;比率与阈值交叉相乘
*/
int scoreMajor(@Param("major") int major,
@Param("sample") int sample);
/**
* 轻微缺陷率得分(0-20)
*/
int scoreMinor(@Param("minor") int minor,
@Param("sample") int sample);
/**
* 质保书/COA 得分(0-15)
*/
int scoreCertificate(@Param("provided") boolean provided);
/**
* 外观得分(0-15)
*/
int scoreAppearance(@Param("pass") boolean pass);
/**
* 尺寸得分(0-10)
*/
int scoreDimension(@Param("pass") boolean pass);
/**
* 环保合规得分(0-10)
*/
int scoreEnvironmental(@Param("compliant") boolean compliant);
/**
* 按综合质量分给出初判
*/
String inspectionLevel(@Param("totalScore") int totalScore);
}
<?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.IncomingMaterialRules">
<java name="scoreMajor" returnClass="int">
<![CDATA[
int def scoreMajor(int:major, int:sample) {
if (major == 0) {
return 30;
} else if (major * 100 < sample * 1) {
return 20;
} else if (major * 100 < sample * 3) {
return 10;
} else {
return 0;
}
}
]]>
</java>
<java name="scoreMinor" returnClass="int">
<![CDATA[
int def scoreMinor(int:minor, int:sample) {
if (minor == 0) {
return 20;
} else if (minor * 100 < sample * 3) {
return 14;
} else if (minor * 100 < sample * 5) {
return 7;
} else {
return 0;
}
}
]]>
</java>
<java name="scoreCertificate" returnClass="int">
<![CDATA[
int def scoreCertificate(boolean:provided) {
if (provided) {
return 15;
} else {
return 0;
}
}
]]>
</java>
<java name="scoreAppearance" returnClass="int">
<![CDATA[
int def scoreAppearance(boolean:pass) {
if (pass) {
return 15;
} else {
return 0;
}
}
]]>
</java>
<java name="scoreDimension" returnClass="int">
<![CDATA[
int def scoreDimension(boolean:pass) {
if (pass) {
return 10;
} else {
return 0;
}
}
]]>
</java>
<java name="scoreEnvironmental" returnClass="int">
<![CDATA[
int def scoreEnvironmental(boolean:compliant) {
if (compliant) {
return 10;
} else {
return 0;
}
}
]]>
</java>
<java name="inspectionLevel" returnClass="java.lang.String">
<![CDATA[
str def inspectionLevel(int:totalScore) {
if (totalScore >= 90) {
return "ACCEPT";
} else if (totalScore >= 70) {
return "CONDITIONAL";
} else {
return "REJECT";
}
}
]]>
</java>
</javas>
package com.github.paohaijiao.demo.service;
import com.github.paohaijiao.demo.IncomingMaterialRules;
import com.github.paohaijiao.demo.model.MaterialInspection;
import com.github.paohaijiao.demo.model.MaterialInspectionDecision;
/**
* 场景 31:来料检验批次接收判定
* <p>
* 致命缺陷、缺质保书、环保不合规零容忍直接拒收;其余按六维质量分入库/让步/退货,
* C 级供应商执行加严验收。
*/
public class IncomingMaterialService {
private final IncomingMaterialRules rules;
public IncomingMaterialService(IncomingMaterialRules rules) {
this.rules = rules;
}
public MaterialInspectionDecision inspect(MaterialInspection m) {
MaterialInspectionDecision d = new MaterialInspectionDecision();
d.setBatchNo(m.getBatchNo());
if (m.getCriticalDefects() > 0 || !m.isCertificateProvided()
|| !m.isEnvironmentalCompliant()) {
d.setTotalScore(0);
d.setAction("REJECT");
d.setHardReject(true);
if (m.getCriticalDefects() > 0) {
d.addDisposition("发现 " + m.getCriticalDefects()
+ " 个致命缺陷,违反零容忍要求,整批拒收并启动供应商问责");
}
if (!m.isCertificateProvided()) {
d.addDisposition("缺少质保书/COA 随附文件,批次不予接收");
}
if (!m.isEnvironmentalCompliant()) {
d.addDisposition("RoHS/环保合规验证不通过,整批拒收并冻结同批次在途库存");
}
return d;
}
int majorScore = rules.scoreMajor(m.getMajorDefects(), m.getSampleSize());
int minorScore = rules.scoreMinor(m.getMinorDefects(), m.getSampleSize());
int certScore = rules.scoreCertificate(m.isCertificateProvided());
int appearanceScore = rules.scoreAppearance(m.isAppearancePass());
int dimensionScore = rules.scoreDimension(m.isDimensionPass());
int envScore = rules.scoreEnvironmental(m.isEnvironmentalCompliant());
int total = majorScore + minorScore + certScore + appearanceScore + dimensionScore + envScore;
d.addDetail("严重缺陷率: " + majorScore + " / 30");
d.addDetail("轻微缺陷率: " + minorScore + " / 20");
d.addDetail("质保文件: " + certScore + " / 15");
d.addDetail("外观: " + appearanceScore + " / 15");
d.addDetail("尺寸: " + dimensionScore + " / 10");
d.addDetail("环保合规: " + envScore + " / 10");
d.setTotalScore(total);
String level = rules.inspectionLevel(total);
if ("C".equals(m.getSupplierGrade()) && !"ACCEPT".equals(level)) {
d.setAction("REJECT");
d.addDisposition("供应商评级为 C,执行加严验收,质量分 " + total
+ " 未达 90 分接收线,整批退货");
collectDefectItems(d, m, majorScore, minorScore);
return d;
}
d.setAction(level);
if ("ACCEPT".equals(level)) {
d.addDisposition("抽检符合接收质量限,准予办理入库手续");
} else if ("CONDITIONAL".equals(level)) {
d.addDisposition("质量分 " + total + ",作让步接收处理,须挑选/复检后上线");
collectDefectItems(d, m, majorScore, minorScore);
} else {
d.addDisposition("质量分 " + total + ",低于 70 分接收底线,整批退货");
collectDefectItems(d, m, majorScore, minorScore);
}
return d;
}
private void collectDefectItems(MaterialInspectionDecision d, MaterialInspection m,
int majorScore, int minorScore) {
if (majorScore == 0) {
d.addDisposition("严重缺陷率超过 AQL 加严线,按退货流程处理");
} else if (m.getMajorDefects() > 0) {
d.addDisposition("存在 " + m.getMajorDefects() + " 个严重缺陷,上线前须全检挑选");
}
if (minorScore == 0) {
d.addDisposition("轻微缺陷率过高,不予让步接收");
} else if (minorScore <= 7 && m.getMinorDefects() > 0) {
d.addDisposition("轻微缺陷率达到加控线,要求供应商提交 8D 整改报告");
}
if (!m.isDimensionPass()) {
d.addDisposition("关键尺寸检验不合格,批次不得上线,须技术会签");
}
if (!m.isAppearancePass()) {
d.addDisposition("外观检验不合格,转外观挑选后复检");
}
}
}
package com.github.paohaijiao.demo;
import com.github.paohaijiao.demo.model.MaterialInspection;
import com.github.paohaijiao.demo.model.MaterialInspectionDecision;
import com.github.paohaijiao.demo.service.IncomingMaterialService;
import com.github.paohaijiao.xml.JQuickJava;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
/**
* 场景 31:原材料来料验收标准动态适配
* <p>
* 6 组批次形态:满分接收入库、轻微缺陷让步接收、缺陷超标退货、
* 致命缺陷零容忍、缺质保书且环保不合规、C 级供应商加严拒收(与 A 级对比)。
*/
public class IncomingMaterialServiceTest {
private IncomingMaterialService service;
@Before
public void setUp() {
IncomingMaterialRules rules = JQuickJava.create()
.createApi(IncomingMaterialRules.class, "demo/incoming-material.xml");
this.service = new IncomingMaterialService(rules);
}
/**
* 50 件全检合格、文件齐全 → 100 分接收
*/
@Test
public void shouldAcceptPerfectBatch() {
MaterialInspection m = base("BAT001", "A");
m.setSampleSize(50);
m.setMajorDefects(0);
m.setMinorDefects(0);
MaterialInspectionDecision d = service.inspect(m);
Assert.assertEquals(100, d.getTotalScore());
Assert.assertEquals("ACCEPT", d.getAction());
Assert.assertFalse(d.isHardReject());
Assert.assertTrue(d.getDispositions().contains("抽检符合接收质量限,准予办理入库手续"));
}
/**
* 50 件中 2 个轻微缺陷(4%)→ 87 分让步接收并要求 8D
*/
@Test
public void shouldConditionallyAcceptMinorDefects() {
MaterialInspection m = base("BAT002", "A");
m.setSampleSize(50);
m.setMinorDefects(2);
MaterialInspectionDecision d = service.inspect(m);
Assert.assertEquals(87, d.getTotalScore());
Assert.assertEquals("CONDITIONAL", d.getAction());
Assert.assertTrue(d.getScoreDetails().contains("轻微缺陷率: 7 / 20"));
Assert.assertTrue(d.getDispositions().contains(
"轻微缺陷率达到加控线,要求供应商提交 8D 整改报告"));
}
/**
* 3 个严重缺陷 + 4 个轻微缺陷 + 尺寸不合格 → 40 分退货
*/
@Test
public void shouldRejectBatchWithExcessiveDefects() {
MaterialInspection m = base("BAT003", "B");
m.setSampleSize(50);
m.setMajorDefects(3);
m.setMinorDefects(4);
m.setDimensionPass(false);
MaterialInspectionDecision d = service.inspect(m);
Assert.assertEquals(40, d.getTotalScore());
Assert.assertEquals("REJECT", d.getAction());
Assert.assertTrue(d.getDispositions().contains("严重缺陷率超过 AQL 加严线,按退货流程处理"));
Assert.assertTrue(d.getDispositions().contains("关键尺寸检验不合格,批次不得上线,须技术会签"));
}
/**
* 其余全合格但出现 1 个致命缺陷 → 零容忍整批拒收
*/
@Test
public void shouldHardRejectCriticalDefect() {
MaterialInspection m = base("BAT004", "A");
m.setSampleSize(50);
m.setCriticalDefects(1);
MaterialInspectionDecision d = service.inspect(m);
Assert.assertEquals(0, d.getTotalScore());
Assert.assertEquals("REJECT", d.getAction());
Assert.assertTrue(d.isHardReject());
Assert.assertTrue(d.getDispositions().get(0).contains("致命缺陷,违反零容忍要求"));
}
/**
* 无质保书且环保验证不通过 → 双硬规则拒收
*/
@Test
public void shouldHardRejectMissingCertificateAndRoHS() {
MaterialInspection m = base("BAT005", "B");
m.setSampleSize(50);
m.setCertificateProvided(false);
m.setEnvironmentalCompliant(false);
MaterialInspectionDecision d = service.inspect(m);
Assert.assertEquals("REJECT", d.getAction());
Assert.assertTrue(d.isHardReject());
Assert.assertTrue(d.getDispositions().contains("缺少质保书/COA 随附文件,批次不予接收"));
Assert.assertTrue(d.getDispositions().contains(
"RoHS/环保合规验证不通过,整批拒收并冻结同批次在途库存"));
}
/**
* 74 分批次:A 级供应商让步接收,C 级供应商加严拒收
*/
@Test
public void shouldTightenAcceptanceForGradeCSupplier() {
MaterialInspection gradeA = base("BAT006", "A");
gradeA.setSampleSize(50);
gradeA.setMajorDefects(1);
gradeA.setMinorDefects(1);
MaterialInspectionDecision d1 = service.inspect(gradeA);
Assert.assertEquals(74, d1.getTotalScore());
Assert.assertEquals("CONDITIONAL", d1.getAction());
Assert.assertFalse(d1.isHardReject());
MaterialInspection gradeC = base("BAT007", "C");
gradeC.setSampleSize(50);
gradeC.setMajorDefects(1);
gradeC.setMinorDefects(1);
MaterialInspectionDecision d2 = service.inspect(gradeC);
Assert.assertEquals(74, d2.getTotalScore());
Assert.assertEquals("REJECT", d2.getAction());
Assert.assertTrue(d2.getDispositions().get(0).contains("供应商评级为 C,执行加严验收"));
}
private MaterialInspection base(String batchNo, String grade) {
MaterialInspection m = new MaterialInspection();
m.setBatchNo(batchNo);
m.setMaterialCode("MAT-8001");
m.setMaterialCategory("ELECTRONIC");
m.setSampleSize(50);
m.setCriticalDefects(0);
m.setMajorDefects(0);
m.setMinorDefects(0);
m.setCertificateProvided(true);
m.setAppearancePass(true);
m.setDimensionPass(true);
m.setEnvironmentalCompliant(true);
m.setSupplierGrade(grade);
return m;
}
}

Run from the project root:

Terminal window
mvn test -Dtest=IncomingMaterialServiceTest

Test coverage (6 @Test methods covering full-score acceptance, conditional acceptance, return, zero tolerance, dual hard rules, and tightened-acceptance comparison by supplier grade):

Test method Business profile Key assertions / expected decision
shouldAcceptPerfectBatch 50 samples with zero defects, complete documents, appearance/dimensions/environmental all pass (grade-A supplier BAT001) Total 100, ACCEPT, hardReject=false, dispositions include “抽检符合接收质量限,准予办理入库手续”
shouldConditionallyAcceptMinorDefects 2 minor defects out of 50 (4%, falling in the 3%-5% band) Total 87, CONDITIONAL; details include “轻微缺陷率: 7 / 20”, dispositions include “要求供应商提交 8D 整改报告”
shouldRejectBatchWithExcessiveDefects 3 major defects + 4 minor defects + dimension failure (grade-B BAT003) Total 40, REJECT; dispositions include “严重缺陷率超过 AQL 加严线,按退货流程处理” and “关键尺寸检验不合格,批次不得上线,须技术会签”
shouldHardRejectCriticalDefect Everything else passes but 1 critical defect appears (BAT004) Total 0, REJECT, hardReject=true; first disposition contains “致命缺陷,违反零容忍要求”
shouldHardRejectMissingCertificateAndRoHS No warranty certificate and RoHS fails (BAT005) REJECT, hardReject=true; the two dispositions contain “缺少质保书/COA 随附文件” and “冻结同批次在途库存” respectively
shouldTightenAcceptanceForGradeCSupplier The same 74-point batch routed through a grade-A (BAT006) and a grade-C (BAT007) supplier Grade A: 74 points CONDITIONAL and not hard-rejected; grade C: same 74 points but REJECT, first disposition contains “供应商评级为 C,执行加严验收”
  • Acceptance standards externalized in XML and dynamically adapted per material: the 1%/3%/5% defect-rate banding lines and the 90/70 acceptance lines all live in incoming-material.xml; electronic, metal, and chemical materials can each maintain their own XML, and rebuilding the proxy hot-updates the rules without releasing the MES main program.
  • One method per criterion, independently tunable and auditable: each of the six scoring dimensions is a separate rule method, and scoreDetails outputs per-dimension lines like “严重缺陷率: 10 / 30”, so supplier quality disputes and external audits can trace the basis of every band score.
  • Hard veto up front + grade-C tightening as a Java-side backstop: the three zero-tolerance conditions — critical defects, missing certificate, environmental non-compliance — short-circuit at the Service entry and cannot be overturned by any score; the tightened line (90 points) for grade-C suppliers is applied by Java on top, keeping the script’s preliminary verdict cleanly layered from supplier policy.
  • Cross-multiplication avoids integer-division traps in the script: defect-rate comparison is written as major * 100 < sample * 3 instead of floating-point ratio math in the script, giving a stable, precision-unambiguous criterion on JDK 8.
  • Name-bound @Param + ASM dynamic proxy: interface parameters are injected into the script by name via @Param, and the proxy object is reusable as a singleton; ASM generates the invoker underneath with a concurrent cache — no separate process, no heavyweight dependencies, embeddable directly into legacy MES and IT-innovation JDK environments.