Esempio n. 1
0
wxString StocksListCtrl::OnGetItemText(long item, long column) const
{
    if (column == COL_ID)           return wxString::Format("%i", m_stocks[item].STOCKID).Trim();
    if (column == COL_DATE)         return mmGetDateForDisplay(mmGetStorageStringAsDate(m_stocks[item].PURCHASEDATE));
    if (column == COL_NAME)         return m_stocks[item].STOCKNAME;
    if (column == COL_SYMBOL)       return m_stocks[item].SYMBOL;
    if (column == COL_NUMBER)
    {
        int precision = m_stocks[item].NUMSHARES == floor(m_stocks[item].NUMSHARES) ? 0 : 4;
        return Model_Currency::toString(m_stocks[item].NUMSHARES, stock_panel_->m_currency, precision);
    }
    if (column == COL_PRICE)        return Model_Currency::toString(m_stocks[item].PURCHASEPRICE, stock_panel_->m_currency);
    if (column == COL_VALUE)        return Model_Currency::toString(m_stocks[item].VALUE, stock_panel_->m_currency, 4);
    if (column == COL_GAIN_LOSS)    return Model_Currency::toString(getGainLoss(item), stock_panel_->m_currency);
    if (column == COL_CURRENT)      return Model_Currency::toString(m_stocks[item].CURRENTPRICE, stock_panel_->m_currency, 4);
    if (column == COL_CURRVALUE)    return Model_Currency::toString(m_stocks[item].CURRENTPRICE*m_stocks[item].NUMSHARES, stock_panel_->m_currency);
    if (column == COL_PRICEDATE)    return mmGetDateForDisplay(mmGetStorageStringAsDate(Model_Stock::instance().lastPriceDate(&m_stocks[item])));
    if (column == COL_COMMISSION)   return Model_Currency::toString(m_stocks[item].COMMISSION, stock_panel_->m_currency);
    if (column == COL_NOTES)
    {
        wxString full_notes = m_stocks[item].NOTES;
        if (Model_Attachment::NrAttachments(Model_Attachment::reftype_desc(Model_Attachment::STOCK), m_stocks[item].STOCKID))
            full_notes = full_notes.Prepend(mmAttachmentManage::GetAttachmentNoteSign());
        return full_notes;
    }

    return wxEmptyString;
}
wxString mmBillsDepositsPanel::getItem(long item, long column)
{
    const Model_Billsdeposits::Full_Data& bill = this->bills_.at(item);
    switch (column)
    {
    case COL_ID:
        return wxString::Format("%i", bill.BDID).Trim();
    case COL_PAYMENT_DATE:
        return mmGetDateForDisplay(Model_Billsdeposits::NEXTOCCURRENCEDATE(bill));
    case COL_DUE_DATE:
        return mmGetDateForDisplay(Model_Billsdeposits::TRANSDATE(bill));
    case COL_ACCOUNT:
        return bill.ACCOUNTNAME;
    case COL_PAYEE:
        return bill.PAYEENAME;
    case COL_STATUS:
        return bill.STATUS;
    case COL_CATEGORY:
        if (bill.has_split())
            return _("Split.......");
        else
            return bill.CATEGNAME;
    case COL_TYPE:
        return wxGetTranslation(bill.TRANSCODE);
    case COL_AMOUNT:
        return Model_Account::toCurrency(bill.TRANSAMOUNT, Model_Account::instance().get(bill.ACCOUNTID));
    case COL_FREQUENCY:
        return GetFrequency(&bill);
    case COL_REPEATS:
        if (bill.NUMOCCURRENCES == -1)
            return L"\x221E";
        else
            return wxString::Format("%i", bill.NUMOCCURRENCES).Trim();
    case COL_AUTO:
    {
        int repeats = bill.REPEATS;
        wxString repeatSTR = _("Manual");
        if (repeats >= BD_REPEATS_MULTIPLEX_BASE)
        {
            repeats -= BD_REPEATS_MULTIPLEX_BASE;
            repeatSTR = _("Suggested");
            if (repeats >= BD_REPEATS_MULTIPLEX_BASE)
            {
                repeats -= BD_REPEATS_MULTIPLEX_BASE;
                repeatSTR = _("Automated");
            }
        }
        return repeatSTR;
    }
    case COL_DAYS:
        return GetRemainingDays(&bill);
    case COL_NUMBER:
        return bill.TRANSACTIONNUMBER;
    case COL_NOTES:
        return bill.NOTES;
    default:
        return wxEmptyString;
    }
}
Esempio n. 3
0
wxString mmAssetsPanel::getItem(long item, long column)
{
    const Model_Asset::Data& asset = this->m_assets[item];
    switch (column)
    {
    case COL_ICON:
        return " ";
    case COL_ID:
        return wxString::Format("%i", asset.ASSETID).Trim();
    case COL_NAME:
        return asset.ASSETNAME;
    case COL_TYPE:
        return wxGetTranslation(asset.ASSETTYPE);
    case COL_VALUE_INITIAL:
        return Model_Currency::toCurrency(asset.VALUE);
    case COL_VALUE_CURRENT:
        return Model_Currency::toCurrency(Model_Asset::value(asset));
    case COL_DATE:
        return mmGetDateForDisplay(Model_Asset::STARTDATE(asset));
    case COL_NOTES:
    {
        wxString full_notes = asset.NOTES;
        if (Model_Attachment::NrAttachments(Model_Attachment::reftype_desc(Model_Attachment::ASSET), asset.ASSETID))
            full_notes = full_notes.Prepend(mmAttachmentManage::GetAttachmentNoteSign());
        return full_notes;
    }
    default:
        return "";
    }
}
Esempio n. 4
0
wxString mmReportChartStocks::getHTMLText()
{
    mmHTMLBuilder hb;
    hb.init();
    hb.addHeader(2, _("Stocks Performance Charts"));

    wxTimeSpan dtDiff = dtRange_->end_date() - dtRange_->start_date();
    if (dtRange_->is_with_date() && dtDiff.GetDays() <= 366)
        hb.DisplayDateHeading(dtRange_->start_date(), dtRange_->end_date(), true);
    hb.addHorizontalLine();

    int count = 0, heldAt = -1;
    bool pointDot = false, showGridLines = false;
    wxTimeSpan dist;
    wxDate dateDt, precDateDt = wxInvalidDateTime;
    for (const auto& stock : Model_Stock::instance().all(Model_Stock::COL_HELDAT))
    {
        int dataCount = 0, freq = 1;
        Model_StockHistory::Data_Set histData = Model_StockHistory::instance().find(Model_StockHistory::SYMBOL(stock.SYMBOL),
            Model_StockHistory::DATE(dtRange_->start_date(), GREATER_OR_EQUAL),
            Model_StockHistory::DATE(dtRange_->end_date(), LESS_OR_EQUAL));
        std::stable_sort(histData.begin(), histData.end(), SorterByDATE());
        if (histData.size() <= 30)
            showGridLines = pointDot = true;
        else if (histData.size() <= 366)
            showGridLines = true;
        else
            freq = histData.size() / 366;
        std::vector<ValueTrio> aData;
        for (const auto& hist : histData)
        {
            if (dataCount % freq == 0)
            {
                ValueTrio val;
                dateDt = Model_StockHistory::DATE(hist);
                if (histData.size() <= 30)
                    val.label = mmGetDateForDisplay(dateDt);
                else if (precDateDt.IsValid() && dateDt.GetMonth() != precDateDt.GetMonth())
                    val.label = dateDt.GetMonthName(dateDt.GetMonth());
                else
                    val.label = "";
                val.amount = hist.VALUE;
                aData.push_back(val);
                precDateDt = dateDt;
            }
            dataCount++;
        }
        if (!aData.empty())
        {
            Model_Account::Data* account = Model_Account::instance().get(stock.HELDAT);
            hb.addHeader(1, wxString::Format("%s - (%s)", stock.STOCKNAME, account->ACCOUNTNAME));
            hb.addLineChart(aData, stock.STOCKNAME, count, 1000, 400, pointDot, showGridLines, true);
        }

        heldAt = stock.HELDAT;
        count++;
    }

    return hb.getHTMLText();
}
Esempio n. 5
0
void mmWebAppDialog::fillControls()
{
    webtranListBox_->DeleteAllItems();
    WebAppTransactions_.clear();

    mmWebApp::WebApp_DownloadNewTransaction(WebAppTransactions_, false);

    for (const auto& WebTran : WebAppTransactions_)
    {
        wxVector<wxVariant> data;
data.push_back(wxVariant(wxString::Format(wxT("%i"),WebTran.ID))); //WEBTRAN_ID
        data.push_back(wxVariant(mmGetDateForDisplay(WebTran.Date))); //WEBTRAN_DATE
        data.push_back(wxVariant(WebTran.Account)); //WEBTRAN_ACCOUNT
        data.push_back(wxVariant(WebTran.Status)); //WEBTRAN_STATUS
        data.push_back(wxVariant(WebTran.Type)); //WEBTRAN_TYPE

        wxString Payee = WebTran.Type != "Transfer" ? WebTran.Payee : "> " + WebTran.ToAccount;
        data.push_back(wxVariant(Payee)); //WEBTRAN_PAYEE

        wxString Category = WebTran.Category;
        if (WebTran.SubCategory != wxEmptyString) Category += ":" + WebTran.SubCategory;
        data.push_back(wxVariant(Category)); //WEBTRAN_CATEGORY

        data.push_back(wxVariant(wxString::Format(wxT("%f"),WebTran.Amount)));
        data.push_back(wxVariant(WebTran.Notes)); //WEBTRAN_NOTES
        data.push_back(wxVariant(WebTran.Attachments)); //WEBTRAN_ATTACHMENTS
        webtranListBox_->AppendItem(data, (wxUIntPtr)WebTran.ID);
    }
}
void mm_html_template::load_context()
{
    (*this)(L"TODAY") = mmGetDateForDisplay(wxDate::Today().FormatISODate())
        + " " + wxDate::Now().FormatISOTime();
    for (const auto &r: Model_Infotable::instance().all())
        (*this)(r.INFONAME.ToStdWstring()) = r.INFOVALUE;
    (*this)(L"INFOTABLE") = Model_Infotable::to_loop_t();

    const Model_Currency::Data* currency = Model_Currency::GetBaseCurrency();
    if (currency) currency->to_template(*this);
}
Esempio n. 7
0
void mmStockDialog::OnHistoryAddButton(wxCommandEvent& /*event*/)
{
    if (m_stock->SYMBOL.IsEmpty())
        return;

    wxString listStr;
    wxDateTime dt;
    long i, histID;
    double dPrice = 0.0;
    Model_Account::Data* account = Model_Account::instance().get(m_stock->HELDAT);
	Model_Currency::Data* currency = Model_Account::currency(account);
    wxString currentPriceStr = currentPrice_->GetValue().Trim();
    if (!Model_Currency::fromString(currentPriceStr, dPrice, currency) || (dPrice < 0.0))
        return;
    histID = Model_StockHistory::instance().addUpdate(m_stock->SYMBOL, priceDate_->GetValue(), dPrice, Model_StockHistory::MANUAL);

    for (i=0; i<priceListBox_->GetItemCount(); i++)
    {
        listStr = priceListBox_->GetItemText(i, 0);
        mmParseDisplayStringToDate(dt, listStr, mmOptions::instance().dateFormat_);
        if (dt.FormatISODate() == priceDate_->GetValue().FormatISODate())
            break;
    }
    if (i == priceListBox_->GetItemCount())
    {
        //add
        for (i=0; i<priceListBox_->GetItemCount(); i++)
        {
            listStr = priceListBox_->GetItemText(i, 0);
            mmParseDisplayStringToDate(dt, listStr, mmOptions::instance().dateFormat_);
            if (dt.FormatISODate() < priceDate_->GetValue().FormatISODate())
                break;
        }
        wxListItem item;
        item.SetId(i);
        item.SetData(histID);
        priceListBox_->InsertItem( item );
    }
    if (i != priceListBox_->GetItemCount())
    {
        listStr = Model_Account::toString(dPrice, account, 4);
        priceListBox_->SetItem(i, 0, mmGetDateForDisplay(priceDate_->GetValue()));
        priceListBox_->SetItem(i, 1, listStr);
        listStr = Model_Account::toString(dPrice - m_stock->PURCHASEPRICE, account, 4);
        priceListBox_->SetItem(i, 2, listStr);
    }
}
void mmMainCurrencyDialog::ShowCurrencyHistory()
{
    valueListBox_->DeleteAllItems();

    int baseCurrencyID = Option::instance().BaseCurrency();
    if (currencyID_ <= 0 || currencyID_ == baseCurrencyID)
    {
        historyButtonAdd_->Disable();
        historyButtonDelete_->Disable();
        return;
    }
    else
    {
        historyButtonAdd_->Enable();
        historyButtonDelete_->Enable();
    }      

    Model_CurrencyHistory::Data_Set histData = Model_CurrencyHistory::instance().find(Model_CurrencyHistory::CURRENCYID(currencyID_));
    std::stable_sort(histData.begin(), histData.end(), SorterByCURRDATE());
    std::reverse(histData.begin(), histData.end());
    if (!histData.empty())
    {
        int idx = 0;
        for (const auto &d : histData)
        {
            wxListItem item;
            item.SetId(idx);
            item.SetData(d.CURRHISTID);
            valueListBox_->InsertItem(item);
            const wxDate dtdt = Model_CurrencyHistory::CURRDATE(d);
            const wxString dispAmount = wxString::Format(wxT("%f"), d.CURRVALUE);
            valueListBox_->SetItem(idx, 0, mmGetDateForDisplay(dtdt));
            valueListBox_->SetItem(idx, 1, dispAmount);
            const wxString& priceAmount = wxEmptyString;
            valueListBox_->SetItem(idx, 2, priceAmount);
            idx++;
        }
        valueListBox_->RefreshItems(0, --idx);
    }
}
Esempio n. 9
0
void  mmReportSummaryStocks::RefreshData()
{
    stocks_.clear();
    gain_loss_sum_total_ = 0.0;
    stockBalance_ = 0.0;

    data_holder line;
    account_holder account;
    for (const auto& a : Model_Account::instance().all(Model_Account::COL_ACCOUNTNAME))
    {
        if (Model_Account::type(a) != Model_Account::INVESTMENT) continue;
        if (Model_Account::status(a) != Model_Account::OPEN) continue;

        account.id = a.id();
        account.name = a.ACCOUNTNAME;
        account.gainloss = 0.0;
        account.total = Model_Account::investment_balance(a).first;
        account.data.clear();

        for (const auto& stock : Model_Stock::instance().find(Model_Stock::HELDAT(a.ACCOUNTID)))
        {
            const Model_Currency::Data* currency = Model_Account::currency(a);
            stockBalance_ += currency->BASECONVRATE * stock.VALUE;
            account.gainloss += stock.VALUE - Model_Stock::value(stock);
            gain_loss_sum_total_ += (stock.VALUE - Model_Stock::value(stock)) * currency->BASECONVRATE;

            line.name = stock.STOCKNAME;
            line.symbol = stock.SYMBOL;
            line.date = mmGetDateForDisplay(Model_Stock::PURCHASEDATE(stock));
            line.qty = stock.NUMSHARES;
            line.purchase = stock.PURCHASEPRICE;
            line.current = stock.CURRENTPRICE;
            line.commission = stock.COMMISSION;
            line.gainloss = stock.VALUE - Model_Stock::value(stock);
            line.value = stock.VALUE;
            account.data.push_back(line);
        }
        stocks_.push_back(account);
    }
}
Esempio n. 10
0
wxString StocksListCtrl::OnGetItemText(long item, long column) const
{
    if (column == COL_DATE)         return mmGetDateForDisplay(mmGetStorageStringAsDate(m_stocks[item].PURCHASEDATE));
    if (column == COL_NAME)         return m_stocks[item].STOCKNAME;
    if (column == COL_NUMBER)
    {
        int precision = m_stocks[item].NUMSHARES == floor(m_stocks[item].NUMSHARES) ? 0 : 4;
        return Model_Currency::toString(m_stocks[item].NUMSHARES, stock_panel_->m_currency, precision);
    }
    if (column == COL_GAIN_LOSS)    return Model_Currency::toString(getGainLoss(item), stock_panel_->m_currency /*, 4*/);
    if (column == COL_VALUE)        return Model_Currency::toString(m_stocks[item].VALUE, stock_panel_->m_currency);
    if (column == COL_CURRENT)      return Model_Currency::toString(m_stocks[item].CURRENTPRICE, stock_panel_->m_currency);
    if (column == COL_NOTES)
    {
        wxString full_notes = m_stocks[item].NOTES;
        if (Model_Attachment::NrAttachments(Model_Attachment::reftype_desc(Model_Attachment::STOCK), m_stocks[item].STOCKID))
            full_notes = full_notes.Prepend(mmAttachmentManage::GetAttachmentNoteSign());
        return full_notes;
    }

    return "";
}
Esempio n. 11
0
void mmStockDialog::showStockHistory()
{
    priceListBox_->DeleteAllItems();
    if (m_stock->SYMBOL.IsEmpty())
        return;

    Model_Account::Data* account = Model_Account::instance().get(m_stock->HELDAT);
    Model_StockHistory::Data_Set histData = Model_StockHistory::instance().find(Model_StockHistory::SYMBOL(m_stock->SYMBOL));
    std::stable_sort(histData.begin(), histData.end(), SorterByDATE());
    std::reverse(histData.begin(), histData.end());
    if (histData.size()>300)
        histData.resize(300);
    if (!histData.empty())
    {
        int idx=0;
        for (const auto &d : histData)
        {
            wxListItem item;
            item.SetId(idx);
            item.SetData(d.HISTID);
            priceListBox_->InsertItem( item );
            const wxDate dtdt = Model_StockHistory::DATE(d);
            const wxString dispAmount = Model_Account::toString(d.VALUE, account, 4);
            priceListBox_->SetItem(idx, 0, mmGetDateForDisplay(dtdt));
            priceListBox_->SetItem(idx, 1, dispAmount);
            if (idx == 0)
            {
                priceDate_->SetValue(dtdt);
                currentPrice_->SetValue(dispAmount);
            }
            const wxString& priceAmount = Model_Account::toString(d.VALUE - m_stock->PURCHASEPRICE, account, 4);
            priceListBox_->SetItem(idx, 2, priceAmount);
            idx++;
        }
        priceListBox_->RefreshItems(0, --idx);
    }
}
Esempio n. 12
0
const wxString mmExportTransaction::getTransactionCSV(const Model_Checking::Full_Data & full_tran, int accountID, const wxString& dateMask)
{
    bool out = full_tran.ACCOUNTID == accountID;

    const wxString delimit = Model_Infotable::instance().GetStringInfo("DELIMITER", mmex::DEFDELIMTER);

    wxString buffer = "";
    int trans_id = full_tran.id();
    const wxString& accountName = (!out ? full_tran.PAYEENAME : full_tran.ACCOUNTNAME);
    wxString categ = full_tran.m_splits.empty() ? full_tran.CATEGNAME : "";
    wxString transNum = full_tran.TRANSACTIONNUMBER;
    wxString notes = (full_tran.NOTES);
    notes.Replace("''", "'");
    notes.Replace("\n", "\\n");

    if (Model_Checking::type(full_tran) == Model_Checking::TRANSFER)
    {
        categ = "[" + (!out ? full_tran.ACCOUNTNAME : full_tran.PAYEENAME) + "]";

        //Transaction number used to make transaction unique
        // to proper merge transfer records
        if (transNum.IsEmpty() && notes.IsEmpty())
            transNum = wxString::Format("#%i", full_tran.id());
    }

    if (!full_tran.m_splits.empty())
    {
        for (const auto &split_entry : full_tran.m_splits)
        {
            double value = split_entry.SPLITTRANSAMOUNT;
            if (Model_Checking::type(full_tran) == Model_Checking::WITHDRAWAL)
                value = -value;
            const wxString split_amount = wxString() << value;

            const wxString split_categ = Model_Category::full_name(split_entry.CATEGID, split_entry.SUBCATEGID);

            buffer << trans_id << delimit
                   << inQuotes(accountName, delimit) << delimit
                   << inQuotes(mmGetDateForDisplay(Model_Checking::TRANSDATE(full_tran)), delimit) << delimit
                   << inQuotes(full_tran.PAYEENAME, delimit) << delimit
                   << full_tran.STATUS << delimit
                   << full_tran.TRANSCODE << delimit
                   << inQuotes(split_categ, delimit) << delimit
                   << inQuotes(split_amount, delimit) << delimit
                   << "" << delimit
                   << inQuotes(notes, delimit)
                   << "\n";
        }

    }
    else
    {
        buffer << trans_id << delimit
               << inQuotes(accountName, delimit) << delimit
               << inQuotes(mmGetDateForDisplay(Model_Checking::TRANSDATE(full_tran)), delimit) << delimit
               << inQuotes(full_tran.PAYEENAME, delimit) << delimit
               << full_tran.STATUS << delimit
               << full_tran.TRANSCODE << delimit
               << inQuotes(categ, delimit) << delimit
               << Model_Checking::balance(full_tran, (out ? full_tran.ACCOUNTID : full_tran.TOACCOUNTID)) << delimit
               << "" << delimit
               << inQuotes(notes, delimit)
               << "\n";
    }
    return buffer;
}
Esempio n. 13
0
/*** Trigger a quote download ***/
bool mmStocksPanel::onlineQuoteRefresh(wxString& sError)
{
    if(listCtrlAccount_->m_stocks.size() < 1)
    {
        sError = _("Nothing to update");
        return false;
    }

    //Symbol, (Amount, Name)
    std::map<wxString, std::pair<double, wxString> > stocks_data;
    wxString site = "";

    for (const auto &stock : listCtrlAccount_->m_stocks)
    {
        const wxString symbol = stock.SYMBOL.Upper();
        if (!symbol.IsEmpty())
        {
            if (stocks_data.find(symbol) == stocks_data.end())
            {
                stocks_data[symbol] = std::make_pair(stock.CURRENTPRICE, "");
                site << symbol << "+";
            }
        }
    }
    if (site.Right(1).Contains("+")) site.RemoveLast(1);

    //Sample: http://finance.yahoo.com/d/quotes.csv?s=SBER.ME+GAZP.ME&f=sl1c4n&e=.csv
    //Sample CSV: "SBER.ME",85.49,"RUB","SBERBANK"
    site = wxString::Format(mmex::weblink::YahooQuotes, site);

    refresh_button_->SetBitmapLabel(wxBitmap(wxImage(led_yellow_xpm).Scale(16,16)));
    stock_details_->SetLabelText(_("Connecting..."));
    wxString sOutput;

    int err_code = site_content(site, sOutput);
    if (err_code != wxURL_NOERR)
    {
        sError = sOutput;
        return false;
    }

    //--//
    wxString StockSymbolWithSuffix, sName, StockQuoteCurrency;
    double dPrice = 0.0;

    wxStringTokenizer tkz(sOutput, "\r\n");
    while (tkz.HasMoreTokens())
    {
        const wxString csvline = tkz.GetNextToken();
        StockSymbolWithSuffix = "";
        StockQuoteCurrency = "";
        wxRegEx pattern("\"([^\"]+)\",([^,][0-9.]+),\"([^\"]*)\",\"([^\"]*)\"");
        if (pattern.Matches(csvline))
        {
            StockSymbolWithSuffix = pattern.GetMatch(csvline, 1);
            pattern.GetMatch(csvline, 2).ToDouble(&dPrice);
            StockQuoteCurrency = pattern.GetMatch(csvline, 3);
            sName = pattern.GetMatch(csvline, 4);
        }

        bool updated = !StockSymbolWithSuffix.IsEmpty();

        /* HACK FOR GBP
        http://sourceforge.net/p/moneymanagerex/bugs/414/
        http://sourceforge.net/p/moneymanagerex/bugs/360/
        1. If the share has GBp as currency, its downloaded value in pence
        2. If the share has another currency, we don't need to modify the price
        */

        if (updated && dPrice > 0)
        {
            if (StockQuoteCurrency == "GBp")
                dPrice = dPrice / 100;
            stocks_data[StockSymbolWithSuffix].first = dPrice;
            stocks_data[StockSymbolWithSuffix].second = sName;
            sError << wxString::Format(_("%s\t -> %s\n")
                , StockSymbolWithSuffix, wxString::Format("%0.4f", dPrice));
        }
    }

    for (auto &s: listCtrlAccount_->m_stocks)
    {
        std::map<wxString, std::pair<double, wxString> >::const_iterator it = stocks_data.find(s.SYMBOL.Upper());
        if (it == stocks_data.end()) continue;
        dPrice = it->second.first;

        s.CURRENTPRICE = dPrice;
        s.VALUE = dPrice * s.NUMSHARES;
        if (s.STOCKNAME.empty()) s.STOCKNAME = it->second.second;
        Model_Stock::instance().save(&s);

        Model_StockHistory::instance().addUpdate(s.SYMBOL, wxDate::Now(), dPrice, Model_StockHistory::ONLINE);
    }

    // Now refresh the display
    int selected_id = -1;
    if (listCtrlAccount_->get_selectedIndex() > -1)
        selected_id = listCtrlAccount_->m_stocks[listCtrlAccount_->get_selectedIndex()].STOCKID;
    listCtrlAccount_->doRefreshItems(selected_id);

    // We are done!
    LastRefreshDT_       = wxDateTime::Now();
    StocksRefreshStatus_ = true;
    refresh_button_->SetBitmapLabel(wxBitmap(wxImage(led_green_xpm).Scale(16,16)));

    strLastUpdate_.Printf(_("%s on %s"), LastRefreshDT_.FormatTime()
        , mmGetDateForDisplay(LastRefreshDT_));
    Model_Infotable::instance().Set("STOCKS_LAST_REFRESH_DATETIME", strLastUpdate_);

    return true;
}
Esempio n. 14
0
void mmReportCashFlow::getStats(double& tInitialBalance, std::vector<ValueTrio>& forecastVector)
{
    int years = cashFlowReportType_ == YEARLY ? 10 : 1;// Monthly for 10 years or Daily for 1 year
    std::map<wxDateTime, double> daily_balance;

    for (const auto& account : Model_Account::instance().all())
    {
        if (Model_Account::status(account) == Model_Account::CLOSED
            || Model_Account::type(account) == Model_Account::INVESTMENT) continue;

        if (accountArray_)
        {
            if (wxNOT_FOUND == accountArray_->Index(account.ACCOUNTNAME)) continue;
        }
        else
        {
            if (! activeTermAccounts_ && Model_Account::type(account) == Model_Account::TERM) continue;
            if (! activeBankAccounts_ && (Model_Account::type(account) == Model_Account::CHECKING 
                || Model_Account::type(account) == Model_Account::CREDIT_CARD)) continue;
        }

        const Model_Currency::Data* currency = Model_Account::currency(account);
        tInitialBalance += account.INITIALBAL * currency->BASECONVRATE;

        for (const auto& tran : Model_Account::transaction(account))
        {
            daily_balance[Model_Checking::TRANSDATE(tran)] += Model_Checking::balance(tran, account.ACCOUNTID) * currency->BASECONVRATE;
        }
    }

    // We now know the total balance on the account
    // Start by walking through the recurring transaction list

    const wxDateTime yearFromNow = today_.Add(wxDateSpan::Years(years));
    forecastVec fvec;

    for (const auto& entry : Model_Billsdeposits::instance().all())
    {
        wxDateTime nextOccurDate = Model_Billsdeposits::NEXTOCCURRENCEDATE(entry);
        if (nextOccurDate > yearFromNow) continue;

        int repeatsType = entry.REPEATS;
        int numRepeats = entry.NUMOCCURRENCES;
        double amt = entry.TRANSAMOUNT;
        double toAmt = entry.TOTRANSAMOUNT;

        // DeMultiplex the Auto Executable fields from the db entry: REPEATS
        if (repeatsType >= BD_REPEATS_MULTIPLEX_BASE)    // Auto Execute User Acknowlegement required
            repeatsType -= BD_REPEATS_MULTIPLEX_BASE;
        if (repeatsType >= BD_REPEATS_MULTIPLEX_BASE)    // Auto Execute Silent mode
            repeatsType -= BD_REPEATS_MULTIPLEX_BASE;

        bool processNumRepeats = numRepeats != -1 || repeatsType == 0;
        if (repeatsType == 0)
        {
            numRepeats = 1;
            processNumRepeats = true;
        }

        int accountID = entry.ACCOUNTID;
        int toAccountID = entry.TOACCOUNTID;

        const Model_Account::Data* account = Model_Account::instance().get(accountID);
        bool isAccountFound = account && !(accountArray_ != nullptr 
            && wxNOT_FOUND == accountArray_->Index(account->ACCOUNTNAME)); //linear search

        const Model_Account::Data* to_account = Model_Account::instance().get(toAccountID);
        bool isToAccountFound = to_account && !(accountArray_ != nullptr 
            && wxNOT_FOUND == accountArray_->Index(to_account->ACCOUNTNAME)); //linear search

        if (!isAccountFound && !isToAccountFound) continue; // skip account

        // Determine if we need to process this account
        if (!m_cashflowSpecificAccounts)
        {
            if (Model_Account::status(account) == Model_Account::CLOSED
                || Model_Account::type(account) == Model_Account::INVESTMENT) continue;
            if (!activeTermAccounts_ && Model_Account::type(account) == Model_Account::TERM) continue;
            if (!activeBankAccounts_ && (Model_Account::type(account) == Model_Account::CHECKING
                || Model_Account::type(account) == Model_Account::CREDIT_CARD)) continue;
        }

        double convRate = (account ? Model_Account::currency(account)->BASECONVRATE : 1.0);
        double toConvRate = (to_account ? Model_Account::currency(to_account)->BASECONVRATE : 1.0);

        // Process all possible recurring transactions for this BD
        while (1)
        {
            if (nextOccurDate > yearFromNow) break;
            if (processNumRepeats) numRepeats--;

            mmRepeatForecast rf;
            rf.date = nextOccurDate;
            rf.amount = 0.0;

            switch (Model_Billsdeposits::type(entry))
            {
            case Model_Billsdeposits::WITHDRAWAL:
                rf.amount = -amt * convRate;
                break;
            case Model_Billsdeposits::DEPOSIT:
                rf.amount = +amt * convRate;
                break;
            case Model_Billsdeposits::TRANSFER:
                if (isAccountFound)
                    rf.amount -= amt * convRate;
                if (isToAccountFound)
                    rf.amount += toAmt * toConvRate;
                break;
            default:
                break;
            }

            fvec.push_back(rf);

            if (processNumRepeats && (numRepeats <= 0)) 
                break;

            nextOccurDate = Model_Billsdeposits::nextOccurDate(repeatsType, numRepeats, nextOccurDate);

            if (repeatsType == Model_Billsdeposits::REPEAT_IN_X_DAYS) // repeat in numRepeats Days (Once only)
            {
                if (numRepeats > 0)
                    numRepeats = -1;
                else
                    break;
            }
            else if (repeatsType == Model_Billsdeposits::REPEAT_IN_X_MONTHS) // repeat in numRepeats Months (Once only)
            {
                if (numRepeats > 0)
                    numRepeats = -1;
                else 
                    break;
            }
            else if (repeatsType == Model_Billsdeposits::REPEAT_EVERY_X_DAYS) // repeat every numRepeats Days
                numRepeats = entry.NUMOCCURRENCES;
            else if (repeatsType == Model_Billsdeposits::REPEAT_EVERY_X_MONTHS) // repeat every numRepeats Months
                numRepeats = entry.NUMOCCURRENCES;
        } // end while
    } //end query

    const wxDateTime& dtBegin = today_;
    for (int idx = 0; idx < (int) forecastVector.size(); idx++)
    {
        wxDateTime dtEnd = cashFlowReportType_ == YEARLY 
            ? dtBegin.Add(wxDateSpan::Months(idx)) : dtBegin.Add(wxDateSpan::Days(idx));

        for (const auto& balance : fvec)
        {
            if (balance.date.IsBetween(dtBegin, dtEnd))
                forecastVector[idx].amount += balance.amount;
        }

        for (const auto& d_balance : daily_balance)
        {
            if (!d_balance.first.IsLaterThan(dtEnd))
                forecastVector[idx].amount += d_balance.second;
        }
        forecastVector[idx].label = mmGetDateForDisplay(dtEnd);
    }
}
Esempio n. 15
0
wxString mmReportSummaryByDate::getHTMLText()
{
    int             i = 0, j;
    double          balancePerDay[5];
    mmHTMLBuilder   hb;
    wxString        datePrec;
    wxDate          date, dateStart = wxDate::Now(), dateEnd = wxDate::Now();
    wxDateSpan      span;
    mmHistoryItem   *pHistItem;
    mmHistoryData   arHistory;
    std::vector<balanceMap> balanceMapVec(Model_Account::instance().all().size());
    std::vector<std::map<wxDate, double>::const_iterator>   arIt(balanceMapVec.size());
    std::vector<double> arBalance(balanceMapVec.size());
    std::vector<wxString>   totBalanceData;
    std::vector<wxDate> arDates;

    hb.init();
    hb.addDivContainer();
    hb.addHeader(2, wxString::Format(_("Accounts Balance - %s"), mode_==0 ? _("Monthly Report"):_("Yearly Report")));
    hb.addDateNow();
    hb.addLineBreak();

    hb.startTable();
    hb.startThead();
    hb.startTableRow();
    hb.addTableHeaderCell(_("Date"));
    hb.addTableHeaderCell(_("Cash"), true);
    hb.addTableHeaderCell(_("Bank Accounts"), true);
    hb.addTableHeaderCell(_("Credit Card Accounts"), true);
    hb.addTableHeaderCell(_("Term Accounts"), true);
    hb.addTableHeaderCell(_("Total"), true);
    hb.addTableHeaderCell(_("Stocks"), true);
    hb.addTableHeaderCell(_("Balance"), true);
    hb.endTableRow();
    hb.endDiv();
    hb.endThead();

    for (const auto& account: Model_Account::instance().all())
    {
        if (Model_Account::type(account) != Model_Account::INVESTMENT)
        {
            //  in balanceMapVec ci sono i totali dei movimenti giorno per giorno
            const Model_Currency::Data* currency = Model_Account::currency(account);
            for (const auto& tran: Model_Account::transaction(account))
                balanceMapVec[i][Model_Checking::TRANSDATE(tran)] += Model_Checking::balance(tran, account.ACCOUNTID) * Model_CurrencyHistory::getDayRate(currency->id(), tran.TRANSDATE);
            if (Model_Account::type(account) != Model_Account::TERM && balanceMapVec[i].size())
            {
                date = balanceMapVec[i].begin()->first;
                if (date.IsEarlierThan(dateStart))
                    dateStart = date;
            }
            arBalance[i] = account.INITIALBAL * Model_CurrencyHistory::getDayRate(currency->id(), dateStart.FormatISODate());
        }
        else
        {
            Model_Stock::Data_Set stocks = Model_Stock::instance().find(Model_Stock::HELDAT(account.id()));
            for (const auto & stock : stocks)
            {
                arHistory.resize(arHistory.size() + 1);
                pHistItem = arHistory.data() + arHistory.size() - 1;
                pHistItem->acctId = account.id();
                pHistItem->stockId = stock.STOCKID;
                pHistItem->purchasePrice = stock.PURCHASEPRICE;
                pHistItem->purchaseDate = Model_Stock::PURCHASEDATE(stock);
                pHistItem->purchaseDateStr = stock.PURCHASEDATE;
                pHistItem->numShares = stock.NUMSHARES;
                pHistItem->stockHist = Model_StockHistory::instance().find(Model_StockHistory::SYMBOL(stock.SYMBOL));
                std::stable_sort(pHistItem->stockHist.begin(), pHistItem->stockHist.end(), SorterByDATE());
                std::reverse(pHistItem->stockHist.begin(), pHistItem->stockHist.end());
            }
        }
        i++;
    }

    if (mode_ == 0)
    {
        dateEnd -= wxDateSpan::Months(1);
        dateEnd.SetToLastMonthDay(dateEnd.GetMonth(), dateEnd.GetYear());
        span = wxDateSpan::Months(1);
    }
    else if (mode_ == 1)
    {
        dateEnd.Set(31, wxDateTime::Dec, wxDateTime::Now().GetYear());
        span = wxDateSpan::Years(1);
    }
    else
        wxASSERT(0);

    date = dateEnd;
    while (date.IsLaterThan(dateStart))
        date -= span;
    dateStart = date;

    i = 0;
    for (const auto& acctMap: balanceMapVec)
        arIt[i++] = acctMap.begin();

    //  prepare the dates array
    while (dateStart <= dateEnd)
    {
        if (mode_ == 0)
            dateStart.SetToLastMonthDay(dateStart.GetMonth(), dateStart.GetYear());
        arDates.push_back(dateStart);
        dateStart += span;
    }
    date = wxDate::Today();
    if (date.GetDay() != dateEnd.GetDay() || date.GetMonth() != dateEnd.GetMonth() || date.GetYear() != dateEnd.GetYear())
        arDates.push_back(date);

    for (const auto & dateStart : arDates)
    {
        i = 0;
        for (auto& account: Model_Account::instance().all())
        {
            if (Model_Account::type(account) != Model_Account::INVESTMENT)
            {
                for (; arIt[i] != balanceMapVec[i].end(); ++arIt[i])
                {
                    if (arIt[i]->first.IsLaterThan(dateStart))
                        break;
                    arBalance[i] += arIt[i]->second;
                }
            }
            else
            {
                double	convRate = 1.0;
                Model_Currency::Data* currency = Model_Account::currency(account);
                if (currency)
                    convRate = Model_CurrencyHistory::getDayRate(currency->id(), dateStart.FormatISODate());
                arBalance[i] = arHistory.getDailyBalanceAt(&account, dateStart) * convRate;
            }
            i++;
        }

        totBalanceData.push_back(dateStart.FormatISODate());
        for (j=0; j<5; j++)
            balancePerDay[j] = 0.0;
        for (j=0; j<5; j++)
        {
            i = 0;
            for (const auto& account: Model_Account::instance().all())
            {
                if ((j == 0 && Model_Account::type(account) == Model_Account::CASH) ||
                    (j == 1 && Model_Account::type(account) == Model_Account::CHECKING) ||
                    (j == 2 && Model_Account::type(account) == Model_Account::CREDIT_CARD) ||
                    (j == 3 && Model_Account::type(account) == Model_Account::TERM) ||
                    (j == 4 && Model_Account::type(account) == Model_Account::INVESTMENT))
                {
                    balancePerDay[j] += arBalance[i];
                }
                i++;
            }
            totBalanceData.push_back(Model_Currency::toCurrency(balancePerDay[j]));
        }
        totBalanceData.push_back(Model_Currency::toCurrency(balancePerDay[0] + balancePerDay[1] + balancePerDay[2] + balancePerDay[3]));
        totBalanceData.push_back(Model_Currency::toCurrency(balancePerDay[0] + balancePerDay[1] + balancePerDay[2] + balancePerDay[3] + balancePerDay[4]));
    }

    hb.startTbody();
    for (i = totBalanceData.size() - 8; i >= 0; i -= 8)
    {
        if (datePrec.Left(4) != totBalanceData[i].Left(4))
        {
            hb.startTotalTableRow();
            hb.addTableCell(totBalanceData[i].Left(4));
            hb.addTableCell("");
            hb.addTableCell("");
            hb.addTableCell("");
            hb.addTableCell("");
            hb.addTableCell("");
            hb.addTableCell("");
            hb.addTableCell("");
            hb.endTableRow();
        }
        hb.startTableRow();
        hb.addTableCell(mmGetDateForDisplay(mmGetStorageStringAsDate(totBalanceData[i])));
        hb.addTableCell(totBalanceData[i + 1], true);
        hb.addTableCell(totBalanceData[i + 2], true);
        hb.addTableCell(totBalanceData[i + 3], true);
        hb.addTableCell(totBalanceData[i + 4], true);
        hb.addTableCell(totBalanceData[i + 6], true);
        hb.addTableCell(totBalanceData[i + 5], true);
        hb.addTableCell(totBalanceData[i + 7], true);
        hb.endTableRow();
        datePrec = totBalanceData[i];
    }
    hb.endTbody();

    hb.endTable();
    hb.end();

    Model_Report::outputReportFile(hb.getHTMLText());
    return "";
}
Esempio n. 16
0
wxString mmReportTransactions::getHTMLText()
{
    mmHTMLBuilder hb;
    hb.init();
    hb.addDivContainer();
    wxString transHeading = _("Transaction List ");
    const Model_Account::Data* acc = Model_Account::instance().get(refAccountID_);
    if (acc)
        transHeading = wxString::Format(_("Transaction List for Account: %s"), acc->ACCOUNTNAME);

    hb.addHeader(2, transHeading);

    hb.addDateNow();
    hb.addLineBreak();

    hb.startSortTable();

    hb.startThead();
    // Display the data Headings
    hb.startTableRow();
    hb.addTableHeaderCell(_("Date"));
    hb.addTableHeaderCell(_("Account"));
    hb.addTableHeaderCell(_("Payee"));
    hb.addTableHeaderCell(_("Status"));
    hb.addTableHeaderCell(_("Category"));
    hb.addTableHeaderCell(_("Type"));
    hb.addTableHeaderCell(_("Number"));
    hb.addTableHeaderCell(_("Notes"));
    hb.addTableHeaderCell(_("Amount"), true);
    hb.endTableRow();
    hb.endThead();

    hb.startTbody();

    double total = 0;
    Model_Account::Data* account = nullptr;
    bool monoAcc = transDialog_->getAccountCheckBox();
    if (monoAcc)
        account = Model_Account::instance().get(transDialog_->getAccountID());

    // Display the data for each row
    for (auto& transaction : trans_)
    {
        hb.startTableRow();
        hb.addTableCell(mmGetDateForDisplay(mmGetStorageStringAsDate(transaction.TRANSDATE)));
        hb.addTableCellLink(wxString::Format("trxid:%d", transaction.TRANSID), transaction.ACCOUNTNAME);
        hb.addTableCell(transaction.PAYEENAME);
        hb.addTableCell(transaction.STATUS);
        hb.addTableCell(transaction.CATEGNAME);
        hb.addTableCell(wxGetTranslation(transaction.TRANSCODE));
        hb.addTableCell(transaction.TRANSACTIONNUMBER);
        hb.addTableCell(transaction.NOTES);
        // Get the exchange rate for the account

        if (!monoAcc)
        {
            account = Model_Account::instance().get(transaction.ACCOUNTID);
            if (account)
            {
                const Model_Currency::Data* currency = Model_Account::currency(account);
                double convRate = 1;
                if (currency)
                    convRate = currency->BASECONVRATE;

                double amount = Model_Checking::balance(transaction, transaction.ACCOUNTID) * convRate;
                hb.addCurrencyCell(amount);
                total += amount;
            }
            else
                hb.addTableCell("");
        }
        else
        {
            double amount = Model_Checking::balance(transaction, account->ACCOUNTID);
            const Model_Currency::Data* currency = Model_Account::currency(account);
            hb.addCurrencyCell(amount, currency);
            total += amount;
        }
        hb.endTableRow();

    }
    hb.endTbody();

    // display the total balance.
    const wxString totalStr = Model_Currency::toCurrency(total, (monoAcc ? Model_Account::currency(account) : Model_Currency::GetBaseCurrency()));
    const std::vector<wxString> v{ totalStr };
    hb.addTotalRow(_("Total Amount: "), 9, v);

    hb.endTable();

    transDialog_->getDescription(hb);
    hb.endDiv();
    hb.end();

    return hb.getHTMLText();
}
Esempio n. 17
0
wxString mmReportSummaryByDate::getHTMLText()
{
    int             i = 0, j;
    double          balancePerDay[5];
    mmHTMLBuilder   hb;
    wxString        datePrec;
    wxDate			date, dateStart = wxDate::Now(), dateEnd = wxDate::Now();
    wxDateSpan      span;
    std::vector<balanceMap> balanceMapVec(Model_Account::instance().all().size());
    std::vector<std::map<wxDate, double>::const_iterator>   arIt(balanceMapVec.size());
    std::vector<double> arBalance(balanceMapVec.size());
    std::vector<wxString>   totBalanceData;

    hb.init();
    hb.addDivContainer();
    hb.addHeader(2, wxString::Format(_("Account Balance - %s"), mode_==0 ? _("Monthly Report"):_("Yearly Report")));
    hb.addDateNow();
    hb.addLineBreak();

    hb.startTable();
    hb.startThead();
    hb.startTableRow();
    hb.addTableHeaderCell(_("Date"));
    hb.addTableHeaderCell(_("Bank Account"), true);
    hb.addTableHeaderCell(_("Credit Card Accounts"), true);
    hb.addTableHeaderCell(_("Term Accounts"), true);
    hb.addTableHeaderCell(_("Stocks"), true);
    hb.addTableHeaderCell(_("Balance"), true);
    hb.endTableRow();
    hb.endDiv();
    hb.endThead();

    for (const auto& account: Model_Account::instance().all())
    {
        if (Model_Account::type(account) == Model_Account::CHECKING || Model_Account::type(account) == Model_Account::CREDIT_CARD || Model_Account::type(account) == Model_Account::TERM)
        {
            //  in balanceMapVec ci sono i totali dei movimenti giorno per giorno
            const Model_Currency::Data* currency = Model_Account::currency(account);
            for (const auto& tran: Model_Account::transaction(account))
                balanceMapVec[i][Model_Checking::TRANSDATE(tran)] += Model_Checking::balance(tran, account.ACCOUNTID) * currency->BASECONVRATE;
            if ((Model_Account::type(account) == Model_Account::CHECKING || Model_Account::type(account) == Model_Account::CREDIT_CARD) && balanceMapVec[i].size())
            {
                date = balanceMapVec[i].begin()->first;
                if (date.IsEarlierThan(dateStart))
                    dateStart = date;
            }
            arBalance[i] = account.INITIALBAL * currency->BASECONVRATE;
        }
        i++;
    }

    if (mode_ == 0)
    {
        dateEnd -= wxDateSpan::Months(1);
        dateEnd.SetToLastMonthDay(dateEnd.GetMonth(), dateEnd.GetYear());
        span = wxDateSpan::Months(1);
    }
    else if (mode_ == 1)
    {
        dateEnd.Set(31, wxDateTime::Dec, wxDateTime::Now().GetYear(), 23, 59, 59);
        span = wxDateSpan::Years(1);
    }
    else
        wxASSERT(0);

    date = dateEnd;
    while (date.IsLaterThan(dateStart))
        date -= span;
    dateStart = date;
    //dateStart.Set(31,wxDateTime::Dec,2013);

    i = 0;
    for (const auto& acctMap: balanceMapVec)
        arIt[i++] = acctMap.begin();

    while (dateStart <= dateEnd)
    {
        if (mode_ == 0 )
            dateStart.SetToLastMonthDay(dateStart.GetMonth(), dateStart.GetYear());

        i = 0;
        for (auto& account: Model_Account::instance().all())
        {
            if (Model_Account::type(account) == Model_Account::CHECKING || Model_Account::type(account) == Model_Account::CREDIT_CARD || Model_Account::type(account) == Model_Account::TERM)
            {
                for (; arIt[i] != balanceMapVec[i].end(); ++arIt[i])
                {
                    if (arIt[i]->first.IsLaterThan(dateStart))
                        break;
                    arBalance[i] += arIt[i]->second;
                }
            }
            else if (Model_Account::type(account) == Model_Account::INVESTMENT)
                arBalance[i] = GetDailyBalanceAt(&account, dateStart);
            i++;
        }

        totBalanceData.push_back(dateStart.FormatISODate());
        for (j=0; j<5; j++)
            balancePerDay[j] = 0.0;
        for (j=0; j<4; j++)
        {
            i = 0;
            for (const auto& account: Model_Account::instance().all())
            {
                if ((j == 0 && Model_Account::type(account) == Model_Account::CHECKING) ||
                    (j == 1 && Model_Account::type(account) == Model_Account::CREDIT_CARD) ||
                    (j == 2 && Model_Account::type(account) == Model_Account::TERM) ||
                    (j == 3 && Model_Account::type(account) == Model_Account::INVESTMENT))
                {
                    balancePerDay[j] += arBalance[i];
                }
                i++;
            }
            totBalanceData.push_back(Model_Currency::toCurrency(balancePerDay[j]));
            balancePerDay[4] += balancePerDay[j];
        }
        totBalanceData.push_back(Model_Currency::toCurrency(balancePerDay[j]));

        dateStart += span;
    }

    hb.startTbody();
    for (i = totBalanceData.size() - 6; i >= 0; i -= 6)
    {
        if (datePrec.Left(4) != totBalanceData[i].Left(4))
        {
            hb.startTotalTableRow();
            hb.addTableCell(totBalanceData[i].Left(4));
            hb.addTableCell("");
            hb.addTableCell("");
            hb.addTableCell("");
            hb.addTableCell("");
            hb.addTableCell("");
            hb.endTableRow();
        }
        hb.startTableRow();
        hb.addTableCell(mmGetDateForDisplay(mmGetStorageStringAsDate(totBalanceData[i])));
        hb.addTableCell(totBalanceData[i + 1], true);
        hb.addTableCell(totBalanceData[i + 2], true);
        hb.addTableCell(totBalanceData[i + 3], true);
        hb.addTableCell(totBalanceData[i + 4], true);
        hb.addTableCell(totBalanceData[i + 5], true);
        hb.endTableRow();
        datePrec = totBalanceData[i];
    }
    hb.endTbody();

    hb.endTable();
    hb.end();

    return hb.getHTMLText();
}
Esempio n. 18
0
void mmHTMLBuilder::addTableCell(const wxDateTime& date)
{
    wxString date_str = mmGetDateForDisplay(date);
    this->addTableCell(date_str);
}
wxString mmReportChartStocks::getHTMLText()
{
    mmHTMLBuilder hb;
    hb.init();
    hb.addDivContainer();
    hb.addHeader(2, getReportTitle());
    hb.addDateNow();

    wxTimeSpan dtDiff = m_date_range->end_date() - m_date_range->start_date();
    if (m_date_range->is_with_date() && dtDiff.GetDays() <= 366)
        hb.DisplayDateHeading(m_date_range->start_date(), m_date_range->end_date(), true);
    hb.addHorizontalLine();

    bool pointDot = false, showGridLines = false;
    wxTimeSpan dist;
    wxDate precDateDt = wxInvalidDateTime;
    for (const auto& stock : Model_Stock::instance().all(Model_Stock::COL_HELDAT))
    {
        int dataCount = 0, freq = 1;
        Model_StockHistory::Data_Set histData = Model_StockHistory::instance().find(Model_StockHistory::SYMBOL(stock.SYMBOL),
            Model_StockHistory::DATE(m_date_range->start_date(), GREATER_OR_EQUAL),
            Model_StockHistory::DATE(m_date_range->end_date(), LESS_OR_EQUAL));
        std::stable_sort(histData.begin(), histData.end(), SorterByDATE());
        if (histData.size() <= 30)
            showGridLines = pointDot = true;
        else if (histData.size() <= 366)
            showGridLines = true;
        else
            freq = histData.size() / 366;
        std::vector<LineGraphData> aData;
        for (const auto& hist : histData)
        {
            if (dataCount % freq == 0)
            {
                LineGraphData val;
                val.xPos = mmGetDateForDisplay(hist.DATE);
                const wxDate dateDt = Model_StockHistory::DATE(hist);
                if (histData.size() <= 30)
                    val.label = val.xPos;
                else if (precDateDt.IsValid() && dateDt.GetMonth() != precDateDt.GetMonth())
                    val.label = wxGetTranslation(dateDt.GetEnglishMonthName(dateDt.GetMonth()));
                else
                    val.label = "";
                val.amount = hist.VALUE;
                aData.push_back(val);
                precDateDt = dateDt;
            }
            dataCount++;
        }
        if (!aData.empty())
        {
            hb.addDivRow();
            Model_Account::Data* account = Model_Account::instance().get(stock.HELDAT);
            hb.addHeader(1, wxString::Format("%s - (%s)", stock.STOCKNAME, account->ACCOUNTNAME));
            hb.addDivCol17_67();
            hb.addLineChart(aData, stock.STOCKNAME, 0, 1000, 400, pointDot, showGridLines);
            hb.endDiv();
            hb.endDiv();
        }
    }

    hb.endDiv();
    hb.end();

    return hb.getHTMLText();
}