package org.nightlabs.jfire.dynamictrade.ui.articlecontainer.detail; import java.text.ParseException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Set; import javax.jdo.FetchPlan; import javax.jdo.JDODetachedFieldAccessException; import javax.jdo.JDOHelper; import org.apache.log4j.Logger; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.window.Window; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.SashForm; import org.eclipse.swt.events.FocusEvent; import org.eclipse.swt.events.FocusListener; import org.eclipse.swt.events.ModifyEvent; import org.eclipse.swt.events.ModifyListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Text; import org.nightlabs.base.ui.composite.FadeableComposite; import org.nightlabs.base.ui.composite.MessageComposite; import org.nightlabs.base.ui.composite.XComboComposite; import org.nightlabs.base.ui.composite.XComposite; import org.nightlabs.base.ui.job.Job; import org.nightlabs.base.ui.message.MessageType; import org.nightlabs.base.ui.resource.SharedImages; import org.nightlabs.i18n.I18nTextBuffer; import org.nightlabs.jdo.NLJDOHelper; import org.nightlabs.jfire.accounting.Currency; import org.nightlabs.jfire.accounting.Invoice; import org.nightlabs.jfire.accounting.Price; import org.nightlabs.jfire.accounting.PriceFragmentType; import org.nightlabs.jfire.accounting.Tariff; import org.nightlabs.jfire.accounting.TariffMapper; import org.nightlabs.jfire.accounting.TariffMapping; import org.nightlabs.jfire.accounting.TariffOrderConfigModule; import org.nightlabs.jfire.accounting.dao.PriceConfigEditDAO; import org.nightlabs.jfire.accounting.gridpriceconfig.FormulaCell; import org.nightlabs.jfire.accounting.gridpriceconfig.IPriceCoordinate; import org.nightlabs.jfire.accounting.gridpriceconfig.PriceCalculationException; import org.nightlabs.jfire.accounting.gridpriceconfig.PriceCell; import org.nightlabs.jfire.accounting.gridpriceconfig.PriceCoordinate; import org.nightlabs.jfire.accounting.gridpriceconfig.StablePriceConfig; import org.nightlabs.jfire.accounting.id.CurrencyID; import org.nightlabs.jfire.accounting.id.TariffID; import org.nightlabs.jfire.accounting.priceconfig.id.PriceConfigID; import org.nightlabs.jfire.base.ui.config.ConfigUtil; import org.nightlabs.jfire.dynamictrade.DynamicProductInfo; import org.nightlabs.jfire.dynamictrade.accounting.priceconfig.DynamicTradePriceConfig; import org.nightlabs.jfire.dynamictrade.accounting.priceconfig.PriceCalculator; import org.nightlabs.jfire.dynamictrade.recurring.DynamicProductTypeRecurringArticle; import org.nightlabs.jfire.dynamictrade.store.DynamicProduct; import org.nightlabs.jfire.dynamictrade.store.DynamicProductType; import org.nightlabs.jfire.dynamictrade.ui.resource.Messages; import org.nightlabs.jfire.idgenerator.IDGenerator; import org.nightlabs.jfire.store.Unit; import org.nightlabs.jfire.store.dao.UnitDAO; import org.nightlabs.jfire.store.id.ProductTypeID; import org.nightlabs.jfire.trade.Article; import org.nightlabs.jfire.trade.ArticleContainer; import org.nightlabs.jfire.trade.CustomerGroup; import org.nightlabs.jfire.trade.CustomerGroupMapper; import org.nightlabs.jfire.trade.CustomerGroupMapping; import org.nightlabs.jfire.trade.Offer; import org.nightlabs.jfire.trade.Order; import org.nightlabs.jfire.trade.dao.ArticleDAO; import org.nightlabs.jfire.trade.id.ArticleID; import org.nightlabs.jfire.trade.id.CustomerGroupID; import org.nightlabs.l10n.NumberFormatter; import org.nightlabs.progress.NullProgressMonitor; import org.nightlabs.progress.ProgressMonitor; import org.nightlabs.progress.SubProgressMonitor; import org.nightlabs.script.JSHTMLExecuter; import org.nightlabs.util.NLLocale; import org.nightlabs.util.Util; public abstract class ArticleBaseComposite extends FadeableComposite { private static Logger logger = Logger.getLogger(ArticleBaseComposite.class); private String nameMessageText; protected ArticleContainer articleContainer; protected ProductTypeID productTypeID; protected Label productTypeNameLabel; protected XComboComposite<Tariff> tariffCombo; protected XComboComposite<Unit> unitCombo; protected Text quantity; protected Text productNameText; protected MessageComposite nameMessageLabel; protected Button productNameDialogButton; protected InputPriceFragmentTypeTable inputPriceFragmentTypeTable; protected XComposite comp1; /** * Used for {@link #createPriceCoordinate()} if it could be set * and for the price fragment table. */ private Currency currency; /** * Used for {@link #createPriceCoordinate()} if it could be set. */ private CustomerGroupID customerGroupID; /** * Set when the Composite was created with an article. * Will be nulled again after {@link #_setArticle(Article)} was called * with it. */ private Article createArticle; /** * Whether to check if the composite should be editable. */ private boolean checkForEditable = false; /** * Whether to check for a script syntax in the name of the dynamic product type */ private boolean isScriptable = false; public boolean isScriptable() { return isScriptable; } public void setScriptable(final boolean isScriptable) { this.isScriptable = isScriptable; } /** * Create a new {@link ArticleBaseComposite} with a {@link DynamicProductType}. * * @param parent * @param articleContainer * @param productType */ public ArticleBaseComposite(final Composite parent, final ArticleContainer articleContainer, final DynamicProductType productType) { super(parent, SWT.NONE, LayoutMode.TIGHT_WRAPPER); this.articleContainer = articleContainer; this.productTypeID = (ProductTypeID) JDOHelper.getObjectId(productType); } public ArticleBaseComposite(final Composite parent, final ArticleContainer articleContainer, final Article article) { this(parent, articleContainer, (DynamicProductType) article.getProductType()); createArticle = article; checkForEditable = true; } protected void createUI() { comp1 = new XComposite(this, SWT.NONE, LayoutMode.TIGHT_WRAPPER); comp1.getGridLayout().numColumns = 4; comp1.getGridData().grabExcessVerticalSpace = false; productTypeNameLabel = new Label(comp1, SWT.NONE); productTypeNameLabel.setLayoutData(new GridData()); // tariffCombo = new ComboComposite<Tariff>(comp1, SWT.BORDER | SWT.READ_ONLY , new LabelProvider() { // @Override // public String getText(Object element) // { // return ((Tariff)element).getName().getText(); // } // }); tariffCombo = new XComboComposite<Tariff>(comp1, getBorderStyle() | SWT.READ_ONLY , new LabelProvider() { @Override public String getText(final Object element) { return ((Tariff)element).getName().getText(); } }); final Tariff dummy = new Tariff(TariffID.create("dummy.org", "_dummy_")); //$NON-NLS-1$ //$NON-NLS-2$ dummy.getName().setText(NLLocale.getDefault().getLanguage(), Messages.getString("org.nightlabs.jfire.dynamictrade.ui.articlecontainer.detail.ArticleBaseComposite.pseudoTariff_loading")); //$NON-NLS-1$ tariffCombo.addElement(dummy); tariffCombo.selectElement(dummy); tariffCombo.addSelectionChangedListener(new ISelectionChangedListener() { public void selectionChanged(final SelectionChangedEvent event) { updateInputPriceFragmentTypes(); } }); final SashForm comp2 = new SashForm(this, SWT.HORIZONTAL); // comp2.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); comp2.setLayoutData(new GridData(GridData.FILL_BOTH)); final XComposite comp3 = new XComposite(comp2, SWT.NONE, LayoutMode.TIGHT_WRAPPER); comp3.getGridLayout().numColumns = 3; final XComposite compName = new XComposite(comp3, SWT.NONE, LayoutMode.TIGHT_WRAPPER); compName.setLayout(new GridLayout(1,false)); nameMessageLabel = new MessageComposite(compName, SWT.NONE, "", MessageType.INFORMATION); //$NON-NLS-1$ nameMessageLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); final GridData data = (GridData)nameMessageLabel.getLayoutData(); data.exclude = true; nameMessageLabel.setVisible(false); productNameText = new Text(compName, getBorderStyle() | SWT.MULTI | SWT.V_SCROLL | SWT.H_SCROLL); productNameText.setLayoutData(new GridData(GridData.FILL_BOTH)); productNameText.addModifyListener(new ModifyListener() { public void modifyText(final ModifyEvent e) { productName.setText(NLLocale.getDefault().getLanguage(), productNameText.getText()); productNameModified = true; } }); final XComposite buttonComp = new XComposite(comp3, SWT.NONE, LayoutMode.TIGHT_WRAPPER); buttonComp.getGridLayout().numColumns = 1; // productNameText.setData(IToolkit.KEY_DRAW_BORDER, IToolkit.TEXT_BORDER); ((GridData)productNameText.getLayoutData()).heightHint = productTypeNameLabel.computeSize(SWT.DEFAULT, SWT.DEFAULT).y * 3; productNameDialogButton = new Button(buttonComp, SWT.PUSH); productNameDialogButton.setText("..."); //$NON-NLS-1$ productNameDialogButton.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_BEGINNING)); productNameDialogButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(final SelectionEvent arg0) { if ((Window.OK == new ProductNameDialog(getShell(), productName, editable,ProductNameDialogType.TEXT_EDIT).open())) { updateProductNameUI(); productNameModified = true; } } }); // if the page supports scripting adds the scripting preview button if(isScriptable()) { final Button previewScriptButton = new Button(buttonComp, SWT.PUSH); previewScriptButton.setImage(SharedImages.PREVIEW_16x16.createImage()); previewScriptButton.setToolTipText("Preview Script"); previewScriptButton.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_BEGINNING)); previewScriptButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(final SelectionEvent arg0) { ProductNameDialogType productNameDialogType; // if the User has entered a valid script then a preview is shown final JSHTMLExecuter script = new JSHTMLExecuter(); if(script.containsValidScript(productNameText.getText()) > 0) productNameDialogType = ProductNameDialogType.SCRIPT_PREVIEW; else productNameDialogType = ProductNameDialogType.SCRIPT_EDIT; if ((Window.OK == new ProductNameDialog(getShell(), productName, editable,productNameDialogType).open())) { updateProductNameUI(); productNameModified = true; } } }); } inputPriceFragmentTypeTable = new InputPriceFragmentTypeTable(comp2) { @Override protected void inputPriceFragmentTypeModified(final InputPriceFragmentType inputPriceFragmentType) { if (dynamicTradePriceConfig == null) return; final IPriceCoordinate priceCoordinate = createPriceCoordinate(); final FormulaCell formulaCell = dynamicTradePriceConfig.createFormulaCell(priceCoordinate); formulaCell.setFormula(inputPriceFragmentType.getPriceFragmentType(), String.valueOf(inputPriceFragmentType.getAmount())); inputPriceFragmentTypeModified = true; try { priceCalculator.calculatePrices(); } catch (final PriceCalculationException e) { throw new RuntimeException(e); } } }; inputPriceFragmentTypeTable.getGridData().grabExcessVerticalSpace = false; comp2.setWeights(new int[] { 1, 1 }); unitCombo = new XComboComposite<Unit>(comp1, getBorderStyle() | SWT.READ_ONLY, new LabelProvider() { @Override public String getText(final Object element) { return ((Unit)element).getName().getText(); } }); unitCombo.addSelectionChangedListener(new ISelectionChangedListener() { @Override public void selectionChanged(final SelectionChangedEvent event) { unitSelected(); } }); quantity = new Text(comp1, getBorderStyle()); quantity.setText(NumberFormatter.formatFloat(1, 2)); final GridData gd = new GridData(); gd.widthHint = 200; quantity.setLayoutData(gd); quantity.addModifyListener(new ModifyListener() { @Override public void modifyText(final ModifyEvent event) { final Unit unit = unitCombo.getSelectedElement(); if (unit == null) return; final String quantityStr = quantity.getText(); if (!"".equals(quantityStr)) { //$NON-NLS-1$ try { lastValidQuantity = NumberFormatter.parseFloat(quantityStr); } catch (final ParseException e) { MessageDialog.openError( getShell(), Messages.getString("org.nightlabs.jfire.dynamictrade.ui.articlecontainer.detail.ArticleBaseComposite.quantityInvalidDialog.title"), //$NON-NLS-1$ String.format(Messages.getString("org.nightlabs.jfire.dynamictrade.ui.articlecontainer.detail.ArticleBaseComposite.quantityInvalidDialog.message"), quantityStr) //$NON-NLS-1$ ); quantity.setText(NumberFormatter.formatFloat(lastValidQuantity, unit.getDecimalDigitCount())); } } } }); createUI_additionalElements_comp1(comp1); setEditable(false); loadDynamicProductType(); if(isScriptable()) { showTextNameMessage(Messages.getString("org.nightlabs.jfire.dynamictrade.ui.articlecontainer.detail.ArticleBaseComposite.textNameMessage"),MessageType.INFORMATION); //$NON-NLS-1$ this.nameMessageText = nameMessageLabel.getMessage(); this.productNameText.addFocusListener( new FocusListener(){ /** remove the error message shown once the user clicks on the text box to enter the name again*/ @Override public void focusGained(final FocusEvent arg0) { showTextNameMessage(nameMessageText,MessageType.INFORMATION); } @Override public void focusLost(final FocusEvent arg0) { // TODO Auto-generated method stub } }); } } private double lastValidQuantity = 1; protected boolean inputPriceFragmentTypeModified = false; protected boolean productNameModified = false; protected void createUI_additionalElements_comp1(final Composite parent) { } protected DynamicProductType dynamicProductType; protected DynamicTradePriceConfig dynamicTradePriceConfig; protected StablePriceConfig resultPriceConfig; protected PriceCalculator priceCalculator; protected List<InputPriceFragmentType> inputPriceFragmentTypes; protected I18nTextBuffer productName = new I18nTextBuffer(); public I18nTextBuffer getProductName() { return productName; } private boolean editable = true; protected void setEditable(final boolean editable) { if (this.editable == editable) return; this.editable = editable; if (isFaded()) return; applyEditableState(); } protected boolean isEditable() { return editable; } @Override public void setFaded(final boolean faded) { super.setFaded(faded); if (!faded) { applyEditableState(); } } private void applyEditableState() { tariffCombo.setEnabled(editable); unitCombo.setEnabled(editable); quantity.setEditable(editable && unitCombo.getSelectedElement() != null); quantity.setEnabled(unitCombo.getSelectedElement() != null); productNameText.setEditable(editable); inputPriceFragmentTypeTable.setEditable(editable); } /** * Creates a new {@link PriceCoordinate} for the customerGroupID, * the tariffID and the currencyID found in the ArticleContainer. * <p> * Note, that this method only creates a valid {@link PriceCoordinate} * if the {@link ArticleContainer} passed is either an {@link Order} * or an {@link Offer}. * </p> * * @return A new {@link PriceCoordinate}. */ protected IPriceCoordinate createPriceCoordinate() { final Order order = (Order) (articleContainer instanceof Order ? articleContainer : null); final Offer offer = (Offer) (articleContainer instanceof Offer ? articleContainer : null); CurrencyID currencyID = null; if (this.currency != null) currencyID = (CurrencyID) JDOHelper.getObjectId(this.currency); else { Currency currency = order != null ? order.getCurrency() : null; if (offer != null) { currency = offer.getCurrency(); } if (currency != null) currencyID = (CurrencyID) JDOHelper.getObjectId(currency); } CustomerGroupID customerGroupID = null; if (this.customerGroupID != null) customerGroupID = this.customerGroupID; else { CustomerGroup customerGroup = order != null ? order.getCustomerGroup() : null; customerGroupID = (CustomerGroupID) JDOHelper.getObjectId(customerGroup); if (offer != null) { customerGroup = offer.getOrder().getCustomerGroup(); } if (customerGroup != null) customerGroupID = (CustomerGroupID) JDOHelper.getObjectId(customerGroup); } final Tariff tariff = tariffCombo.getSelectedElement(); final TariffID tariffID = (TariffID) JDOHelper.getObjectId(tariff); return new PriceCoordinate(customerGroupID, tariffID, currencyID); } private static final String[] FETCH_GROUPS_UNIT = { FetchPlan.DEFAULT, Unit.FETCH_GROUP_NAME }; // private static long temporaryResultPriceConfigID = IDGenerator.nextID(PriceConfig.class); private static String temporaryResultPriceConfigID = "temporary.resultPriceConfigID"; //$NON-NLS-1$ /** * Though it was volatile I had strange timing issues and changed to synchronizing. * I also initialized the value to true, because in theory (and unfortunately it also happened to me) * setArticle can be called in the time the load job is scheduled (in the constructor) * and the time the Job actually starts. */ // private volatile boolean loadDynamicProductType_inProcess = false; private boolean[] loadDynamicProductType_inProcess = new boolean[] {true}; private void loadDynamicProductType() { setFaded(true); final Job job = new Job(Messages.getString("org.nightlabs.jfire.dynamictrade.ui.articlecontainer.detail.ArticleBaseComposite.loadDynamicProductTypeJob.name")) { //$NON-NLS-1$ @Override protected IStatus run(final ProgressMonitor monitor) throws Exception { boolean error = true; synchronized (loadDynamicProductType_inProcess) { // if (loadDynamicProductType_inProcess[0]) // throw new IllegalStateException("Why the hell is this Job executed twice at the same time?!"); //$NON-NLS-1$ loadDynamicProductType_inProcess[0] = true; } try { dynamicProductType = (DynamicProductType) Util.cloneSerializable( PriceConfigEditDAO.sharedInstance().getProductTypeForPriceConfigEditing(productTypeID, monitor)); // TODO we should only list tariffs that are available to the current user according to the TariffUserSet. Marco. dynamicTradePriceConfig = (DynamicTradePriceConfig) dynamicProductType.getInnerPriceConfig(); final List<Tariff> tariffs = new ArrayList<Tariff>(dynamicTradePriceConfig.getTariffs()); resultPriceConfig = new StablePriceConfig(PriceConfigID.create(IDGenerator.getOrganisationID(), temporaryResultPriceConfigID)); dynamicProductType.setPackagePriceConfig(resultPriceConfig); resultPriceConfig.adoptParameters(dynamicTradePriceConfig); priceCalculator = new PriceCalculator( dynamicProductType, new CustomerGroupMapper(new ArrayList<CustomerGroupMapping>(0)), // no crosstrade supported => no mappings needed! new TariffMapper(new ArrayList<TariffMapping>(0))); priceCalculator.preparePriceCalculation(); try { priceCalculator.calculatePrices(); } catch (final PriceCalculationException e) { throw new RuntimeException(e); } Collections.sort(tariffs, new Comparator<Tariff>() { public int compare(final Tariff t1, final Tariff t2) { return t1.getName().getText().compareTo(t2.getName().getText()); } }); final Set<PriceFragmentType> pfts = dynamicTradePriceConfig.getInputPriceFragmentTypes(); final List<InputPriceFragmentType> ipfts = new ArrayList<InputPriceFragmentType>(pfts.size()); for (final PriceFragmentType priceFragmentType : pfts) ipfts.add(new InputPriceFragmentType(priceFragmentType)); Collections.sort(ipfts, new Comparator<InputPriceFragmentType>() { public int compare(final InputPriceFragmentType ipft1, final InputPriceFragmentType ipft2) { return ipft1.getPriceFragmentType().getName().getText().compareTo(ipft2.getPriceFragmentType().getName().getText()); } }); final List<Unit> units = UnitDAO.sharedInstance().getUnits(FETCH_GROUPS_UNIT, NLJDOHelper.MAX_FETCH_DEPTH_NO_LIMIT, monitor); Collections.sort(units, new Comparator<Unit>() { public int compare(final Unit u1, final Unit u2) { return u1.getName().getText().compareTo(u2.getName().getText()); } }); // Sort the tariffs according to the config module final String[] fetchGroups = new String[] { TariffOrderConfigModule.FETCH_GROUP_TARIFF_ORDER_CONFIG_MODULE , FetchPlan.DEFAULT }; final TariffOrderConfigModule cfMod = ConfigUtil.getUserCfMod(TariffOrderConfigModule.class, fetchGroups, -1, new NullProgressMonitor()); Collections.sort(tariffs, cfMod.getTariffComparator()); // Sorting done // check for the currencyID, that is needed for the // labelprovider of the table. checkCurrency(monitor); // check for the customerGroupID checkCustomerGroup(monitor); // check if the composite should be editable // with the data it has loaded final boolean shouldBeEditable = checkEditable(monitor); Display.getDefault().asyncExec(new Runnable() { public void run() { Article deferredArticle = null; try { if (tariffCombo.isDisposed()) return; if (tariffs.isEmpty()) { showMessageNoPricesAvailable(); return; } if (currency != null) { // should always be true, but we play safe. // check, if the price configuration contains this currency. final CurrencyID currencyID = CurrencyID.create(currency.getCurrencyID()); if (dynamicTradePriceConfig.getCurrency(currencyID, false) == null) { showMessageNoPricesAvailable(); return; } } productTypeNameLabel.setText( dynamicProductType.getName().getText(NLLocale.getDefault().getLanguage())); tariffCombo.removeAll(); tariffCombo.addElements(tariffs); if (!tariffs.isEmpty()) tariffCombo.setSelection(0); inputPriceFragmentTypes = ipfts; inputPriceFragmentTypeTable.setCurrency(currency); inputPriceFragmentTypeTable.setInput(ipfts); fireCompositeContentChangeEvent(); inputPriceFragmentTypeTable.layout(true, true); unitCombo.removeAll(); unitCombo.addElements(units); unitCombo.setSelection(0); unitSelected(); updateInputPriceFragmentTypes(); setEditable(shouldBeEditable); setFaded(false); } finally { synchronized (loadDynamicProductType_inProcess) { deferredArticle = deferredArticleAssignment; deferredArticleAssignment = null; loadDynamicProductType_inProcess[0] = false; } } if (createArticle != null) _setArticle(createArticle); else if (deferredArticle != null) _setArticle(deferredArticle); } }); error = false; } finally { if (error) { synchronized (loadDynamicProductType_inProcess) { loadDynamicProductType_inProcess[0] = false; } } } return Status.OK_STATUS; } }; job.setPriority(org.eclipse.core.runtime.jobs.Job.SHORT); job.schedule(); } private void _relayout() { Composite parent = getParent(); while (parent.getParent() != null) { parent = parent.getParent(); } parent.layout(true, true); } private void showMessageNoPricesAvailable() { // remove all UI that has been created previously for (final Control child : getChildren()) child.dispose(); final MessageComposite mc = new MessageComposite( this, SWT.NONE, String.format(Messages.getString("org.nightlabs.jfire.dynamictrade.ui.articlecontainer.detail.ArticleBaseComposite.message"), dynamicProductType.getName().getText()), //$NON-NLS-1$ MessageType.WARNING ); mc.adaptToToolkit(); // Text txt = new Text(this, SWT.WRAP); // txt.setText(String.format("The price configuration of the product type \"%s\" contains no tariffs or no currencies; or they are not available to you.", dynamicProductType.getName().getText())); _relayout(); } private void unitSelected() { if (Display.getCurrent() == null) throw new IllegalStateException("Wrong thread! This method must be called on the UI thread!"); //$NON-NLS-1$ final Unit unit = unitCombo.getSelectedElement(); if (unit != null) { double d; try { d = NumberFormatter.parseFloat(quantity.getText()); } catch (final ParseException e) { logger.warn("Failed to parse the quantity \"" + quantity.getText() + "\" as float value! Using 1 as fallback!", e); //$NON-NLS-1$ //$NON-NLS-2$ d = 1; } quantity.setText(NumberFormatter.formatFloat(d, unit.getDecimalDigitCount())); } applyEditableState(); } private boolean checkEditable(final ProgressMonitor monitor) { if (!checkForEditable) return true; boolean finalized = true; if (articleContainer instanceof Offer) { finalized = ((Offer) articleContainer).isFinalized(); } else if (createArticle != null) { finalized = getArticleContainerFinalized(createArticle, monitor); } else if (deferredArticleAssignment != null) { finalized = getArticleContainerFinalized(deferredArticleAssignment, monitor); } else if (article != null) { finalized = getArticleContainerFinalized(article, monitor); } return !finalized; } private boolean getArticleContainerFinalized(final Article article, final ProgressMonitor monitor) { try { // try to get the offer#finalized value return article.getOffer().isFinalized(); } catch (final JDODetachedFieldAccessException ex) { // it was not detached, we get the article with the offer final Article _article = ArticleDAO.sharedInstance().getArticle( (ArticleID) JDOHelper.getObjectId(article), new String[] {FetchPlan.DEFAULT, Article.FETCH_GROUP_OFFER}, NLJDOHelper.MAX_FETCH_DEPTH_NO_LIMIT, new SubProgressMonitor(monitor, 100)); return _article.getOffer().isFinalized(); } } private void checkCurrency(final ProgressMonitor monitor) { if (currency == null) { final ArticleContainer ac = articleContainer; if (ac instanceof Order) currency = ((Order) ac).getCurrency(); else if (ac instanceof Offer) currency = ((Offer) ac).getCurrency(); else if (ac instanceof Invoice) { currency = ((Invoice) ac).getCurrency(); } else { // try to get the Currency from the deferredArticle, very ugly, but should work if (createArticle != null) { /* Hmm, this was instantiated with an article, * but strangely the Currency was not set. * To be sure, we get the Article with correct fetch-groups. */ final Article article = ArticleDAO.sharedInstance().getArticle( (ArticleID) JDOHelper.getObjectId(createArticle), new String[] {FetchPlan.DEFAULT, Article.FETCH_GROUP_CURRENCY}, NLJDOHelper.MAX_FETCH_DEPTH_NO_LIMIT, new SubProgressMonitor(monitor, 100)); currency = article.getCurrency(); } else if (deferredArticleAssignment != null) { // we are lucky ;-) // Seems that setArticle was called while we were loading other stuff currency = deferredArticleAssignment.getCurrency(); } throw new IllegalStateException("Could not determine the Currency for the given articleContainer or article, this is a programming 'mistake' and timing issue!"); //$NON-NLS-1$ } } } private void checkCustomerGroup(final ProgressMonitor monitor) { if (customerGroupID == null) { CustomerGroup customerGroup = null; if (articleContainer instanceof Order) { customerGroup = ((Order) articleContainer).getCustomerGroup(); } else if (articleContainer instanceof Offer) { customerGroup = ((Offer) articleContainer).getOrder().getCustomerGroup(); } else if (createArticle != null) { customerGroup = getArticleCustomerGroup(createArticle, monitor); } else if (deferredArticleAssignment != null) { customerGroup = getArticleCustomerGroup(deferredArticleAssignment, monitor); } if (customerGroup != null) customerGroupID = (CustomerGroupID) JDOHelper.getObjectId(customerGroup); } } private CustomerGroup getArticleCustomerGroup(final Article article, final ProgressMonitor monitor) { try { return article.getOrder().getCustomerGroup(); } catch (final JDODetachedFieldAccessException ex) { // not detached, need to access it final Article _article = ArticleDAO.sharedInstance().getArticle( (ArticleID) JDOHelper.getObjectId(article), new String[] {FetchPlan.DEFAULT, Article.FETCH_GROUP_ORDER, Order.FETCH_GROUP_CUSTOMER_GROUP}, NLJDOHelper.MAX_FETCH_DEPTH_NO_LIMIT, monitor); return _article.getOrder().getCustomerGroup(); } } protected abstract void fireCompositeContentChangeEvent(); private void updateInputPriceFragmentTypes() { if (inputPriceFragmentTypes == null) return; final IPriceCoordinate priceCoordinate = createPriceCoordinate(); final PriceCell priceCell = resultPriceConfig.getPriceCell(priceCoordinate, false); this.setEnabled(priceCell != null); if (priceCell == null) { MessageDialog.openError(getShell(), Messages.getString("org.nightlabs.jfire.dynamictrade.ui.articlecontainer.detail.ArticleBaseComposite.errorNoPriceCellDialog.title"), Messages.getString("org.nightlabs.jfire.dynamictrade.ui.articlecontainer.detail.ArticleBaseComposite.errorNoPriceCellDialog.message")); //$NON-NLS-1$ //$NON-NLS-2$ return; } for (final InputPriceFragmentType ipft : inputPriceFragmentTypes) { final long amount = priceCell.getPrice().getAmount(ipft.getPriceFragmentType()); ipft.setAmount(amount); } inputPriceFragmentTypeTable.refresh(); } private Article deferredArticleAssignment = null; public void setArticle(final Article article) { if (Display.getCurrent() == null) throw new IllegalStateException("This method must be called on the UI Thread! Wrong Thread!"); //$NON-NLS-1$ synchronized (loadDynamicProductType_inProcess) { if (loadDynamicProductType_inProcess[0]) { deferredArticleAssignment = article; return; } } _setArticle(article); } protected void updateProductNameUI() { productNameText.setText(productName.getText(NLLocale.getDefault().getLanguage())); } protected Article article = null; protected void _setArticle(final Article _article) { if (!_article.getProductType().equals(dynamicProductType)) throw new IllegalArgumentException("article.productType != this.productType : " + _article.getProductType() + " != " + dynamicProductType); //$NON-NLS-1$ //$NON-NLS-2$ this.article = _article; final DynamicProduct product = (DynamicProduct) article.getProduct(); if (!tariffCombo.selectElement(article.getTariff())) throw new IllegalStateException("Tariff not in combo!"); // TODO we should handle this situation - it might happen //$NON-NLS-1$ DynamicProductInfo dynamicProductInfo; if (product != null) dynamicProductInfo = product; else dynamicProductInfo = (DynamicProductInfo) article; productName.copyFrom(dynamicProductInfo.getName()); updateProductNameUI(); productNameModified = false; if (!unitCombo.selectElement(dynamicProductInfo.getUnit())) throw new IllegalStateException("Unit not in combo!"); // TODO we should handle this situation - it might happen //$NON-NLS-1$ quantity.setText(NumberFormatter.formatFloat(dynamicProductInfo.getQuantityAsDouble(), dynamicProductInfo.getUnit().getDecimalDigitCount())); // product.singlePrice.fragments is probably not detached in the ArticleEdit's Articles try { for (final InputPriceFragmentType ipft : inputPriceFragmentTypes) { final long amount = dynamicProductInfo.getSinglePrice().getAmount(ipft.getPriceFragmentType()); final PriceCell priceCell = resultPriceConfig.getPriceCell(createPriceCoordinate(), false); if (priceCell != null) priceCell.getPrice().setAmount(ipft.getPriceFragmentType(), amount); ipft.setAmount(amount); } inputPriceFragmentTypeTable.refresh(); inputPriceFragmentTypeModified = false; } catch (final JDODetachedFieldAccessException x) { setFaded(true); final Job job = new Job(Messages.getString("org.nightlabs.jfire.dynamictrade.ui.articlecontainer.detail.ArticleBaseComposite.loadArticleWithPriceFragmentsJob.name")) { //$NON-NLS-1$ @Override protected IStatus run(final ProgressMonitor monitor) throws Exception { monitor.beginTask(Messages.getString("org.nightlabs.jfire.dynamictrade.ui.articlecontainer.detail.ArticleBaseComposite.task.loadArticle"), 100); //$NON-NLS-1$ try { final ArticleID articleID = (ArticleID) JDOHelper.getObjectId(article); final Article articleWithPriceFragments = ArticleDAO.sharedInstance().getArticle( articleID, new String[] { FetchPlan.DEFAULT, DynamicProductTypeRecurringArticle.FETCH_GROUP_DYNAMIC_PRODUCT_TYPE_RECURRING_ARTICLE_SINGLEPRICE, Article.FETCH_GROUP_PRODUCT, DynamicProduct.FETCH_GROUP_SINGLE_PRICE, Price.FETCH_GROUP_THIS_PRICE }, NLJDOHelper.MAX_FETCH_DEPTH_NO_LIMIT, new SubProgressMonitor(monitor, 100)); Display.getDefault().asyncExec(new Runnable() { public void run() { final DynamicProduct product = (DynamicProduct) articleWithPriceFragments.getProduct(); DynamicProductInfo dynamicProductInfo; if (product != null) dynamicProductInfo = product; else dynamicProductInfo = (DynamicProductInfo) articleWithPriceFragments; for (final InputPriceFragmentType ipft : inputPriceFragmentTypes) { final long amount = dynamicProductInfo.getSinglePrice().getAmount(ipft.getPriceFragmentType()); final PriceCell priceCell = resultPriceConfig.getPriceCell(createPriceCoordinate(), false); if (priceCell != null) priceCell.getPrice().setAmount(ipft.getPriceFragmentType(), amount); ipft.setAmount(amount); } inputPriceFragmentTypeTable.refresh(); inputPriceFragmentTypeModified = false; setFaded(false); } }); } catch (final Exception x) { setFadedOnUIThread(false); } finally { monitor.done(); } return Status.OK_STATUS; } }; job.schedule(); } } private void setFadedOnUIThread(final boolean faded) { Display.getDefault().asyncExec(new Runnable() { public void run() { setFaded(faded); } }); } /** * shows the statues Text Message Label above the name text dialog, * this method should be called after the GUI has been created. */ public void showTextNameMessage(final String message,final MessageType msgType) { if(nameMessageLabel != null) { if(!nameMessageLabel.isVisible()) { final GridData data = (GridData)nameMessageLabel.getLayoutData(); data.exclude = false; nameMessageLabel.setVisible(true); } nameMessageLabel.setMessage(message, msgType); productNameText.setToolTipText(message); } } }