chenrui
6 天以前 4b6ed9aec826d49abc239f8d768bfecb91eceabc
Merge branch 'master' of http://114.132.189.42:9002/r/product-inventory-management-after
已修改13个文件
已添加4个文件
626 ■■■■ 文件已修改
src/main/java/com/ruoyi/purchase/controller/PurchaseLedgerController.java 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/controller/TicketRegistrationController.java 29 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/dto/PaymentRegistrationDto.java 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/dto/PurchaseLedgerDto.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/dto/TicketRegistrationDto.java 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/mapper/ProductRecordMapper.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/pojo/PaymentRegistration.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/pojo/ProductRecord.java 124 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/pojo/TicketRegistration.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/service/IProductRecordService.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/service/IPurchaseLedgerService.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/service/ITicketRegistrationService.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/service/impl/PaymentRegistrationServiceImpl.java 101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/service/impl/ProductRecordServiceImpl.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/service/impl/PurchaseLedgerServiceImpl.java 103 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/service/impl/TicketRegistrationServiceImpl.java 115 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/purchase/PaymentRegistrationMapper.xml 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/purchase/controller/PurchaseLedgerController.java
@@ -95,6 +95,14 @@
    }
    /**
     * æ ¹æ®id查询采购合同号
     */
    @GetMapping("/getPurchaseNoById")
    public AjaxResult getPurchaseNoById(Long id) {
        return AjaxResult.success(purchaseLedgerService.getPurchaseNoById(id));
    }
    /**
     * æ ¹æ®é‡‡è´­åˆåŒå·æŸ¥è¯¢äº§å“
     */
    @GetMapping("/getProduct")
@@ -106,7 +114,7 @@
     * æ ¹æ®é‡‡è´­åˆåŒå·æŸ¥è¯¢äº§å“
     */
    @GetMapping("/getInfo")
    public PurchaseLedgerDto getInfo(PurchaseLedgerDto purchaseLedgerDto){
        return purchaseLedgerService.getInfo(purchaseLedgerDto);
    public AjaxResult getInfo(PurchaseLedgerDto purchaseLedgerDto) {
        return AjaxResult.success(purchaseLedgerService.getInfo(purchaseLedgerDto));
    }
}
src/main/java/com/ruoyi/purchase/controller/TicketRegistrationController.java
@@ -7,7 +7,9 @@
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.framework.web.page.TableDataInfo;
import com.ruoyi.purchase.dto.TicketRegistrationDto;
import com.ruoyi.purchase.pojo.ProductRecord;
import com.ruoyi.purchase.pojo.TicketRegistration;
import com.ruoyi.purchase.service.IProductRecordService;
import com.ruoyi.purchase.service.ITicketRegistrationService;
import com.ruoyi.sales.service.ICommonFileService;
import lombok.AllArgsConstructor;
@@ -33,12 +35,13 @@
    private ICommonFileService commonFileService;
    private IProductRecordService productRecordService;
    /**
     * æŸ¥è¯¢æ¥ç¥¨ç™»è®°åˆ—表
     */
    @GetMapping("/list")
    public TableDataInfo list(TicketRegistration ticketRegistration)
    {
    public TableDataInfo list(TicketRegistration ticketRegistration) {
        startPage();
        List<TicketRegistration> list = ticketRegistrationService.selectTicketRegistrationList(ticketRegistration);
        return getDataTable(list);
@@ -49,8 +52,7 @@
     */
    @Log(title = "来票登记", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    public void export(HttpServletResponse response, TicketRegistration ticketRegistration)
    {
    public void export(HttpServletResponse response, TicketRegistration ticketRegistration) {
        List<TicketRegistration> list = ticketRegistrationService.selectTicketRegistrationList(ticketRegistration);
        ExcelUtil<TicketRegistration> util = new ExcelUtil<TicketRegistration>(TicketRegistration.class);
        util.exportExcel(response, list, "来票登记数据");
@@ -78,8 +80,7 @@
     */
    @Log(title = "来票登记", businessType = BusinessType.DELETE)
    @DeleteMapping("/delRegistration")
    public AjaxResult delRegistration(@RequestBody Long[] ids)
    {
    public AjaxResult delRegistration(@RequestBody Long[] ids) {
        return toAjax(ticketRegistrationService.delRegistration(ids));
    }
@@ -92,4 +93,20 @@
        }
    }
    /**
     * æŸ¥è¯¢äº§å“ä¿¡æ¯å¼€ç¥¨è®°å½•列表
     */
    @GetMapping("/productRecordList")
    public List<ProductRecord> productRecordList(TicketRegistrationDto ticketRegistrationDto) {
        List<ProductRecord> list = productRecordService.selectProductRecordList(ticketRegistrationDto);
        return list;
    }
    /**
     * æŸ¥è¯¢å‘票号
     */
    @GetMapping("/getTicketNo")
    public AjaxResult getTicketNo(TicketRegistrationDto ticketRegistrationDto) {
        return AjaxResult.success(ticketRegistrationService.getTicketNo(ticketRegistrationDto));
    }
}
src/main/java/com/ruoyi/purchase/dto/PaymentRegistrationDto.java
@@ -30,5 +30,8 @@
    private BigDecimal unPaymentAmount;
    // ç¨Žçއ
    private BigDecimal taxRate;
    private String taxRate;
    // ç™»è®°äºº
    private String registrant;
}
src/main/java/com/ruoyi/purchase/dto/PurchaseLedgerDto.java
@@ -5,6 +5,7 @@
import com.ruoyi.sales.pojo.SalesLedgerProduct;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
@@ -116,4 +117,21 @@
     * äº§å“è§„æ ¼id
     */
    private Long productModelId;
    /**
     * å‘票号
     */
    private String invoiceNumber;
    /**
     * å‘票金额(元)
     */
    private BigDecimal invoiceAmount;
    /**
     * æ¥ç¥¨ç™»è®°id
     */
    private Long ticketRegistrationId;
}
src/main/java/com/ruoyi/purchase/dto/TicketRegistrationDto.java
@@ -1,13 +1,20 @@
package com.ruoyi.purchase.dto;
import com.baomidou.mybatisplus.annotation.TableName;
import com.ruoyi.purchase.pojo.TicketRegistration;
import com.ruoyi.sales.pojo.CommonFile;
import com.ruoyi.sales.pojo.SalesLedgerProduct;
import lombok.Data;
import java.util.List;
/**
 * æ¥ç¥¨ç™»è®°è¡¨
 */
@Data
public class TicketRegistrationDto {
@TableName("ticket_registration")
public class TicketRegistrationDto extends TicketRegistration {
    /**
     * ä¸»é”®ID
src/main/java/com/ruoyi/purchase/mapper/ProductRecordMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,14 @@
package com.ruoyi.purchase.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.purchase.pojo.ProductRecord;
/**
 * é‡‡è´­å°è´¦äº§å“å¼€ç¥¨è®°å½•Mapper接口
 *
 * @author ruoyi
 * @date 2025-05-23
 */
public interface ProductRecordMapper extends BaseMapper<ProductRecord> {
}
src/main/java/com/ruoyi/purchase/pojo/PaymentRegistration.java
@@ -43,7 +43,7 @@
    /**
     * å‘票id
     */
    private Long invoicePurchaseId;
    private Long ticketRegistrationId;
    /**
     * æœ¬æ¬¡ä»˜æ¬¾é‡‘额
@@ -54,6 +54,11 @@
     * ä»˜æ¬¾å½¢å¼
     */
    private String paymentMethod;
    /**
     * ç¨Žçއ
     */
    private String taxRate;
    /**
     * ç™»è®°äºº
@@ -70,6 +75,12 @@
    private Date paymentDate;
    /**
     * ç™»è®°æ—¥æœŸ
     */
    @JsonFormat(pattern = "yyyy-MM-dd")
    private Date registrationtDate;
    /**
     * åˆ›å»ºæ—¥æœŸ
     */
    @TableField(fill = FieldFill.INSERT)
src/main/java/com/ruoyi/purchase/pojo/ProductRecord.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,124 @@
package com.ruoyi.purchase.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
/**
 * é‡‡è´­å°è´¦äº§å“å¼€ç¥¨è®°å½•对象 product_record
 *
 * @author ruoyi
 * @date 2025-05-23
 */
@Data
@TableName("product_record")
public class ProductRecord {
    private static final long serialVersionUID = 1L;
    /**
     * ä¸»é”®id
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    /**
     * æ¥ç¥¨ç™»è®°id
     */
    private Long ticketRegistrationId;
    /**
     * é‡‡è´­å°è´¦id
     */
    private Long purchaseLedgerId;
    /**
     * äº§å“å¤§ç±»
     */
    private String productCategory;
    /**
     * è§„格型号
     */
    private String specificationModel;
    /**
     * å•位
     */
    private String unit;
    /**
     * æ•°é‡
     */
    private BigDecimal quantity;
    /**
     * ç¨Žçއ
     */
    private BigDecimal taxRate;
    /**
     * å«ç¨Žå•ä»·
     */
    private BigDecimal taxInclusiveUnitPrice;
    /**
     * å«ç¨Žæ€»ä»·
     */
    private BigDecimal taxInclusiveTotalPrice;
    /**
     * ä¸å«ç¨Žæ€»ä»·
     */
    private BigDecimal taxExclusiveTotalPrice;
    /**
     * å‘票类型
     */
    private String invoiceType;
    /**
     * 1.销售台账,2.采购台账
     */
    private String type;
    /**
     * æœ¬æ¬¡æ¥ç¥¨æ•°
     */
    private Long ticketsNum;
    /**
     * æœ¬æ¬¡æ¥ç¥¨é‡‘额(元)
     */
    private BigDecimal ticketsAmount;
    /**
     * æœªæ¥ç¥¨æ•°
     */
    private Long futureTickets;
    /**
     * æœªæ¥ç¥¨é‡‘额(元)
     */
    private BigDecimal futureTicketsAmount;
    /**
     * äº§å“id
     */
    private Long productId;
    /**
     * åž‹å·id
     */
    private Long productModelId;
    /**
     * åˆ›å»ºæ—¶é—´
     */
    @JsonFormat(pattern = "yyyy-MM-dd")
    private Date createdAt;
}
src/main/java/com/ruoyi/purchase/pojo/TicketRegistration.java
@@ -78,6 +78,12 @@
    private BigDecimal invoiceAmount;
    /**
     * å¼€ç¥¨äººID
     */
    @Excel(name = "开票人")
    private String issUerId;
    /**
     * å¼€ç¥¨äºº
     */
    @Excel(name = "开票人")
src/main/java/com/ruoyi/purchase/service/IProductRecordService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
package com.ruoyi.purchase.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.purchase.dto.TicketRegistrationDto;
import com.ruoyi.purchase.pojo.ProductRecord;
import java.util.List;
/**
 * é‡‡è´­å°è´¦äº§å“å¼€ç¥¨è®°å½•Service接口
 *
 * @author ruoyi
 * @date 2025-05-23
 */
public interface IProductRecordService extends IService<ProductRecord> {
    List<ProductRecord> selectProductRecordList(TicketRegistrationDto ticketRegistrationDto);
}
src/main/java/com/ruoyi/purchase/service/IPurchaseLedgerService.java
@@ -29,4 +29,6 @@
    PurchaseLedgerDto getInfo(PurchaseLedgerDto purchaseLedgerDto);
    List getPurchasesNo();
    PurchaseLedgerDto getPurchaseNoById(Long id);
}
src/main/java/com/ruoyi/purchase/service/ITicketRegistrationService.java
@@ -22,4 +22,6 @@
    int delRegistration(Long[] ids);
    TicketRegistrationDto getRegistrationById(TicketRegistrationDto ticketRegistrationDto);
    List getTicketNo(TicketRegistrationDto ticketRegistrationDto);
}
src/main/java/com/ruoyi/purchase/service/impl/PaymentRegistrationServiceImpl.java
@@ -2,6 +2,7 @@
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.basic.mapper.SupplierManageMapper;
import com.ruoyi.basic.pojo.SupplierManage;
@@ -10,18 +11,17 @@
import com.ruoyi.framework.security.LoginUser;
import com.ruoyi.purchase.dto.PaymentLedgerDto;
import com.ruoyi.purchase.dto.PaymentRegistrationDto;
import com.ruoyi.purchase.mapper.InvoicePurchaseMapper;
import com.ruoyi.purchase.mapper.PaymentRegistrationMapper;
import com.ruoyi.purchase.mapper.PurchaseLedgerMapper;
import com.ruoyi.purchase.pojo.InvoicePurchase;
import com.ruoyi.purchase.mapper.*;
import com.ruoyi.purchase.pojo.PaymentRegistration;
import com.ruoyi.purchase.pojo.ProductRecord;
import com.ruoyi.purchase.pojo.PurchaseLedger;
import com.ruoyi.purchase.pojo.TicketRegistration;
import com.ruoyi.purchase.service.IPaymentRegistrationService;
import com.ruoyi.sales.mapper.SalesLedgerMapper;
import com.ruoyi.sales.mapper.SalesLedgerProductMapper;
import com.ruoyi.sales.pojo.SalesLedger;
import com.ruoyi.sales.pojo.SalesLedgerProduct;
import org.springframework.beans.factory.annotation.Autowired;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
@@ -37,24 +37,23 @@
 * @date 2025-05-15
 */
@Service
@AllArgsConstructor
public class PaymentRegistrationServiceImpl extends ServiceImpl<PaymentRegistrationMapper, PaymentRegistration> implements IPaymentRegistrationService {
    @Autowired
    private PaymentRegistrationMapper paymentRegistrationMapper;
    @Autowired
    private PurchaseLedgerMapper purchaseLedgerMapper;
    @Autowired
    private InvoicePurchaseMapper invoicePurchaseMapper;
    @Autowired
    private SalesLedgerMapper salesLedgerMapper;
    @Autowired
    private SupplierManageMapper supplierManageMapper;
    @Autowired
    private SalesLedgerProductMapper salesLedgerProductMapper;
    private TicketRegistrationMapper ticketRegistrationMapper;
    private ProductRecordMapper productRecordMapper;
    /**
     * æŸ¥è¯¢ä»˜æ¬¾ç™»è®°
@@ -78,7 +77,7 @@
        List<PaymentRegistrationDto> list = paymentRegistrationMapper.selectPaymentRegistrationList(paymentRegistrationDto);
        for (PaymentRegistrationDto registrationDto : list) {
            List<PaymentRegistration> paymentRegistrations = paymentRegistrationMapper.selectList(new QueryWrapper<PaymentRegistration>()
                    .eq("invoice_purchase_id", registrationDto.getInvoicePurchaseId()));
                    .eq("ticket_registration_id", registrationDto.getTicketRegistrationId()));
            BigDecimal total = paymentRegistrations.stream().map(PaymentRegistration::getCurrentPaymentAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
            registrationDto.setUnPaymentAmount(registrationDto.getInvoiceAmount().subtract(total));
        }
@@ -103,25 +102,24 @@
        paymentRegistration.setSaleLedgerId(salesLedger.getId());
        paymentRegistration.setSupplierId(purchaseLedger.getSupplierId());
        List<InvoicePurchase> invoicePurchases = invoicePurchaseMapper.selectList(new QueryWrapper<InvoicePurchase>().
                eq("purchase_contract_no", purchaseLedger.getPurchaseContractNumber()));
        if (invoicePurchases == null || invoicePurchases.size() == 0) {
        TicketRegistration tr = ticketRegistrationMapper.selectOne(new LambdaQueryWrapper<TicketRegistration>().eq(TicketRegistration::getId, paymentRegistration.getTicketRegistrationId()));
        if (tr == null) {
            throw new RuntimeException("关联发票不存在");
        }
        paymentRegistration.setInvoicePurchaseId(invoicePurchases.get(0).getId());
        List<PaymentRegistration> paymentRegistrations = paymentRegistrationMapper.selectList(new QueryWrapper<PaymentRegistration>()
                .eq("invoice_purchase_id", invoicePurchases.get(0).getId()));
                .eq("ticket_registration_id", tr.getId()));
        BigDecimal total = paymentRegistrations.stream().map(PaymentRegistration::getCurrentPaymentAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        if (total.add(paymentRegistration.getCurrentPaymentAmount()).compareTo(invoicePurchases.get(0).getInvoiceAmount()) > 0) {
        if (total.add(paymentRegistration.getCurrentPaymentAmount()).compareTo(tr.getInvoiceAmount()) > 0) {
            throw new RuntimeException("付款金额超出发票金额");
        }
        LoginUser loginUser = SecurityUtils.getLoginUser();
        Integer tenantId = loginUser.getTenantId();
        paymentRegistration.setTenantId(tenantId.longValue());
        paymentRegistration.setRegistrantId(loginUser.getUserId());
        paymentRegistration.setCreateTime(DateUtils.getNowDate());
        paymentRegistration.setUpdateTime(DateUtils.getNowDate());
        return paymentRegistrationMapper.insert(paymentRegistration);
@@ -135,13 +133,13 @@
     */
    @Override
    public int updatePaymentRegistration(PaymentRegistration paymentRegistration) {
        InvoicePurchase invoicePurchase = invoicePurchaseMapper.selectById(paymentRegistration.getInvoicePurchaseId());
        TicketRegistration ticketRegistration = ticketRegistrationMapper.selectById(paymentRegistration.getTicketRegistrationId());
        List<PaymentRegistration> paymentRegistrations = paymentRegistrationMapper.selectList(new QueryWrapper<PaymentRegistration>()
                .eq("invoice_purchase_id", paymentRegistration.getInvoicePurchaseId()).ne("id", paymentRegistration.getId()));
                .eq("ticket_registration_id", paymentRegistration.getTicketRegistrationId()).ne("id", paymentRegistration.getId()));
        BigDecimal total = paymentRegistrations.stream().map(PaymentRegistration::getCurrentPaymentAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        if (total.add(paymentRegistration.getCurrentPaymentAmount()).compareTo(invoicePurchase.getInvoiceAmount()) > 0) {
        if (total.add(paymentRegistration.getCurrentPaymentAmount()).compareTo(ticketRegistration.getInvoiceAmount()) > 0) {
            throw new RuntimeException("付款金额超出发票金额");
        }
@@ -168,12 +166,11 @@
        paymentRegistrationDto.setSupplierName(purchaseLedger.getSupplierName());
        paymentRegistrationDto.setSupplierId(purchaseLedger.getSupplierId());
        List<InvoicePurchase> invoicePurchaseList = invoicePurchaseMapper.selectList(new QueryWrapper<InvoicePurchase>()
                .eq("purchase_contract_no", purchaseLedger.getPurchaseContractNumber()));
        if (invoicePurchaseList != null && invoicePurchaseList.size() > 0) {
            paymentRegistrationDto.setInvoiceNumber(invoicePurchaseList.get(0).getInvoiceNumber());
            paymentRegistrationDto.setInvoiceAmount(invoicePurchaseList.get(0).getInvoiceAmount());
            paymentRegistrationDto.setTaxRate(invoicePurchaseList.get(0).getTaxRate());
        List<TicketRegistration> ticketRegistrations = ticketRegistrationMapper.selectList(new QueryWrapper<TicketRegistration>()
                .eq("purchase_contract_number", purchaseLedger.getPurchaseContractNumber()));
        if (ticketRegistrations != null && ticketRegistrations.size() > 0) {
            paymentRegistrationDto.setInvoiceNumber(ticketRegistrations.get(0).getInvoiceNumber());
            paymentRegistrationDto.setInvoiceAmount(ticketRegistrations.get(0).getInvoiceAmount());
        }
        return paymentRegistrationDto;
    }
@@ -197,18 +194,36 @@
            // åº”付金额
            BigDecimal payableAmount = BigDecimal.ZERO;
            List<SalesLedger> salesLedgers = salesLedgerMapper.selectList(new QueryWrapper<SalesLedger>().eq("customer_id", supplierManage.getId()));
            if (salesLedgers != null && salesLedgers.size() > 0) {
            List<PurchaseLedger> purchaseLedgers = purchaseLedgerMapper.selectList(new QueryWrapper<PurchaseLedger>().eq("supplier_id", supplierManage.getId()));
            if (purchaseLedgers != null && purchaseLedgers.size() > 0) {
                List<SalesLedgerProduct> salesLedgerProducts = salesLedgerProductMapper.selectList(new QueryWrapper<SalesLedgerProduct>()
                        .in("sales_ledger_id", salesLedgers.stream().map(SalesLedger::getId).collect(Collectors.toList())));
                        .in("sales_ledger_id", purchaseLedgers.stream().map(PurchaseLedger::getId).collect(Collectors.toList())));
                // åº”付金额
                payableAmount = salesLedgerProducts.stream().map(SalesLedgerProduct::getTaxInclusiveTotalPrice).reduce(BigDecimal.ZERO, BigDecimal::add);
            }
            // å¼€ç¥¨é‡‘额
            BigDecimal invoiceAmount = salesLedgers.stream().map(SalesLedger::getContractAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
            BigDecimal invoiceAmount = BigDecimal.ZERO;
            List<TicketRegistration> ticketRegistrations = Collections.emptyList();
            // å¢žåŠ ç©ºå€¼æ£€æŸ¥ï¼Œé¿å…NullPointerException
            if (CollectionUtils.isNotEmpty(purchaseLedgers)) {
                Long[] ids = purchaseLedgers.stream()
                        .map(PurchaseLedger::getId)
                        .toArray(Long[]::new);
                // æ£€æŸ¥æ•°ç»„是否有元素
                if (ids.length > 0) {
                    ticketRegistrations = ticketRegistrationMapper.selectList(
                            new LambdaQueryWrapper<TicketRegistration>()
                                    .in(TicketRegistration::getPurchaseLedgerId, ids)
                    );
                }
            }
            if (ticketRegistrations != null && ticketRegistrations.size() > 0) {
                // æ¥ç¥¨é‡‘额
                invoiceAmount = ticketRegistrations.stream().map(TicketRegistration::getInvoiceAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
            }
            // ä»˜æ¬¾é‡‘额
            List<PaymentRegistration> paymentRegistrations = paymentRegistrationMapper.selectList(new QueryWrapper<PaymentRegistration>()
                    .eq("supplier_id", supplierManage.getId()));
@@ -226,21 +241,25 @@
            List<Map<String, Object>> details = new ArrayList<>();
            for (PaymentRegistration paymentRegistration : paymentRegistrations) {
                Map<String, Object> detail = new HashMap<>();
                detail.put("voteCount", 1); // ç¥¨æ•°ï¼ŒæœªçŸ¥æ•°æ®æºï¼Œæš‚时用1
                detail.put("paymentAmount", paymentRegistration.getCurrentPaymentAmount()); // ä»˜æ¬¾é‡‘额
                InvoicePurchase  invoicePurchase = invoicePurchaseMapper.selectById(paymentRegistration.getInvoicePurchaseId());
                detail.put("payableAmount", invoicePurchase.getInvoiceAmount()); // åº”付金额
                TicketRegistration ticketRegistration = ticketRegistrationMapper.selectById(paymentRegistration.getTicketRegistrationId());
                detail.put("payableAmount", ticketRegistration.getInvoiceAmount()); // åº”付金额
                BigDecimal voteCount = productRecordMapper.selectList(
                                new LambdaQueryWrapper<ProductRecord>()
                                        .eq(ProductRecord::getTicketRegistrationId, ticketRegistration.getId()))
                        .stream()
                        .map(ProductRecord::getTicketsNum)
                        .map(BigDecimal::new)
                        .reduce(BigDecimal.ZERO, BigDecimal::add);
                detail.put("voteCount", voteCount); // ç¥¨æ•°
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
                String formattedDate = sdf.format(paymentRegistration.getPaymentDate());
                detail.put("createTime", formattedDate); // å‘生时间
                detail.put("paymentDate", formattedDate); // å‘生时间
                details.add(detail);
            }
            res.put("details", paymentRegistrations);
            res.put("details", details);
            result.add(res);
        }
        return result;
    }
}
src/main/java/com/ruoyi/purchase/service/impl/ProductRecordServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,39 @@
package com.ruoyi.purchase.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.purchase.dto.TicketRegistrationDto;
import com.ruoyi.purchase.mapper.ProductRecordMapper;
import com.ruoyi.purchase.pojo.ProductRecord;
import com.ruoyi.purchase.service.IProductRecordService;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.List;
/**
 * é‡‡è´­å°è´¦äº§å“å¼€ç¥¨è®°å½•Service业务层处理
 *
 * @author ruoyi
 * @date 2025-05-23
 */
@Service
@AllArgsConstructor
public class ProductRecordServiceImpl extends ServiceImpl<ProductRecordMapper, ProductRecord> implements IProductRecordService {
    private ProductRecordMapper productRecordMapper;
    /**
     * æŸ¥è¯¢é‡‡è´­å°è´¦äº§å“å¼€ç¥¨è®°å½•
     *
     * @param ticketRegistrationDto é‡‡è´­å°è´¦äº§å“å¼€ç¥¨è®°å½•主键
     * @return é‡‡è´­å°è´¦äº§å“å¼€ç¥¨è®°å½•
     */
    @Override
    public List<ProductRecord> selectProductRecordList(TicketRegistrationDto ticketRegistrationDto) {
        return productRecordMapper.selectList(new LambdaQueryWrapper<ProductRecord>().eq(ProductRecord::getTicketRegistrationId, ticketRegistrationDto.getId())
                .eq(ProductRecord::getType, "2"));
    }
}
src/main/java/com/ruoyi/purchase/service/impl/PurchaseLedgerServiceImpl.java
@@ -17,8 +17,12 @@
import com.ruoyi.project.system.domain.SysUser;
import com.ruoyi.project.system.mapper.SysUserMapper;
import com.ruoyi.purchase.dto.PurchaseLedgerDto;
import com.ruoyi.purchase.mapper.ProductRecordMapper;
import com.ruoyi.purchase.mapper.PurchaseLedgerMapper;
import com.ruoyi.purchase.mapper.TicketRegistrationMapper;
import com.ruoyi.purchase.pojo.ProductRecord;
import com.ruoyi.purchase.pojo.PurchaseLedger;
import com.ruoyi.purchase.pojo.TicketRegistration;
import com.ruoyi.purchase.service.IPurchaseLedgerService;
import com.ruoyi.sales.mapper.CommonFileMapper;
import com.ruoyi.sales.mapper.SalesLedgerMapper;
@@ -73,6 +77,10 @@
    private final ProductModelMapper productModelMapper;
    private final TicketRegistrationMapper ticketRegistrationMapper;
    private final ProductRecordMapper productRecordMapper;
    @Value("${file.upload-dir}")
    private String uploadDir;
@@ -118,7 +126,7 @@
        // 4. å¤„理子表数据
        List<SalesLedgerProduct> productList = purchaseLedgerDto.getProductData();
        if (productList != null && !productList.isEmpty()) {
            handleSalesLedgerProducts(purchaseLedger.getId(), purchaseLedgerDto.getProductId(), purchaseLedgerDto.getProductModelId(), productList, purchaseLedgerDto.getType());
            handleSalesLedgerProducts(purchaseLedger.getId(), productList, purchaseLedgerDto.getType());
        }
        // 5. è¿ç§»ä¸´æ—¶æ–‡ä»¶åˆ°æ­£å¼ç›®å½•
@@ -129,19 +137,52 @@
        return 1;
    }
    private void handleSalesLedgerProducts(Long salesLedgerId, Long productId, Long productModelId, List<SalesLedgerProduct> products, Integer type) {
        Product pro = productMapper.selectById(productId);
        ProductModel productModel = productModelMapper.selectById(productModelId);
    private void handleSalesLedgerProducts(Long salesLedgerId, List<SalesLedgerProduct> products, Integer type) {
        if (products == null || products.isEmpty()) {
            throw new BaseException("产品信息不存在");
        }
        // æŒ‰ID分组,区分新增和更新的记录
        // æå‰æ”¶é›†æ‰€æœ‰éœ€è¦æŸ¥è¯¢çš„ID
        Set<Long> productIds = products.stream()
                .map(SalesLedgerProduct::getProductId)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());
        Set<Long> modelIds = products.stream()
                .map(SalesLedgerProduct::getProductModelId)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());
        // ä¸€æ¬¡æ€§æŸ¥è¯¢äº§å“å’Œåž‹å·ä¿¡æ¯
        Map<Long, String> productMap = new HashMap<>();
        if (!productIds.isEmpty()) {
            List<Product> productList = productMapper.selectBatchIds(productIds);
            productList.forEach(p -> productMap.put(p.getId(), p.getProductName()));
        }
        Map<Long, String> modelMap = new HashMap<>();
        if (!modelIds.isEmpty()) {
            List<ProductModel> modelList = productModelMapper.selectBatchIds(modelIds);
            modelList.forEach(m -> modelMap.put(m.getId(), m.getModel()));
        }
        // è®¾ç½®å­—段
        for (SalesLedgerProduct product : products) {
            product.setSalesLedgerId(salesLedgerId);
            Long productId = product.getProductId();
            if (productId != null && productMap.containsKey(productId)) {
                product.setProductCategory(productMap.get(productId));
            }
            Long productModelId = product.getProductModelId();
            if (productModelId != null && modelMap.containsKey(productModelId)) {
                product.setSpecificationModel(modelMap.get(productModelId));
            }
        }
        // åˆ†ç»„处理
        Map<Boolean, List<SalesLedgerProduct>> partitionedProducts = products.stream()
                .peek(p -> {
                    p.setSalesLedgerId(salesLedgerId);
                    p.setProductId(productId);
                    p.setProductCategory(pro.getProductName());
                    p.setProductModelId(productModelId);
                    p.setSpecificationModel(productModel.getModel());
                })
                .collect(Collectors.partitioningBy(p -> p.getId() != null));
        List<SalesLedgerProduct> updateList = partitionedProducts.get(true);
@@ -248,6 +289,23 @@
    @Override
    public int deletePurchaseLedgerByIds(Long[] ids) {
        if (ids == null || ids.length == 0) {
           throw new BaseException("请选中至少一条数据");
        }
        // æ‰¹é‡åˆ é™¤å…³è”的采购台账产品
        LambdaQueryWrapper<SalesLedgerProduct> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(SalesLedgerProduct::getSalesLedgerId, ids)
                .eq(SalesLedgerProduct::getType, "2");
        salesLedgerProductMapper.delete(queryWrapper);
        // æ‰¹é‡åˆ é™¤å…³è”的采购台账的来票登记
        LambdaQueryWrapper<TicketRegistration> ticketRegistrationLambdaQueryWrapper = new LambdaQueryWrapper<>();
        ticketRegistrationLambdaQueryWrapper.in(TicketRegistration::getSalesLedgerId,ids);
        ticketRegistrationMapper.delete(ticketRegistrationLambdaQueryWrapper);
        // æ‰¹é‡åˆ é™¤å…³è”的采购台账的来票登记记录
        LambdaQueryWrapper<ProductRecord> productRecordLambdaQueryWrapper = new LambdaQueryWrapper<>();
        productRecordLambdaQueryWrapper.in(ProductRecord::getPurchaseLedgerId,ids);
        productRecordMapper.delete(productRecordLambdaQueryWrapper);
        // æ‰¹é‡åˆ é™¤é‡‡è´­å°è´¦
        return purchaseLedgerMapper.deleteBatchIds(Arrays.asList(ids));
    }
@@ -315,8 +373,13 @@
        queryWrapper.eq(SalesLedgerProduct::getSalesLedgerId, purchaseLedger.getId())
                .eq(SalesLedgerProduct::getType, 2);
        List<SalesLedgerProduct> productList = salesLedgerProductMapper.selectList(queryWrapper);
        productList.forEach(product -> {
            product.setFutureTickets(product.getFutureTickets() != null ? product.getFutureTickets() : product.getQuantity().longValue());
            product.setFutureTicketsAmount(product.getFutureTicketsAmount() != null ? product.getFutureTicketsAmount() : product.getTaxInclusiveTotalPrice());
            product.setTicketsNum(null);
            product.setTicketsAmount(null);
        });
        resultDto.setProductData(productList);
        return resultDto;
    }
@@ -336,6 +399,20 @@
        ).collect(Collectors.toList());
    }
    @Override
    public PurchaseLedgerDto getPurchaseNoById(Long id) {
        PurchaseLedgerDto purchaseLedgerDto = new PurchaseLedgerDto();
        PurchaseLedger purchaseLedger = purchaseLedgerMapper.selectById(id);
        BeanUtils.copyProperties(purchaseLedger, purchaseLedgerDto);
//        TicketRegistration ticketRegistration = ticketRegistrationMapper.selectOne(new LambdaQueryWrapper<TicketRegistration>().eq(TicketRegistration::getPurchaseLedgerId, id));
//        if (ticketRegistration != null) {
//            purchaseLedgerDto.setInvoiceNumber(ticketRegistration.getInvoiceNumber());
//            purchaseLedgerDto.setInvoiceAmount(ticketRegistration.getInvoiceAmount());
//            purchaseLedgerDto.setTicketRegistrationId(ticketRegistration.getId());
//        }
        return purchaseLedgerDto;
    }
    /**
     * ä¸‹åˆ’线命名转驼峰命名
     */
src/main/java/com/ruoyi/purchase/service/impl/TicketRegistrationServiceImpl.java
@@ -4,13 +4,16 @@
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.exception.base.BaseException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.bean.BeanUtils;
import com.ruoyi.other.mapper.TempFileMapper;
import com.ruoyi.other.pojo.TempFile;
import com.ruoyi.purchase.dto.TicketRegistrationDto;
import com.ruoyi.purchase.mapper.ProductRecordMapper;
import com.ruoyi.purchase.mapper.PurchaseLedgerMapper;
import com.ruoyi.purchase.mapper.TicketRegistrationMapper;
import com.ruoyi.purchase.pojo.ProductRecord;
import com.ruoyi.purchase.pojo.PurchaseLedger;
import com.ruoyi.purchase.pojo.TicketRegistration;
import com.ruoyi.purchase.service.ITicketRegistrationService;
@@ -23,6 +26,7 @@
import org.apache.commons.io.FilenameUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.IOException;
import java.math.BigDecimal;
@@ -35,6 +39,7 @@
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;
@@ -59,6 +64,8 @@
    private final TempFileMapper tempFileMapper;
    private final ProductRecordMapper productRecordMapper;
    @Value("${file.upload-dir}")
    private String uploadDir;
@@ -67,38 +74,59 @@
    public List<TicketRegistration> selectTicketRegistrationList(TicketRegistration ticketRegistration) {
        LambdaQueryWrapper<TicketRegistration> queryWrapper = new LambdaQueryWrapper<>();
        if (StringUtils.isNotBlank(ticketRegistration.getPurchaseContractNumber())) {
            queryWrapper.like(TicketRegistration::getPurchaseContractNumber, ticketRegistration.getPurchaseContractNumber());
            queryWrapper.like(TicketRegistration::getPurchaseContractNumber, ticketRegistration.getPurchaseContractNumber())
                    .like(TicketRegistration::getSupplierName, ticketRegistration.getSupplierName())
                    .eq(TicketRegistration::getIssueDate, ticketRegistration.getIssueDate());
        }
        return ticketRegistrationMapper.selectList(queryWrapper);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int addOrUpdateRegistration(TicketRegistrationDto ticketRegistrationDto) throws IOException {
        // 1. æŸ¥è¯¢é‡‡è´­å°è´¦è®°å½•
        PurchaseLedger purchaseLedger = purchaseLedgerMapper.selectById(ticketRegistrationDto.getPurchaseLedgerId());
        if (purchaseLedger == null) {
            // å¤„理采购台账不存在的情况,例如抛出异常或返回错误
            throw new IllegalArgumentException("采购台账记录不存在,ID: " + ticketRegistrationDto.getPurchaseLedgerId());
        }
        // 3. åˆ›å»ºæˆ–更新票据登记实体
        TicketRegistration ticketRegistration = new TicketRegistration();
        BeanUtils.copyProperties(ticketRegistrationDto, ticketRegistration);
        ticketRegistration.setPurchaseContractNumber(purchaseLedger.getPurchaseContractNumber());
        ticketRegistration.setTenantId(purchaseLedger.getTenantId());
        ticketRegistration.setContractAmount(purchaseLedger.getContractAmount());
        // å¤„理子表数据
        ticketRegistration.setSalesLedgerId(purchaseLedger.getSalesLedgerId());
        // 4. å¤„理子表数据
        List<SalesLedgerProduct> productData = ticketRegistrationDto.getProductData();
        if (productData != null && !productData.isEmpty()) {
        if (CollectionUtils.isNotEmpty(productData)) {
            handleSalesLedgerProducts(purchaseLedger.getId(), productData, 2);
        }
        // æ‰§è¡Œæ’入或更新操作
        int i;
        if (ticketRegistrationDto.getId() == null) {
            i = ticketRegistrationMapper.insert(ticketRegistration);
        } else {
            i = ticketRegistrationMapper.updateById(ticketRegistration);
        }
        // 5. æ‰§è¡Œæ’入或更新操作
        int rowsAffected = ticketRegistrationMapper.insert(ticketRegistration);
        // 6. å¢žåŠ é‡‡è´­å°è´¦äº§å“å¼€ç¥¨è®°å½•
        List<SalesLedgerProduct> salesLedgerProducts = ticketRegistrationDto.getProductData();
        if (CollectionUtils.isNotEmpty(salesLedgerProducts)){
            for (SalesLedgerProduct salesLedgerProduct : salesLedgerProducts) {
                ProductRecord productRecord = new ProductRecord();
                productRecord.setTicketRegistrationId(ticketRegistration.getId());
                productRecord.setPurchaseLedgerId(ticketRegistrationDto.getPurchaseLedgerId());
                productRecord.setCreatedAt(DateUtils.getNowDate());
                BeanUtils.copyProperties(salesLedgerProduct,productRecord);
                productRecord.setId(null);
                productRecord.setType("2");
                productRecordMapper.insert(productRecord);
            }
        }
        // è¿ç§»ä¸´æ—¶æ–‡ä»¶åˆ°æ­£å¼ç›®å½•
        if (ticketRegistrationDto.getTempFileIds() != null && !ticketRegistrationDto.getTempFileIds().isEmpty()) {
            migrateTempFilesToFormal(ticketRegistration.getId(), ticketRegistrationDto.getTempFileIds());
        }
        return i;
        return rowsAffected;
    }
@@ -195,8 +223,21 @@
                .eq(SalesLedgerProduct::getType, 2);
        List<SalesLedgerProduct> productList = salesLedgerProductMapper.selectList(queryWrapper);
        resultDto.setProductData(productList);
        return resultDto;
    }
    @Override
    public List getTicketNo(TicketRegistrationDto ticketRegistrationDto) {
        LambdaQueryWrapper<TicketRegistration> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.select(TicketRegistration::getId,TicketRegistration::getInvoiceNumber,TicketRegistration::getInvoiceAmount)
                .eq(TicketRegistration::getPurchaseLedgerId,ticketRegistrationDto.getId());
        List<Map<String, Object>> result = ticketRegistrationMapper.selectMaps(queryWrapper);
        // å°†ä¸‹åˆ’线命名转换为驼峰命名
        return result.stream().map(map -> map.entrySet().stream()
                .collect(Collectors.toMap(
                        entry -> underlineToCamel(entry.getKey()),
                        Map.Entry::getValue))
        ).collect(Collectors.toList());
    }
    private void handleSalesLedgerProducts(Long salesLedgerId, List<SalesLedgerProduct> products, Integer type) {
@@ -216,11 +257,57 @@
        // æ‰¹é‡æ›´æ–°ï¼ˆéœ€è¦ MyBatis æä¾›æ‰¹é‡æ›´æ–°æ–¹æ³•)
        if (!updateList.isEmpty()) {
            updateList.forEach(product -> {
                product.setFutureTickets(product.getQuantity().subtract(new BigDecimal(product.getTicketsNum())).longValue());
                product.setFutureTicketsAmount(product.getTaxExclusiveTotalPrice().subtract(product.getTicketsAmount()));
                // éžç©ºæ ¡éªŒï¼Œä»»ä¸€å­—段为空则抛出异常
                if (product.getQuantity() == null) {
                    throw new BaseException("数量不能为空");
                }
                if (product.getTicketsNum() == null) {
                    throw new BaseException("已开票数量不能为空");
                }
                if (product.getTaxInclusiveTotalPrice() == null) {
                    throw new BaseException("含税总价不能为空");
                }
                if (product.getTicketsAmount() == null) {
                    throw new BaseException("本次来票金额(元)不能为空");
                }
                // è®¡ç®— futureTickets(直接使用 BigDecimal è®¡ç®—,避免精度丢失)
                product.setFutureTickets(
                        product.getQuantity()
                                .subtract(BigDecimal.valueOf(product.getTicketsNum()))
                                .longValueExact() // ä½¿ç”¨ exact æ–¹æ³•确保无小数部分
                );
                // è®¡ç®— futureTicketsAmount
                product.setFutureTicketsAmount(
                        product.getTaxInclusiveTotalPrice()
                                .subtract(product.getTicketsAmount())
                );
                product.setType(type);
                salesLedgerProductMapper.updateById(product);
            });
        }
    }
    /**
     * ä¸‹åˆ’线命名转驼峰命名
     */
    private String underlineToCamel(String param) {
        if (param == null || "".equals(param.trim())) {
            return "";
        }
        int len = param.length();
        StringBuilder sb = new StringBuilder(len);
        for (int i = 0; i < len; i++) {
            char c = param.charAt(i);
            if (c == '_') {
                if (++i < len) {
                    sb.append(Character.toUpperCase(param.charAt(i)));
                }
            } else {
                sb.append(Character.toLowerCase(c));
            }
        }
        return sb.toString();
    }
}
src/main/resources/mapper/purchase/PaymentRegistrationMapper.xml
@@ -9,7 +9,7 @@
        <result property="saleLedgerId" column="sale_ledger_id"/>
        <result property="purchaseLedgerId" column="purchase_ledger_id"/>
        <result property="supplierId" column="supplier_id"/>
        <result property="invoicePurchaseId" column="invoice_purchase_id"/>
        <result property="ticketRegistrationId" column="ticket_registration_id"/>
        <result property="currentPaymentAmount" column="current_payment_amount"/>
        <result property="paymentMethod" column="payment_method"/>
        <result property="registrantId" column="registrant_id"/>
@@ -17,6 +17,7 @@
        <result property="createTime" column="create_time"/>
        <result property="updateTime" column="update_time"/>
        <result property="tenantId" column="tenant_id"/>
        <result property="registrationtDate" column="registrationt_date"/>
    </resultMap>
    <resultMap type="com.ruoyi.purchase.dto.PaymentRegistrationDto" id="PaymentRegistrationDtoResult">
@@ -24,7 +25,7 @@
        <result property="saleLedgerId" column="sale_ledger_id"/>
        <result property="purchaseLedgerId" column="purchase_ledger_id"/>
        <result property="supplierId" column="supplier_id"/>
        <result property="invoicePurchaseId" column="invoice_purchase_id"/>
        <result property="ticketRegistrationId" column="ticket_registration_id"/>
        <result property="currentPaymentAmount" column="current_payment_amount"/>
        <result property="paymentMethod" column="payment_method"/>
        <result property="registrantId" column="registrant_id"/>
@@ -38,6 +39,7 @@
        <result property="taxRate" column="tax_rate"/>
        <result property="invoiceAmount" column="invoice_amount"/>
        <result property="tenantId" column="tenant_id"/>
        <result property="registrationtDate" column="registrationt_date"/>
    </resultMap>
    <sql id="selectPaymentRegistrationVo">
@@ -45,7 +47,7 @@
               pr.sale_ledger_id,
               pr.purchase_ledger_id,
               pr.supplier_id,
               pr.invoice_purchase_id,
               pr.ticket_registration_id,
               pr.current_payment_amount,
               pr.payment_method,
               pr.registrant_id,
@@ -57,13 +59,15 @@
               pl.purchase_contract_number as purchase_contract_number,
               sm.supplier_name as supplier_name,
               ip.invoice_number as invoice_number,
               ip.tax_rate as tax_rate,
               ip.invoice_amount as invoice_amount
               ip.invoice_amount as invoice_amount,
               pr.tax_rate,
               pr.registrationt_date,
               su.nick_name as registrant
        from payment_registration pr
                 left join sales_ledger sl on pr.sale_ledger_id = sl.id
                 left join purchase_ledger pl on pr.purchase_ledger_id = pl.id
                 left join supplier_manage sm on pr.supplier_id = sm.id
                 left join invoice_purchase ip on pr.invoice_purchase_id = ip.id
                 left join ticket_registration ip on pr.ticket_registration_id = ip.id
                 left join sys_user su on pr.registrant_id = su.user_id
    </sql>