Skip to content

Ninniku IT Hub

Provide open-source solutions for businesses.

Menu
  • Home
  • Technical Support
  • Traning
    • Arduino for IoT and ERP Integration
  • Expert Certification
  • Plug-ins (Taiwan)
    • Accounting for Taiwan
    • Meeting Room Booking
Menu

Mastering Currency Rate Check Skipping: Configuration and Coding Guide

Posted on 2023-08-19

Summary

In the post-iDempiere 7.1 era, a new feature was introduced where the system checks for overlapping currency rates after input and save. However, there are instances when you need to establish a long-term rate alongside a spot currency rate, potentially triggering an unnecessary overlap alert. As shown in Figure 1. This tutorial delves into a practical solution for this scenario, offering step-by-step guidance on how to effectively code and implement a mechanism to bypass the overlap check. With clear explanations and hands-on examples, this guide equips you to seamlessly navigate and modify the currency rate check process in iDempiere to suit your specific business needs.

Usage

Within the System Configuration, create a new configuration named “Is_CurrencyRate_Overlap” and assign the value “N” to it.

Using “Y” will activate the Overlap check. Using “N” will deactivate the Overlap check.
As shown in Figure 2.

Figure 2

Coding

Instructions:

  1. Begin by creating a new class named CustomMConversionRate that extends from org.compiere.model.MConversionRate.
  2. Override two methods: beforeSave and afterSave, as demonstrated in the code snippet below.
package tw.ninniku.trade.model;

import java.math.BigDecimal;
import java.sql.ResultSet;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.Properties;

import org.compiere.model.MSysConfig;
import org.compiere.model.PO;
import org.compiere.model.Query;
import org.compiere.util.DisplayType;
import org.compiere.util.Env;
import org.compiere.util.Msg;

public class MConversionRate extends org.compiere.model.MConversionRate {

	/**
	 * 
	 */
	private static final long serialVersionUID = 6241095402675778073L;
	private boolean recursiveCall = false;

	public MConversionRate(PO po, int C_ConversionType_ID, int C_Currency_ID, int C_Currency_ID_To,
			BigDecimal MultiplyRate, Timestamp ValidFrom) {
		super(po, C_ConversionType_ID, C_Currency_ID, C_Currency_ID_To, MultiplyRate, ValidFrom);
		// TODO Auto-generated constructor stub
	}

	public MConversionRate(Properties ctx, int C_Conversion_Rate_ID, String trxName) {
		super(ctx, C_Conversion_Rate_ID, trxName);
		// TODO Auto-generated constructor stub
	}

	public MConversionRate(Properties ctx, ResultSet rs, String trxName) {
		super(ctx, rs, trxName);
		// TODO Auto-generated constructor stub
	}

	@Override
	protected boolean beforeSave(boolean newRecord) {
		//	From - To is the same
		if (getC_Currency_ID() == getC_Currency_ID_To())
		{
			log.saveError("Error", Msg.parseTranslation(getCtx(), "@C_Currency_ID@ = @C_Currency_ID@"));
			return false;
		}
		//	Nothing to convert
		if (getMultiplyRate().compareTo(Env.ZERO) <= 0)
		{
			log.saveError("Error", Msg.parseTranslation(getCtx(), "@MultiplyRate@ <= 0"));
			return false;
		}

		//	Date Range Check
		Timestamp from = getValidFrom();
		if (getValidTo() == null) {
			// setValidTo (TimeUtil.getDay(2056, 1, 29));	//	 no exchange rates after my 100th birthday
			log.saveError("FillMandatory", Msg.getElement(getCtx(), COLUMNNAME_ValidTo));
			return false;
		}
		Timestamp to = getValidTo();
		
		if (to.before(from))
		{
			SimpleDateFormat df = DisplayType.getDateFormat(DisplayType.Date);
			log.saveError("Error", df.format(to) + " < " + df.format(from));
			return false;
		}
		//Use the settings in MSysconfig to determine whether to skip the Overlap check.
		if(!MSysConfig.getBooleanValue ("Is_CorrencyRate_Overlap", true,getAD_Client_ID()))
		{
			return true;
		}
		if (isActive()) {
			String whereClause = "(? BETWEEN ValidFrom AND ValidTo OR ? BETWEEN ValidFrom AND ValidTo) "
					+ "AND C_Currency_ID=? AND C_Currency_ID_To=? "
					+ "AND C_Conversiontype_ID=? "
					+ "AND AD_Client_ID=? AND AD_Org_ID=?";
			List<MConversionRate> convs = new Query(getCtx(), MConversionRate.Table_Name, whereClause, get_TrxName())
					.setOnlyActiveRecords(true)
					.setParameters(getValidFrom(), getValidTo(), 
							getC_Currency_ID(), getC_Currency_ID_To(),
							getC_ConversionType_ID(),
							getAD_Client_ID(), getAD_Org_ID())
					.list();
			for (MConversionRate conv : convs) {
				if (conv.getC_Conversion_Rate_ID() != getC_Conversion_Rate_ID()) {
					log.saveError("Error", "Conversion rate overlaps with: "	+ conv.getValidFrom());
					return false;
				}
			}
		}

		return true;
	}

	@Override
	protected boolean afterSave(boolean newRecord, boolean success) {
		if (success && !recursiveCall ) {
			
			
			
			String whereClause = "ValidFrom=? AND ValidTo=? "
					+ "AND C_Currency_ID=? AND C_Currency_ID_To=? "
					+ "AND C_ConversionType_ID=? "
					+ "AND AD_Client_ID=? AND AD_Org_ID=?";
			
			List<MConversionRate> list = new Query(getCtx(), MConversionRate.Table_Name, whereClause, get_TrxName())
					.setParameters(getValidFrom(), getValidTo(), 
							getC_Currency_ID_To(), getC_Currency_ID(),
							getC_ConversionType_ID(),
							getAD_Client_ID(), getAD_Org_ID())
					.setOrderBy(MConversionRate.COLUMNNAME_ValidFrom + " DESC")
					.list();
			MConversionRate reciprocal  = null;
			for (MConversionRate rate : list) {
				reciprocal = rate;
				break;
			}
			
			if (reciprocal == null) {
				// create reciprocal rate
				reciprocal = new MConversionRate(getCtx(), 0, get_TrxName());
				reciprocal.setValidFrom(getValidFrom());
				reciprocal.setValidTo(getValidTo());
				reciprocal.setC_ConversionType_ID(getC_ConversionType_ID());
				reciprocal.setAD_Client_ID(getAD_Client_ID());
				reciprocal.setAD_Org_ID(getAD_Org_ID());
				// invert
				reciprocal.setC_Currency_ID(getC_Currency_ID_To());
				reciprocal.setC_Currency_ID_To(getC_Currency_ID());
			}
			// avoid recalculation
			reciprocal.set_Value(COLUMNNAME_DivideRate, getMultiplyRate());
			reciprocal.set_Value(COLUMNNAME_MultiplyRate, getDivideRate());
			recursiveCall = true;
			try {
				reciprocal.saveEx();
			} finally {
				recursiveCall = false;
			}
		}
		return success;
	}

}

Implementation

Certainly, you can place the model class into a plugin using ModelFactory. For a detailed step-by-step process, you can refer to the article provided below.

Feel free to explore the article for comprehensive guidance on how to incorporate the model class into a plugin using ModelFactory.

https://wiki.idempiere.org/en/Developing_plug-ins_without_affecting_the_trunk

https://wiki.idempiere.org/en/Developing_Plug-Ins_-_IModelFactory

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Categories

  • Apple
  • iDempiere
  • IoT
  • IT Tools
  • Linux
  • Productivity
  • SAP
  • uncategorized
  • Wordpress

Tags

Access Arduino Bad Dept Booking Business One Button Confirmation Developer ERP Exam Free git github GPIO iDempiere Jasper Report Java Language LED Linux M1 MacOS Material Receipt Maven Meeting room Open Source OSGi Period Control Permission Plug-In PostgreSQL Potentiometer Premiere Pro Process PWM Raspberry PI Resistor Role Sales Management SWT Tips Ubuntu Video Editor Wordpress Workflow

Recent Posts

  • Unlocking Creative Potential: Top Open-Source Alternatives to Adobe Premiere Pro for Video Editing
  • Enhancing Meeting Room Efficiency with the iDempiere Meeting Room Booking Plug-in
  • How to Create a GitHub Repository from a Local Folder
  • Demystifying OSGi Service Ranking: A Comprehensive Guide
  • PostgreSQL Time Travel: Upgrading from Version 9.6 to 14

Meta

  • Register
  • Log in
  • Entries feed
  • Comments feed
  • WordPress.org
©2023 Ninniku IT Hub | Design: Newspaperly WordPress Theme