void SubcategoryEditor::addNewChild()
{
    //launch dialog to ask for the new child. Using this same dialog.
    SubcategoryEditor *scEditor = new SubcategoryEditor(this);
    scEditor->setDb(db);
    Azahar *myDb = new Azahar;
    myDb->setDatabase(db);

    scEditor->setCatList( catList );
    scEditor->setScatList( scatList );
    scEditor->setDialogType(dialogType+1);//incrementing because this dialog is a child dialog (parentType+1)

    if (dialogType == 1) {
        //From "Add Department" dialog, creating a category.
        scEditor->setLabelForName(i18n("New category:"));
        scEditor->setLabelForList(i18n("Select the child subcategories for this category:"));
        scEditor->populateList( myDb->getSubCategoriesList() );
    } else if (dialogType == 2) {
        //From "Add Category" dialog, creating a subcategory. No child allowed.
        scEditor->setLabelForName(i18n("New subcategory:"));
        scEditor->setLabelForList(i18n(""));
        scEditor->hideListView(); //subcategories does not have children.
    }
        
    if ( scEditor->exec() ) {
        QString text = scEditor->getName();
        QStringList children = scEditor->getChildren();
        qDebug()<<text<<" CHILDREN:"<<children;

        if (dialogType == 1) { //The dialog is launched from the "Add Department" dialog. So we are going to create a category.
            //Create the category
            if (!myDb->insertCategory(text)) {
                qDebug()<<"Error:"<<myDb->lastError();
                delete myDb;
                return;
            }
            //insert new category to the box
            catList << text;
            ui->listView->addItem(text);
            //mark item as checkable
            ui->listView->item(ui->listView->count()-1)->setFlags(ui->listView->item(ui->listView->count()-1)->flags() |Qt::ItemIsUserCheckable);
            ui->listView->item(ui->listView->count()-1)->setCheckState(Qt::Checked); //mark as checked.
            qulonglong cId = myDb->getCategoryId(text);
            //create the m2m  relations for the new category->subcategory.
            foreach(QString cat, children) {
                //get subcategory id
                qulonglong scId = myDb->getSubCategoryId(cat);
                //create the link [category] --> [subcategory]
                myDb->insertM2MCategorySubcategory(cId, scId);
            }
        } else if (dialogType == 2) { //The dialog is launched from the "Add Category" dialog. So we are going to create a subcategory which does have a child.
Пример #2
0
void PurchaseEditor::checkIfCodeExists()
{
  Azahar *myDb = new Azahar;
  myDb->setDatabase(db);
  gelem = "";
  QString codeStr = ui->editCode->text();
  if (codeStr.isEmpty()) codeStr = "0";
  ProductInfo pInfo = myDb->getProductInfo(codeStr);
  if (pInfo.code ==0 && pInfo.desc=="Ninguno") productExists = false;
  if (pInfo.code > 0) {
    status = estatusMod;
    productExists = true;
    qtyOnDb  = pInfo.stockqty;
    //Prepopulate dialog...
    ui->editDesc->setText(pInfo.desc);
    setCategory(pInfo.category);
    setMeasure(pInfo.units);
    ui->editCost->setText(QString::number(pInfo.cost));
    ui->editTax->setText(QString::number(pInfo.tax));
    //FIXME: add tax models
    //ui->editExtraTaxes->setText(QString::number(pInfo.extratax));
    ui->editFinalPrice->setText(QString::number(pInfo.price));
    ui->editPoints->setText(QString::number(pInfo.points));
    ui->chIsAGroup->setChecked(pInfo.isAGroup);
    gelem = pInfo.groupElementsStr;
    if (!(pInfo.photo).isEmpty()) {
      QPixmap photo;
      photo.loadFromData(pInfo.photo);
      setPhoto(photo);
    }
  } else {
    qDebug()<< "no product found with code "<<codeStr;
    qulonglong codeSaved = getCode();
    resetEdits();
    setCode(codeSaved);
  }
}
void SpecialOrderEditor::addItem()
{
  double dqty = 1; //if not any value, default is ONE
  QStringList msgP;
  QStringList msgU;
  int incTime = 0;
  Azahar *myDb = new Azahar;
  myDb->setDatabase(db);
  
  if (!ui->editAddQty->text().isEmpty()) {
    dqty = ui->editAddQty->text().toDouble();
  }
  //get selected items from source view
  QItemSelectionModel *selectionModel = ui->sourcePView->selectionModel();
  QModelIndexList indexList = selectionModel->selectedRows(); // pasar el indice que quiera (0=code, 1=name)
  foreach(QModelIndex index, indexList) {
    qulonglong code    = index.data().toULongLong();
    QString    codeStr = index.data().toString();
    bool exists = false;
    ProductInfo pInfo;
    //get product info from hash or db
    if (groupInfo.productsList.contains(code)) {
      pInfo = groupInfo.productsList.take(code);
      //check measures for the product
      if (pInfo.units == 1 ) { //by pieces
        QString tmpStr = ui->editAddQty->text();
        if (tmpStr.endsWith(".00")) tmpStr.chop(3); //discard .00's
        if (tmpStr.endsWith(".0")) tmpStr.chop(2);//discard .00's
        if (tmpStr.contains(".") || tmpStr.contains(",")) {
          int tmp = dqty;
          dqty = tmp; //pass to integer instead of double
          msgP.append(i18n("<i>%1</i> rounded to %2",pInfo.desc,tmp));
          incTime += 1000;
        }
      }
      if (pInfo.stockqty >= pInfo.qtyOnList+dqty)
        pInfo.qtyOnList += dqty; //increment it
      else {
        incTime += 1000;
        msgU.append(i18n("<i>%1</i> <b>requested %2, on stock %3</b><br>", pInfo.desc,dqty,pInfo.stockqty));
      }
      //if not enough, its not incremented.
      exists = true;
    } else {
      pInfo = myDb->getProductInfo(codeStr);
      //check measures for the product
      if (pInfo.units == 1) { //by pieces
        QString tmpStr = ui->editAddQty->text();
        if (tmpStr.endsWith(".00")) tmpStr.chop(3);//discard .00's
        if (tmpStr.endsWith(".0")) tmpStr.chop(2);//discard .00's
        if (tmpStr.contains(".") || tmpStr.contains(",")) {
          int tmp = dqty;
          dqty = tmp; //pass to integer instead of double
          msgP.append(i18n("<i>%1</i> rounded to %2",pInfo.desc,tmp));
          incTime += 1000;
        }
      }
      pInfo.qtyOnList = 0;
      if (pInfo.stockqty >= dqty)
        pInfo.qtyOnList = dqty;
      else {
        incTime += 1000;
        msgU.append(i18n("<i>%1</i> <b>requested %2, on stock %3</b><br>", pInfo.desc,dqty,pInfo.stockqty));
      }
    }

    // Insert/Update GroupView
    if (!exists) {
      //check if it is available.
      if (pInfo.qtyOnList <= 0 ) {
        //NO, its not available.
        continue;
        qDebug()<<"Continue...";
      }
      // Insert into the groupView
      int rowCount = ui->groupView->rowCount();
      ui->groupView->insertRow(rowCount);
      ui->groupView->setItem(rowCount, 0, new QTableWidgetItem(QString::number(dqty)));
      ui->groupView->setItem(rowCount, 1, new QTableWidgetItem(pInfo.desc));
    } else {
      if (pInfo.qtyOnList <= 0 ) {
        //NO, its not available.
        continue;
        qDebug()<<"Continue...";
      }
      //simply update the groupView with the new qty
      for (int ri=0; ri<ui->groupView->rowCount(); ++ri)
      {
        QTableWidgetItem * item = ui->groupView->item(ri, 1);
        QString name = item->data(Qt::DisplayRole).toString();
        if (name == pInfo.desc) {
          //update
          QTableWidgetItem *itemQ = ui->groupView->item(ri, 0);//item qty
          itemQ->setData(Qt::EditRole, QVariant(pInfo.qtyOnList));
          continue;
        }
      }
    }
    // update info of the group
    groupInfo.count = groupInfo.count+dqty;
    groupInfo.cost  += pInfo.cost*dqty;  //pInfo.qtyOnList;
    groupInfo.price += pInfo.price*dqty; //pInfo.qtyOnList;
    //NOTE:group price is not affected by any product discount, it takes normal price.
    //     Discounts are taken into consideration after adding to the purchase list, when calculating taxes and price for the SO.
    bool yes = false;
    if (pInfo.stockqty >= dqty )
      yes = true;
    groupInfo.isAvailable = (groupInfo.isAvailable && yes );
    // Insert product to the group hash
    groupInfo.productsList.insert(code, pInfo);
    if (!yes) {
      //Just warning!... the insert into the view is above...
      qDebug()<<"Product is not available!";
    }
  }
Пример #4
0
void PurchaseEditor::insertProduct(ProductInfo info)
{
  //When a product is already on list, increment qty.
  bool existed = false;
  if (info.code>0) {
    if (productsHash.contains(info.code)) {
      info = productsHash.take(info.code); //re get it from hash
      info.purchaseQty += getPurchaseQty();
      itemCount += getPurchaseQty();
      totalBuy = totalBuy + info.cost*getPurchaseQty();
      existed = true;
    } else {
      itemCount += info.purchaseQty;
      totalBuy = totalBuy + info.cost*info.purchaseQty;
    }

    //calculate taxes for this item. Calculated from the costs.
    Azahar *myDb = new Azahar;
    myDb->setDatabase(db);
    double cWOTax = 0;
    if (myDb->getConfigTaxIsIncludedInPrice())
      cWOTax= (info.cost)/(1+((info.tax+info.extratax)/100));
    else
      cWOTax = info.cost;
    double tax = cWOTax*info.purchaseQty*(info.tax/100);
    double tax2= cWOTax*info.purchaseQty*(info.extratax/100);
    totalTaxes += tax + tax2; // add this product taxes to the total taxes in the purchase.
    qDebug()<<"Total taxes updated:"<<totalTaxes<<" Taxes for this product:"<<tax+tax2;
    delete myDb;
    
    double finalCount = info.purchaseQty + info.stockqty;
    info.groupElementsStr=""; //grouped products cannot be a group.
    //insert item to productsHash
    productsHash.insert(info.code, info);
    //insert item to ListView

    if (!existed) {
      int rowCount = ui->tableView->rowCount();
      ui->tableView->insertRow(rowCount);
      ui->tableView->setItem(rowCount, 0, new QTableWidgetItem(QString::number(info.code)));
      ui->tableView->setItem(rowCount, 1, new QTableWidgetItem(info.desc));
      ui->tableView->setItem(rowCount, 2, new QTableWidgetItem(QString::number(info.purchaseQty)));
      ui->tableView->setItem(rowCount, 3, new QTableWidgetItem(QString::number(finalCount)));
    } else {
      //simply update the groupView with the new qty
      for (int ri=0; ri<ui->tableView->rowCount(); ++ri)
      {
        QTableWidgetItem * item = ui->tableView->item(ri, 1);
        QString name = item->data(Qt::DisplayRole).toString();
        if (name == info.desc) {
          //update
          QTableWidgetItem *itemQ = ui->tableView->item(ri, 2);
          itemQ->setData(Qt::EditRole, QVariant(QString::number(info.purchaseQty)));
          itemQ = ui->tableView->item(ri, 3);
          itemQ->setData(Qt::EditRole, QVariant(QString::number(finalCount)));
          continue; //HEY PURIST, WHEN I GOT SOME TIME I WILL CLEAN IT
        }
      }
    }
    
    ui->tableView->horizontalHeader()->setResizeMode(QHeaderView::Interactive);
    ui->tableView->resizeRowsToContents();
    //totalBuy = totalBuy + info.cost*info.purchaseQty;
    //itemCount = itemCount + info.purchaseQty;

    //update info on group caption
    ui->groupBox->setTitle( i18n("Items in this purchase [ %1  items,  %2 ]",itemCount,
                                 KGlobal::locale()->formatMoney(totalBuy, QString(), 2)
                                 ) );
    
    qDebug()<<"totalBuy until now:"<<totalBuy<<" itemCount:"<<itemCount<<"info.cost:"<<info.cost<<"info.purchaseQty:"<<info.purchaseQty;
  }  else  qDebug()<<"Item "<<info.code<<" already on hash";
}
Пример #5
0
void PurchaseEditor::calculatePrice()
{
 double finalPrice=0.0;
 bool costOk,profitOk,taxOk,etaxOk;
 
 if (ui->editCost->text().isEmpty() && !ui->groupBoxedItem->isChecked() ) {
   ui->editCost->setText("0.0");
   //ui->editCost->setFocus();
   ui->editCost->setStyleSheet("background-color: rgb(255,100,0); color:white; selection-color: white; font-weight:bold;");
   costOk = false;
 } else {
   ui->editCost->setStyleSheet("");
   costOk=true;
 }
 
 if (ui->editUtility->text().isEmpty()) {
   //ui->editUtility->setFocus();
   ui->editUtility->setText("0.0");
   ui->editUtility->setStyleSheet("background-color: rgb(255,100,0); color:white; selection-color: white; font-weight:bold;");
   profitOk=false;
 } else {
   ui->editUtility->setStyleSheet("");
   profitOk=true;
 }
 
 if (ui->editTax->text().isEmpty()) {
   ui->editTax->setText("0.0");
   //ui->editTax->setFocus();
   ui->editTax->selectAll();
   ui->editTax->setStyleSheet("background-color: rgb(255,100,0); color:white; selection-color: white; font-weight:bold;");
   taxOk=false;
 } else {
   ui->editTax->setStyleSheet("");
   taxOk=true;
 }

 if (ui->editExtraTaxes->text().isEmpty()) {
   ui->editExtraTaxes->setText("0.0");
   //ui->editExtraTaxes->setFocus();
   ui->editExtraTaxes->selectAll();
   ui->editExtraTaxes->setStyleSheet("background-color: rgb(255,100,0); color:white; selection-color: white; font-weight:bold;");
   etaxOk = false;
  } else {
    etaxOk = true;
    ui->editExtraTaxes->setStyleSheet("");
  }

  //now check which are ZEROs
//   if (!ui->editExtraTaxes->text().isEmpty() && ui->editExtraTaxes->text().toDouble()<=0)
//     ui->editExtraTaxes->setStyleSheet("background-color: rgb(255,100,0); color:white; selection-color: white; font-weight:bold;");
//   else ui->editExtraTaxes->setStyleSheet("");
  if (!ui->editTax->text().isEmpty() && ui->editTax->text().toDouble()<=0)
    ui->editTax->setStyleSheet("background-color: rgb(255,100,0); color:white; selection-color: white; font-weight:bold;");
  else ui->editTax->setStyleSheet("");
  if (!ui->editUtility->text().isEmpty() && ui->editUtility->text().toDouble()<=0)
    ui->editUtility->setStyleSheet("background-color: rgb(255,100,0); color:white; selection-color: white; font-weight:bold;");
  else ui->editUtility->setStyleSheet("");
  if (!ui->editCost->text().isEmpty() && ui->editCost->text().toDouble()<=0)
    ui->editCost->setStyleSheet("background-color: rgb(255,100,0); color:white; selection-color: white; font-weight:bold;");
  else ui->editCost->setStyleSheet("");

  if (costOk && profitOk && taxOk && etaxOk ) {
  //TODO: if TAXes are included in cost...
  double cWOTax = 0;
  double tax     = ui->editTax->text().toDouble();
  double tax2    = ui->editExtraTaxes->text().toDouble();
  double utility = ui->editUtility->text().toDouble();

  Azahar *myDb = new Azahar;
  myDb->setDatabase(db);

  // We assume that tax rules for prices also apply to costs.
  if (myDb->getConfigTaxIsIncludedInPrice())
    cWOTax= (ui->editCost->text().toDouble())/(1+((tax+tax2)/100));
  else
    cWOTax = ui->editCost->text().toDouble();
  
  double cost    = cWOTax;
  utility = ((utility/100)*cost);
  double cu=cost+utility;
  //We need the tax recalculated from the costs+utility, this time taxes are expressed in $
  tax     = ((tax/100)*(cost)); ///NOTE fixed:when paying for a product we pay taxes for the cost not the cost+profit
  tax2    = ((tax2/100)*(cost));


  if (ui->groupBoxedItem->isChecked()){
    double itemsPerBox = 0;
    double pricePerBox = 0;
    if (!ui->editItemsPerBox->text().isEmpty()) itemsPerBox = ui->editItemsPerBox->text().toDouble();
    if (!ui->editPricePerBox->text().isEmpty()) pricePerBox = ui->editPricePerBox->text().toDouble();
    if (!ui->editItemsPerBox->text().isEmpty() || !ui->editPricePerBox->text().isEmpty()) return;
    tax     = ui->editTax->text().toDouble();
    tax2    = ui->editExtraTaxes->text().toDouble();
    if (myDb->getConfigTaxIsIncludedInPrice())
      cWOTax= (pricePerBox/itemsPerBox)/(1+((tax+tax2)/100));
    else
      cWOTax = pricePerBox/itemsPerBox;

    cost = cWOTax;
    ui->editCost->setText(QString::number(cost));
    utility = ((ui->editUtility->text().toDouble()/100)*cost);
    cu = cost + utility;
    tax     = ((tax/100)*(cost));///NOTE fixed:when paying for a product we pay taxes for the cost not the cost+profit
    tax2    = ((tax2/100)*(cost));
    finalPrice = cu + tax + tax2;
    
  }
  else finalPrice = cost + utility + tax + tax2;

  qDebug()<<"cWOTax ="<<cWOTax<<" tax1="<<tax<<" tax2="<<tax2<<" FinalPrice:"<<finalPrice;

  ui->editFinalPrice->setText(QString::number(finalPrice));
  ui->editFinalPrice->selectAll();
  delete myDb;
  }
}
Пример #6
0
void PromoEditor::populateCategoriesCombo()
{
  Azahar *myDb = new Azahar;
  myDb->setDatabase(db);
  ui->comboCategory->addItems(myDb->getCategoriesList());
}
Пример #7
0
void PurchaseEditor::addItemToList()
{
  ProductInfo pInfo;
  Azahar *myDb = new Azahar;
  myDb->setDatabase(db);
  bool ok=false;

  if (ui->editCode->text().isEmpty()) ui->editCode->setFocus();
  else if (ui->editDesc->text().isEmpty()) ui->editDesc->setFocus();
  else if (ui->editPoints->text().isEmpty()) ui->editPoints->setFocus();
  else if (ui->editCost->text().isEmpty()) ui->editCost->setFocus();
  else if (ui->editTax->text().isEmpty()) ui->editTax->setFocus();
  else if (ui->editFinalPrice->text().isEmpty()) ui->editFinalPrice->setFocus();
  else if (ui->editQty->text().isEmpty() || ui->editQty->text()=="0") ui->editQty->setFocus();
  else if ((ui->editUtility->text().isEmpty() && ui->editFinalPrice->text().isEmpty()) || ui->editFinalPrice->text().toDouble()<=ui->editCost->text().toDouble() ) ui->editFinalPrice->setFocus();
  else if (ui->groupBoxedItem->isChecked() && (ui->editItemsPerBox->text().isEmpty() || ui->editItemsPerBox->text()=="0"))  ui->editItemsPerBox->setFocus();
  else if (ui->groupBoxedItem->isChecked() && (ui->editPricePerBox->text().isEmpty() || ui->editPricePerBox->text()=="0")) ui->editPricePerBox->setFocus();
  else ok = true;

  if (ok) {
    ProductInfo info = myDb->getProductInfo(QString::number(getCode()));
    if (info.code == 0) {
      info.code = getCode();
      info.lastProviderId=1; //for now.. fixme in the future
    }
    //update p.info from the dialog
    info.desc    = getDescription();
    info.price   = getPrice();
    info.cost    = getCost();
    info.tax     = getTax1();
    //FIXME: add tax models
    //info.extratax= getTax2();
    info.photo   = getPhotoBA();
    info.units   = getMeasureId();
    info.category= getCategoryId();
    info.profit  = getProfit();
    info.points  = getPoints();
    info.stockqty= getQtyOnDb();
    info.purchaseQty = getPurchaseQty();
    double finalCount = info.purchaseQty + info.stockqty; // WHAT FOR??
    info.validDiscount = productExists; //used to check if product is already on db.
    //FIXME: NEXT 2 lines are temporal remove on 0.8 version
    //info.alphaCode = "-NA-";
    //FIXME: last providerId for an existent must be gotten from db since we dont have that field here. Provide a combobox to select one and a button to add a new one.
    //info.lastProviderId = 1;

    if (info.isAGroup) {
      // get each product fo the group/pack
      QStringList list = gelem.split(",");
      for (int i=0; i<list.count(); ++i) {
        QStringList tmp = list.at(i).split("/");
        if (tmp.count() == 2) { //ok 2 fields
          qulonglong  code  = tmp.at(0).toULongLong();
          pInfo = myDb->getProductInfo(QString::number(code));
          pInfo.purchaseQty = getPurchaseQty();
          pInfo.validDiscount = true; // all grouped products exists
          insertProduct(pInfo); ///inserting each product of the group
        } // correct fields
      }//for each element
    } else insertProduct(info);

    resetEdits();
    ui->editCode->setFocus();
  }
}
Пример #8
0
void PurchaseEditor::doPurchase()
{
    if (!db.isOpen()) db.open();
    if (!db.isOpen()) {return;}

    QStringList items;
    items.clear();

    //temporal items list
    items.append("empty list"); //just a tweak for creating the transaction, cleaning after creating it.

    Azahar *myDb = new Azahar;
    myDb->setDatabase(db);

    qDebug()<<"doPurchase...";
    qDebug()<<"DATE:"<<getDate().toString()<<getTime().toString();

    TransactionInfo tInfo;
    if (getPurchased()) {
        tInfo.type    = tBuy;
    } else {
        tInfo.type    = tDonation;
    }
    tInfo.amount  = getTotalBuy();
    tInfo.date    = getDate();
    tInfo.time    = getTime();
    tInfo.paywith = 0.0;
    tInfo.changegiven = 0.0;
    tInfo.paymethod   = pCash;
    tInfo.state   = tCompleted;
    tInfo.userid  = loggedUserId;
    tInfo.clientid= 0;
    tInfo.cardnumber  = "-NA-";
    tInfo.cardauthnum = "-NA-";
    tInfo.itemcount   = getItemCount();
    tInfo.itemlist    = items.join(";");
    tInfo.utility     = 0; //FIXME: utility is calculated until products are sold, not before.
    tInfo.terminalnum = 0; //NOTE: Not really a terminal... from admin computer.
    tInfo.providerid  = 1; //FIXME!
    tInfo.specialOrders = "";
    tInfo.balanceId = 0;
    tInfo.donor       = getDonor();
    tInfo.note= getNote();
    qulonglong trnum = myDb->insertTransaction(tInfo); //to get the transaction number to insert in the log.
    if ( trnum <= 0 ) {
        qDebug()<<"ERROR: Could not create a Purchase Transaction ::doPurchase()";
        qDebug()<<"Error:"<<myDb->lastError();
        //TODO: Notify the user about the error.
    }
    //Now cleaning the items to fill with real items...
    items.clear();
    //assigning new transaction id to the tInfo.
    tInfo.id = trnum;

    QHash<QString, ProductInfo> hash = getHash();
    ProductInfo info;
    TransactionItemInfo tItemInfo;
    //Iterate the hash
    QHashIterator<QString, ProductInfo> i(hash);
    int j=-1;
    while (i.hasNext()) {
        j+=1;
        i.next();
        info = i.value();
        double oldstockqty = info.stockqty;
        info.stockqty = info.purchaseQty+oldstockqty;
        //Modify data on mysql...
        //validDiscount is for checking if product already exists on db. see line # 396 of purchaseeditor.cpp
        if (info.validDiscount) {
            if (!myDb->updateProduct(info, info.code))
                qDebug()<<myDb->lastError();
            else {
                //FIXME: loggedUserId, logging in widgets!?
                myDb->insertLog(loggedUserId, QDate::currentDate(), QTime::currentTime(), "[PURCHASE] "+i18n("Purchase #%4 - %1 x %2 (%3)", info.purchaseQty, info.desc, info.code, trnum));
                qDebug()<<"Product updated [purchase] ok..."<<i18n("Purchase #%4 - %1 x %2 (%3)", info.purchaseQty, info.desc, info.code, trnum);
            }

        } else {
            if (!myDb->insertProduct(info))
                qDebug()<<myDb->lastError();
            else {
                myDb->insertLog(loggedUserId, QDate::currentDate(), QTime::currentTime(), "[PURCHASE] "+i18n("Purchase #%4 - [new] - %1 x %2 (%3)", info.purchaseQty, info.desc, info.code, trnum) );
                qDebug()<<i18n("Purchase #%4 - [new] - %1 x %2 (%3)", info.purchaseQty, info.desc, info.code, trnum);
            }
        }

        ui->p->m_model->select();
        items.append(info.code+"/"+QString::number(info.purchaseQty));

        // Compiling transactionitems
        tItemInfo.transactionid   = tInfo.id;
        tItemInfo.position        = j;
        tItemInfo.productCode     = i.key();
        tItemInfo.unitStr         = info.unitStr;
        tItemInfo.qty             = info.purchaseQty;
        tItemInfo.cost            = info.cost;
        tItemInfo.price           = info.price;
        tItemInfo.disc            = info.disc * info.purchaseQty;
        tItemInfo.total           = info.cost * info.purchaseQty;
        tItemInfo.completePayment = true;
        if (info.isAGroup)
          tItemInfo.name            = info.desc.replace("\n", "|");
        else
          tItemInfo.name            = info.desc;
        tItemInfo.isGroup = info.isAGroup;
        myDb->insertTransactionItem(tItemInfo);
    }
    //update items in transaction data
    tInfo.itemlist = items.join(";");
    myDb->updateTransaction(tInfo);
    delete myDb;
}