<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: GoodGuyGopes</title>
    <description>The latest articles on DEV Community by GoodGuyGopes (@goodguygopes).</description>
    <link>https://dev.to/goodguygopes</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3494656%2F947db26c-6d65-47fc-94cb-fc3a773ddc3f.webp</url>
      <title>DEV Community: GoodGuyGopes</title>
      <link>https://dev.to/goodguygopes</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/goodguygopes"/>
    <language>en</language>
    <item>
      <title>From Idea to App: Developing a Bookkeeping System in Hubleto (Part 1)</title>
      <dc:creator>GoodGuyGopes</dc:creator>
      <pubDate>Thu, 11 Sep 2025 14:31:22 +0000</pubDate>
      <link>https://dev.to/goodguygopes/from-idea-to-app-developing-a-bookkeeping-system-in-hubleto-part-1-3a45</link>
      <guid>https://dev.to/goodguygopes/from-idea-to-app-developing-a-bookkeeping-system-in-hubleto-part-1-3a45</guid>
      <description>&lt;p&gt;So, the other day I was tasked with developing a bookkeeping app using the Hubleto framework. In this blog post, I want to take you through the process, show you how I built it, and explain how you can easily create similar apps using Hubleto!&lt;/p&gt;

&lt;h2&gt;
  
  
  The Assignment
&lt;/h2&gt;

&lt;p&gt;My goal was to create a simple yet powerful bookkeeping app for Hubleto. Since I have almost no financial background, I did some research, explored demos, and identified the key components needed:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Chart of Accounts&lt;/strong&gt;: Data models for financial accounts - the backbone of any accounting system.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Journal&lt;/strong&gt;: Essential for tracking all credits and debits within a company.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transactions&lt;/strong&gt; and &lt;strong&gt;Bank Reconciliation&lt;/strong&gt;: These keep track of all bank transactions and link journal entries so that bank records align with company books.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accounts Receivable &amp;amp; Payable&lt;/strong&gt;: The heart of double-entry bookkeeping.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is a straightforward structure that every solid bookkeeping app should have. Of course, there are other parts like the General Ledger and Financial Statements, but these rely on the basics above, so we’ll leave them for later.&lt;/p&gt;

&lt;p&gt;Naturally, there was more planning involved. Before I started coding, I sketched out all the models, mapped their relationships, and made sure nothing was missed. To keep this article focused, I’ll leave those details out for now. If you want to dive deeper into the specifics of each model, check out this &lt;a href="https://github.com/hubleto/erp/issues/207" rel="noopener noreferrer"&gt;Issue&lt;/a&gt; in the Hubleto GitHub repo.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Things that are impossible just take longer." – Ian Hickson&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The Preparation
&lt;/h2&gt;

&lt;p&gt;For those unfamiliar, Hubleto is organized into apps, with a Core that manages cooperation between them. There are community apps and external apps. When building for Hubleto, you can choose whether to keep your app private as an external app or contribute publicly as a community app in the &lt;code&gt;hubleto/erp&lt;/code&gt; repository.&lt;/p&gt;

&lt;p&gt;I decided to make mine a community app, so I began by forking the &lt;code&gt;hubleto/erp&lt;/code&gt; repo. Then, I set up my development environment following &lt;a href="https://developer.hubleto.com/v0/tutorials/set-up-environment-for-development" rel="noopener noreferrer"&gt;this tutorial&lt;/a&gt; and got ready to dive in!&lt;/p&gt;

&lt;h2&gt;
  
  
  The Work
&lt;/h2&gt;

&lt;p&gt;To get started, we first needed to decide how to structure our app. In Hubleto, one app usually corresponds to a single sidebar menu item, typically with one major model and some supporting minor models (for example, a major model for Account, with minor models for Account Type, Account Category, etc.). With this in mind, I chose to split my bookkeeping app - or rather, my idea for it - into multiple Hubleto community apps.&lt;/p&gt;

&lt;p&gt;Starting a new project isn’t easy, especially when you’re new to the environment and still figuring out how everything works. I began by copying one of the many apps already available in Hubleto. These served as great references since many essential features I needed were already implemented there.&lt;/p&gt;

&lt;p&gt;So, inside the &lt;code&gt;hubleto/erp&lt;/code&gt; repo, I copied an app (I can’t recall which one exactly), removed everything unnecessary, and started coding.&lt;/p&gt;

&lt;h5&gt;
  
  
  1. I highly recommend reading through Hubleto’s documentation, especially &lt;a href="https://developer.hubleto.com/v0/docs/apps/folder-structure" rel="noopener noreferrer"&gt;this page&lt;/a&gt;.
&lt;/h5&gt;

&lt;p&gt;It explains the structure of Hubleto apps so you can decide which components you need and which you don’t. For example, some community apps include files like &lt;code&gt;Pipeline.php&lt;/code&gt;, &lt;code&gt;Calendar.php&lt;/code&gt;, or various managers. Since we were starting small and building just the skeleton, we didn’t need any of those. After trimming down to essentials, the app structure looked something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Journal
├── Controllers
│   ├── Api
│   └── Entries.php
├── Lang
│   ├── de.json
│   ├── fr.json
│   ├── pl.json
│   └── sk.json
├── Loader.php
├── Loader.tsx
├── manifest.yaml
├── Models
│   ├── EntryLine.php
│   ├── Entry.php
│   └── RecordManagers
│       ├── EntryLine.php
│       └── Entry.php
└── Views
    └── Entries.twig
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  2. Next, define your models and then their relationships.
&lt;/h5&gt;

&lt;p&gt;You can do this in the &lt;code&gt;Models&lt;/code&gt; directory and in the &lt;code&gt;RecordManagers&lt;/code&gt; subdirectory. The most important part of a model is the &lt;code&gt;describeColumns()&lt;/code&gt; function because it defines the database structure of the model (database table). The page about defining models in the documentation is apparently still under construction, but it shouldn't be too complicated. As I explained before, when in doubt, take a look at the many apps that are already being used in Hubleto.&lt;/p&gt;

&lt;h5&gt;
  
  
  3. Relationships in models are defined using Eloquent, so it's almost exactly the same as in Laravel.
&lt;/h5&gt;

&lt;p&gt;To define a relationship, create for each model a corresponding &lt;strong&gt;RecordManager&lt;/strong&gt; in the &lt;code&gt;RecordManagers&lt;/code&gt; subdirectory. The basic structure of a RecordManager can be copied from an existing app (this is always a good idea!). All you then need to do is define the &lt;code&gt;$table&lt;/code&gt; variable and the functions for each relationship. To find out how to define relationships, check out the &lt;a href="https://laravel.com/docs/12.x/eloquent-relationships" rel="noopener noreferrer"&gt;Laravel Documentation&lt;/a&gt;. Then don't forget to also add the relationships to the base model, like this:&lt;/p&gt;

&lt;p&gt;Models/RecordManagers/Entry.php&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Entry extends \Hubleto\Erp\RecordManager
{
  public $table = 'journal_entry';

  /** @return hasMany&amp;lt;EntryLine, covariant Entry&amp;gt; */
  public function JOURNAL_ENTRY_LINE(): hasMany
  {
    return $this-&amp;gt;hasMany(EntryLine::class, 'id', 'id_entry');
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Models/Entry.php&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  public string $recordManagerClass = RecordManagers\Entry::class;

  public array $relations = [
    'JOURNAL_ENTRY_LINE' =&amp;gt; [ self::HAS_MANY, EntryLine::class, 'id_entry', 'id'  ],
  ];
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  4. Define your &lt;code&gt;Loader.php&lt;/code&gt;
&lt;/h5&gt;

&lt;p&gt;In this case, there is not much to add that isn't already explained by the &lt;a href="https://developer.hubleto.com/v0/docs/apps/loader" rel="noopener noreferrer"&gt;great explanation&lt;/a&gt; in the Hubleto documentation about what the Loader file is. In a nutshell, here you define your routes and how your models should be initialized. For now, that's all we need. Our &lt;code&gt;Loader.php&lt;/code&gt; can look something like this:&lt;/p&gt;

&lt;p&gt;Loader.php&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  public function init(): void
  {
    parent::init();

    $this-&amp;gt;router()-&amp;gt;get([
      '/^journal\/entries\/?$/' =&amp;gt; Controllers\Entries::class,
      '/^journal\/entries\/add\/?$/' =&amp;gt; ['controller' =&amp;gt; Controllers\Entries::class, 'vars' =&amp;gt; ['recordId' =&amp;gt; -1]],
    ]);
  }

  public function installTables(int $round): void
  {
    if ($round == 1) {
      $this-&amp;gt;getModel(Models\Entry::class)-&amp;gt;dropTableIfExists()-&amp;gt;install();
    } else if ($round == 2) {
      $this-&amp;gt;getModel(Models\EntryLine::class)-&amp;gt;dropTableIfExists()-&amp;gt;install();
    }
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note that the EntryLine model is installed in the second round because it depends on the Entry model, which is installed in the first round.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h5&gt;
  
  
  5. We are heading towards a fully working app at light speed.
&lt;/h5&gt;

&lt;p&gt;Now, we only need two more things: a controller and a twig. The &lt;strong&gt;controller&lt;/strong&gt; is something that takes in a request from the Router, processes it, and displays the result (a twig). Luckily, most of what we usually have to do, like configuring all the CRUD operations (e.g., if we were developing this app using Laravel), &lt;strong&gt;is done by Hubleto automatically!&lt;/strong&gt; So, we only need to display a website that shows a table of all instances of the model. Since this is such a simple operation, our controller can be almost empty:&lt;/p&gt;

&lt;p&gt;Controllers/Entries.php&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Entries extends \Hubleto\Erp\Controller
{
  public function getBreadcrumbs(): array
  {
    return array_merge(parent::getBreadcrumbs(), [
      [ 'url' =&amp;gt; 'journal/entries', 'content' =&amp;gt; $this-&amp;gt;translate('Journal Entries') ],
    ]);
  }

  public function prepareView(): void
  {
    parent::prepareView();
    $this-&amp;gt;setView('@Hubleto:App:Community:Journal/Entries.twig');
  }

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  6. As you probably noticed, in the controller, we specified a twig that we have not created yet.
&lt;/h5&gt;

&lt;p&gt;So let's solve this issue now. Creating a twig for our controller is the easiest part of it all. It's a single line and it is just the pre-built, Hubleto's in-house, &lt;code&gt;app-table&lt;/code&gt; element. In the &lt;code&gt;string:model&lt;/code&gt; attribute, you define which model you want the table to display. Curious readers might also ask what the &lt;code&gt;int:record-id="{{ viewParams.recordId }}"&lt;/code&gt; attribute does. It is for automatically processing routes like &lt;code&gt;entries?recordId=-1&lt;/code&gt; to open the add form without the user needing to press the button first.&lt;/p&gt;

&lt;p&gt;Views/Entries.twig&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;app-table string:model="Hubleto/App/Community/Journal/Models/Entry" int:record-id="{{ viewParams.recordId }}"&amp;gt;&amp;lt;/app-table&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  7. Last but not least, we must define the manifest of our app.
&lt;/h5&gt;

&lt;p&gt;A manifest introduces the app to the system. It tells who made it and what the name of the app is. You can look up the exact structure of a manifest in the Hubleto documentation on &lt;a href="https://developer.hubleto.com/v0/docs/apps/manifest" rel="noopener noreferrer"&gt;this page&lt;/a&gt;. I'm sure you will have it ready in no time (it's just a few lines). My manifest looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;namespace: Hubleto\App\Community\Journal
appType: community
sidebarGroup: finance
rootUrlSlug: journal/entries
name: Journal
icon: fas fa-receipt
highlight: Journal
author:
  company: "wai.blue"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Prototype
&lt;/h2&gt;

&lt;p&gt;In the previous section, we saw how to make an app for Hubleto. Of course, we only covered how to create the very basics of the Journal app. To make our bookkeeping app production ready, not only would we need to repeat this process for all the categories we determined at the beginning, but there would also be some additional UX/UI work (correctly working CRUD functionality is not yet enough for a good bookkeeping app, right?). However, a first basic prototype to verify that we are on the right track is a very important stepping stone in every development process.&lt;/p&gt;

&lt;p&gt;To test our app, we need to create an initialization config file for &lt;code&gt;php hubleto init&lt;/code&gt;. Check &lt;a href="https://developer.hubleto.com/v0/cli/init" rel="noopener noreferrer"&gt;this page&lt;/a&gt; in the Hubleto documentation to learn how it's done. Most importantly, add the following lines at the end of your init file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;appsToInstall:
   Hubleto\App\Community\Journal: []
   ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the &lt;code&gt;appsToInstall:&lt;/code&gt; section, list the namespaces of any community apps you want to install that aren't already included in the &lt;code&gt;packagesToInstall&lt;/code&gt; property—like our new community app, which isn’t part of it yet since it’s brand new.&lt;/p&gt;

&lt;p&gt;After that, run the command &lt;code&gt;php hubleto init &amp;lt;path to your config file&amp;gt;&lt;/code&gt; to install Hubleto. Once complete, you should see your newly created app in the sidebar under the group you chose. For me, it appears in the &lt;code&gt;finance&lt;/code&gt; group.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fviccllxct93umrrxd0jv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fviccllxct93umrrxd0jv.png" alt="Functioning Hubleto app" width="800" height="356"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And just like that, we’re ready to start using our company Journal. Isn’t it amazing how just a few files and lines of code give us a fully functional table, form, and CRUD operations? &lt;strong&gt;This and much more show just how powerful the Hubleto framework really is.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Thanks for sticking with me through this article. I hope you found it helpful, and I look forward to sharing more soon. In the next part, we’ll build on this foundation - because right now, we really only have the basics - and make our bookkeeping app truly practical. That means improving the UX/UI and turning it into more than just an Excel sheet with a shiny interface.&lt;/p&gt;

&lt;p&gt;You can find all the source code covered here on my GitHub: &lt;a href="https://github.com/mrgopes/hubleto" rel="noopener noreferrer"&gt;https://github.com/mrgopes/hubleto&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Until next time!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs0x7seo0qm4jsmv72iir.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs0x7seo0qm4jsmv72iir.png" alt="Hubleto logo" width="800" height="187"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(Cover photo by Leeloo The First: &lt;a href="https://www.pexels.com/photo/a-notebook-and-pen-near-the-laptop-and-documents-on-the-table-8962476/" rel="noopener noreferrer"&gt;https://www.pexels.com/photo/a-notebook-and-pen-near-the-laptop-and-documents-on-the-table-8962476/&lt;/a&gt;)&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>programming</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
