pom.xml
@@ -91,6 +91,29 @@ </dependency> <!-- Intuit's QuickBooks Java SDK --> <dependency> <groupId> com.intuit.quickbooks-online </groupId> <artifactId> ipp-v3-java-data </artifactId> <version> 6.0.7 </version> </dependency> <!-- devkit jar with dependencies--> <dependency> <groupId>com.intuit.quickbooks-online</groupId> <artifactId>ipp-v3-java-devkit</artifactId> <classifier>jar-with-dependencies</classifier> <version> 6.0.7 </version> </dependency> <!-- oauth jar with dependencies--> <dependency> <groupId>com.intuit.quickbooks-online</groupId> <artifactId>oauth2-platform-api</artifactId> <classifier>jar-with-dependencies</classifier> <version> 6.0.7 </version> </dependency> <!--需要分布式session的话需要放开注释--> <!--<dependency>--> src/main/java/com/stylefeng/guns/core/common/exception/BizExceptionEnum.java
@@ -69,7 +69,8 @@ /** * 其他 */ AUTH_REQUEST_ERROR(400, "账号密码错误"); AUTH_REQUEST_ERROR(400, "账号密码错误"), CALLBACK_ERROR(400, "FAIL TO CALLBACK"); BizExceptionEnum(int code, String message) { this.code = code; src/main/java/com/stylefeng/guns/modular/api/ApiController.java
@@ -1,9 +1,23 @@ package com.stylefeng.guns.modular.api; import com.baomidou.mybatisplus.mapper.EntityWrapper; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.intuit.ipp.core.IEntity; import com.intuit.ipp.data.*; import com.intuit.ipp.exception.FMSException; import com.intuit.ipp.services.DataService; import com.intuit.ipp.services.QueryResult; import com.intuit.oauth2.data.BearerTokenResponse; import com.intuit.oauth2.exception.OAuthException; import com.stylefeng.guns.core.base.controller.BaseController; import com.stylefeng.guns.core.base.tips.ErrorTip; import com.stylefeng.guns.core.common.constant.factory.ConstantFactory; import com.stylefeng.guns.core.common.exception.BizExceptionEnum; import com.stylefeng.guns.core.exception.GunsException; import com.stylefeng.guns.core.shiro.ShiroUser; import com.stylefeng.guns.core.util.Convert; import com.stylefeng.guns.core.util.JwtTokenUtil; @@ -15,17 +29,24 @@ import com.stylefeng.guns.modular.system.model.User; import com.stylefeng.guns.modular.system.model.UserInfo; import com.stylefeng.guns.modular.system.utils.EmailUtil; import com.stylefeng.guns.modular.system.utils.InvoicesDataUploadUtil; import com.stylefeng.guns.modular.system.utils.RedisUtil; import com.stylefeng.guns.modular.system.utils.tips.SuccessTip; import io.swagger.annotations.Api; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParams; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.RandomStringUtils; import org.json.JSONObject; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import javax.servlet.http.HttpSession; import java.math.BigDecimal; import java.util.*; /** @@ -37,6 +58,7 @@ @RestController @Api(tags = "登录") @RequestMapping("/gunsApi") @Slf4j public class ApiController extends BaseController { @Autowired @@ -47,6 +69,10 @@ @Autowired private RedisUtil redisUtil; private final String INVOICE_DATA = "CARINVOICE:"; private static final String ACCOUNT_QUERY = "select * from Account where AccountType='%s' maxresults 1"; /** * api登录接口,通过账号密码获取token @@ -188,4 +214,177 @@ return sb.toString(); } @ApiOperation(value = "redirectURL接口",notes="redirectURL接口") @GetMapping(value = "/oauth2redirectOfCar") @ResponseBody @Transactional public String callbackIntuit(@RequestParam("code") String authCode, @RequestParam("state") String state, @RequestParam(value = "realmId", required = false) String realmId, HttpSession session) throws OAuthException { String accessToken = ""; if (com.stylefeng.guns.core.util.ToolUtil.isEmpty(realmId)) { return new JSONObject().put("response","No realm ID. QBO calls only work if the accounting scope was passed!").toString(); } try { session.setAttribute("realmId", realmId); session.setAttribute("auth_code", authCode); // OAuth2PlatformClient client = factory.getOAuth2PlatformClient(); // String redirectUri = factory.getRedirectUrl(); BearerTokenResponse bearerTokenResponse = InvoicesDataUploadUtil.callBackFromOAuth(authCode); accessToken = bearerTokenResponse.getAccessToken(); session.setAttribute("access_token", bearerTokenResponse.getAccessToken()); session.setAttribute("refresh_token", bearerTokenResponse.getRefreshToken()); }catch (Exception e){ e.printStackTrace(); } try { String value = redisUtil.getValue(INVOICE_DATA); log.info("value:{}",value); if (ToolUtil.isEmpty(value)){ return "ERROR"; } Gson gson = new Gson(); JsonArray jsonArray = gson.fromJson(value, JsonArray.class); for (JsonElement jsonElement : jsonArray) { JsonObject asJsonObject = jsonElement.getAsJsonObject(); String name = asJsonObject.get("name").getAsString(); String amount = asJsonObject.get("amount").getAsString(); String unitPrice = asJsonObject.get("unitPrice").getAsString(); DataService service = InvoicesDataUploadUtil.getDataService(realmId, accessToken); Customer customer = getCustomerWithAllFields(name,"testconceptsample@mailinator.com"); Customer savedCustomer = service.add(customer); Item item = getItemFields(service, unitPrice == null || unitPrice == "ABC Corporations" ? "0" : unitPrice ); Item savedItem = service.add(item); Invoice invoice = getInvoiceFields(savedCustomer, savedItem, amount == null || amount == "" ? "0" : amount); Invoice savedInvoice = service.add(invoice); service.sendEmail(savedInvoice, customer.getPrimaryEmailAddr().getAddress()); Payment payment = getPaymentFields(savedCustomer, savedInvoice); Payment savedPayment = service.add(payment); createResponse(savedPayment); } } catch (Exception e) { e.printStackTrace(); throw new GunsException(BizExceptionEnum.CALLBACK_ERROR); } return "SUCCESS"; } private Customer getCustomerWithAllFields(String companyName,String mailAddr) { Customer customer = new Customer(); customer.setDisplayName(RandomStringUtils.randomAlphanumeric(6)); customer.setCompanyName(companyName); EmailAddress emailAddr = new EmailAddress(); emailAddr.setAddress(mailAddr); customer.setPrimaryEmailAddr(emailAddr); return customer; } private Item getItemFields(DataService service,String unitPrice) throws FMSException { Item item = new Item(); item.setName("Item" + RandomStringUtils.randomAlphanumeric(5)); item.setTaxable(false); item.setUnitPrice(unitPrice == null || unitPrice == "" ? BigDecimal.ZERO :new BigDecimal(unitPrice)); item.setType(ItemTypeEnum.SERVICE); Account incomeAccount = getIncomeBankAccount(service); item.setIncomeAccountRef(createRef(incomeAccount)); return item; } private Account getIncomeBankAccount(DataService service) throws FMSException { QueryResult queryResult = service.executeQuery(String.format(ACCOUNT_QUERY, AccountTypeEnum.INCOME.value())); List<? extends IEntity> entities = queryResult.getEntities(); if(!entities.isEmpty()) { return (Account)entities.get(0); } return createIncomeBankAccount(service); } private ReferenceType createRef(IntuitEntity entity) { ReferenceType referenceType = new ReferenceType(); referenceType.setValue(entity.getId()); return referenceType; } private Account createIncomeBankAccount(DataService service) throws FMSException { Account account = new Account(); account.setName("Incom" + RandomStringUtils.randomAlphabetic(5)); account.setAccountType(AccountTypeEnum.INCOME); return service.add(account); } private Invoice getInvoiceFields(Customer customer, Item item,String amount) { Invoice invoice = new Invoice(); invoice.setCustomerRef(createRef(customer)); BigDecimal balance = invoice.getBalance(); log.info("balance:{}",balance); List<Line> invLine = new ArrayList<Line>(); Line line = new Line(); line.setAmount(amount == null || amount == "" ? BigDecimal.ZERO : new BigDecimal(amount)); line.setDetailType(LineDetailTypeEnum.SALES_ITEM_LINE_DETAIL); SalesItemLineDetail silDetails = new SalesItemLineDetail(); silDetails.setItemRef(createRef(item)); line.setSalesItemLineDetail(silDetails); invLine.add(line); invoice.setLine(invLine); return invoice; } private Payment getPaymentFields(Customer customer, Invoice invoice) { Payment payment = new Payment(); payment.setCustomerRef(createRef(customer)); payment.setTotalAmt(invoice.getTotalAmt()); List<LinkedTxn> linkedTxnList = new ArrayList<LinkedTxn>(); LinkedTxn linkedTxn = new LinkedTxn(); linkedTxn.setTxnId(invoice.getId()); linkedTxn.setTxnType(TxnTypeEnum.INVOICE.value()); linkedTxnList.add(linkedTxn); Line line1 = new Line(); line1.setAmount(invoice.getTotalAmt()); line1.setLinkedTxn(linkedTxnList); List<Line> lineList = new ArrayList<Line>(); lineList.add(line1); payment.setLine(lineList); return payment; } private String createResponse(Object entity) { ObjectMapper mapper = new ObjectMapper(); String jsonInString; try { jsonInString = mapper.writeValueAsString(entity); } catch (Exception e) { return createErrorResponse(e); } return jsonInString; } private String createErrorResponse(Exception e) { log.error("Exception while calling QBO ", e); return new JSONObject().put("response","Failed").toString(); } } src/main/java/com/stylefeng/guns/modular/system/controller/FinanceController.java
@@ -3,8 +3,11 @@ import cn.hutool.core.date.DateUtil; import com.baomidou.mybatisplus.mapper.EntityWrapper; import com.baomidou.mybatisplus.plugins.Page; import com.intuit.oauth2.exception.InvalidRequestException; import com.stylefeng.guns.core.util.ToolUtil; import com.stylefeng.guns.modular.system.service.ITPriceService; import com.stylefeng.guns.modular.system.utils.InvoicesDataUploadUtil; import com.stylefeng.guns.modular.system.utils.RedisUtil; import com.stylefeng.guns.modular.system.utils.tips.ErrorTip; import com.stylefeng.guns.modular.system.model.*; import com.stylefeng.guns.modular.system.service.ITOrderService; @@ -13,28 +16,33 @@ import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParams; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.apache.ibatis.annotations.Param; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.cache.CacheProperties; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.View; import javax.annotation.Resource; import java.math.BigDecimal; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.List; import java.util.*; @Controller @Api(tags = "财务信息") @RequestMapping("/api/tFinance") @Slf4j public class FinanceController { @Autowired private ITOrderService orderService; @Autowired private RedisUtil redisUtil; private final String INVOICE_DATA = "CARINVOICE:"; @ApiOperation(value = "卡车公司-应收报表列表",notes="卡车公司-应收报表列表") @@ -92,6 +100,26 @@ return new SuccessTip(receivableVoPage); } @ApiOperation(value = "应收款票据quickBooks",notes="应收款票据quickBooks") @ApiImplicitParams({ @ApiImplicitParam(name = "Authorization", value = "用户token(Bearer +token)", required = true, dataType = "String", paramType = "header", defaultValue = "Bearer eyJhbGciOiJIUzUxMiJ9....."), }) @GetMapping(value = "/exportQuickBook") @ResponseBody public Object exportQuickBooks(){ List<Map<String,Object>> orders = orderService.queryAllOrderDatas(); redisUtil.setStrValue(INVOICE_DATA+"",orders.toString()); try { View view = InvoicesDataUploadUtil.connectToQuickBooks(); log.info("view:{}",view); } catch (InvalidRequestException e) { e.printStackTrace(); } return new com.stylefeng.guns.core.base.tips.SuccessTip(); } @ApiOperation(value = "应收款票据详情",notes="应收款票据详情") @ApiImplicitParams({ @ApiImplicitParam(name = "Authorization", value = "用户token(Bearer +token)", required = true, dataType = "String", paramType = "header", defaultValue = "Bearer eyJhbGciOiJIUzUxMiJ9....."), src/main/java/com/stylefeng/guns/modular/system/dao/TOrderMapper.java
@@ -8,6 +8,7 @@ import java.math.BigDecimal; import java.util.List; import java.util.Map; /** * <p> @@ -58,4 +59,5 @@ Double getCompanyIncome(@Param("companyId") Integer companyId, @Param("sTime") String sTime, @Param("eTime") String eTime); List<Map<String, Object>> queryAllOrderDatas(); } src/main/java/com/stylefeng/guns/modular/system/dao/mapping/TOrderMapper.xml
@@ -212,5 +212,17 @@ and success_time BETWEEN #{sTime} and #{eTime} </if> </select> <select id="queryAllOrderDatas" resultType="java.util.Map"> SELECT too.id, tc.`name`, too.s_email as mailAddr, tp.car_price as amount, too.paid as unitPrice FROM `t_order` AS too JOIN t_user tu ON too.user_id = tu.id JOIN t_company tc ON too.company_id = tc.id join t_price tp on too.id = tp.order_id WHERE too.STATUS = 1 </select> </mapper> src/main/java/com/stylefeng/guns/modular/system/service/ITOrderService.java
@@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.service.IService; import java.util.List; import java.util.Map; /** * <p> @@ -44,4 +45,7 @@ Boolean addOrderFile(Long orderId, String url, String name); List<Map<String, Object>> queryAllOrderDatas(); } src/main/java/com/stylefeng/guns/modular/system/service/impl/TOrderServiceImpl.java
@@ -540,6 +540,11 @@ return this.baseMapper.addOrderFile(orderId,url,name); } @Override public List<Map<String, Object>> queryAllOrderDatas() { return this.baseMapper.queryAllOrderDatas(); } public static void main(String[] args) throws ParseException { src/main/java/com/stylefeng/guns/modular/system/utils/InvoicesDataUploadUtil.java
New file @@ -0,0 +1,106 @@ package com.stylefeng.guns.modular.system.utils; import com.intuit.ipp.core.Context; import com.intuit.ipp.core.ServiceType; import com.intuit.ipp.data.*; import com.intuit.ipp.exception.FMSException; import com.intuit.ipp.security.OAuth2Authorizer; import com.intuit.ipp.services.DataService; import com.intuit.ipp.util.Config; import com.intuit.oauth2.client.OAuth2PlatformClient; import com.intuit.oauth2.config.Environment; import com.intuit.oauth2.config.OAuth2Config; import com.intuit.oauth2.config.Scope; import com.intuit.oauth2.data.BearerTokenResponse; import com.intuit.oauth2.exception.InvalidRequestException; import com.intuit.oauth2.exception.OAuthException; import lombok.extern.slf4j.Slf4j; import org.springframework.web.servlet.View; import org.springframework.web.servlet.view.RedirectView; import java.util.ArrayList; import java.util.List; /** * <p> * * </p> * * @Author: lisy * @date: 2023-05-23 14:37 * @Description: */ @Slf4j public class InvoicesDataUploadUtil { // private static final String redirectUrl = "http://cca19.com:8010/gunsApi/oauth2redirect"; private static final String redirectUrl = "http://cca19.com:8010/gunsApi/oauth2redirectOfCar"; private static final String createInvoiceUrl = "https://sandbox-quickbooks.api.intuit.com"; private static final String clientId = "ABXjfdoTHLWaBrkWRkoe6C8sk9mixpQJ1OlrFVkZznsdUzLPIf"; private static final String clientSecret = "WJ01VwbagGa23FJZQVJW4LGKPG9vaJIBgzxEEfEA"; public static View connectToQuickBooks() throws InvalidRequestException { OAuth2Config oauth2Config = new OAuth2Config.OAuth2ConfigBuilder(clientId,clientSecret) .callDiscoveryAPI(Environment.SANDBOX) .buildConfig(); String csrf = oauth2Config.generateCSRFToken(); List<Scope> scopes = new ArrayList<Scope>(); scopes.add(Scope.Accounting); String prepareUrl = oauth2Config.prepareUrl(scopes, redirectUrl, csrf); System.out.println(prepareUrl); return new RedirectView(prepareUrl, true, true, false); } public static String refreshToken() throws OAuthException { //Prepare the config OAuth2Config oauth2Config = new OAuth2Config.OAuth2ConfigBuilder(clientId,clientSecret).callDiscoveryAPI(Environment.SANDBOX).buildConfig(); //Prepare OAuth2PlatformClient OAuth2PlatformClient client = new OAuth2PlatformClient(oauth2Config); //Call refresh endpoint BearerTokenResponse bearerTokenResponse = client.refreshToken("refreshToken"); //set refresh token return bearerTokenResponse.getAccessToken(); } private static Context prepareContext(String realmId, String accessToken) throws FMSException { String url = new OAuth2PlatformClientFactory().getHost() + "/v3/company"; Config.setProperty(Config.BASE_URL_QBO, url); //create oauth object OAuth2Authorizer oauth = new OAuth2Authorizer(accessToken); //create context Context context = new Context(oauth, ServiceType.QBO, realmId); return context; } public static DataService getDataService(String realmId, String accessToken ) throws FMSException { Context context = prepareContext(realmId, accessToken); // create dataservice return new DataService(context); } public static BearerTokenResponse callBackFromOAuth(String code) throws OAuthException { OAuth2Config oauth2Config = new OAuth2Config.OAuth2ConfigBuilder(clientId,clientSecret) .callDiscoveryAPI(Environment.SANDBOX) .buildConfig(); //prepare OAuth2Platform client OAuth2PlatformClient client = new OAuth2PlatformClient(oauth2Config); //retrieve access token by calling the token endpoint return client.retrieveBearerTokens(code, redirectUrl); } } src/main/java/com/stylefeng/guns/modular/system/utils/OAuth2PlatformClientFactory.java
New file @@ -0,0 +1,56 @@ package com.stylefeng.guns.modular.system.utils; import com.intuit.oauth2.client.OAuth2PlatformClient; import com.intuit.oauth2.config.Environment; import com.intuit.oauth2.config.OAuth2Config; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; /** * * @author dderose * */ @Component public class OAuth2PlatformClientFactory { private static final String clientId = "ABXjfdoTHLWaBrkWRkoe6C8sk9mixpQJ1OlrFVkZznsdUzLPIf"; private static final String secret = "WJ01VwbagGa23FJZQVJW4LGKPG9vaJIBgzxEEfEA"; private static final String url = "http://cca19.com:8010/gunsApi/oauth2redirectOfCar"; private static final String host = "https://sandbox-quickbooks.api.intuit.com"; OAuth2PlatformClient client; OAuth2Config oauth2Config; @PostConstruct public void init() { //initialize the config oauth2Config = new OAuth2Config.OAuth2ConfigBuilder(clientId, secret) //set client id, secret .callDiscoveryAPI(Environment.SANDBOX) // call discovery API to populate urls .buildConfig(); //build the client client = new OAuth2PlatformClient(oauth2Config); } public OAuth2PlatformClient getOAuth2PlatformClient() { return client; } public OAuth2Config getOAuth2Config() { return oauth2Config; } public String getRedirectUrl() { return url; } public String getHost() { return host; } }