/** * Utility method create a proposal with the given parameters and triggering the associated work * flow steps * * @param pm Persistence manager instance to use - let open at the end to allow possible object * updates later * @param parameters Parameters produced by the Command line parser or transmitted via the REST * API * @param owner Sale associate who's going to own the created proposal * @param saConsumerRecord Consumer record attached to the sale associate * @return Just created proposal * @throws DataSourceException if the retrieval of the last created proposal or of the location * information fail * @throws ClientException if there's an issue with the given parameters */ public static Proposal createProposal( PersistenceManager pm, JsonObject parameters, SaleAssociate owner, Consumer saConsumerRecord) throws DataSourceException, ClientException { // Data validation & propagation // FIXME: deprecate Demand.REFERENCE in favor of Proposal.DEMAND_KEY, be careful with the // CommandLineParser with uses Demand.REFERENCE if (!parameters.containsKey(Proposal.DEMAND_KEY) && !parameters.containsKey(Demand.REFERENCE)) { throw new InvalidIdentifierException("Missing " + Proposal.DEMAND_KEY + " attribute!"); } if (!parameters.containsKey(Proposal.DEMAND_KEY)) { parameters.put(Proposal.DEMAND_KEY, parameters.getLong(Demand.REFERENCE)); } Long demandKey = parameters.getLong(Proposal.DEMAND_KEY); Demand demand = getDemandOperations().getDemand(pm, demandKey, null); if (!parameters.containsKey(Proposal.CONSUMER_KEY)) { parameters.put(Proposal.CONSUMER_KEY, demand.getOwnerKey()); } else if (!demand.getOwnerKey().equals(parameters.getLong(Proposal.CONSUMER_KEY))) { throw new ClientException( "Proposed " + Proposal.CONSUMER_KEY + " does not match the key of the demand owner!"); } parameters.put(Proposal.LOCATION_KEY, owner.getLocationKey()); // Entity creation Proposal proposal = getProposalOperations().createProposal(pm, parameters, owner); // Related workflow step MaelzelServlet.triggerValidationTask(proposal); return proposal; }
/** * Utility method update a proposal with the given parameters and triggering the associated * workflow steps * * @param pm Persistence manager instance to use - let open at the end to allow possible object * updates later * @param rawCommand Reference of the command which initiated the process, is <code>null</code> if * initiated by a REST API call * @param proposalKey Resource identifier * @param parameters Parameters produced by the Command line parser or transmitted via the REST * API * @param ownerKey Key of the sale associate who owns the proposal to be updated * @param saConsumerRecordKey Key of the consumer record attached to the sale associate * @param isUserAdmin * @return Just updated proposal * @throws DataSourceException if the retrieval of the last created proposal or of the location * information fail * @throws InvalidIdentifierException if there's an issue with the Proposal identifier is invalid * @throws InvalidStateException if the Proposal is not update-able * @throws CommunicationException if the communication of the update confirmation fails */ public static Proposal updateProposal( PersistenceManager pm, RawCommand rawCommand, Long proposalKey, JsonObject parameters, Long ownerKey, Long saConsumerRecordKey, boolean isUserAdmin) throws DataSourceException, InvalidIdentifierException, InvalidStateException, CommunicationException { SaleAssociate owner = getSaleAssociateOperations().getSaleAssociate(pm, ownerKey); Consumer saConsumerRecord = getConsumerOperations().getConsumer(pm, saConsumerRecordKey); Proposal proposal = getProposalOperations() .getProposal( pm, proposalKey, isUserAdmin ? null : ownerKey, isUserAdmin ? null : owner.getStoreKey()); State currentState = proposal.getState(); // Workflow state change if (parameters.size() == 1 && parameters.containsKey(Command.STATE)) { String proposedState = parameters.getString(Command.STATE); // Close if (State.confirmed.equals(currentState) && State.closed.toString().equals(proposedState)) { // Update the user's statistics owner.setClosedProposalNb( owner.getClosedProposalNb() == null ? 1 : owner.getClosedProposalNb() + 1); owner = BaseSteps.getSaleAssociateOperations().updateSaleAssociate(pm, owner); // Update the store's statistics Store store = getStoreOperations().getStore(pm, proposal.getStoreKey()); store.setClosedProposalNb( store.getClosedProposalNb() == null ? 1 : store.getClosedProposalNb() + 1); store = BaseSteps.getStoreOperations().updateStore(pm, store); // Get the associated demand Demand demand = getDemandOperations().getDemand(pm, proposal.getDemandKey(), null); if (rawCommand != null && !Source.robot.equals(rawCommand.getSource())) { Locale locale = saConsumerRecord.getLocale(); MessageGenerator msgGen = new MessageGenerator(rawCommand.getSource(), demand.getHashTags(), locale); msgGen .put("proposal>owner>name", saConsumerRecord.getName()) .fetch(demand) .fetch(proposal) .put("message>footer", msgGen.getAlternateMessage(MessageId.messageFooter)); String subject = null; if (Source.mail.equals(msgGen.getCommunicationChannel())) { subject = rawCommand.getSubject(); } if (subject == null) { subject = msgGen.getAlternateMessage(MessageId.messageSubject, msgGen.getParameters()); } subject = MailConnector.prepareSubjectAsResponse(subject, locale); communicateToConsumer( msgGen.getCommunicationChannel(), subject, saConsumerRecord, new String[] {msgGen.getMessage(MessageId.PROPOSAL_CLOSING_OK_TO_ASSOCIATE)}); } if (!State.closed.equals(demand.getState())) { // Get demand owner Consumer demandOwner = getConsumerOperations().getConsumer(pm, demand.getOwnerKey()); Location location = getLocationOperations().getLocation(pm, store.getLocationKey()); Registrar registrar = getRegistrarOperations().getRegistrar(pm, store.getRegistrarKey()); // Inform Proposal owner about the closing Locale locale = demandOwner.getLocale(); MessageGenerator msgGen = new MessageGenerator( demandOwner.getPreferredConnection(), demand.getHashTags(), locale); msgGen .put("demand>owner>name", demandOwner.getName()) .fetch(demand) .fetch(proposal) .fetch(store) .fetch(location, "store") .fetch(registrar) .put("message>footer", msgGen.getAlternateMessage(MessageId.messageFooter)) .put( "command>footer", LabelExtractor.get(ResourceFileId.fourth, "command_message_footer", locale)); String closeDemand = LabelExtractor.get( ResourceFileId.fourth, "command_message_body_demand_close", msgGen.getParameters(), locale); String subject = null; if (Source.mail.equals(msgGen.getCommunicationChannel()) && Source.mail.equals(demand.getSource())) { subject = BaseSteps.getRawCommandOperations() .getRawCommand(pm, demand.getRawCommandId()) .getSubject(); } if (subject == null) { subject = msgGen.getAlternateMessage(MessageId.messageSubject, msgGen.getParameters()); } subject = MailConnector.prepareSubjectAsResponse(subject, locale); msgGen .put( "command>threadSubject", BaseConnector.prepareMailToSubject( MailConnector.prepareSubjectAsResponse(subject, locale))) .put("command>closeDemand", BaseConnector.prepareMailToBody(closeDemand)); try { communicateToConsumer( msgGen.getCommunicationChannel(), subject, demandOwner, new String[] {msgGen.getMessage(MessageId.PROPOSAL_CLOSING_OK_TO_CONSUMER)}); } catch (CommunicationException e) { // Not a critical error, should not block the rest of the process getLogger().warning("Cannot inform " + demand.getOwnerKey()); } } // No need to bother CC-ed } // Cancel else if (!State.closed.equals(currentState) && State.cancelled.toString().equals(proposedState)) { proposal.setCancelerKey(saConsumerRecord.getKey()); // Get the associated demand Demand demand = getDemandOperations().getDemand(pm, proposal.getDemandKey(), null); demand.removeProposalKey(proposalKey); // Confirm the proposal canceling to the owner if (rawCommand != null) { Locale locale = saConsumerRecord.getLocale(); MessageGenerator msgGen = new MessageGenerator(rawCommand.getSource(), demand.getHashTags(), locale); msgGen .put("proposal>owner>name", saConsumerRecord.getName()) .fetch(demand) .fetch(proposal) .put("message>footer", msgGen.getAlternateMessage(MessageId.messageFooter)); String subject = null; if (Source.mail.equals(msgGen.getCommunicationChannel())) { subject = rawCommand.getSubject(); } if (subject == null) { subject = msgGen.getAlternateMessage(MessageId.messageSubject, msgGen.getParameters()); } subject = MailConnector.prepareSubjectAsResponse(subject, locale); communicateToConsumer( msgGen.getCommunicationChannel(), subject, saConsumerRecord, new String[] {msgGen.getMessage(MessageId.PROPOSAL_CANCELLATION_OK_TO_ASSOCIATE)}); } if (State.confirmed.equals(currentState)) { Consumer demandOwner = getConsumerOperations().getConsumer(pm, demand.getOwnerKey()); Location location = getLocationOperations().getLocation(pm, demand.getLocationKey()); // FIXME: Place the associated demand in the published state again if not expired demand.setState(State.published); demand.updateModificationDate(); // To be sure it's picked-up by the next cron job // 'processPublishedDemands' // Notify Consumer about the confirmed Proposal cancellation Locale locale = demandOwner.getLocale(); MessageGenerator msgGen = new MessageGenerator( demandOwner.getPreferredConnection(), demand.getHashTags(), locale); msgGen .put("demand>owner>name", demandOwner.getName()) .fetch(demand) .fetch(location, "demand") .fetch(proposal) .put("message>footer", msgGen.getAlternateMessage(MessageId.messageFooter)) .put( "command>footer", LabelExtractor.get(ResourceFileId.fourth, "command_message_footer", locale)); String cancelDemand = LabelExtractor.get( ResourceFileId.fourth, "command_message_body_demand_cancel", msgGen.getParameters(), locale); String updateDemand = LabelExtractor.get( ResourceFileId.fourth, "command_message_body_demand_update", msgGen.getParameters(), locale); String subject = null; if (Source.mail.equals(msgGen.getCommunicationChannel()) && Source.mail.equals(demand.getSource())) { subject = getRawCommandOperations().getRawCommand(pm, demand.getRawCommandId()).getSubject(); } if (subject == null) { subject = msgGen.getAlternateMessage(MessageId.messageSubject, msgGen.getParameters()); } subject = MailConnector.prepareSubjectAsResponse(subject, locale); msgGen .put("command>threadSubject", BaseConnector.prepareMailToSubject(subject)) .put("command>cancelDemand", BaseConnector.prepareMailToBody(cancelDemand)) .put("command>updateDemand", BaseConnector.prepareMailToBody(updateDemand)); try { communicateToConsumer( msgGen.getCommunicationChannel(), subject, demandOwner, new String[] { msgGen.getMessage(MessageId.PROPOSAL_CONFIRMED_CANCELLATION_OK_TO_CONSUMER) }); } catch (CommunicationException e) { // Not a critical error, should not block the rest of the process getLogger().warning("Cannot inform " + demand.getOwnerKey()); } } getDemandOperations().updateDemand(pm, demand); } else { throw new InvalidStateException( "Invalid state change attempt to: " + proposedState, currentState.toString(), proposedState); } proposal.setState(proposedState); proposal = getProposalOperations().updateProposal(pm, proposal); } // Normal attribute update else if (State.opened.equals(currentState) || State.published.equals(currentState) || State.invalid.equals(currentState)) { // Integrate updates proposal.fromJson(parameters, isUserAdmin, false); // Prepare as a new Demand proposal.setState(State.opened); // Will force the re-validation of the entire demand // Persist updates proposal = getProposalOperations().updateProposal(pm, proposal); // Detach the proposal from the associated demand (just in case it's now invalid) Demand demand = getDemandOperations().getDemand(pm, proposal.getDemandKey(), null); demand.removeProposalKey(proposalKey); getDemandOperations().updateDemand(pm, demand); // Related workflow step MaelzelServlet.triggerValidationTask(proposal); } else { throw new InvalidStateException( "Entity not in modifiable state", currentState.toString(), null); } return proposal; }