Compare commits
27 Commits
7a6c70d3d8
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 0d1f83872c | |||
| 4c2fefe9c3 | |||
| 7d90548717 | |||
| 0a16246138 | |||
| abbabf4af8 | |||
| f145b9e2ec | |||
| ea969beedc | |||
| f715a8d637 | |||
| aa326838fc | |||
| 8a56e2f485 | |||
| c7ec369234 | |||
| 81e35f56a0 | |||
| 3113d77242 | |||
| 776f22ae24 | |||
| 3ac6316da4 | |||
| be5a02150e | |||
| 36ad02a222 | |||
| cdacd1dc40 | |||
| a371fe3bac | |||
| c2c206d585 | |||
| 6439eb79a1 | |||
| 77a65abdb5 | |||
| 09ce444fd0 | |||
| 88f2cd5ea7 | |||
| 28f282988d | |||
| 275652f7f7 | |||
| 442980a2f1 |
@@ -0,0 +1,5 @@
|
||||
# CodeGraph data files — local to each machine, not for committing.
|
||||
# Ignore everything in .codegraph/ except this file itself, so transient
|
||||
# files (the database, daemon.pid, sockets, logs) never show up in git.
|
||||
*
|
||||
!.gitignore
|
||||
+12
@@ -1,5 +1,12 @@
|
||||
.DS_Store
|
||||
|
||||
application/config/config.php
|
||||
application/config/database.php
|
||||
application/controllers/Dbcompare.php
|
||||
.htaccess
|
||||
|
||||
uploads/temp_qr/*
|
||||
|
||||
application/cache/*
|
||||
!application/cache/index.html
|
||||
|
||||
@@ -28,3 +35,8 @@ user_guide_src/cilexer/pycilexer.egg-info/*
|
||||
*.sublime-project
|
||||
/tests/tests/
|
||||
/tests/results/
|
||||
/dev-docs/
|
||||
/ai-rules/
|
||||
*.omo/
|
||||
|
||||
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine On
|
||||
RewriteBase /accounting_dev
|
||||
|
||||
# Redirect requests to index.php
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule ^(.*)$ index.php?/$1 [L]
|
||||
</IfModule>
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"sessionID": "ses_068c091cfffeSZ25RoesHkRZHA",
|
||||
"updatedAt": "2026-07-25T03:09:04.625Z",
|
||||
"sources": {
|
||||
"background-task": {
|
||||
"state": "idle",
|
||||
"updatedAt": "2026-07-25T03:09:04.625Z"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"sessionID": "ses_0690d5681fferVVn0tDzMM9Ow7",
|
||||
"updatedAt": "2026-07-25T08:09:46.929Z",
|
||||
"sources": {
|
||||
"background-task": {
|
||||
"state": "idle",
|
||||
"updatedAt": "2026-07-25T08:09:46.929Z"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
# TODO - Update Cronjob depreciation_asset()
|
||||
|
||||
- [x] Review dan lock aturan tanggal penyusutan bulanan berdasarkan tanggal_perolehan
|
||||
- [x] Update logic due date bulanan per aset (min(day_perolehan, last_day_of_month))
|
||||
- [x] Pastikan penyusutan mulai bulan berikutnya setelah perolehan
|
||||
- [x] Batasi eksekusi hanya saat runDate == dueDateThisMonth
|
||||
- [x] Pertahankan idempotency 1x per bulan
|
||||
- [x] Samakan tanggal pencatatan mutation dan journal ke dueDateThisMonth
|
||||
- [x] Verifikasi struktur response details (success/skip reason) tetap informatif
|
||||
|
||||
## TODO Baru - Catch-up penyusutan multi-bulan
|
||||
|
||||
- [x] Refactor logic depreciation_asset agar memproses multi-bulan (catch-up)
|
||||
- [x] Terapkan loop per bulan dari bulan setelah tanggal patokan sampai bulan berjalan
|
||||
- [x] Pertahankan idempotent check per bulan berdasarkan asset_mutations
|
||||
- [x] Generate mutasi + jurnal per bulan yang eligible
|
||||
- [x] Update nilai buku & akumulasi penyusutan asset setelah proses loop
|
||||
- [x] Tambahkan detail response per bulan (processed/skipped reason)
|
||||
- [x] Validasi syntax PHP untuk Cronjob.php
|
||||
@@ -1,6 +0,0 @@
|
||||
<IfModule authz_core_module>
|
||||
Require all denied
|
||||
</IfModule>
|
||||
<IfModule !authz_core_module>
|
||||
Deny from all
|
||||
</IfModule>
|
||||
@@ -1,533 +0,0 @@
|
||||
<?php
|
||||
defined('BASEPATH') OR exit('No direct script access allowed');
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Base Site URL
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| URL to your CodeIgniter root. Typically this will be your base URL,
|
||||
| WITH a trailing slash:
|
||||
|
|
||||
| http://example.com/
|
||||
|
|
||||
| WARNING: You MUST set this value!
|
||||
|
|
||||
| If it is not set, then CodeIgniter will try to guess the protocol and
|
||||
| path to your installation, but due to security concerns the hostname will
|
||||
| be set to $_SERVER['SERVER_ADDR'] if available, or localhost otherwise.
|
||||
| The auto-detection mechanism exists only for convenience during
|
||||
| development and MUST NOT be used in production!
|
||||
|
|
||||
| If you need to allow multiple domains, remember that this file is still
|
||||
| a PHP script and you can easily do that on your own.
|
||||
|
|
||||
*/
|
||||
$config['base_url'] = 'http://localhost/accounting_dev/';
|
||||
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Index File
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Typically this will be your index.php file, unless you've renamed it to
|
||||
| something else. If you are using mod_rewrite to remove the page set this
|
||||
| variable so that it is blank.
|
||||
|
|
||||
*/
|
||||
$config['index_page'] = '';
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| URI PROTOCOL
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This item determines which server global should be used to retrieve the
|
||||
| URI string. The default setting of 'REQUEST_URI' works for most servers.
|
||||
| If your links do not seem to work, try one of the other delicious flavors:
|
||||
|
|
||||
| 'REQUEST_URI' Uses $_SERVER['REQUEST_URI']
|
||||
| 'QUERY_STRING' Uses $_SERVER['QUERY_STRING']
|
||||
| 'PATH_INFO' Uses $_SERVER['PATH_INFO']
|
||||
|
|
||||
| WARNING: If you set this to 'PATH_INFO', URIs will always be URL-decoded!
|
||||
*/
|
||||
$config['uri_protocol'] = 'REQUEST_URI';
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| URL suffix
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option allows you to add a suffix to all URLs generated by CodeIgniter.
|
||||
| For more information please see the user guide:
|
||||
|
|
||||
| https://codeigniter.com/userguide3/general/urls.html
|
||||
|
|
||||
| Note: This option is ignored for CLI requests.
|
||||
*/
|
||||
$config['url_suffix'] = '';
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Language
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This determines which set of language files should be used. Make sure
|
||||
| there is an available translation if you intend to use something other
|
||||
| than english.
|
||||
|
|
||||
*/
|
||||
$config['language'] = 'english';
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Character Set
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This determines which character set is used by default in various methods
|
||||
| that require a character set to be provided.
|
||||
|
|
||||
| See http://php.net/htmlspecialchars for a list of supported charsets.
|
||||
|
|
||||
*/
|
||||
$config['charset'] = 'UTF-8';
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Enable/Disable System Hooks
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| If you would like to use the 'hooks' feature you must enable it by
|
||||
| setting this variable to TRUE (boolean). See the user guide for details.
|
||||
|
|
||||
*/
|
||||
$config['enable_hooks'] = FALSE;
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Class Extension Prefix
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This item allows you to set the filename/classname prefix when extending
|
||||
| native libraries. For more information please see the user guide:
|
||||
|
|
||||
| https://codeigniter.com/userguide3/general/core_classes.html
|
||||
| https://codeigniter.com/userguide3/general/creating_libraries.html
|
||||
|
|
||||
*/
|
||||
$config['subclass_prefix'] = 'MY_';
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Composer auto-loading
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Enabling this setting will tell CodeIgniter to look for a Composer
|
||||
| package auto-loader script in application/vendor/autoload.php.
|
||||
|
|
||||
| $config['composer_autoload'] = TRUE;
|
||||
|
|
||||
| Or if you have your vendor/ directory located somewhere else, you
|
||||
| can opt to set a specific path as well:
|
||||
|
|
||||
| $config['composer_autoload'] = '/path/to/vendor/autoload.php';
|
||||
|
|
||||
| For more information about Composer, please visit http://getcomposer.org/
|
||||
|
|
||||
| Note: This will NOT disable or override the CodeIgniter-specific
|
||||
| autoloading (application/config/autoload.php)
|
||||
*/
|
||||
$config['composer_autoload'] = FALSE;
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Allowed URL Characters
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This lets you specify which characters are permitted within your URLs.
|
||||
| When someone tries to submit a URL with disallowed characters they will
|
||||
| get a warning message.
|
||||
|
|
||||
| As a security measure you are STRONGLY encouraged to restrict URLs to
|
||||
| as few characters as possible. By default only these are allowed: a-z 0-9~%.:_-
|
||||
|
|
||||
| Leave blank to allow all characters -- but only if you are insane.
|
||||
|
|
||||
| The configured value is actually a regular expression character group
|
||||
| and it will be executed as: ! preg_match('/^[<permitted_uri_chars>]+$/i
|
||||
|
|
||||
| DO NOT CHANGE THIS UNLESS YOU FULLY UNDERSTAND THE REPERCUSSIONS!!
|
||||
|
|
||||
*/
|
||||
$config['permitted_uri_chars'] = 'a-z 0-9~%.:_\-';
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Enable Query Strings
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| By default CodeIgniter uses search-engine friendly segment based URLs:
|
||||
| example.com/who/what/where/
|
||||
|
|
||||
| You can optionally enable standard query string based URLs:
|
||||
| example.com?who=me&what=something&where=here
|
||||
|
|
||||
| Options are: TRUE or FALSE (boolean)
|
||||
|
|
||||
| The other items let you set the query string 'words' that will
|
||||
| invoke your controllers and its functions:
|
||||
| example.com/index.php?c=controller&m=function
|
||||
|
|
||||
| Please note that some of the helpers won't work as expected when
|
||||
| this feature is enabled, since CodeIgniter is designed primarily to
|
||||
| use segment based URLs.
|
||||
|
|
||||
*/
|
||||
$config['enable_query_strings'] = FALSE;
|
||||
$config['controller_trigger'] = 'c';
|
||||
$config['function_trigger'] = 'm';
|
||||
$config['directory_trigger'] = 'd';
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Allow $_GET array
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| By default CodeIgniter enables access to the $_GET array. If for some
|
||||
| reason you would like to disable it, set 'allow_get_array' to FALSE.
|
||||
|
|
||||
| WARNING: This feature is DEPRECATED and currently available only
|
||||
| for backwards compatibility purposes!
|
||||
|
|
||||
*/
|
||||
$config['allow_get_array'] = TRUE;
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Error Logging Threshold
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| You can enable error logging by setting a threshold over zero. The
|
||||
| threshold determines what gets logged. Threshold options are:
|
||||
|
|
||||
| 0 = Disables logging, Error logging TURNED OFF
|
||||
| 1 = Error Messages (including PHP errors)
|
||||
| 2 = Debug Messages
|
||||
| 3 = Informational Messages
|
||||
| 4 = All Messages
|
||||
|
|
||||
| You can also pass an array with threshold levels to show individual error types
|
||||
|
|
||||
| array(2) = Debug Messages, without Error Messages
|
||||
|
|
||||
| For a live site you'll usually only enable Errors (1) to be logged otherwise
|
||||
| your log files will fill up very fast.
|
||||
|
|
||||
*/
|
||||
$config['log_threshold'] = 4;
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Error Logging Directory Path
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Leave this BLANK unless you would like to set something other than the default
|
||||
| application/logs/ directory. Use a full server path with trailing slash.
|
||||
|
|
||||
*/
|
||||
$config['log_path'] = '';
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Log File Extension
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The default filename extension for log files. The default 'php' allows for
|
||||
| protecting the log files via basic scripting, when they are to be stored
|
||||
| under a publicly accessible directory.
|
||||
|
|
||||
| Note: Leaving it blank will default to 'php'.
|
||||
|
|
||||
*/
|
||||
$config['log_file_extension'] = '';
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Log File Permissions
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The file system permissions to be applied on newly created log files.
|
||||
|
|
||||
| IMPORTANT: This MUST be an integer (no quotes) and you MUST use octal
|
||||
| integer notation (i.e. 0700, 0644, etc.)
|
||||
*/
|
||||
$config['log_file_permissions'] = 0644;
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Date Format for Logs
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Each item that is logged has an associated date. You can use PHP date
|
||||
| codes to set your own date formatting
|
||||
|
|
||||
*/
|
||||
$config['log_date_format'] = 'Y-m-d H:i:s';
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Error Views Directory Path
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Leave this BLANK unless you would like to set something other than the default
|
||||
| application/views/errors/ directory. Use a full server path with trailing slash.
|
||||
|
|
||||
*/
|
||||
$config['error_views_path'] = '';
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Cache Directory Path
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Leave this BLANK unless you would like to set something other than the default
|
||||
| application/cache/ directory. Use a full server path with trailing slash.
|
||||
|
|
||||
*/
|
||||
$config['cache_path'] = '';
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Cache Include Query String
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Whether to take the URL query string into consideration when generating
|
||||
| output cache files. Valid options are:
|
||||
|
|
||||
| FALSE = Disabled
|
||||
| TRUE = Enabled, take all query parameters into account.
|
||||
| Please be aware that this may result in numerous cache
|
||||
| files generated for the same page over and over again.
|
||||
| array('q') = Enabled, but only take into account the specified list
|
||||
| of query parameters.
|
||||
|
|
||||
*/
|
||||
$config['cache_query_string'] = FALSE;
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Encryption Key
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| If you use the Encryption class, you must set an encryption key.
|
||||
| See the user guide for more info.
|
||||
|
|
||||
| https://codeigniter.com/userguide3/libraries/encryption.html
|
||||
|
|
||||
*/
|
||||
$config['encryption_key'] = '';
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Session Variables
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| 'sess_driver'
|
||||
|
|
||||
| The storage driver to use: files, database, redis, memcached
|
||||
|
|
||||
| 'sess_cookie_name'
|
||||
|
|
||||
| The session cookie name, must contain only [0-9a-z_-] characters
|
||||
|
|
||||
| 'sess_samesite'
|
||||
|
|
||||
| Session cookie SameSite attribute: Lax (default), Strict or None
|
||||
|
|
||||
| 'sess_expiration'
|
||||
|
|
||||
| The number of SECONDS you want the session to last.
|
||||
| Setting to 0 (zero) means expire when the browser is closed.
|
||||
|
|
||||
| 'sess_save_path'
|
||||
|
|
||||
| The location to save sessions to, driver dependent.
|
||||
|
|
||||
| For the 'files' driver, it's a path to a writable directory.
|
||||
| WARNING: Only absolute paths are supported!
|
||||
|
|
||||
| For the 'database' driver, it's a table name.
|
||||
| Please read up the manual for the format with other session drivers.
|
||||
|
|
||||
| IMPORTANT: You are REQUIRED to set a valid save path!
|
||||
|
|
||||
| 'sess_match_ip'
|
||||
|
|
||||
| Whether to match the user's IP address when reading the session data.
|
||||
|
|
||||
| WARNING: If you're using the database driver, don't forget to update
|
||||
| your session table's PRIMARY KEY when changing this setting.
|
||||
|
|
||||
| 'sess_time_to_update'
|
||||
|
|
||||
| How many seconds between CI regenerating the session ID.
|
||||
|
|
||||
| 'sess_regenerate_destroy'
|
||||
|
|
||||
| Whether to destroy session data associated with the old session ID
|
||||
| when auto-regenerating the session ID. When set to FALSE, the data
|
||||
| will be later deleted by the garbage collector.
|
||||
|
|
||||
| Other session cookie settings are shared with the rest of the application,
|
||||
| except for 'cookie_prefix' and 'cookie_httponly', which are ignored here.
|
||||
|
|
||||
*/
|
||||
$config['sess_driver'] = 'files';
|
||||
$config['sess_cookie_name'] = 'ci_session';
|
||||
$config['sess_samesite'] = 'Lax';
|
||||
$config['sess_expiration'] = 7200;
|
||||
$config['sess_save_path'] = sys_get_temp_dir();
|
||||
$config['sess_match_ip'] = FALSE;
|
||||
$config['sess_time_to_update'] = 300;
|
||||
$config['sess_regenerate_destroy'] = FALSE;
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Cookie Related Variables
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| 'cookie_prefix' = Set a cookie name prefix if you need to avoid collisions
|
||||
| 'cookie_domain' = Set to .your-domain.com for site-wide cookies
|
||||
| 'cookie_path' = Typically will be a forward slash
|
||||
| 'cookie_secure' = Cookie will only be set if a secure HTTPS connection exists.
|
||||
| 'cookie_httponly' = Cookie will only be accessible via HTTP(S) (no javascript)
|
||||
| 'cookie_samesite' = Cookie's samesite attribute (Lax, Strict or None)
|
||||
|
|
||||
| Note: These settings (with the exception of 'cookie_prefix' and
|
||||
| 'cookie_httponly') will also affect sessions.
|
||||
|
|
||||
*/
|
||||
$config['cookie_prefix'] = '';
|
||||
$config['cookie_domain'] = '';
|
||||
$config['cookie_path'] = '/';
|
||||
$config['cookie_secure'] = FALSE;
|
||||
$config['cookie_httponly'] = FALSE;
|
||||
$config['cookie_samesite'] = 'Lax';
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Standardize newlines
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Determines whether to standardize newline characters in input data,
|
||||
| meaning to replace \r\n, \r, \n occurrences with the PHP_EOL value.
|
||||
|
|
||||
| WARNING: This feature is DEPRECATED and currently available only
|
||||
| for backwards compatibility purposes!
|
||||
|
|
||||
*/
|
||||
$config['standardize_newlines'] = FALSE;
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Global XSS Filtering
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Determines whether the XSS filter is always active when GET, POST or
|
||||
| COOKIE data is encountered
|
||||
|
|
||||
| WARNING: This feature is DEPRECATED and currently available only
|
||||
| for backwards compatibility purposes!
|
||||
|
|
||||
*/
|
||||
$config['global_xss_filtering'] = FALSE;
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Cross Site Request Forgery
|
||||
|--------------------------------------------------------------------------
|
||||
| Enables a CSRF cookie token to be set. When set to TRUE, token will be
|
||||
| checked on a submitted form. If you are accepting user data, it is strongly
|
||||
| recommended CSRF protection be enabled.
|
||||
|
|
||||
| 'csrf_token_name' = The token name
|
||||
| 'csrf_cookie_name' = The cookie name
|
||||
| 'csrf_expire' = The number in seconds the token should expire.
|
||||
| 'csrf_regenerate' = Regenerate token on every submission
|
||||
| 'csrf_exclude_uris' = Array of URIs which ignore CSRF checks
|
||||
*/
|
||||
$config['csrf_protection'] = FALSE;
|
||||
$config['csrf_token_name'] = 'csrf_test_name';
|
||||
$config['csrf_cookie_name'] = 'csrf_cookie_name';
|
||||
$config['csrf_expire'] = 7200;
|
||||
$config['csrf_regenerate'] = TRUE;
|
||||
$config['csrf_exclude_uris'] = array();
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Output Compression
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Enables Gzip output compression for faster page loads. When enabled,
|
||||
| the output class will test whether your server supports Gzip.
|
||||
| Even if it does, however, not all browsers support compression
|
||||
| so enable only if you are reasonably sure your visitors can handle it.
|
||||
|
|
||||
| Only used if zlib.output_compression is turned off in your php.ini.
|
||||
| Please do not use it together with httpd-level output compression.
|
||||
|
|
||||
| VERY IMPORTANT: If you are getting a blank page when compression is enabled it
|
||||
| means you are prematurely outputting something to your browser. It could
|
||||
| even be a line of whitespace at the end of one of your scripts. For
|
||||
| compression to work, nothing can be sent before the output buffer is called
|
||||
| by the output class. Do not 'echo' any values with compression enabled.
|
||||
|
|
||||
*/
|
||||
$config['compress_output'] = FALSE;
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Master Time Reference
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Options are 'local' or any PHP supported timezone. This preference tells
|
||||
| the system whether to use your server's local time as the master 'now'
|
||||
| reference, or convert it to the configured one timezone. See the 'date
|
||||
| helper' page of the user guide for information regarding date handling.
|
||||
|
|
||||
*/
|
||||
$config['time_reference'] = 'local';
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Rewrite PHP Short Tags
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| If your PHP installation does not have short tag support enabled CI
|
||||
| can rewrite the tags on-the-fly, enabling you to utilize that syntax
|
||||
| in your view files. Options are TRUE or FALSE (boolean)
|
||||
|
|
||||
| Note: You need to have eval() enabled for this to work.
|
||||
|
|
||||
*/
|
||||
$config['rewrite_short_tags'] = FALSE;
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Reverse Proxy IPs
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| If your server is behind a reverse proxy, you must whitelist the proxy
|
||||
| IP addresses from which CodeIgniter should trust headers such as
|
||||
| HTTP_X_FORWARDED_FOR and HTTP_CLIENT_IP in order to properly identify
|
||||
| the visitor's IP address.
|
||||
|
|
||||
| You can use both an array or a comma-separated list of proxy addresses,
|
||||
| as well as specifying whole subnets. Here are a few examples:
|
||||
|
|
||||
| Comma-separated: '10.0.1.200,192.168.5.0/24'
|
||||
| Array: array('10.0.1.200', '192.168.5.0/24')
|
||||
*/
|
||||
$config['proxy_ips'] = '';
|
||||
@@ -1,98 +0,0 @@
|
||||
<?php
|
||||
defined('BASEPATH') OR exit('No direct script access allowed');
|
||||
|
||||
/*
|
||||
| -------------------------------------------------------------------
|
||||
| DATABASE CONNECTIVITY SETTINGS
|
||||
| -------------------------------------------------------------------
|
||||
| This file will contain the settings needed to access your database.
|
||||
|
|
||||
| For complete instructions please consult the 'Database Connection'
|
||||
| page of the User Guide.
|
||||
|
|
||||
| -------------------------------------------------------------------
|
||||
| EXPLANATION OF VARIABLES
|
||||
| -------------------------------------------------------------------
|
||||
|
|
||||
| ['dsn'] The full DSN string describe a connection to the database.
|
||||
| ['hostname'] The hostname of your database server.
|
||||
| ['username'] The username used to connect to the database
|
||||
| ['password'] The password used to connect to the database
|
||||
| ['database'] The name of the database you want to connect to
|
||||
| ['dbdriver'] The database driver. e.g.: mysqli.
|
||||
| Currently supported:
|
||||
| cubrid, ibase, mssql, mysql, mysqli, oci8,
|
||||
| odbc, pdo, postgre, sqlite, sqlite3, sqlsrv
|
||||
| ['dbprefix'] You can add an optional prefix, which will be added
|
||||
| to the table name when using the Query Builder class
|
||||
| ['pconnect'] TRUE/FALSE - Whether to use a persistent connection
|
||||
| ['db_debug'] TRUE/FALSE - Whether database errors should be displayed.
|
||||
| ['cache_on'] TRUE/FALSE - Enables/disables query caching
|
||||
| ['cachedir'] The path to the folder where cache files should be stored
|
||||
| ['char_set'] The character set used in communicating with the database
|
||||
| ['dbcollat'] The character collation used in communicating with the database
|
||||
| NOTE: For MySQL and MySQLi databases, this setting is only used
|
||||
| as a backup if your server is running PHP < 5.2.3 or MySQL < 5.0.7
|
||||
| (and in table creation queries made with DB Forge).
|
||||
| There is an incompatibility in PHP with mysql_real_escape_string() which
|
||||
| can make your site vulnerable to SQL injection if you are using a
|
||||
| multi-byte character set and are running versions lower than these.
|
||||
| Sites using Latin-1 or UTF-8 database character set and collation are unaffected.
|
||||
| ['swap_pre'] A default table prefix that should be swapped with the dbprefix
|
||||
| ['encrypt'] Whether or not to use an encrypted connection.
|
||||
|
|
||||
| 'mysql' (deprecated), 'sqlsrv' and 'pdo/sqlsrv' drivers accept TRUE/FALSE
|
||||
| 'mysqli' and 'pdo/mysql' drivers accept an array with the following options:
|
||||
|
|
||||
| 'ssl_key' - Path to the private key file
|
||||
| 'ssl_cert' - Path to the public key certificate file
|
||||
| 'ssl_ca' - Path to the certificate authority file
|
||||
| 'ssl_capath' - Path to a directory containing trusted CA certificates in PEM format
|
||||
| 'ssl_cipher' - List of *allowed* ciphers to be used for the encryption, separated by colons (':')
|
||||
| 'ssl_verify' - TRUE/FALSE; Whether verify the server certificate or not
|
||||
|
|
||||
| ['compress'] Whether or not to use client compression (MySQL only)
|
||||
| ['stricton'] TRUE/FALSE - forces 'Strict Mode' connections
|
||||
| - good for ensuring strict SQL while developing
|
||||
| ['ssl_options'] Used to set various SSL options that can be used when making SSL connections.
|
||||
| ['failover'] array - A array with 0 or more data for connections if the main should fail.
|
||||
| ['save_queries'] TRUE/FALSE - Whether to "save" all executed queries.
|
||||
| NOTE: Disabling this will also effectively disable both
|
||||
| $this->db->last_query() and profiling of DB queries.
|
||||
| When you run a query, with this setting set to TRUE (default),
|
||||
| CodeIgniter will store the SQL statement for debugging purposes.
|
||||
| However, this may cause high memory usage, especially if you run
|
||||
| a lot of SQL queries ... disable this to avoid that problem.
|
||||
|
|
||||
| The $active_group variable lets you choose which connection group to
|
||||
| make active. By default there is only one group (the 'default' group).
|
||||
|
|
||||
| The $query_builder variables lets you determine whether or not to load
|
||||
| the query builder class.
|
||||
*/
|
||||
$active_group = 'default';
|
||||
$query_builder = TRUE;
|
||||
|
||||
$db['default'] = array(
|
||||
'dsn' => '',
|
||||
'hostname' => '103.242.106.56', // tanpa port
|
||||
'port' => 3310, // port PostgreSQL
|
||||
'username' => 'accointing_dev',
|
||||
'password' => 'accointing_dev',
|
||||
'database' => 'accointing_dev',
|
||||
'dbdriver' => 'mysqli', // WAJIB: driver PostgreSQL
|
||||
'dbprefix' => '',
|
||||
'pconnect' => FALSE,
|
||||
'db_debug' => (ENVIRONMENT !== 'production'),
|
||||
'cache_on' => FALSE,
|
||||
'cachedir' => '',
|
||||
'char_set' => 'utf8',
|
||||
'dbcollat' => 'utf8_general_ci', // PostgreSQL abaikan ini, tapi biarkan saja
|
||||
'swap_pre' => '',
|
||||
'encrypt' => FALSE,
|
||||
'compress' => FALSE,
|
||||
'stricton' => FALSE,
|
||||
'failover' => array(),
|
||||
'save_queries' => TRUE
|
||||
);
|
||||
|
||||
@@ -56,3 +56,10 @@ $route['translate_uri_dashes'] = FALSE;
|
||||
// untuk webhook absensi
|
||||
$route['iclock/cdata'] = 'AttendanceWebhook/cdata';
|
||||
|
||||
// db compare structure
|
||||
$route['dbcompare/sql'] = 'Dbcompare/sql';
|
||||
$route['dbcompare/sql_download'] = 'Dbcompare/sql_download';
|
||||
|
||||
// teknisi log barang
|
||||
$route['teknisi/log_barang'] = 'teknisi/log_barang';
|
||||
|
||||
|
||||
@@ -6,6 +6,10 @@ class Asset extends CI_Controller {
|
||||
public function __construct(){
|
||||
parent::__construct();
|
||||
$this->load->database();
|
||||
|
||||
if (!$this->session->userdata('logged_in')) {
|
||||
redirect('auth');
|
||||
}
|
||||
}
|
||||
|
||||
public function index(){
|
||||
@@ -18,10 +22,11 @@ class Asset extends CI_Controller {
|
||||
// ================= GET DATA =================
|
||||
public function get_data(){
|
||||
$data = $this->db
|
||||
->select('assets.*, lokasi_asset.nama as nama_lokasi')
|
||||
->select('assets.*, lokasi_asset.nama as nama_lokasi, item_barcodes.barcode as barcode, item_barcodes.id as barcode_id')
|
||||
->from('assets')
|
||||
->join('lokasi_asset', 'lokasi_asset.id = assets.lokasi_asset_id', 'left')
|
||||
->order_by('assets.id','DESC')
|
||||
->join('item_barcodes', 'item_barcodes.id = assets.barcode_id', 'left')
|
||||
->order_by('assets.tanggal_perolehan','DESC')
|
||||
->get()
|
||||
->result();
|
||||
|
||||
@@ -33,8 +38,9 @@ class Asset extends CI_Controller {
|
||||
$no++,
|
||||
$d->tanggal_perolehan,
|
||||
$d->kode_asset ?? '-',
|
||||
$d->nama_asset,
|
||||
$d->nama_asset . ($d->barcode_id ? '<br><small class="text-muted"><i>'.$d->barcode.'</i></small>' : ''),
|
||||
$d->nama_lokasi ?? '-',
|
||||
$d->keterangan ?? '-',
|
||||
number_format($d->nilai_perolehan),
|
||||
$d->masa_manfaat . ' bln',
|
||||
number_format($d->penyusutan_per_bulan),
|
||||
@@ -101,11 +107,12 @@ class Asset extends CI_Controller {
|
||||
'nilai_residu' => $residu,
|
||||
'penyusutan_per_bulan' => $penyusutan,
|
||||
'nilai_buku' => $nilai_perolehan,
|
||||
'sumber' => 'manual',
|
||||
'sumber' => 'pembelian',
|
||||
'account_debit_id' => $account_debit_id,
|
||||
'account_kredit_id' => $account_kredit_id,
|
||||
'lokasi_asset_id' => $lokasi_asset_id,
|
||||
'keterangan' => $keterangan
|
||||
'keterangan' => $keterangan,
|
||||
'qty' => 1
|
||||
];
|
||||
|
||||
$this->db->insert('assets',$data);
|
||||
@@ -142,11 +149,18 @@ class Asset extends CI_Controller {
|
||||
'asset_id' => $asset_id,
|
||||
'tipe' => 'perolehan',
|
||||
'nilai' => $nilai_perolehan,
|
||||
'keterangan' => 'Perolehan manual: '.$keterangan
|
||||
'keterangan' => 'Perolehan manual: '.$keterangan,
|
||||
'tanggal_dibuat' => $tanggal
|
||||
]);
|
||||
|
||||
$this->db->trans_complete();
|
||||
|
||||
if ($this->db->trans_status()) {
|
||||
log_activity('Asset', 'create', 'Menambah asset manual: '.$nama_asset.' (ID: '.$asset_id.')');
|
||||
echo json_encode(['status'=>true]);
|
||||
} else {
|
||||
echo json_encode(['status'=>false]);
|
||||
}
|
||||
}
|
||||
|
||||
// ================= SAVE FROM STOCK =================
|
||||
@@ -157,6 +171,12 @@ class Asset extends CI_Controller {
|
||||
$masa = (int)$this->input->post('s_masa');
|
||||
$residu = preg_replace('/\D/','',$this->input->post('s_residu'));
|
||||
$tanggal = $this->input->post('s_tanggal');
|
||||
$keterangan = $this->input->post('s_keterangan');
|
||||
$barcode_id = $this->input->post('barcode_id');
|
||||
$qty = (int)$this->input->post('qty');
|
||||
if($qty < 1){
|
||||
$qty = 1;
|
||||
}
|
||||
|
||||
$item = $this->db->get_where('items',['id'=>$item_id])->row();
|
||||
|
||||
@@ -165,21 +185,27 @@ class Asset extends CI_Controller {
|
||||
return;
|
||||
}
|
||||
|
||||
if($item->stok < 1){
|
||||
if(!$barcode_id){
|
||||
echo json_encode(['status'=>false,'message'=>'Barcode wajib dipilih']);
|
||||
return;
|
||||
}
|
||||
|
||||
if($item->stok < $qty){
|
||||
echo json_encode(['status'=>false,'message'=>'Stok tidak cukup']);
|
||||
return;
|
||||
}
|
||||
|
||||
$harga = $item->harga_beli;
|
||||
$nama_asset = $item->nama_barang;
|
||||
$nilai_perolehan = $harga;
|
||||
$penyusutan = ($harga - $residu) / ($masa ?: 1);
|
||||
$nilai_perolehan = $harga * $qty;
|
||||
$penyusutan = ($nilai_perolehan - $residu) / ($masa ?: 1);
|
||||
|
||||
$this->db->trans_start();
|
||||
|
||||
// INSERT ASSET
|
||||
$this->db->insert('assets',[
|
||||
'item_id' => $item_id,
|
||||
'barcode_id' => $barcode_id,
|
||||
'kode_asset' => 'AST-'.time(),
|
||||
'nama_asset' => $nama_asset,
|
||||
'harga_per_unit' => $harga,
|
||||
@@ -192,11 +218,44 @@ class Asset extends CI_Controller {
|
||||
'sumber' => 'gudang',
|
||||
'account_debit_id' => $account_debit_id,
|
||||
'account_kredit_id' => 21, // Persediaan
|
||||
'lokasi_asset_id' => $lokasi_asset_id
|
||||
'lokasi_asset_id' => $lokasi_asset_id,
|
||||
'keterangan' => $keterangan,
|
||||
'qty' => $qty
|
||||
]);
|
||||
|
||||
$asset_id = $this->db->insert_id();
|
||||
|
||||
// START - Item Movement & item barcodes update status
|
||||
$this->db->where('item_id', $item_id);
|
||||
$this->db->order_by('created_at', 'DESC');
|
||||
$this->db->limit(1);
|
||||
$item_move = $this->db->get('item_movements')->row();
|
||||
|
||||
// get item_barcode
|
||||
$itemBar = $this->db->get_where('item_barcodes', ['id'=>$barcode_id])->row();
|
||||
|
||||
if($itemBar->qty_sisa < $qty) {
|
||||
echo json_encode(['status'=>false, 'message'=>'Stok tidak cukup. Sisa: '.$itemBar->qty_sisa]);
|
||||
$this->db->trans_rollback();
|
||||
return;
|
||||
}
|
||||
|
||||
$total_sisa = $itemBar->qty_sisa - $qty;
|
||||
$status_bar = ($total_sisa == 0) ? 'installed' : 'available';
|
||||
|
||||
$this->db->insert('item_movements',[
|
||||
'item_id' => $item_id,
|
||||
'barcode_id' => $barcode_id,
|
||||
'qty' => $qty,
|
||||
'from_type' => $item_move ? $item_move->to_type : 'warehouse', //enum('supplier','warehouse','technician','customer','invoice','asset')
|
||||
'to_type' => 'asset', //enum('supplier','warehouse','technician','customer','invoice','asset')
|
||||
'movement_type' => 'installation', //enum('purchase','transfer','installation','uninstallation','supplier_return','customer_return','adjustment','sold_out','asset')
|
||||
'notes' => $keterangan,
|
||||
'created_at' => date('Y-m-d H:i:s')
|
||||
]);
|
||||
// Status yang boleh masuk : enum('available','reserved','installed','damaged','lost','returned_supplier','sold_out','asset')
|
||||
$this->db->where('id', $barcode_id)->update('item_barcodes', ['status' => $status_bar, 'qty_sisa' => $total_sisa]);
|
||||
|
||||
// Journal
|
||||
$this->db->insert('journals',[
|
||||
'tanggal' => $tanggal,
|
||||
@@ -225,9 +284,9 @@ class Asset extends CI_Controller {
|
||||
'kredit' => $nilai_perolehan
|
||||
]);
|
||||
|
||||
// UPDATE STOK ITEM (kurangi 1)
|
||||
// UPDATE STOK ITEM (kurangi sesuai qty)
|
||||
$this->db->where('id', $item_id)
|
||||
->set('stok', 'stok-1', FALSE)
|
||||
->set('stok', 'stok-'.$qty, FALSE)
|
||||
->update('items');
|
||||
|
||||
// MUTATION DARI GUDANG
|
||||
@@ -235,12 +294,18 @@ class Asset extends CI_Controller {
|
||||
'asset_id' => $asset_id,
|
||||
'tipe' => 'perolehan',
|
||||
'nilai' => $nilai_perolehan,
|
||||
'keterangan' => 'Perolehan dari gudang: '.$nama_asset
|
||||
'keterangan' => 'Perolehan dari gudang: '.$nama_asset,
|
||||
'tanggal_dibuat' => $tanggal
|
||||
]);
|
||||
|
||||
$this->db->trans_complete();
|
||||
|
||||
if ($this->db->trans_status()) {
|
||||
log_activity('Asset', 'create', 'Menambah asset dari gudang: '.$nama_asset.' (ID: '.$asset_id.', Qty: '.$qty.')');
|
||||
echo json_encode(['status'=>true]);
|
||||
} else {
|
||||
echo json_encode(['status'=>false]);
|
||||
}
|
||||
}
|
||||
|
||||
// ================= UPDATE =================
|
||||
@@ -270,7 +335,13 @@ class Asset extends CI_Controller {
|
||||
]);
|
||||
|
||||
$this->db->trans_complete();
|
||||
|
||||
if ($this->db->trans_status()) {
|
||||
log_activity('Asset', 'update', 'Update asset ID: '.$id.' Nama: '.$nama_asset);
|
||||
echo json_encode(['status'=>true]);
|
||||
} else {
|
||||
echo json_encode(['status'=>false]);
|
||||
}
|
||||
}
|
||||
|
||||
// ================= DELETE =================
|
||||
@@ -302,7 +373,13 @@ class Asset extends CI_Controller {
|
||||
]);
|
||||
|
||||
$this->db->trans_complete();
|
||||
|
||||
if ($this->db->trans_status()) {
|
||||
log_activity('Asset', 'delete', 'Hapus asset ID: '.$id.' Alasan: '.$alasan);
|
||||
echo json_encode(['status'=>true]);
|
||||
} else {
|
||||
echo json_encode(['status'=>false]);
|
||||
}
|
||||
}
|
||||
|
||||
// ================= GET DATA SELECT =================
|
||||
@@ -326,11 +403,49 @@ class Asset extends CI_Controller {
|
||||
echo json_encode($data);
|
||||
}
|
||||
|
||||
public function get_items(){
|
||||
$data = $this->db
|
||||
->where('stok > 0')
|
||||
->get('items')
|
||||
->result();
|
||||
public function get_items()
|
||||
{
|
||||
// $data = $this->db
|
||||
// ->select('items.*, kode_barang.*')
|
||||
// ->from('items')
|
||||
// ->join('kode_barang', 'items.kode_id = kode_barang.id', 'left')
|
||||
// ->where('items.stok >', 0)
|
||||
// ->get()
|
||||
// ->result();
|
||||
|
||||
$this->db->select('
|
||||
items.id,
|
||||
items.nama_barang,
|
||||
items.harga_jual,
|
||||
items.harga_beli,
|
||||
items.kode_detail,
|
||||
kode_barang.tracking_type,
|
||||
COALESCE(SUM(
|
||||
stock_logs.qty * IF(stock_logs.tipe="masuk",1,-1)
|
||||
),0) as stok
|
||||
');
|
||||
|
||||
$this->db->where('status', 'active');
|
||||
$this->db->from('items');
|
||||
|
||||
$this->db->join('stock_logs',
|
||||
'stock_logs.item_id = items.id' ,
|
||||
'left');
|
||||
|
||||
$this->db->join('kode_barang',
|
||||
'items.kode_id = kode_barang.id' ,
|
||||
'left');
|
||||
|
||||
// $this->db->where('items.account_id', $account_id);
|
||||
|
||||
$this->db->group_by('items.id');
|
||||
|
||||
// ✅ INI KUNCINYA
|
||||
$this->db->having('stok >', 0);
|
||||
|
||||
|
||||
$data = $this->db->get()->result();
|
||||
|
||||
echo json_encode($data);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
defined('BASEPATH') OR exit('No direct script access allowed');
|
||||
|
||||
class Attendancemonthly extends CI_Controller {
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->load->database();
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
$data = [
|
||||
"active_menu" => "attendance_monthly"
|
||||
];
|
||||
|
||||
$this->load->view('partials/header', $data);
|
||||
$this->load->view('employees/attendance_monthly');
|
||||
$this->load->view('partials/footer');
|
||||
}
|
||||
|
||||
public function get_data()
|
||||
{
|
||||
$date = $this->input->get('date') . '-01';
|
||||
|
||||
if(!$date){
|
||||
$date = date('Y-m-d');
|
||||
}
|
||||
|
||||
$data = $this->db->query("
|
||||
SELECT
|
||||
a.*,
|
||||
e.employee_code,
|
||||
e.full_name
|
||||
FROM k_attendance_monthly a
|
||||
LEFT JOIN k_employees e ON e.id = a.employee_id
|
||||
WHERE a.month = '$date'
|
||||
ORDER BY a.updated_at DESC
|
||||
")->result();
|
||||
|
||||
$rows = [];
|
||||
$no = 1;
|
||||
|
||||
foreach($data as $row){
|
||||
|
||||
$rows[] = [
|
||||
$no++,
|
||||
$row->employee_code,
|
||||
$row->full_name,
|
||||
tanggal_indo($row->month,'F Y'),
|
||||
$row->status,
|
||||
'
|
||||
<button class="btn btn-sm btn-info btn-detail"
|
||||
data-id="'.$row->id.'" data-month="'.$row->month.'">Detail</button>
|
||||
'
|
||||
];
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'data' => $rows
|
||||
]);
|
||||
}
|
||||
|
||||
// public function detail($id)
|
||||
// {
|
||||
// echo json_encode(
|
||||
// $this->db->get_where('k_attendance_monthly',['id'=>$id])->row()
|
||||
// );
|
||||
// }
|
||||
|
||||
public function detail($id, $month)
|
||||
{
|
||||
$header = $this->db
|
||||
->select('a.*, e.full_name, e.employee_code')
|
||||
->from('k_attendance_monthly a')
|
||||
->join('k_employees e','e.id = a.employee_id', 'left')
|
||||
->where('a.id',$id)
|
||||
->where('a.month',$month)
|
||||
->get()
|
||||
->row();
|
||||
|
||||
$startDate = $month;
|
||||
$endDate = substr($month, 0, 7) . date('-t');
|
||||
|
||||
$detail = $this->db
|
||||
->select('a.attendance_date, a.attendance_status, b.shift_name')
|
||||
->from('k_attendances a')
|
||||
->join('k_shifts b', 'b.id = a.shift_id', 'left')
|
||||
->where('a.employee_id', $header->employee_id)
|
||||
->where('a.attendance_date >=', $startDate)
|
||||
->where('a.attendance_date <=', $endDate)
|
||||
->get()
|
||||
->result();
|
||||
|
||||
echo json_encode([
|
||||
'header'=>$header,
|
||||
'detail'=>$detail
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -9,9 +9,9 @@ class Cronjob extends CI_Controller {
|
||||
$this->load->database();
|
||||
|
||||
// ONLY CLI ACCESS
|
||||
if (!$this->input->is_cli_request()) {
|
||||
exit('No direct access allowed');
|
||||
}
|
||||
// if (!$this->input->is_cli_request()) {
|
||||
// exit('No direct access allowed');
|
||||
// }
|
||||
}
|
||||
|
||||
public function generate_insight()
|
||||
@@ -137,4 +137,536 @@ class Cronjob extends CI_Controller {
|
||||
'period' => $period
|
||||
]);
|
||||
}
|
||||
|
||||
// =====================================================
|
||||
// CRONJOB UNTUK LIHAT KEHADIRAN SELAMA 1 BULAN
|
||||
// =====================================================
|
||||
|
||||
// | Kriteria | Pengurangan |
|
||||
// | ---------------------- | ------------------: |
|
||||
// | Alpha | -20 poin / hari |
|
||||
// | Terlambat | -2 poin / kejadian |
|
||||
// | Izin (permit) | -1 poin / hari |
|
||||
// | Akumulasi telat | -1 poin / 60 menit |
|
||||
// | Akumulasi pulang cepat | -1 poin / 120 menit |
|
||||
|
||||
public function generateMonthlyAttendance()
|
||||
{
|
||||
$month = date('Y-m-01');
|
||||
|
||||
$startDate = date('Y-m-01', strtotime($month));
|
||||
$endDate = date('Y-m-t', strtotime($month));
|
||||
|
||||
$employees = $this->db
|
||||
->where('is_active', 1)
|
||||
->get('k_employees')
|
||||
->result();
|
||||
|
||||
$processed = 0;
|
||||
|
||||
$this->db->trans_begin();
|
||||
|
||||
try {
|
||||
|
||||
foreach ($employees as $employee) {
|
||||
|
||||
// Total Hari Kerja
|
||||
$schedule = $this->db->query("
|
||||
SELECT
|
||||
COUNT(*) AS total_hari_kerja
|
||||
FROM k_employee_shift_schedules
|
||||
WHERE employee_id = ?
|
||||
AND status IN ('published','done')
|
||||
AND schedule_date BETWEEN ? AND ?
|
||||
", array(
|
||||
$employee->id,
|
||||
$startDate,
|
||||
$endDate
|
||||
))->row();
|
||||
|
||||
$total_hari_kerja = (int) $schedule->total_hari_kerja;
|
||||
|
||||
// Rekap Absensi
|
||||
$attendance = $this->db->query("
|
||||
SELECT
|
||||
|
||||
COALESCE(SUM(attendance_status='present'),0) AS total_present,
|
||||
COALESCE(SUM(attendance_status='late'),0) AS total_late,
|
||||
COALESCE(SUM(attendance_status='alpha'),0) AS total_alpha,
|
||||
COALESCE(SUM(attendance_status='sick'),0) AS total_sick,
|
||||
COALESCE(SUM(attendance_status='permit'),0) AS total_permit,
|
||||
COALESCE(SUM(attendance_status='annual'),0) AS total_annual,
|
||||
|
||||
COALESCE(SUM(late_minutes),0) AS total_late_minutes,
|
||||
COALESCE(SUM(early_leave_minutes),0) AS total_early_leave_minutes,
|
||||
|
||||
COALESCE(SUM(work_hours),0) AS total_work_hours,
|
||||
COALESCE(SUM(overtime_hours),0) AS total_overtime_hours
|
||||
|
||||
FROM k_attendances
|
||||
WHERE employee_id = ?
|
||||
AND attendance_date BETWEEN ? AND ?
|
||||
", array(
|
||||
$employee->id,
|
||||
$startDate,
|
||||
$endDate
|
||||
))->row();
|
||||
|
||||
$total_present = (int) $attendance->total_present;
|
||||
$total_late = (int) $attendance->total_late;
|
||||
$total_alpha = (int) $attendance->total_alpha;
|
||||
$total_sick = (int) $attendance->total_sick;
|
||||
$total_permit = (int) $attendance->total_permit;
|
||||
$total_annual = (int) $attendance->total_annual;
|
||||
|
||||
// Kehadiran
|
||||
$total_hadir = $total_present + $total_late;
|
||||
|
||||
$attendance_percentage = 0;
|
||||
|
||||
if ($total_hari_kerja > 0) {
|
||||
$attendance_percentage = round(
|
||||
($total_hadir / $total_hari_kerja) * 100,
|
||||
2
|
||||
);
|
||||
}
|
||||
|
||||
// SCOR
|
||||
$score = 100;
|
||||
|
||||
// Alpha
|
||||
$score -= ($total_alpha * 20);
|
||||
|
||||
// Telat
|
||||
$score -= ($total_late * 2);
|
||||
|
||||
// Izin
|
||||
$score -= ($total_permit * 1);
|
||||
|
||||
// Total menit telat
|
||||
$score -= floor(
|
||||
((int) $attendance->total_late_minutes) / 60
|
||||
);
|
||||
|
||||
// Total menit pulang cepat
|
||||
$score -= floor(
|
||||
((int) $attendance->total_early_leave_minutes) / 120
|
||||
);
|
||||
|
||||
if ($score < 0) {
|
||||
$score = 0;
|
||||
}
|
||||
|
||||
// LOGIKA STATUS
|
||||
if ($score >= 90) {
|
||||
$status = 'baik sekali';
|
||||
} elseif ($score >= 75) {
|
||||
$status = 'baik';
|
||||
} elseif ($score >= 60) {
|
||||
$status = 'kurang baik';
|
||||
} else {
|
||||
$status = 'buruk';
|
||||
}
|
||||
|
||||
// INSERT OR UPDATE TO k_attendance_monthly
|
||||
$sql = "
|
||||
INSERT INTO k_attendance_monthly (
|
||||
employee_id, month,
|
||||
total_present, total_late, total_alpha, total_sick, total_permit, total_annual,
|
||||
total_late_minutes, total_early_leave_minutes,
|
||||
total_work_hours, total_overtime_hours,
|
||||
total_hari_kerja, total_hadir, attendance_percentage,
|
||||
score, status
|
||||
)
|
||||
VALUES (
|
||||
?, ?,
|
||||
?, ?, ?, ?, ?, ?,
|
||||
?, ?,
|
||||
?, ?,
|
||||
?, ?, ?,
|
||||
?, ?
|
||||
)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
total_present = VALUES(total_present),
|
||||
total_late = VALUES(total_late),
|
||||
total_alpha = VALUES(total_alpha),
|
||||
total_sick = VALUES(total_sick),
|
||||
total_permit = VALUES(total_permit),
|
||||
total_annual = VALUES(total_annual),
|
||||
total_late_minutes = VALUES(total_late_minutes),
|
||||
total_early_leave_minutes = VALUES(total_early_leave_minutes),
|
||||
total_work_hours = VALUES(total_work_hours),
|
||||
total_overtime_hours = VALUES(total_overtime_hours),
|
||||
total_hari_kerja = VALUES(total_hari_kerja),
|
||||
total_hadir = VALUES(total_hadir),
|
||||
attendance_percentage = VALUES(attendance_percentage),
|
||||
score = VALUES(score),
|
||||
status = VALUES(status),
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
";
|
||||
|
||||
$this->db->query($sql, array(
|
||||
$employee->id,
|
||||
$month,
|
||||
$total_present,
|
||||
$total_late,
|
||||
$total_alpha,
|
||||
$total_sick,
|
||||
$total_permit,
|
||||
$total_annual,
|
||||
(int)$attendance->total_late_minutes,
|
||||
(int)$attendance->total_early_leave_minutes,
|
||||
(float)$attendance->total_work_hours,
|
||||
(float)$attendance->total_overtime_hours,
|
||||
$total_hari_kerja,
|
||||
$total_hadir,
|
||||
$attendance_percentage,
|
||||
$score,
|
||||
$status
|
||||
));
|
||||
|
||||
$processed++;
|
||||
}
|
||||
|
||||
if ($this->db->trans_status() === FALSE) {
|
||||
throw new Exception('Transaction failed');
|
||||
}
|
||||
|
||||
$this->db->trans_commit();
|
||||
|
||||
echo json_encode(array(
|
||||
'status' => true,
|
||||
'month' => $month,
|
||||
'processed' => $processed,
|
||||
'message' => 'Monthly attendance generated successfully'
|
||||
));
|
||||
|
||||
} catch (Exception $e) {
|
||||
|
||||
$this->db->trans_rollback();
|
||||
|
||||
echo json_encode(array(
|
||||
'status' => false,
|
||||
'month' => $month,
|
||||
'message' => $e->getMessage()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// =====================================================
|
||||
// CRONJOB AUTO PENYUSUTAN ASSET BULANAN
|
||||
// =====================================================
|
||||
public function depreciation_asset()
|
||||
{
|
||||
$period = date('Y-m');
|
||||
$runDate = date('Y-m-d');
|
||||
$akun_beban_penyusutan = 54;
|
||||
|
||||
$assets = $this->db
|
||||
->select('id, nama_asset, nilai_perolehan, nilai_buku, nilai_residu, penyusutan_per_bulan, masa_manfaat, tanggal_perolehan, akumulasi_penyusutan, account_debit_id')
|
||||
->from('assets')
|
||||
->where('status', 'aktif')
|
||||
->where('nilai_buku > nilai_residu', null, false)
|
||||
->where('penyusutan_per_bulan >', 0)
|
||||
->get()
|
||||
->result();
|
||||
|
||||
$processed = 0;
|
||||
$success = 0;
|
||||
$skipped = 0;
|
||||
$errors = 0;
|
||||
$details = [];
|
||||
|
||||
$this->db->trans_begin();
|
||||
|
||||
try {
|
||||
foreach ($assets as $a) {
|
||||
$processed++;
|
||||
|
||||
$assetDetails = [
|
||||
'asset_id' => (int)$a->id,
|
||||
'asset_name' => $a->nama_asset,
|
||||
'processed_months' => [],
|
||||
'skipped_months' => []
|
||||
];
|
||||
|
||||
$account_parent = $this->db
|
||||
->select('id')
|
||||
->where('parent_id', $a->account_debit_id)
|
||||
->get('accounts')
|
||||
->row();
|
||||
|
||||
if (!$account_parent || empty($account_parent->id)) {
|
||||
$skipped++;
|
||||
$assetDetails['status'] = 'skip';
|
||||
$assetDetails['reason'] = 'account_parent_not_found';
|
||||
$details[] = $assetDetails;
|
||||
continue;
|
||||
}
|
||||
|
||||
$akun_akumulasi_penyusutan = (int)$account_parent->id;
|
||||
|
||||
$mutasiPatokan = $this->db
|
||||
->select('tanggal_dibuat')
|
||||
->from('asset_mutations')
|
||||
->where('asset_id', $a->id)
|
||||
->where_in('tipe', ['perolehan', 'penambahan'])
|
||||
->order_by('tanggal_dibuat', 'DESC')
|
||||
->order_by('id', 'DESC')
|
||||
->limit(1)
|
||||
->get()
|
||||
->row();
|
||||
|
||||
$tanggalPatokan = null;
|
||||
if ($mutasiPatokan && !empty($mutasiPatokan->tanggal_dibuat)) {
|
||||
$tanggalPatokan = $mutasiPatokan->tanggal_dibuat;
|
||||
}
|
||||
|
||||
if (empty($tanggalPatokan)) {
|
||||
$tanggalPatokan = $a->tanggal_perolehan;
|
||||
}
|
||||
|
||||
if (empty($tanggalPatokan)) {
|
||||
$skipped++;
|
||||
$assetDetails['status'] = 'skip';
|
||||
$assetDetails['reason'] = 'tanggal_patokan_tidak_ditemukan';
|
||||
$details[] = $assetDetails;
|
||||
continue;
|
||||
}
|
||||
|
||||
$startMonth = date('Y-m', strtotime(date('Y-m-01', strtotime($tanggalPatokan)) . ' +1 month'));
|
||||
$currentMonth = $period;
|
||||
|
||||
if ($startMonth > $currentMonth) {
|
||||
$skipped++;
|
||||
$assetDetails['status'] = 'skip';
|
||||
$assetDetails['reason'] = 'start_next_month_after_acquisition';
|
||||
$assetDetails['tanggal_patokan'] = $tanggalPatokan;
|
||||
$details[] = $assetDetails;
|
||||
continue;
|
||||
}
|
||||
|
||||
$nilaiPerolehan = (float)$a->nilai_perolehan;
|
||||
$nilaiResidu = (float)$a->nilai_residu;
|
||||
$susutPerBulan = (float)$a->penyusutan_per_bulan;
|
||||
$masaManfaat = (int)$a->masa_manfaat;
|
||||
|
||||
$akumulasiMutasi = $this->db
|
||||
->select('COALESCE(SUM(nilai),0) total', false)
|
||||
->from('asset_mutations')
|
||||
->where('asset_id', $a->id)
|
||||
->where('tipe', 'penyusutan')
|
||||
->get()
|
||||
->row();
|
||||
|
||||
$akumulasiBerjalan = (float)($akumulasiMutasi ? $akumulasiMutasi->total : 0);
|
||||
$nilaiBukuBerjalan = $nilaiPerolehan - $akumulasiBerjalan;
|
||||
|
||||
if ($nilaiBukuBerjalan < $nilaiResidu) {
|
||||
$nilaiBukuBerjalan = $nilaiResidu;
|
||||
$akumulasiBerjalan = $nilaiPerolehan - $nilaiBukuBerjalan;
|
||||
}
|
||||
|
||||
$iterMonth = $startMonth;
|
||||
$assetHasSuccess = false;
|
||||
|
||||
while ($iterMonth <= $currentMonth) {
|
||||
$monthsUsed = 0;
|
||||
$start = new DateTime(date('Y-m-01', strtotime($tanggalPatokan)));
|
||||
$end = new DateTime(date('Y-m-01', strtotime($iterMonth . '-01')));
|
||||
$diff = $start->diff($end);
|
||||
$monthsUsed = ($diff->y * 12) + $diff->m;
|
||||
|
||||
if ($monthsUsed < 0) {
|
||||
$skipped++;
|
||||
$assetDetails['skipped_months'][] = [
|
||||
'period' => $iterMonth,
|
||||
'reason' => 'tanggal_patokan_after_period'
|
||||
];
|
||||
$iterMonth = date('Y-m', strtotime($iterMonth . '-01 +1 month'));
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($masaManfaat > 0 && $monthsUsed >= $masaManfaat) {
|
||||
$skipped++;
|
||||
$assetDetails['skipped_months'][] = [
|
||||
'period' => $iterMonth,
|
||||
'reason' => 'masa_manfaat_finished'
|
||||
];
|
||||
break;
|
||||
}
|
||||
|
||||
$acqDay = (int)date('d', strtotime($tanggalPatokan));
|
||||
$lastDayOfMonth = (int)date('t', strtotime($iterMonth . '-01'));
|
||||
$dueDay = min($acqDay, $lastDayOfMonth);
|
||||
$dueDateThisMonth = $iterMonth . '-' . str_pad($dueDay, 2, '0', STR_PAD_LEFT);
|
||||
|
||||
// Untuk bulan berjalan, tetap hormati due date.
|
||||
// Untuk bulan yang sudah lewat, langsung boleh diproses (catch-up).
|
||||
if ($iterMonth === $currentMonth && strtotime($runDate) < strtotime($dueDateThisMonth)) {
|
||||
$skipped++;
|
||||
$assetDetails['skipped_months'][] = [
|
||||
'period' => $iterMonth,
|
||||
'reason' => 'not_due_date_today',
|
||||
'due_date' => $dueDateThisMonth,
|
||||
'run_date' => $runDate
|
||||
];
|
||||
$iterMonth = date('Y-m', strtotime($iterMonth . '-01 +1 month'));
|
||||
continue;
|
||||
}
|
||||
|
||||
$startDate = $iterMonth . '-01';
|
||||
$endDate = date('Y-m-d', strtotime($startDate . ' +1 month'));
|
||||
|
||||
$already = $this->db
|
||||
->where('asset_id', $a->id)
|
||||
->where('tipe', 'penyusutan')
|
||||
->where('tanggal_dibuat >=', $startDate)
|
||||
->where('tanggal_dibuat <', $endDate)
|
||||
->count_all_results('asset_mutations');
|
||||
|
||||
if ($already > 0) {
|
||||
$skipped++;
|
||||
$assetDetails['skipped_months'][] = [
|
||||
'period' => $iterMonth,
|
||||
'reason' => 'already_depreciated_this_month'
|
||||
];
|
||||
$iterMonth = date('Y-m', strtotime($iterMonth . '-01 +1 month'));
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($nilaiBukuBerjalan <= $nilaiResidu) {
|
||||
$skipped++;
|
||||
$assetDetails['skipped_months'][] = [
|
||||
'period' => $iterMonth,
|
||||
'reason' => 'nothing_to_depreciate'
|
||||
];
|
||||
break;
|
||||
}
|
||||
|
||||
$maxSusut = max(0, $nilaiBukuBerjalan - $nilaiResidu);
|
||||
$susut = min($susutPerBulan, $maxSusut);
|
||||
|
||||
if ($susut <= 0) {
|
||||
$skipped++;
|
||||
$assetDetails['skipped_months'][] = [
|
||||
'period' => $iterMonth,
|
||||
'reason' => 'nothing_to_depreciate'
|
||||
];
|
||||
break;
|
||||
}
|
||||
|
||||
$akumulasiBefore = $akumulasiBerjalan;
|
||||
$nilaiBukuBefore = $nilaiBukuBerjalan;
|
||||
|
||||
$akumulasiBerjalan += $susut;
|
||||
$nilaiBukuBerjalan = $nilaiPerolehan - $akumulasiBerjalan;
|
||||
|
||||
if ($nilaiBukuBerjalan < $nilaiResidu) {
|
||||
$nilaiBukuBerjalan = $nilaiResidu;
|
||||
$akumulasiBerjalan = $nilaiPerolehan - $nilaiBukuBerjalan;
|
||||
}
|
||||
|
||||
$this->db->insert('asset_mutations', [
|
||||
'asset_id' => $a->id,
|
||||
'tipe' => 'penyusutan',
|
||||
'nilai' => $susut,
|
||||
'keterangan' => 'Penyusutan otomatis periode ' . $iterMonth . ' (cronjob)',
|
||||
'tanggal_dibuat' => $dueDateThisMonth
|
||||
]);
|
||||
|
||||
$no_ref = 'DEP-' . date('YmdHis') . '-' . $a->id . '-' . str_replace('-', '', $iterMonth);
|
||||
$this->db->insert('journals', [
|
||||
'tanggal' => $dueDateThisMonth,
|
||||
'no_ref' => $no_ref,
|
||||
'keterangan' => 'Penyusutan Asset ' . $a->nama_asset . ' periode ' . $iterMonth,
|
||||
'ref_type' => 'assets',
|
||||
'ref_id' => $a->id,
|
||||
'created_by' => 1
|
||||
]);
|
||||
|
||||
$jid = $this->db->insert_id();
|
||||
|
||||
$this->db->insert('journal_details', [
|
||||
'journal_id' => $jid,
|
||||
'account_id' => $akun_beban_penyusutan,
|
||||
'debit' => $susut,
|
||||
'kredit' => 0
|
||||
]);
|
||||
|
||||
$this->db->insert('journal_details', [
|
||||
'journal_id' => $jid,
|
||||
'account_id' => $akun_akumulasi_penyusutan,
|
||||
'debit' => 0,
|
||||
'kredit' => $susut
|
||||
]);
|
||||
|
||||
$success++;
|
||||
$assetHasSuccess = true;
|
||||
|
||||
$assetDetails['processed_months'][] = [
|
||||
'period' => $iterMonth,
|
||||
'depreciation' => (float)$susut,
|
||||
'due_date' => $dueDateThisMonth,
|
||||
'akumulasi_before' => (float)$akumulasiBefore,
|
||||
'akumulasi_after' => (float)$akumulasiBerjalan,
|
||||
'nilai_buku_before' => (float)$nilaiBukuBefore,
|
||||
'nilai_buku_after' => (float)$nilaiBukuBerjalan,
|
||||
'journal_no_ref' => $no_ref
|
||||
];
|
||||
|
||||
$iterMonth = date('Y-m', strtotime($iterMonth . '-01 +1 month'));
|
||||
}
|
||||
|
||||
if ($assetHasSuccess) {
|
||||
$this->db->where('id', $a->id)->update('assets', [
|
||||
'akumulasi_penyusutan' => $akumulasiBerjalan,
|
||||
'nilai_buku' => $nilaiBukuBerjalan
|
||||
]);
|
||||
|
||||
$assetDetails['status'] = 'success';
|
||||
$assetDetails['akumulasi_final'] = (float)$akumulasiBerjalan;
|
||||
$assetDetails['nilai_buku_final'] = (float)$nilaiBukuBerjalan;
|
||||
} else {
|
||||
$assetDetails['status'] = 'skip';
|
||||
if (empty($assetDetails['reason'])) {
|
||||
$assetDetails['reason'] = 'no_eligible_month_to_process';
|
||||
}
|
||||
}
|
||||
|
||||
$details[] = $assetDetails;
|
||||
}
|
||||
|
||||
if ($this->db->trans_status() === FALSE) {
|
||||
throw new Exception('Transaction failed');
|
||||
}
|
||||
|
||||
$this->db->trans_commit();
|
||||
|
||||
echo json_encode([
|
||||
'status' => true,
|
||||
'period' => $period,
|
||||
'processed' => $processed,
|
||||
'success' => $success,
|
||||
'skipped' => $skipped,
|
||||
'errors' => $errors,
|
||||
'message' => 'Depreciation cron executed',
|
||||
'details' => $details
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
$this->db->trans_rollback();
|
||||
$errors++;
|
||||
|
||||
echo json_encode([
|
||||
'status' => false,
|
||||
'period' => $period,
|
||||
'processed' => $processed,
|
||||
'success' => $success,
|
||||
'skipped' => $skipped,
|
||||
'errors' => $errors,
|
||||
'message' => $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -135,6 +135,8 @@ class Customers extends CI_Controller {
|
||||
// =========================
|
||||
public function delete($id)
|
||||
{
|
||||
$customer = $this->dm->setTable('customers')->get_by_id($id);
|
||||
|
||||
$this->dm->setTable('customers')->delete($id);
|
||||
|
||||
// ================= LOG ACTIVITY =================
|
||||
|
||||
@@ -10,6 +10,10 @@ class Employees extends CI_Controller {
|
||||
$this->load->database();
|
||||
$this->load->helper(['url', 'file']);
|
||||
|
||||
if (!$this->session->userdata('logged_in')) {
|
||||
redirect('auth');
|
||||
}
|
||||
|
||||
date_default_timezone_set('Asia/Jakarta');
|
||||
}
|
||||
|
||||
@@ -192,6 +196,14 @@ class Employees extends CI_Controller {
|
||||
$data['photo'] = $photo;
|
||||
|
||||
$this->db->insert('k_employees',$data);
|
||||
$employee_id = $this->db->insert_id();
|
||||
|
||||
log_activity(
|
||||
'employees',
|
||||
'create',
|
||||
'Menambah karyawan: '.$data['full_name'].' (ID: '.$employee_id.')',
|
||||
'success'
|
||||
);
|
||||
|
||||
return $this->json([
|
||||
'status' => true,
|
||||
@@ -247,6 +259,13 @@ class Employees extends CI_Controller {
|
||||
->where('id',$id)
|
||||
->update('k_employees',$data);
|
||||
|
||||
log_activity(
|
||||
'employees',
|
||||
'update',
|
||||
'Update karyawan ID: '.$id.' Nama: '.($data['full_name'] ?? '-'),
|
||||
'success'
|
||||
);
|
||||
|
||||
return $this->json([
|
||||
'status' => true,
|
||||
'message' => 'Berhasil update data'
|
||||
@@ -292,6 +311,13 @@ class Employees extends CI_Controller {
|
||||
'id'=>$id
|
||||
]);
|
||||
|
||||
log_activity(
|
||||
'employees',
|
||||
'delete',
|
||||
'Hapus karyawan ID: '.$id.' Nama: '.($employee->full_name ?? '-'),
|
||||
'success'
|
||||
);
|
||||
|
||||
return $this->json([
|
||||
'status' => true,
|
||||
'message' => 'Berhasil hapus data'
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
<?php
|
||||
defined('BASEPATH') OR exit('No direct script access allowed');
|
||||
|
||||
class Generatebuku extends CI_Controller
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->load->model('DynamicModel', 'dm');
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// PDF GENERATE — BUKU BESAR
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
public function generatepdf($account_id, $start, $end)
|
||||
{
|
||||
if ($this->session->userdata('role') != 'Admin') {
|
||||
show_error('Akses ditolak', 403);
|
||||
}
|
||||
|
||||
if (ob_get_length()) {
|
||||
ob_end_clean();
|
||||
}
|
||||
|
||||
// =============================================================
|
||||
// AMBIL INFO AKUN
|
||||
// =============================================================
|
||||
$akun = null;
|
||||
if (!empty($account_id)) {
|
||||
$akun = $this->db->get_where('accounts', ['id' => (int) $account_id])->row();
|
||||
}
|
||||
|
||||
// =============================================================
|
||||
// QUERY DATA
|
||||
// =============================================================
|
||||
$this->db->select('
|
||||
journals.tanggal,
|
||||
journals.no_ref,
|
||||
journals.keterangan,
|
||||
journal_details.debit,
|
||||
journal_details.kredit,
|
||||
accounts.tipe
|
||||
');
|
||||
$this->db->from('journal_details');
|
||||
$this->db->join('journals', 'journals.id = journal_details.journal_id');
|
||||
$this->db->join('accounts', 'accounts.id = journal_details.account_id');
|
||||
|
||||
if (!empty($account_id)) {
|
||||
$this->db->where('journal_details.account_id', (int) $account_id);
|
||||
}
|
||||
|
||||
if (!empty($start) && !empty($end)) {
|
||||
$this->db->where('journals.tanggal >=', $start);
|
||||
$this->db->where('journals.tanggal <=', $end);
|
||||
}
|
||||
|
||||
$this->db->group_start();
|
||||
$this->db->where('journal_details.debit >', 0);
|
||||
$this->db->or_where('journal_details.kredit >', 0);
|
||||
$this->db->group_end();
|
||||
|
||||
$this->db->order_by('journals.tanggal', 'ASC');
|
||||
|
||||
$rows = $this->db->get()->result();
|
||||
|
||||
// =============================================================
|
||||
// HITUNG SALDO BERJALAN
|
||||
// =============================================================
|
||||
$saldo = 0;
|
||||
$result = [];
|
||||
|
||||
foreach ($rows as $r) {
|
||||
if (in_array($r->tipe, ['asset', 'expense'])) {
|
||||
$saldo += ($r->debit - $r->kredit);
|
||||
} else {
|
||||
$saldo += ($r->kredit - $r->debit);
|
||||
}
|
||||
|
||||
$result[] = [
|
||||
'tanggal' => $r->tanggal,
|
||||
'no_ref' => $r->no_ref,
|
||||
'keterangan' => $r->keterangan,
|
||||
'debit' => $r->debit,
|
||||
'kredit' => $r->kredit,
|
||||
'saldo' => $saldo,
|
||||
];
|
||||
}
|
||||
|
||||
$total_debit = array_sum(array_column($result, 'debit'));
|
||||
$total_kredit = array_sum(array_column($result, 'kredit'));
|
||||
|
||||
// =============================================================
|
||||
// DATA PERUSAHAAN
|
||||
// =============================================================
|
||||
$company = $this->db->get('pengaturan')->row();
|
||||
|
||||
// =============================================================
|
||||
// BUILD STRING INFO LAPORAN
|
||||
// =============================================================
|
||||
$namaAkun = $akun
|
||||
? $akun->kode_akun . ' - ' . $akun->nama_akun
|
||||
: 'Semua Akun';
|
||||
|
||||
$periodeStr = (!empty($start) && !empty($end))
|
||||
? 'Periode : ' . date('d-m-Y', strtotime($start)) . ' s/d ' . date('d-m-Y', strtotime($end))
|
||||
: 'Periode : Semua Tanggal';
|
||||
|
||||
// =============================================================
|
||||
// LOAD LIBRARY PDF
|
||||
// =============================================================
|
||||
require_once(APPPATH . 'third_party/fpdf/fpdf.php');
|
||||
require_once(APPPATH . 'libraries/PDF_BukuBesar.php');
|
||||
|
||||
$pdf = new PDF ('P', 'mm', 'A4');
|
||||
$pdf->SetMargins(10, 10, 10);
|
||||
|
||||
$pdf->setHeaderData(
|
||||
$company->nama_perusahaan,
|
||||
$company->alamat_perusahaan,
|
||||
FCPATH . 'uploads/img/logo-ljn.png'
|
||||
);
|
||||
|
||||
$pdf->setInfoLaporan($namaAkun, $periodeStr);
|
||||
|
||||
$pdf->AddPage('P', 'A4');
|
||||
$pdf->BukuBesarTable($result);
|
||||
$pdf->BukuBesarTotal($total_debit, $total_kredit, $saldo);
|
||||
$pdf->Pembuat('Tito Ngudiana', 'Finance Department');
|
||||
|
||||
// =============================================================
|
||||
// OUTPUT
|
||||
// =============================================================
|
||||
$filename = 'Buku_Besar_' . date('YmdHis') . '.pdf';
|
||||
$pdf->Output('D', $filename);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
defined('BASEPATH') OR exit('No direct script access allowed');
|
||||
|
||||
class Generateitems extends CI_Controller {
|
||||
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->load->model('DynamicModel', 'dm');
|
||||
}
|
||||
|
||||
|
||||
public function generatepdf()
|
||||
{
|
||||
if(ob_get_length()){
|
||||
ob_end_clean();
|
||||
}
|
||||
|
||||
$this->db->select('
|
||||
items.id,
|
||||
items.nama_barang,
|
||||
items.kode_detail,
|
||||
items.harga_beli,
|
||||
items.harga_jual,
|
||||
items.created_at,
|
||||
items.tanggal_pembelian,
|
||||
items.stok,
|
||||
(
|
||||
SELECT GROUP_CONCAT(DISTINCT w.nama SEPARATOR ", ")
|
||||
FROM stock_logs sl
|
||||
LEFT JOIN warehouses w ON sl.warehouse_id = w.id
|
||||
WHERE sl.item_id = items.id
|
||||
) as gudang
|
||||
');
|
||||
|
||||
$this->db->where('items.status','active');
|
||||
$this->db->where('items.stok >', 0); // <-- stok 0 agar tidak muncul
|
||||
|
||||
$this->db->order_by('(items.stok = 0)','ASC',false);
|
||||
$this->db->order_by('items.tanggal_pembelian','ASC');
|
||||
$this->db->order_by('items.kode_detail','ASC');
|
||||
|
||||
$data = $this->db->get('items')->result();
|
||||
|
||||
|
||||
$company = $this->db->get('pengaturan')->row_array();
|
||||
|
||||
|
||||
require_once(APPPATH . 'third_party/fpdf/fpdf.php');
|
||||
require_once(APPPATH. 'libraries/PDF_Items.php');
|
||||
|
||||
$pdf = new PDF();
|
||||
|
||||
$pdf->setHeaderData(
|
||||
$company['nama_perusahaan'],
|
||||
$company['alamat_perusahaan'],
|
||||
$company['telepon_perusahaan'],
|
||||
FCPATH.'uploads/img/logo-ljn.png'
|
||||
);
|
||||
|
||||
$pdf->AddPage('P', 'A4');
|
||||
$pdf->ItemsTable($data);
|
||||
$pdf->TotalBarang(count($data));
|
||||
// $pdf->Pembuat($company['nama_bendahara']);
|
||||
$pdf->Output('I','Data_Barang_'.date('YmdHis').'.pdf');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
<?php
|
||||
defined('BASEPATH') OR exit('No direct script access allowed');
|
||||
|
||||
class Generatelabarugi extends CI_Controller {
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->load->model('DynamicModel', 'dm');
|
||||
|
||||
if (!$this->session->userdata('logged_in')) {
|
||||
redirect('auth');
|
||||
}
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
if($this->session->userdata('role') != 'Admin'){
|
||||
show_error('Akses ditolak', 403);
|
||||
}
|
||||
|
||||
$data = ["active_menu" => "laba_rugi"];
|
||||
$this->load->view('partials/header', $data);
|
||||
$this->load->view('laporan/laba_rugi');
|
||||
$this->load->view('partials/footer');
|
||||
}
|
||||
|
||||
// =========================
|
||||
// AMBIL DATA LABA RUGI (DIPAKAI BERSAMA OLEH get_data() & generatepdf())
|
||||
// =========================
|
||||
private function get_labarugi_data($tanggal_dari = null, $tanggal_sampai = null)
|
||||
{
|
||||
$this->db->select('
|
||||
a.id,
|
||||
a.kode_akun,
|
||||
a.nama_akun,
|
||||
a.tipe,
|
||||
COALESCE(SUM(jd.debit),0) as debit,
|
||||
COALESCE(SUM(jd.kredit),0) as kredit
|
||||
');
|
||||
$this->db->from('accounts a');
|
||||
|
||||
// ================= JOIN
|
||||
$this->db->join('journal_details jd','a.id = jd.account_id','LEFT');
|
||||
$this->db->join('journals j','j.id = jd.journal_id','LEFT');
|
||||
|
||||
// ================= FILTER AKUN
|
||||
$this->db->where('a.kategori','laba_rugi');
|
||||
$this->db->where('a.is_active',1);
|
||||
|
||||
// ================= FILTER TANGGAL (AMAN)
|
||||
if($tanggal_dari){
|
||||
$this->db->where('(j.tanggal >= "'.$tanggal_dari.'" OR j.tanggal IS NULL)');
|
||||
}
|
||||
|
||||
if($tanggal_sampai){
|
||||
$this->db->where('(j.tanggal <= "'.$tanggal_sampai.'" OR j.tanggal IS NULL)');
|
||||
}
|
||||
|
||||
$this->db->group_by('a.id');
|
||||
$this->db->order_by('a.kode_akun','ASC');
|
||||
|
||||
$rows = $this->db->get()->result();
|
||||
|
||||
$pendapatan = [];
|
||||
$beban = [];
|
||||
|
||||
$revenue = 0;
|
||||
$expense = 0;
|
||||
|
||||
foreach ($rows as $r) {
|
||||
|
||||
if ($r->tipe == 'revenue') {
|
||||
$saldo = $r->kredit - $r->debit;
|
||||
$revenue += $saldo;
|
||||
|
||||
$pendapatan[] = [
|
||||
'kode' => $r->kode_akun,
|
||||
'nama' => $r->nama_akun,
|
||||
'saldo' => $saldo
|
||||
];
|
||||
}
|
||||
|
||||
if ($r->tipe == 'expense') {
|
||||
$saldo = $r->debit - $r->kredit;
|
||||
$expense += $saldo;
|
||||
|
||||
$beban[] = [
|
||||
'kode' => $r->kode_akun,
|
||||
'nama' => $r->nama_akun,
|
||||
'saldo' => $saldo
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'data' => $rows,
|
||||
'pendapatan' => $pendapatan,
|
||||
'beban' => $beban,
|
||||
'revenue' => $revenue,
|
||||
'expense' => $expense,
|
||||
'total_pendapatan'=> $revenue,
|
||||
'total_beban' => $expense,
|
||||
'laba' => $revenue - $expense
|
||||
];
|
||||
}
|
||||
|
||||
// =========================
|
||||
// GET DATA LABA RUGI (JSON, untuk AJAX/datatable)
|
||||
// =========================
|
||||
public function get_data()
|
||||
{
|
||||
$tanggal_dari = $this->input->get('tanggal_dari');
|
||||
$tanggal_sampai = $this->input->get('tanggal_sampai');
|
||||
|
||||
$result = $this->get_labarugi_data($tanggal_dari, $tanggal_sampai);
|
||||
|
||||
echo json_encode([
|
||||
'data' => $result['data'],
|
||||
'revenue' => $result['revenue'],
|
||||
'expense' => $result['expense'],
|
||||
'laba' => $result['laba']
|
||||
]);
|
||||
}
|
||||
|
||||
// =========================
|
||||
// PDF GENERATE — LABA RUGI
|
||||
// =========================
|
||||
public function generatepdf($start = null, $end = null)
|
||||
{
|
||||
if ($this->session->userdata('role') != 'Admin') {
|
||||
show_error('Akses ditolak', 403);
|
||||
}
|
||||
|
||||
if (ob_get_length()) {
|
||||
ob_end_clean();
|
||||
}
|
||||
|
||||
// Normalisasi parameter URL (0000-00-00 dianggap kosong)
|
||||
$tanggal_dari = (!empty($start) && $start != '0000-00-00') ? $start : null;
|
||||
$tanggal_sampai = (!empty($end) && $end != '0000-00-00') ? $end : null;
|
||||
|
||||
// =============================================================
|
||||
// AMBIL DATA
|
||||
// =============================================================
|
||||
$labarugi = $this->get_labarugi_data($tanggal_dari, $tanggal_sampai);
|
||||
|
||||
// =============================================================
|
||||
// DATA PERUSAHAAN
|
||||
// =============================================================
|
||||
$company = $this->db->get('pengaturan')->row();
|
||||
|
||||
// =============================================================
|
||||
// BUILD STRING PERIODE
|
||||
// =============================================================
|
||||
$periodeStr = (!empty($tanggal_dari) && !empty($tanggal_sampai))
|
||||
? 'Periode : ' . date('d-m-Y', strtotime($tanggal_dari))
|
||||
. ' s/d ' . date('d-m-Y', strtotime($tanggal_sampai))
|
||||
: ((!empty($tanggal_sampai))
|
||||
? 'Per Tanggal : ' . date('d-m-Y', strtotime($tanggal_sampai))
|
||||
: 'Per Tanggal : ' . date('d-m-Y'));
|
||||
|
||||
// =============================================================
|
||||
// LOAD LIBRARY PDF
|
||||
// =============================================================
|
||||
require_once(APPPATH . 'third_party/fpdf/fpdf.php');
|
||||
require_once(APPPATH . 'libraries/PDF_Labarugi.php');
|
||||
|
||||
$pdf = new PDF ('P', 'mm', 'A4');
|
||||
$pdf->SetMargins(10, 10, 10);
|
||||
|
||||
$pdf->setHeaderData(
|
||||
$company->nama_perusahaan,
|
||||
$company->alamat_perusahaan,
|
||||
FCPATH . 'uploads/img/logo-ljn.png'
|
||||
);
|
||||
|
||||
$pdf->setInfoLaporan($periodeStr);
|
||||
|
||||
$pdf->AddPage('P', 'A4');
|
||||
$pdf->LabaRugiTable(
|
||||
$labarugi['pendapatan'],
|
||||
$labarugi['beban'],
|
||||
$labarugi['total_pendapatan'],
|
||||
$labarugi['total_beban']
|
||||
);
|
||||
$pdf->LabaRugiRingkasan($labarugi['laba']);
|
||||
$pdf->Pembuat('Tito Ngudiana', 'Finance Department');
|
||||
|
||||
// =============================================================
|
||||
// OUTPUT
|
||||
// =============================================================
|
||||
$filename = 'Laba_Rugi_' . date('YmdHis') . '.pdf';
|
||||
$pdf->Output('D', $filename);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
<?php
|
||||
|
||||
defined('BASEPATH') OR exit('No direct script access allowed');
|
||||
|
||||
class Generateneraca extends CI_Controller {
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->load->model('DynamicModel', 'dm');
|
||||
|
||||
if (!$this->session->userdata('logged_in')) {
|
||||
redirect('auth');
|
||||
}
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
if($this->session->userdata('role') != 'Admin'){
|
||||
show_error('Akses ditolak', 403);
|
||||
}
|
||||
|
||||
$data = ["active_menu" => "neraca"];
|
||||
$this->load->view('partials/header', $data);
|
||||
$this->load->view('laporan/neraca');
|
||||
$this->load->view('partials/footer');
|
||||
}
|
||||
|
||||
// =========================
|
||||
// LABA BERJALAN (PAKAI TANGGAL JOURNAL)
|
||||
// =========================
|
||||
private function get_laba_berjalan($tanggal_dari = null, $tanggal_sampai = null)
|
||||
{
|
||||
$this->db->select('
|
||||
accounts.tipe,
|
||||
COALESCE(SUM(jd.debit),0) as debit,
|
||||
COALESCE(SUM(jd.kredit),0) as kredit
|
||||
');
|
||||
$this->db->from('accounts');
|
||||
$this->db->join('journal_details jd','jd.account_id = accounts.id','LEFT');
|
||||
$this->db->join('journals j','j.id = jd.journal_id','LEFT');
|
||||
|
||||
$this->db->where_in('accounts.tipe',['revenue','expense']);
|
||||
|
||||
// ================= FILTER TANGGAL
|
||||
if($tanggal_dari){
|
||||
$this->db->where('(j.tanggal >= "'.$tanggal_dari.'" OR j.tanggal IS NULL)');
|
||||
}
|
||||
|
||||
if($tanggal_sampai){
|
||||
$this->db->where('(j.tanggal <= "'.$tanggal_sampai.'" OR j.tanggal IS NULL)');
|
||||
}
|
||||
|
||||
$rows = $this->db->get()->result();
|
||||
|
||||
$revenue = 0;
|
||||
$expense = 0;
|
||||
|
||||
foreach ($rows as $r) {
|
||||
if ($r->tipe == 'revenue') {
|
||||
$revenue += ($r->kredit - $r->debit);
|
||||
} else {
|
||||
$expense += ($r->debit - $r->kredit);
|
||||
}
|
||||
}
|
||||
|
||||
return $revenue - $expense;
|
||||
}
|
||||
|
||||
// =========================
|
||||
// AMBIL DATA NERACA (DIPAKAI BERSAMA OLEH get_data() & generatepdf())
|
||||
// =========================
|
||||
private function get_neraca_data($tanggal_dari = null, $tanggal_sampai = null)
|
||||
{
|
||||
$this->db->select('
|
||||
a.id,
|
||||
a.kode_akun,
|
||||
a.nama_akun,
|
||||
a.tipe,
|
||||
a.posisi,
|
||||
a.is_kontra,
|
||||
COALESCE(SUM(jd.debit),0) as debit,
|
||||
COALESCE(SUM(jd.kredit),0) as kredit
|
||||
');
|
||||
$this->db->from('accounts a');
|
||||
$this->db->join('journal_details jd','jd.account_id = a.id','LEFT');
|
||||
$this->db->join('journals j','j.id = jd.journal_id','LEFT');
|
||||
|
||||
$this->db->where('a.kategori','neraca');
|
||||
|
||||
// ================= FILTER TANGGAL
|
||||
if($tanggal_dari){
|
||||
$this->db->where('(j.tanggal >= "'.$tanggal_dari.'" OR j.tanggal IS NULL)');
|
||||
}
|
||||
|
||||
if($tanggal_sampai){
|
||||
$this->db->where('(j.tanggal <= "'.$tanggal_sampai.'" OR j.tanggal IS NULL)');
|
||||
}
|
||||
|
||||
$this->db->group_by('a.id');
|
||||
$this->db->order_by('a.kode_akun','ASC');
|
||||
|
||||
$rows = $this->db->get()->result();
|
||||
|
||||
$activa = [];
|
||||
$pasiva = [];
|
||||
|
||||
$total_activa = 0;
|
||||
$total_pasiva = 0;
|
||||
|
||||
// ================= LABA BERJALAN
|
||||
$laba = $this->get_laba_berjalan($tanggal_dari, $tanggal_sampai);
|
||||
|
||||
foreach ($rows as $r) {
|
||||
|
||||
// ================= SALDO NORMAL
|
||||
if ($r->posisi == 'debit') {
|
||||
$saldo = $r->debit - $r->kredit;
|
||||
} else {
|
||||
$saldo = $r->kredit - $r->debit;
|
||||
}
|
||||
|
||||
// ================= KONTRA ACCOUNT
|
||||
if ($r->is_kontra == 1) {
|
||||
$saldo = -$saldo;
|
||||
}
|
||||
|
||||
// ================= LABA BERJALAN
|
||||
if ($r->kode_akun == '302') {
|
||||
$saldo = $laba;
|
||||
}
|
||||
|
||||
// ================= ACTIVA
|
||||
if ($r->tipe == 'asset') {
|
||||
|
||||
$activa[] = [
|
||||
'kode' => $r->kode_akun,
|
||||
'nama' => $r->nama_akun,
|
||||
'saldo' => $saldo
|
||||
];
|
||||
|
||||
$total_activa += $saldo;
|
||||
}
|
||||
|
||||
// ================= PASIVA
|
||||
if (in_array($r->tipe, ['liability','equity'])) {
|
||||
|
||||
$pasiva[] = [
|
||||
'kode' => $r->kode_akun,
|
||||
'nama' => $r->nama_akun,
|
||||
'saldo' => $saldo
|
||||
];
|
||||
|
||||
$total_pasiva += $saldo;
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'activa' => $activa,
|
||||
'pasiva' => $pasiva,
|
||||
'total_activa' => $total_activa,
|
||||
'total_pasiva' => $total_pasiva,
|
||||
'laba' => $laba,
|
||||
'selisih' => $total_activa - $total_pasiva
|
||||
];
|
||||
}
|
||||
|
||||
// =========================
|
||||
// GET DATA NERACA (JSON, untuk AJAX/datatable)
|
||||
// =========================
|
||||
public function get_data()
|
||||
{
|
||||
$tanggal_dari = $this->input->get('tanggal_dari');
|
||||
$tanggal_sampai = $this->input->get('tanggal_sampai');
|
||||
|
||||
$data = $this->get_neraca_data($tanggal_dari, $tanggal_sampai);
|
||||
|
||||
echo json_encode($data);
|
||||
}
|
||||
|
||||
// =========================
|
||||
// PDF GENERATE — NERACA
|
||||
// =========================
|
||||
public function generatepdf($start = null, $end = null)
|
||||
{
|
||||
if ($this->session->userdata('role') != 'Admin') {
|
||||
show_error('Akses ditolak', 403);
|
||||
}
|
||||
|
||||
if (ob_get_length()) {
|
||||
ob_end_clean();
|
||||
}
|
||||
|
||||
// Normalisasi parameter URL (0000-00-00 dianggap kosong)
|
||||
$tanggal_dari = (!empty($start) && $start != '0000-00-00') ? $start : null;
|
||||
$tanggal_sampai = (!empty($end) && $end != '0000-00-00') ? $end : null;
|
||||
|
||||
// =============================================================
|
||||
// AMBIL DATA
|
||||
// =============================================================
|
||||
$neraca = $this->get_neraca_data($tanggal_dari, $tanggal_sampai);
|
||||
|
||||
// =============================================================
|
||||
// DATA PERUSAHAAN
|
||||
// =============================================================
|
||||
$company = $this->db->get('pengaturan')->row();
|
||||
|
||||
// =============================================================
|
||||
// BUILD STRING PERIODE
|
||||
// =============================================================
|
||||
$periodeStr = (!empty($tanggal_dari) && !empty($tanggal_sampai))
|
||||
? 'Per Tanggal : ' . date('d-m-Y', strtotime($tanggal_sampai))
|
||||
. ' (Sejak ' . date('d-m-Y', strtotime($tanggal_dari)) . ')'
|
||||
: ((!empty($tanggal_sampai))
|
||||
? 'Per Tanggal : ' . date('d-m-Y', strtotime($tanggal_sampai))
|
||||
: 'Per Tanggal : ' . date('d-m-Y'));
|
||||
|
||||
// =============================================================
|
||||
// LOAD LIBRARY PDF
|
||||
// =============================================================
|
||||
require_once(APPPATH . 'third_party/fpdf/fpdf.php');
|
||||
require_once(APPPATH . 'libraries/PDF_Neraca.php');
|
||||
|
||||
$pdf = new PDF ('P', 'mm', 'A4');
|
||||
$pdf->SetMargins(10, 10, 10);
|
||||
|
||||
$pdf->setHeaderData(
|
||||
$company->nama_perusahaan,
|
||||
$company->alamat_perusahaan,
|
||||
FCPATH . 'uploads/img/logo-ljn.png'
|
||||
);
|
||||
|
||||
$pdf->setInfoLaporan($periodeStr);
|
||||
|
||||
$pdf->AddPage('P', 'A4');
|
||||
$pdf->NeracaTable(
|
||||
$neraca['activa'],
|
||||
$neraca['pasiva'],
|
||||
$neraca['total_activa'],
|
||||
$neraca['total_pasiva']
|
||||
);
|
||||
$pdf->NeracaRingkasan($neraca['laba'], $neraca['selisih']);
|
||||
$pdf->Pembuat('Tito Ngudiana', 'Finance Department');
|
||||
|
||||
// =============================================================
|
||||
// OUTPUT
|
||||
// =============================================================
|
||||
$filename = 'Neraca_' . date('YmdHis') . '.pdf';
|
||||
$pdf->Output('D', $filename);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
<?php
|
||||
defined('BASEPATH') OR exit('No direct script access allowed');
|
||||
|
||||
class Generateneracasaldo extends CI_Controller {
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->load->model('DynamicModel', 'dm');
|
||||
|
||||
if (!$this->session->userdata('logged_in')) {
|
||||
redirect('auth');
|
||||
}
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
|
||||
if($this->session->userdata('role') != 'Admin'){
|
||||
show_error('Akses ditolak', 403);
|
||||
}
|
||||
|
||||
$data = ["active_menu" => "neraca_saldo"];
|
||||
$this->load->view('partials/header', $data);
|
||||
$this->load->view('laporan/neraca_saldo');
|
||||
$this->load->view('partials/footer');
|
||||
}
|
||||
|
||||
// =========================
|
||||
// AMBIL DATA NERACA SALDO
|
||||
// DIPAKAI OLEH AJAX & PDF
|
||||
// =========================
|
||||
private function get_neracasaldo_data()
|
||||
{
|
||||
$tanggal_awal = $this->input->get('tanggal_awal');
|
||||
$tanggal_akhir = $this->input->get('tanggal_akhir');
|
||||
|
||||
$this->db->select("
|
||||
accounts.id,
|
||||
accounts.kode_akun,
|
||||
accounts.nama_akun,
|
||||
accounts.kategori,
|
||||
accounts.tipe,
|
||||
COALESCE(SUM(journal_details.debit),0) AS debit,
|
||||
COALESCE(SUM(journal_details.kredit),0) AS kredit
|
||||
");
|
||||
|
||||
$this->db->from('accounts');
|
||||
|
||||
// ======================================
|
||||
// FILTER PERIODE
|
||||
// ======================================
|
||||
if (!empty($tanggal_awal) && !empty($tanggal_akhir)) {
|
||||
|
||||
$this->db->join(
|
||||
"(SELECT jd.*
|
||||
FROM journal_details jd
|
||||
JOIN journals j ON j.id = jd.journal_id
|
||||
WHERE j.tanggal BETWEEN ".$this->db->escape($tanggal_awal)."
|
||||
AND ".$this->db->escape($tanggal_akhir)."
|
||||
) journal_details",
|
||||
"journal_details.account_id = accounts.id",
|
||||
"left",
|
||||
false
|
||||
);
|
||||
|
||||
} else {
|
||||
|
||||
$this->db->join(
|
||||
'journal_details',
|
||||
'journal_details.account_id = accounts.id',
|
||||
'left'
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
$rows = $this->db
|
||||
->where('accounts.is_active',1)
|
||||
->group_by('accounts.id')
|
||||
->order_by('accounts.kode_akun','ASC')
|
||||
->get()
|
||||
->result();
|
||||
|
||||
$total_debit = 0;
|
||||
$total_kredit = 0;
|
||||
|
||||
foreach($rows as $row){
|
||||
|
||||
$row->debit = (float)$row->debit;
|
||||
$row->kredit = (float)$row->kredit;
|
||||
|
||||
$total_debit += $row->debit;
|
||||
$total_kredit += $row->kredit;
|
||||
}
|
||||
|
||||
return [
|
||||
'tanggal_awal' => $tanggal_awal,
|
||||
'tanggal_akhir' => $tanggal_akhir,
|
||||
'rows' => $rows,
|
||||
'total_debit' => $total_debit,
|
||||
'total_kredit' => $total_kredit
|
||||
];
|
||||
}
|
||||
|
||||
public function get_data()
|
||||
{
|
||||
$result = $this->get_neracasaldo_data();
|
||||
|
||||
echo json_encode($result['rows']);
|
||||
}
|
||||
|
||||
public function generatepdf()
|
||||
{
|
||||
if ($this->session->userdata('role') != 'Admin') {
|
||||
show_error('Akses ditolak',403);
|
||||
}
|
||||
|
||||
if (ob_get_length()) {
|
||||
ob_end_clean();
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// DATA
|
||||
// ==========================================
|
||||
$neracasaldo = $this->get_neracasaldo_data();
|
||||
|
||||
// ==========================================
|
||||
// PERUSAHAAN
|
||||
// ==========================================
|
||||
$company = $this->db->get('pengaturan')->row();
|
||||
|
||||
// ==========================================
|
||||
// PERIODE
|
||||
// ==========================================
|
||||
if(!empty($neracasaldo['tanggal_awal']) && !empty($neracasaldo['tanggal_akhir'])){
|
||||
|
||||
$periodeStr = 'Periode : '
|
||||
. date('d-m-Y',strtotime($neracasaldo['tanggal_awal']))
|
||||
.' s/d '
|
||||
. date('d-m-Y',strtotime($neracasaldo['tanggal_akhir']));
|
||||
|
||||
}else{
|
||||
|
||||
$periodeStr = 'Seluruh Periode';
|
||||
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// PDF
|
||||
// ==========================================
|
||||
require_once(APPPATH.'third_party/fpdf/fpdf.php');
|
||||
require_once(APPPATH.'libraries/PDF_Neracasaldo.php');
|
||||
|
||||
$pdf = new PDF('P','mm','A4');
|
||||
$pdf->SetMargins(10,10,10);
|
||||
|
||||
$pdf->setHeaderData(
|
||||
$company->nama_perusahaan,
|
||||
$company->alamat_perusahaan,
|
||||
FCPATH.'uploads/img/logo-ljn.png'
|
||||
);
|
||||
|
||||
$pdf->setInfoLaporan($periodeStr);
|
||||
|
||||
$pdf->AddPage();
|
||||
|
||||
$pdf->NeracaSaldoTable(
|
||||
$neracasaldo['rows'],
|
||||
$neracasaldo['total_debit'],
|
||||
$neracasaldo['total_kredit']
|
||||
);
|
||||
|
||||
$pdf->NeracaSaldoRingkasan(
|
||||
$neracasaldo['total_debit'],
|
||||
$neracasaldo['total_kredit']
|
||||
);
|
||||
|
||||
$pdf->Pembuat(
|
||||
$this->session->userdata('nama') ?: 'Administrator',
|
||||
'Finance Department'
|
||||
);
|
||||
|
||||
$filename = 'Neraca_Saldo_'.date('YmdHis').'.pdf';
|
||||
|
||||
$pdf->Output('I',$filename);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -25,7 +25,7 @@ class Kodebarang extends CI_Controller {
|
||||
// =========================
|
||||
public function get_data()
|
||||
{
|
||||
$data = $this->db->get('kode_barang')->result();
|
||||
$data = $this->db->order_by('id', 'DESC')->get('kode_barang')->result();
|
||||
|
||||
$result = [];
|
||||
$no = 1;
|
||||
@@ -35,7 +35,10 @@ class Kodebarang extends CI_Controller {
|
||||
$no++,
|
||||
$row->kode_barang,
|
||||
$row->nama,
|
||||
$row->limit_stock,
|
||||
(int)$row->limit_stock,
|
||||
strtoupper($row->tracking_type ?? 'UNIT'),
|
||||
strtolower($row->need_serial_number ?? 'false') === 'true' ? 'Ya' : 'Tidak',
|
||||
$row->unit ?? 'PCS',
|
||||
'
|
||||
<button class="btn btn-sm btn-secondary btn-editkode" data-id="'.$row->id.'">Edit</button>
|
||||
<button class="btn btn-sm btn-danger btn-delete" data-id="'.$row->id.'">Delete</button>
|
||||
@@ -61,29 +64,57 @@ class Kodebarang extends CI_Controller {
|
||||
// =========================
|
||||
public function save()
|
||||
{
|
||||
$nama = $this->input->post('nama');
|
||||
$kode = $this->input->post('kode');
|
||||
$limit_stock = $this->input->post('limit_stock');
|
||||
$nama = trim($this->input->post('nama'));
|
||||
$kode = trim($this->input->post('kode'));
|
||||
$limit_stock = (int)$this->input->post('limit_stock');
|
||||
$tracking_type = strtoupper(trim($this->input->post('tracking_type')));
|
||||
$need_serial_number = strtolower(trim($this->input->post('need_serial_number')));
|
||||
$unit = trim($this->input->post('unit'));
|
||||
|
||||
if(!$nama){
|
||||
echo json_encode(['status'=>false,'message'=>'Nama wajib diisi']);
|
||||
if(!$nama || !$kode){
|
||||
echo json_encode(['status'=>false,'message'=>'Kode dan nama wajib diisi']);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!in_array($tracking_type, ['UNIT','QTY'])){
|
||||
echo json_encode(['status'=>false,'message'=>'Tracking type tidak valid']);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!in_array($need_serial_number, ['true','false'])){
|
||||
echo json_encode(['status'=>false,'message'=>'Need serial number tidak valid']);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!in_array($unit, ['PCS','Meter','Lembar'])){
|
||||
echo json_encode(['status'=>false,'message'=>'Unit tidak valid']);
|
||||
return;
|
||||
}
|
||||
|
||||
$exists = $this->db->get_where('kode_barang', ['kode_barang' => $kode])->num_rows();
|
||||
if($exists > 0){
|
||||
echo json_encode(['status'=>false,'message'=>'Kode barang sudah digunakan']);
|
||||
return;
|
||||
}
|
||||
|
||||
$this->db->insert('kode_barang', [
|
||||
'kode_barang' => $kode,
|
||||
'nama' => $nama,
|
||||
'limit_stock' => $limit_stock
|
||||
'created_at' => date('Y-m-d H:i:s'),
|
||||
'limit_stock' => $limit_stock,
|
||||
'tracking_type' => $tracking_type,
|
||||
'need_serial_number' => $need_serial_number,
|
||||
'unit' => $unit
|
||||
]);
|
||||
|
||||
log_activity(
|
||||
'kode_barang',
|
||||
'create',
|
||||
'Menambahkan kode barang: ' . $nama,
|
||||
'Menambahkan kode barang: ' . $kode . ' - ' . $nama,
|
||||
'success'
|
||||
);
|
||||
|
||||
echo json_encode(['status'=>true,'message'=>'Gudang berhasil ditambahkan']);
|
||||
echo json_encode(['status'=>true,'message'=>'Kode barang berhasil ditambahkan']);
|
||||
}
|
||||
|
||||
// =========================
|
||||
@@ -91,30 +122,62 @@ class Kodebarang extends CI_Controller {
|
||||
// =========================
|
||||
public function update()
|
||||
{
|
||||
$id = $this->input->post('id');
|
||||
$kode = $this->input->post('kode');
|
||||
$nama = $this->input->post('nama');
|
||||
$limit_stock = $this->input->post('limit_stock');
|
||||
$id = (int)$this->input->post('id');
|
||||
$kode = trim($this->input->post('kode'));
|
||||
$nama = trim($this->input->post('nama'));
|
||||
$limit_stock = (int)$this->input->post('limit_stock');
|
||||
$tracking_type = strtoupper(trim($this->input->post('tracking_type')));
|
||||
$need_serial_number = strtolower(trim($this->input->post('need_serial_number')));
|
||||
$unit = trim($this->input->post('unit'));
|
||||
|
||||
if(!$nama){
|
||||
echo json_encode(['status'=>false,'message'=>'Nama wajib diisi']);
|
||||
if(!$id || !$nama || !$kode){
|
||||
echo json_encode(['status'=>false,'message'=>'ID, kode dan nama wajib diisi']);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!in_array($tracking_type, ['UNIT','QTY'])){
|
||||
echo json_encode(['status'=>false,'message'=>'Tracking type tidak valid']);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!in_array($need_serial_number, ['true','false'])){
|
||||
echo json_encode(['status'=>false,'message'=>'Need serial number tidak valid']);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!in_array($unit, ['PCS','Meter','Lembar'])){
|
||||
echo json_encode(['status'=>false,'message'=>'Unit tidak valid']);
|
||||
return;
|
||||
}
|
||||
|
||||
$exists = $this->db
|
||||
->where('kode_barang', $kode)
|
||||
->where('id !=', $id)
|
||||
->get('kode_barang')
|
||||
->num_rows();
|
||||
|
||||
if($exists > 0){
|
||||
echo json_encode(['status'=>false,'message'=>'Kode barang sudah digunakan']);
|
||||
return;
|
||||
}
|
||||
|
||||
$this->db->where('id',$id)->update('kode_barang', [
|
||||
'kode_barang' => $kode,
|
||||
'nama' => $nama,
|
||||
'limit_stock' => $limit_stock
|
||||
'limit_stock' => $limit_stock,
|
||||
'tracking_type' => $tracking_type,
|
||||
'need_serial_number' => $need_serial_number,
|
||||
'unit' => $unit
|
||||
]);
|
||||
|
||||
log_activity(
|
||||
'kode_barang',
|
||||
'update',
|
||||
'Update kode barang ID: ' . $id . ' nama: ' . $nama,
|
||||
'Update kode barang ID: ' . $id . ' (' . $kode . ' - ' . $nama . ')',
|
||||
'success'
|
||||
);
|
||||
|
||||
echo json_encode(['status'=>true,'message'=>'Gudang berhasil diupdate']);
|
||||
echo json_encode(['status'=>true,'message'=>'Kode barang berhasil diupdate']);
|
||||
}
|
||||
|
||||
// =========================
|
||||
@@ -122,13 +185,23 @@ class Kodebarang extends CI_Controller {
|
||||
// =========================
|
||||
public function delete($id)
|
||||
{
|
||||
// optional: cek relasi ke items
|
||||
$cek = $this->db->get_where('items',['warehouse_id'=>$id])->num_rows();
|
||||
$row = $this->db->get_where('kode_barang', ['id'=>$id])->row();
|
||||
|
||||
if(!$row){
|
||||
echo json_encode([
|
||||
'status'=>false,
|
||||
'message'=>'Data kode barang tidak ditemukan'
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
// cek relasi ke items (mengacu ke kode_id)
|
||||
$cek = $this->db->get_where('items',['kode_id'=>$id])->num_rows();
|
||||
|
||||
if($cek > 0){
|
||||
echo json_encode([
|
||||
'status'=>false,
|
||||
'message'=>'Gudang tidak bisa dihapus karena masih dipakai item'
|
||||
'message'=>'Kode barang tidak bisa dihapus karena masih dipakai item'
|
||||
]);
|
||||
return;
|
||||
}
|
||||
@@ -138,11 +211,11 @@ class Kodebarang extends CI_Controller {
|
||||
log_activity(
|
||||
'kode_barang',
|
||||
'delete',
|
||||
'Hapus gudang ID: ' . $id,
|
||||
'Hapus kode barang ID: ' . $id . ' (' . $row->kode_barang . ' - ' . $row->nama . ')',
|
||||
'success'
|
||||
);
|
||||
|
||||
echo json_encode(['status'=>true,'message'=>'Gudang berhasil dihapus']);
|
||||
echo json_encode(['status'=>true,'message'=>'Kode barang berhasil dihapus']);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -26,86 +26,53 @@ class Neracasaldo extends CI_Controller {
|
||||
$this->load->view('partials/footer');
|
||||
}
|
||||
|
||||
// =========================
|
||||
// HITUNG LABA BERJALAN (REALTIME)
|
||||
// =========================
|
||||
private function get_laba_berjalan()
|
||||
{
|
||||
$this->db->select('
|
||||
accounts.tipe,
|
||||
COALESCE(SUM(journal_details.debit),0) as debit,
|
||||
COALESCE(SUM(journal_details.kredit),0) as kredit
|
||||
');
|
||||
$this->db->from('accounts');
|
||||
$this->db->join('journal_details','journal_details.account_id = accounts.id','LEFT');
|
||||
$this->db->where_in('accounts.tipe',['revenue','expense']);
|
||||
|
||||
$rows = $this->db->get()->result();
|
||||
|
||||
$revenue = 0;
|
||||
$expense = 0;
|
||||
|
||||
foreach ($rows as $r) {
|
||||
if ($r->tipe == 'revenue') {
|
||||
$revenue += ($r->kredit - $r->debit);
|
||||
} else {
|
||||
$expense += ($r->debit - $r->kredit);
|
||||
}
|
||||
}
|
||||
|
||||
return $revenue - $expense;
|
||||
}
|
||||
|
||||
public function get_data()
|
||||
{
|
||||
$this->db->select('
|
||||
$tanggal_awal = $this->input->get('tanggal_awal');
|
||||
$tanggal_akhir = $this->input->get('tanggal_akhir');
|
||||
|
||||
$this->db->select("
|
||||
accounts.id,
|
||||
accounts.kode_akun,
|
||||
accounts.nama_akun,
|
||||
accounts.kategori,
|
||||
accounts.tipe,
|
||||
accounts.posisi,
|
||||
COALESCE(SUM(journal_details.debit),0) as debit,
|
||||
COALESCE(SUM(journal_details.kredit),0) as kredit
|
||||
');
|
||||
COALESCE(SUM(journal_details.debit),0) AS debit,
|
||||
COALESCE(SUM(journal_details.kredit),0) AS kredit
|
||||
");
|
||||
|
||||
$this->db->from('accounts');
|
||||
$this->db->join('journal_details','journal_details.account_id = accounts.id','LEFT');
|
||||
|
||||
// OPTIONAL (best practice)
|
||||
$this->db->where('accounts.is_active',1);
|
||||
$this->db->where('accounts.kategori','neraca');
|
||||
if (!empty($tanggal_awal) && !empty($tanggal_akhir)) {
|
||||
|
||||
$this->db->group_by('accounts.id');
|
||||
$this->db->order_by('accounts.kode_akun','ASC');
|
||||
$this->db->join(
|
||||
"(SELECT jd.*
|
||||
FROM journal_details jd
|
||||
JOIN journals j ON j.id = jd.journal_id
|
||||
WHERE j.tanggal BETWEEN ".$this->db->escape($tanggal_awal)."
|
||||
AND ".$this->db->escape($tanggal_akhir)."
|
||||
) journal_details",
|
||||
"journal_details.account_id = accounts.id",
|
||||
"left",
|
||||
false
|
||||
);
|
||||
|
||||
$rows = $this->db->get()->result();
|
||||
|
||||
// =========================
|
||||
// HITUNG LABA BERJALAN
|
||||
// =========================
|
||||
$laba = $this->get_laba_berjalan();
|
||||
|
||||
foreach ($rows as $r) {
|
||||
|
||||
// ================= SALDO NORMAL
|
||||
if ($r->posisi == 'debit') {
|
||||
$saldo = $r->debit - $r->kredit;
|
||||
} else {
|
||||
$saldo = $r->kredit - $r->debit;
|
||||
|
||||
$this->db->join(
|
||||
'journal_details',
|
||||
'journal_details.account_id = accounts.id',
|
||||
'left'
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
// ================= OVERRIDE LABA BERJALAN
|
||||
if ($r->kode_akun == '302') {
|
||||
$saldo = $laba;
|
||||
}
|
||||
|
||||
// ================= NORMALISASI KE KOLOM
|
||||
if ($saldo >= 0 AND $r->posisi == 'debit') {
|
||||
$r->saldo_debit = $saldo;
|
||||
$r->saldo_kredit = 0;
|
||||
} else {
|
||||
$r->saldo_debit = 0;
|
||||
$r->saldo_kredit = abs($saldo);
|
||||
}
|
||||
}
|
||||
$rows = $this->db
|
||||
->where('accounts.is_active', 1)
|
||||
->group_by('accounts.id')
|
||||
->order_by('accounts.kode_akun', 'ASC')
|
||||
->get()
|
||||
->result();
|
||||
|
||||
echo json_encode($rows);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,518 @@
|
||||
<?php
|
||||
defined('BASEPATH') OR exit('No direct script access allowed');
|
||||
|
||||
class Teknisi extends CI_Controller {
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->load->model('PeralatanTeknisi_model', 'ptm');
|
||||
|
||||
if (!$this->session->userdata('logged_in')) {
|
||||
redirect('auth');
|
||||
}
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
$data = [
|
||||
"active_menu" => "peralatan_teknisi",
|
||||
"teknisi_list" => $this->ptm->get_teknisi_list()
|
||||
];
|
||||
|
||||
$this->load->view('partials/header', $data);
|
||||
$this->load->view('peralatan_teknisi/layout', $data);
|
||||
$this->load->view('partials/footer');
|
||||
}
|
||||
|
||||
public function get_data()
|
||||
{
|
||||
$user_id = $this->input->get('user_id');
|
||||
$status = $this->input->get('status') ?: 'active';
|
||||
|
||||
log_message('debug', '[get_data] user_id: ' . var_export($user_id, true) . ', status: ' . $status);
|
||||
|
||||
$data = $this->ptm->get_barang_dibawa($user_id, $status);
|
||||
|
||||
log_message('debug', '[get_data] query returned ' . count($data) . ' rows');
|
||||
|
||||
$result = [];
|
||||
$no = 1;
|
||||
|
||||
foreach ($data as $row) {
|
||||
$result[] = [
|
||||
$no++,
|
||||
htmlspecialchars($row->barcode, ENT_QUOTES, 'UTF-8'),
|
||||
htmlspecialchars($row->nama_barang, ENT_QUOTES, 'UTF-8'),
|
||||
htmlspecialchars($row->serial_number, ENT_QUOTES, 'UTF-8'),
|
||||
];
|
||||
}
|
||||
|
||||
log_message('debug', '[get_data] response: ' . json_encode(['data' => $result]));
|
||||
echo json_encode(['data' => $result]);
|
||||
}
|
||||
|
||||
public function scan_item()
|
||||
{
|
||||
$barcode = trim($this->input->get('barcode'));
|
||||
|
||||
if (empty($barcode)) {
|
||||
echo json_encode(['status' => false, 'message' => 'QR Code tidak boleh kosong']);
|
||||
return;
|
||||
}
|
||||
|
||||
$item = $this->ptm->get_item_by_barcode($barcode);
|
||||
|
||||
if (!$item) {
|
||||
echo json_encode(['status' => false, 'message' => 'Barcode tidak ditemukan']);
|
||||
return;
|
||||
}
|
||||
|
||||
$teknisi = $this->ptm->get_current_teknisi($item->barcode);
|
||||
|
||||
echo json_encode([
|
||||
'status' => true,
|
||||
'message' => 'Barang ditemukan',
|
||||
'data' => [
|
||||
'barcode' => $item->barcode,
|
||||
'nama_barang' => $item->nama_barang,
|
||||
'serial_number' => $item->serial_number,
|
||||
'barcode_id' => $item->barcode_id,
|
||||
'item_id' => $item->item_id,
|
||||
'barcode_status' => $item->barcode_status,
|
||||
'teknisi_id' => $teknisi ? $teknisi->user_id : null,
|
||||
'teknisi_name' => $teknisi ? $teknisi->full_name : null,
|
||||
'teknisi_code' => $teknisi ? $teknisi->employee_code : null
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
function get_date_by_employee_id()
|
||||
{
|
||||
$user_id = (int)$this->input->get('user_id');
|
||||
if (!$user_id) {
|
||||
echo json_encode(['status' => false, 'message' => 'ID teknisi tidak valid']);
|
||||
return;
|
||||
}
|
||||
|
||||
$dates = $this->ptm->get_dates_by_employee_id($user_id);
|
||||
echo json_encode(['status' => true, 'data' => $dates]);
|
||||
}
|
||||
|
||||
public function batch_kembalikan()
|
||||
{
|
||||
$barcodes = $this->input->post('barcodes');
|
||||
|
||||
if (empty($barcodes) || !is_array($barcodes)) {
|
||||
echo json_encode(['status' => false, 'message' => 'Tidak ada barcode yang dipilih']);
|
||||
return;
|
||||
}
|
||||
|
||||
$result = $this->ptm->batch_kembalikan($barcodes);
|
||||
echo json_encode($result);
|
||||
}
|
||||
|
||||
public function batch_dibawa()
|
||||
{
|
||||
$barcodes = $this->input->post('barcodes');
|
||||
$user_id = (int)$this->input->post('user_id');
|
||||
|
||||
if (empty($barcodes) || !is_array($barcodes)) {
|
||||
echo json_encode(['status' => false, 'message' => 'Tidak ada barcode yang dipilih']);
|
||||
return;
|
||||
}
|
||||
|
||||
if (empty($user_id)) {
|
||||
echo json_encode(['status' => false, 'message' => 'Pilih teknisi terlebih dahulu']);
|
||||
return;
|
||||
}
|
||||
|
||||
$result = $this->ptm->batch_dibawa($barcodes, $user_id);
|
||||
echo json_encode($result);
|
||||
}
|
||||
|
||||
public function get_teknisi_table()
|
||||
{
|
||||
$draw = (int)$this->input->get('draw');
|
||||
$start = (int)$this->input->get('start');
|
||||
$length = (int)$this->input->get('length');
|
||||
$search = $this->input->get('search')['value'] ?? '';
|
||||
$order_col = (int)$this->input->get('order')[0]['column'] ?? 1;
|
||||
$order_dir = $this->input->get('order')[0]['dir'] ?? 'asc';
|
||||
|
||||
$data = $this->ptm->get_teknisi_table_data($start, $length, $search, $order_col, $order_dir);
|
||||
|
||||
$total_filtered = $this->ptm->count_filtered_teknisi($search);
|
||||
$total_all = $this->ptm->count_all_teknisi();
|
||||
|
||||
$result = [];
|
||||
$no = $start + 1;
|
||||
|
||||
foreach ($data as $row) {
|
||||
$item_count = (int)$row->item_count;
|
||||
$item_text = $item_count . ' Item';
|
||||
|
||||
$aksi = '<button class="btn btn-info btn-sm btn-detail-teknisi" data-id="'.$row->id.'" title="Detail">
|
||||
<i class="bi bi-eye"></i>
|
||||
</button>
|
||||
<button class="btn btn-warning btn-sm btn-form-barang-bawaan" data-id="'.$row->id.'" data-nama="'.htmlspecialchars($row->full_name, ENT_QUOTES, 'UTF-8').'" title="Form Barang Bawaan">
|
||||
<i class="bi bi-file-earmark-text"></i>
|
||||
</button>';
|
||||
|
||||
$result[] = [
|
||||
$no++,
|
||||
htmlspecialchars($row->full_name, ENT_QUOTES, 'UTF-8'),
|
||||
htmlspecialchars($row->employee_code, ENT_QUOTES, 'UTF-8'),
|
||||
htmlspecialchars($row->department_name, ENT_QUOTES, 'UTF-8') ?: '-',
|
||||
$item_text,
|
||||
$aksi
|
||||
];
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'draw' => $draw,
|
||||
'recordsTotal' => $total_all,
|
||||
'recordsFiltered' => $total_filtered,
|
||||
'data' => $result
|
||||
]);
|
||||
}
|
||||
|
||||
public function teknisi_detail()
|
||||
{
|
||||
$user_id = (int)$this->input->get('user_id');
|
||||
|
||||
if (!$user_id) {
|
||||
echo json_encode(null);
|
||||
return;
|
||||
}
|
||||
|
||||
$info = $this->ptm->get_teknisi_info($user_id);
|
||||
echo json_encode($info);
|
||||
}
|
||||
|
||||
public function get_teknisi_items()
|
||||
{
|
||||
$user_id = (int)$this->input->get('user_id');
|
||||
$draw = (int)$this->input->get('draw');
|
||||
$start = (int)$this->input->get('start');
|
||||
$length = (int)$this->input->get('length');
|
||||
$search = $this->input->get('search')['value'] ?? '';
|
||||
$order_col = (int)$this->input->get('order')[0]['column'] ?? 2;
|
||||
$order_dir = $this->input->get('order')[0]['dir'] ?? 'asc';
|
||||
|
||||
if (!$user_id) {
|
||||
echo json_encode(['draw' => $draw, 'recordsTotal' => 0, 'recordsFiltered' => 0, 'data' => []]);
|
||||
return;
|
||||
}
|
||||
|
||||
$data = $this->ptm->get_teknisi_items_data($user_id, $start, $length, $search, $order_col, $order_dir);
|
||||
|
||||
$total_filtered = $this->ptm->count_filtered_teknisi_items($user_id, $search);
|
||||
$total_all = $this->ptm->count_all_teknisi_items($user_id);
|
||||
|
||||
$result = [];
|
||||
$no = $start + 1;
|
||||
|
||||
foreach ($data as $row) {
|
||||
$badge = $row->status == 'active'
|
||||
? '<span class="badge bg-success">Dibawa</span>'
|
||||
: '<span class="badge bg-secondary">' . htmlspecialchars($row->status, ENT_QUOTES, 'UTF-8') . '</span>';
|
||||
|
||||
$result[] = [
|
||||
$no++,
|
||||
$row->tanggal ?? date('d/m/Y'),
|
||||
htmlspecialchars($row->barcode, ENT_QUOTES, 'UTF-8'),
|
||||
htmlspecialchars($row->nama_barang, ENT_QUOTES, 'UTF-8'),
|
||||
htmlspecialchars($row->serial_number, ENT_QUOTES, 'UTF-8') ?: '-',
|
||||
htmlspecialchars($row->qty_sisa, ENT_QUOTES, 'UTF-8') ?: '0',
|
||||
$badge
|
||||
];
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'draw' => $draw,
|
||||
'recordsTotal' => $total_all,
|
||||
'recordsFiltered' => $total_filtered,
|
||||
'data' => $result
|
||||
]);
|
||||
}
|
||||
|
||||
public function get_form_barang_bawaan_data()
|
||||
{
|
||||
$user_id = (int)$this->input->get('user_id');
|
||||
|
||||
if (!$user_id) {
|
||||
echo json_encode(['status' => false, 'message' => 'ID teknisi tidak valid']);
|
||||
return;
|
||||
}
|
||||
|
||||
$info = $this->ptm->get_teknisi_info($user_id);
|
||||
if (!$info) {
|
||||
echo json_encode(['status' => false, 'message' => 'Teknisi tidak ditemukan']);
|
||||
return;
|
||||
}
|
||||
|
||||
$tanggal_param = $this->input->get('tanggal');
|
||||
$items = [];
|
||||
if ($tanggal_param) {
|
||||
$items = $this->ptm->get_teknisi_items_full($user_id, $tanggal_param);
|
||||
}
|
||||
|
||||
if ($tanggal_param) {
|
||||
if ($tanggal_param === 'all') {
|
||||
$noFaktur = 'GRJN/K/ALL' . date('/m/Y');
|
||||
$tanggal = date('d F Y');
|
||||
} else {
|
||||
$ts = strtotime($tanggal_param);
|
||||
$noFaktur = 'GRJN/K/' . date('d/m/Y', $ts);
|
||||
$tanggal = date('d F Y', $ts);
|
||||
}
|
||||
} else {
|
||||
$noFaktur = 'GRJN/K/' . date('d/m/Y');
|
||||
$tanggal = date('d F Y');
|
||||
}
|
||||
|
||||
$itemsData = [];
|
||||
$no = 1;
|
||||
foreach ($items as $item) {
|
||||
$itemsData[] = [
|
||||
'no' => $no++,
|
||||
'tanggal' => $item->tanggal ?? date('d/m/Y'),
|
||||
'barcode' => $item->barcode ?? '-',
|
||||
'nama_barang' => $item->nama_barang ?? '-',
|
||||
'serial_number' => $item->serial_number ?? '-',
|
||||
'jumlah' => isset($item->qty_sisa) ? (int)$item->qty_sisa : 1,
|
||||
'keterangan' => ''
|
||||
];
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'status' => true,
|
||||
'data' => [
|
||||
'no_faktur' => $noFaktur,
|
||||
'tanggal' => $tanggal,
|
||||
'teknisi' => $info->full_name,
|
||||
'teknisi_id' => $info->id,
|
||||
'employee_code' => $info->employee_code,
|
||||
'position_name' => $info->position_name,
|
||||
'department_name' => $info->department_name,
|
||||
'total_item' => count($itemsData),
|
||||
'items' => $itemsData
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
public function generate_pdf_barang_bawaan($user_id = null)
|
||||
{
|
||||
|
||||
$user_id = (int)$user_id;
|
||||
if (!$user_id) {
|
||||
show_error('ID teknisi tidak valid', 400);
|
||||
}
|
||||
|
||||
if (ob_get_length()) {
|
||||
ob_end_clean();
|
||||
}
|
||||
|
||||
$info = $this->ptm->get_teknisi_info($user_id);
|
||||
if (!$info) {
|
||||
show_error('Teknisi tidak ditemukan', 404);
|
||||
}
|
||||
|
||||
$tanggal_param = $this->input->get('tanggal');
|
||||
|
||||
if ($tanggal_param === 'all') {
|
||||
$items = $this->ptm->get_teknisi_items_full($user_id);
|
||||
$noFaktur = 'GRJN/K/ALL' . date('/m/Y');
|
||||
$tanggal = 'Semua Tanggal';
|
||||
} elseif ($tanggal_param) {
|
||||
$ts = strtotime($tanggal_param);
|
||||
$items = $this->ptm->get_teknisi_items_full($user_id, $tanggal_param);
|
||||
$noFaktur = 'GRJN/K/' . date('d/m/Y', $ts);
|
||||
$tanggal = date('d/m/Y', $ts);
|
||||
} else {
|
||||
$items = $this->ptm->get_teknisi_items_full($user_id);
|
||||
$noFaktur = 'GRJN/K/' . date('d/m/Y');
|
||||
$tanggal = date('d/m/Y');
|
||||
}
|
||||
|
||||
require_once(APPPATH . 'third_party/fpdf/fpdf.php');
|
||||
require_once(APPPATH . 'libraries/PDF_BarangBawaan.php');
|
||||
|
||||
$pdf = new PDF_BarangBawaan('L', 'mm', 'A5');
|
||||
$pdf->AliasNbPages();
|
||||
$pdf->SetMargins(5, 5, 5);
|
||||
$pdf->SetAutoPageBreak(true, 10);
|
||||
|
||||
$pdf->setFormInfo($noFaktur, $tanggal, $info->full_name);
|
||||
|
||||
$pdf->AddPage('L', 'A5');
|
||||
$pdf->BarangBawaanTable($items);
|
||||
$pdf->SignatureArea();
|
||||
|
||||
$namaFile = 'FORM_BARANG_BAWAAN_' . str_replace(' ', '_', $info->full_name) . '_' . date('Ymd') . '.pdf';
|
||||
$pdf->Output('I', $namaFile);
|
||||
exit;
|
||||
}
|
||||
|
||||
public function get_transfer_data()
|
||||
{
|
||||
$data = $this->ptm->get_transfer_data();
|
||||
|
||||
$result = [];
|
||||
$no = 1;
|
||||
|
||||
foreach ($data as $row) {
|
||||
$result[] = [
|
||||
$no++,
|
||||
htmlspecialchars($row->barcode, ENT_QUOTES, 'UTF-8'),
|
||||
htmlspecialchars($row->nama_barang, ENT_QUOTES, 'UTF-8'),
|
||||
htmlspecialchars(($row->from_code ?? '') . ' - ' . ($row->from_name ?? ''), ENT_QUOTES, 'UTF-8'),
|
||||
htmlspecialchars(($row->to_code ?? '') . ' - ' . ($row->to_name ?? ''), ENT_QUOTES, 'UTF-8'),
|
||||
htmlspecialchars($row->created_at, ENT_QUOTES, 'UTF-8'),
|
||||
'<span class="badge bg-success">Selesai</span>'
|
||||
];
|
||||
}
|
||||
|
||||
echo json_encode(['data' => $result]);
|
||||
}
|
||||
|
||||
public function batch_transfer()
|
||||
{
|
||||
$barcodes = $this->input->post('barcodes');
|
||||
$from_user_id = (int)$this->input->post('from_user_id');
|
||||
$to_user_id = (int)$this->input->post('to_user_id');
|
||||
|
||||
$technician = $this->ptm->get_teknisi_info($to_user_id);
|
||||
|
||||
log_message('debug', 'batch_transfer POST: ' . json_encode([
|
||||
'barcodes' => $barcodes,
|
||||
'from_user_id' => $from_user_id,
|
||||
'to_user_id' => $to_user_id
|
||||
]));
|
||||
|
||||
if (empty($barcodes) || !is_array($barcodes)) {
|
||||
echo json_encode(['status' => false, 'message' => 'Tidak ada barcode yang dipilih']);
|
||||
return;
|
||||
}
|
||||
|
||||
if (empty($from_user_id) || empty($to_user_id)) {
|
||||
echo json_encode(['status' => false, 'message' => 'Pilih teknisi asal dan tujuan']);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($from_user_id == $to_user_id) {
|
||||
echo json_encode(['status' => false, 'message' => 'Teknisi tujuan tidak boleh sama dengan teknisi asal']);
|
||||
return;
|
||||
}
|
||||
|
||||
$now = date('Y-m-d H:i:s');
|
||||
$errors = [];
|
||||
|
||||
$this->db->trans_start();
|
||||
|
||||
foreach ($barcodes as $barcode) {
|
||||
$barcode = trim($barcode);
|
||||
if ($barcode == '') continue;
|
||||
|
||||
$item = $this->db
|
||||
->select('it.*, ib.id as barcode_id, tec.full_name as teknisi_name')
|
||||
->from('item_technician it')
|
||||
->join('item_barcodes ib', 'ib.barcode = it.barcode', 'left')
|
||||
->join('k_employees tec', 'tec.id = it.user_id', 'left')
|
||||
->where('it.barcode', $barcode)
|
||||
->where('it.status', 'active')
|
||||
->where('it.user_id', $from_user_id)
|
||||
->get()
|
||||
->row();
|
||||
|
||||
if (!$item) {
|
||||
$errors[] = "Barcode $barcode tidak ditemukan pada teknisi asal";
|
||||
continue;
|
||||
}
|
||||
|
||||
log_message('debug', 'batch_transfer item: ' . json_encode($item));
|
||||
|
||||
$this->db
|
||||
->where('barcode', $item->barcode)
|
||||
->where('user_id', $from_user_id)
|
||||
->where('status', 'active')
|
||||
->update('item_technician', [
|
||||
'user_id' => $to_user_id
|
||||
]);
|
||||
|
||||
$this->db->insert('item_movements', [
|
||||
'item_id' => $item->item_id,
|
||||
'barcode_id' => $item->barcode_id,
|
||||
'qty' => 1,
|
||||
'from_type' => 'technician',
|
||||
'from_id' => $from_user_id,
|
||||
'to_type' => 'technician',
|
||||
'to_id' => $to_user_id,
|
||||
'movement_type' => 'transfer',
|
||||
'notes' => 'Transfer Barang dari: ' . $item->teknisi_name . ' ke ' . $technician->full_name,
|
||||
'created_at' => $now
|
||||
]);
|
||||
}
|
||||
|
||||
$this->db->trans_complete();
|
||||
|
||||
if ($this->db->trans_status() === FALSE) {
|
||||
$db_error = $this->db->error();
|
||||
log_message('error', 'batch_transfer gagal: ' . json_encode($db_error));
|
||||
echo json_encode([
|
||||
'status' => false,
|
||||
'message' => 'Gagal memproses transfer',
|
||||
'debug' => $db_error['message'] ?? 'Unknown database error'
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
$success_count = count($barcodes) - count($errors);
|
||||
$msg = "$success_count barang berhasil ditransfer";
|
||||
if (count($errors) > 0) {
|
||||
$msg .= '. ' . implode(', ', $errors);
|
||||
}
|
||||
|
||||
echo json_encode(['status' => true, 'message' => $msg, 'errors' => $errors]);
|
||||
}
|
||||
|
||||
public function get_log_data()
|
||||
{
|
||||
$data = $this->ptm->get_log_barang();
|
||||
|
||||
$result = [];
|
||||
$no = 1;
|
||||
|
||||
foreach ($data as $row) {
|
||||
$status_badge = $row->status === 'active'
|
||||
? '<span class="badge bg-success">ACTIVE</span>'
|
||||
: '<span class="badge bg-secondary">INACTIVE</span>';
|
||||
|
||||
$result[] = [
|
||||
$no++,
|
||||
htmlspecialchars($row->teknisi_name ?? '-', ENT_QUOTES, 'UTF-8'),
|
||||
htmlspecialchars($row->item_code ?? '-', ENT_QUOTES, 'UTF-8'),
|
||||
htmlspecialchars($row->barcode, ENT_QUOTES, 'UTF-8'),
|
||||
htmlspecialchars($row->nama_barang ?? '-', ENT_QUOTES, 'UTF-8'),
|
||||
$row->created_at ? date('d/m/Y H:i', strtotime($row->created_at)) : '-',
|
||||
$row->status === 'inactive' && $row->deleted_at ? date('d/m/Y H:i', strtotime($row->deleted_at)) : '-',
|
||||
$status_badge,
|
||||
];
|
||||
}
|
||||
|
||||
echo json_encode(['data' => $result]);
|
||||
}
|
||||
|
||||
public function log_barang()
|
||||
{
|
||||
$data = [
|
||||
"active_menu" => "peralatan_teknisi",
|
||||
];
|
||||
|
||||
$this->load->view('partials/header', $data);
|
||||
$this->load->view('peralatan_teknisi/log_barang', $data);
|
||||
$this->load->view('partials/footer');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
<?php
|
||||
|
||||
require_once(APPPATH . 'third_party/fpdf/fpdf.php');
|
||||
|
||||
class PDF_BarangBawaan extends FPDF
|
||||
{
|
||||
private $noFaktur;
|
||||
private $tanggal;
|
||||
private $teknisi;
|
||||
|
||||
private $colWidths = [8, 18, 27, 42, 24, 10, 46, 15];
|
||||
|
||||
public function __construct($orientation = 'L', $unit = 'mm', $size = 'A5')
|
||||
{
|
||||
parent::__construct($orientation, $unit, $size);
|
||||
$this->SetDisplayMode('fullpage');
|
||||
}
|
||||
|
||||
function SetMargins($left, $top, $right = null)
|
||||
{
|
||||
parent::SetMargins(10, $top, 10);
|
||||
}
|
||||
|
||||
public function setFormInfo($noFaktur, $tanggal, $teknisi)
|
||||
{
|
||||
$this->noFaktur = $noFaktur;
|
||||
$this->tanggal = $tanggal;
|
||||
$this->teknisi = $teknisi;
|
||||
}
|
||||
|
||||
function Header()
|
||||
{
|
||||
$this->SetFont('Arial', 'B', 11);
|
||||
$this->SetTextColor(245, 140, 0);
|
||||
$this->Cell(0, 6, 'FORM BARANG BAWAAN TEKNISI', 0, 1, 'L');
|
||||
$this->SetTextColor(0, 0, 0);
|
||||
$this->Ln(2);
|
||||
|
||||
$this->SetFont('Arial', '', 8);
|
||||
$this->Cell(0, 5, 'Tanggal : ' . $this->tanggal . ' Teknisi : ' . $this->teknisi . ' No. Faktur : ' . $this->noFaktur, 0, 1, 'C');
|
||||
$this->Ln(2);
|
||||
$this->_drawTableHeader();
|
||||
}
|
||||
|
||||
function Footer()
|
||||
{
|
||||
$this->SetY(-8);
|
||||
$this->SetFont('Arial', 'I', 7);
|
||||
$this->Cell(0, 4, 'Hal ' . $this->PageNo(), 0, 0, 'C');
|
||||
}
|
||||
|
||||
private function _drawTableHeader()
|
||||
{
|
||||
$startX = $this->lMargin;
|
||||
$this->SetX($startX);
|
||||
$this->SetFillColor(245, 140, 0);
|
||||
$this->SetTextColor(255, 255, 255);
|
||||
$this->SetFont('Arial', 'B', 7);
|
||||
|
||||
$headers = ['No', 'Tanggal', 'Barcode', 'Nama Barang', 'Serial Number', 'Jumlah', 'Keterangan', 'Server'];
|
||||
foreach ($headers as $i => $h) {
|
||||
$this->Cell($this->colWidths[$i], 7, $h, 1, 0, 'C', true);
|
||||
}
|
||||
$this->Ln();
|
||||
|
||||
$this->SetTextColor(0, 0, 0);
|
||||
}
|
||||
|
||||
public function BarangBawaanTable($items)
|
||||
{
|
||||
$this->SetFont('Arial', '', 7);
|
||||
$lineH = 5.5;
|
||||
$minRowH = 8;
|
||||
$no = 1;
|
||||
|
||||
$colsMultiCell = [false, false, true, true, true, false, true, false];
|
||||
$cellAligns = ['C', 'C', 'L', 'L', 'L', 'C', 'L', 'L'];
|
||||
|
||||
foreach ($items as $item) {
|
||||
$keterangan = isset($item->keterangan) ? $item->keterangan : '';
|
||||
$namaBarang = $item->nama_barang ?? '-';
|
||||
$serial = $item->serial_number ?? '-';
|
||||
$barcode = $item->barcode ?? '-';
|
||||
$jumlah = isset($item->qty_sisa) ? (int)$item->qty_sisa : 1;
|
||||
$server = '';
|
||||
|
||||
$cellValues = [
|
||||
(string)$no,
|
||||
$item->tanggal ?? '-',
|
||||
$barcode,
|
||||
$namaBarang,
|
||||
$serial,
|
||||
(string)$jumlah,
|
||||
$keterangan,
|
||||
$server
|
||||
];
|
||||
|
||||
$maxLines = 1;
|
||||
foreach ($cellValues as $i => $val) {
|
||||
if ($colsMultiCell[$i]) {
|
||||
$lines = $this->NbLines($this->colWidths[$i], $val);
|
||||
if ($lines > $maxLines) {
|
||||
$maxLines = $lines;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$rowH = max($minRowH, $maxLines * $lineH);
|
||||
|
||||
if ($this->GetY() + $rowH > $this->PageBreakTrigger) {
|
||||
$this->AddPage();
|
||||
}
|
||||
|
||||
$startX = $this->lMargin;
|
||||
$startY = $this->GetY();
|
||||
|
||||
$currentX = $startX;
|
||||
foreach ($this->colWidths as $i => $w) {
|
||||
$this->Rect($currentX, $startY, $w, $rowH);
|
||||
$currentX += $w;
|
||||
}
|
||||
|
||||
$currentX = $startX;
|
||||
foreach ($cellValues as $i => $val) {
|
||||
$this->SetXY($currentX, $startY);
|
||||
if ($colsMultiCell[$i]) {
|
||||
$colLines = $this->NbLines($this->colWidths[$i], $val);
|
||||
$contentH = $colLines * $lineH;
|
||||
$offsetY = ($rowH - $contentH) / 2;
|
||||
$this->SetXY($currentX, $startY + $offsetY);
|
||||
$this->MultiCell($this->colWidths[$i], $lineH, $val, 0, $cellAligns[$i]);
|
||||
} else {
|
||||
$this->Cell($this->colWidths[$i], $rowH, $val, 0, 0, $cellAligns[$i]);
|
||||
}
|
||||
$currentX += $this->colWidths[$i];
|
||||
}
|
||||
|
||||
$this->SetXY($startX, $startY + $rowH);
|
||||
$no++;
|
||||
}
|
||||
}
|
||||
|
||||
public function SignatureArea()
|
||||
{
|
||||
$this->Ln(6);
|
||||
|
||||
$sigW = 56;
|
||||
$gapW = 11;
|
||||
$blockW = 3 * $sigW + 2 * $gapW;
|
||||
$startX = $this->lMargin + ($this->w - $this->lMargin - $this->rMargin - $blockW) / 2;
|
||||
|
||||
$signatures = [
|
||||
['title' => 'Admin Gudang', 'name' => 'Gono Soewisto'],
|
||||
['title' => 'Leader Teknisi', 'name' => 'Chandra Nursani'],
|
||||
['title' => 'Mengetahui', 'name' => 'Tito Ngudiana'],
|
||||
];
|
||||
|
||||
$sigY = $this->GetY();
|
||||
|
||||
foreach ($signatures as $i => $sig) {
|
||||
$x = $startX + $i * ($sigW + $gapW);
|
||||
|
||||
$this->SetXY($x, $sigY);
|
||||
$this->SetFont('Arial', '', 8);
|
||||
$this->Cell($sigW, 5, $sig['title'], 0, 1, 'C');
|
||||
|
||||
$this->SetX($x);
|
||||
$this->Ln(10);
|
||||
|
||||
$this->SetX($x);
|
||||
$this->SetFont('Arial', 'U', 8);
|
||||
$this->Cell($sigW, 5, ' ', 0, 1, 'C');
|
||||
|
||||
$this->SetX($x);
|
||||
$this->SetFont('Arial', 'I', 7);
|
||||
$this->Cell($sigW, 4, $sig['name'], 0, 1, 'C');
|
||||
}
|
||||
|
||||
$this->SetY($sigY + 5 + 10 + 5 + 4);
|
||||
}
|
||||
|
||||
private function NbLines($w, $txt)
|
||||
{
|
||||
$cw = &$this->CurrentFont['cw'];
|
||||
if ($w == 0) {
|
||||
$w = $this->w - $this->rMargin - $this->x;
|
||||
}
|
||||
$wmax = ($w - 2 * $this->cMargin) * 1000 / $this->FontSize;
|
||||
$s = str_replace("\r", '', $txt);
|
||||
$nb = strlen($s);
|
||||
if ($nb > 0 && $s[$nb - 1] == "\n") { $nb--; }
|
||||
$sep = -1;
|
||||
$i = 0;
|
||||
$j = 0;
|
||||
$l = 0;
|
||||
$nl = 1;
|
||||
while ($i < $nb) {
|
||||
$c = $s[$i];
|
||||
if ($c == "\n") { $i++; $sep = -1; $j = $i; $l = 0; $nl++; continue; }
|
||||
if ($c == ' ') { $sep = $i; }
|
||||
$l += isset($cw[ord($c)]) ? $cw[ord($c)] : 600;
|
||||
if ($l > $wmax) {
|
||||
if ($sep == -1) { if ($i == $j) $i++; }
|
||||
else { $i = $sep + 1; }
|
||||
$sep = -1; $j = $i; $l = 0; $nl++;
|
||||
} else { $i++; }
|
||||
}
|
||||
return $nl;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,329 @@
|
||||
<?php
|
||||
|
||||
require_once(APPPATH . 'third_party/fpdf/fpdf.php');
|
||||
|
||||
class PDF extends FPDF
|
||||
{
|
||||
// ----------------------------------------------------------------
|
||||
// PROPERTI HEADER
|
||||
// ----------------------------------------------------------------
|
||||
private $nama;
|
||||
private $alamat;
|
||||
private $logo;
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// PROPERTI TABEL
|
||||
// ----------------------------------------------------------------
|
||||
private $isTable = false;
|
||||
private $tableWidth = [];
|
||||
private $tableHeader = [];
|
||||
|
||||
// Konfigurasi tabel A4 Portrait (lebar efektif = 190mm, margin 10+10)
|
||||
// Tanggal : 22 | No Ref : 28 | Keterangan : 60 | Debit : 26 | Kredit : 26 | Saldo : 28 → TOTAL : 190
|
||||
private $colWidths = [22, 28, 60, 26, 26, 28];
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// PROPERTI INFO LAPORAN
|
||||
// ----------------------------------------------------------------
|
||||
private $namaAkun = '';
|
||||
private $periodeStr = '';
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// SETTER
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
public function setHeaderData($nama, $alamat, $logo = '')
|
||||
{
|
||||
$this->nama = $nama;
|
||||
$this->alamat = $alamat;
|
||||
$this->logo = $logo;
|
||||
}
|
||||
|
||||
public function setInfoLaporan($namaAkun, $periodeStr)
|
||||
{
|
||||
$this->namaAkun = $namaAkun;
|
||||
$this->periodeStr = $periodeStr;
|
||||
}
|
||||
|
||||
public function setTableHeader($header, $width)
|
||||
{
|
||||
$this->tableHeader = $header;
|
||||
$this->tableWidth = $width;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// HEADER HALAMAN
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
function Header()
|
||||
{
|
||||
// Lebar halaman A4 Portrait = 210mm (area cetak 10..200)
|
||||
$pageRight = 200;
|
||||
|
||||
// Logo (opsional)
|
||||
if (!empty($this->logo) && file_exists($this->logo)) {
|
||||
$this->Image($this->logo, 10, 6, 18);
|
||||
$this->SetXY(31, 10);
|
||||
} else {
|
||||
$this->SetXY(10, 10);
|
||||
}
|
||||
|
||||
// Nama perusahaan
|
||||
$this->SetFont('Arial', 'B', 12);
|
||||
$this->Cell(0, 6, $this->nama, 0, 1, 'L');
|
||||
|
||||
// Alamat
|
||||
$this->SetFont('Arial', '', 9);
|
||||
$x = !empty($this->logo) && file_exists($this->logo) ? 31 : 10;
|
||||
$this->SetX($x);
|
||||
$this->Cell(0, 5, $this->alamat, 0, 1, 'L');
|
||||
|
||||
// Garis pemisah
|
||||
$this->Ln(2);
|
||||
$this->SetLineWidth(0.6);
|
||||
$this->Line(10, $this->GetY(), $pageRight, $this->GetY());
|
||||
$this->SetLineWidth(0.2);
|
||||
$this->Line(10, $this->GetY() + 1, $pageRight, $this->GetY() + 1);
|
||||
$this->Ln(5);
|
||||
|
||||
// Judul
|
||||
$this->SetFont('Arial', 'B', 13);
|
||||
$this->SetTextColor(245, 140, 0);
|
||||
$this->Cell(0, 7, 'LAPORAN BUKU BESAR', 0, 1, 'C');
|
||||
$this->SetTextColor(0, 0, 0);
|
||||
|
||||
// Periode
|
||||
$this->SetFont('Arial', '', 10);
|
||||
$this->Cell(0, 5, $this->periodeStr, 0, 1, 'C');
|
||||
|
||||
$this->Ln(3);
|
||||
|
||||
// Info akun & tanggal cetak
|
||||
$colLabel = 35;
|
||||
$colSep = 5;
|
||||
$startX = ($this->GetPageWidth() - array_sum($this->colWidths)) / 2;
|
||||
|
||||
$this->SetX($startX);
|
||||
$this->SetFont('Arial', 'B', 10);
|
||||
$this->Cell($colLabel, 6, 'Akun');
|
||||
$this->SetFont('Arial', '', 10);
|
||||
$this->Cell($colSep, 6, ':');
|
||||
$this->Cell(0, 6, $this->namaAkun, 0, 1);
|
||||
|
||||
$this->SetX($startX);
|
||||
$this->SetFont('Arial', 'B', 10);
|
||||
$this->Cell($colLabel, 6, 'Di Cetak Pada');
|
||||
$this->SetFont('Arial', '', 10);
|
||||
$this->Cell($colSep, 6, ':');
|
||||
$this->Cell(0, 6, date('d-m-Y') . ' Jam ' . date('H:i:s'), 0, 1);
|
||||
|
||||
$this->Ln(3);
|
||||
|
||||
if ($this->isTable && !empty($this->tableHeader)) {
|
||||
$this->_drawTableHeader();
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// FOOTER HALAMAN
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
function Footer()
|
||||
{
|
||||
$this->SetY(-10);
|
||||
$this->SetFont('Arial', 'I', 8);
|
||||
$this->Cell(0, 5, 'Halaman ' . $this->PageNo(), 0, 0, 'C');
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// HELPER: Gambar baris header tabel
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
private function _drawTableHeader()
|
||||
{
|
||||
$startX = ($this->GetPageWidth() - array_sum($this->colWidths)) / 2;
|
||||
$this->SetX($startX);
|
||||
$this->SetFillColor(245, 140, 0);
|
||||
$this->SetTextColor(255, 255, 255);
|
||||
$this->SetFont('Arial', 'B', 9);
|
||||
|
||||
foreach ($this->tableHeader as $k => $v) {
|
||||
$this->Cell($this->tableWidth[$k], 8, $v, 1, 0, 'C', true);
|
||||
}
|
||||
|
||||
$this->SetTextColor(0, 0, 0);
|
||||
$this->Ln();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// TABEL BUKU BESAR
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
public function BukuBesarTable($result)
|
||||
{
|
||||
$header = ['Tanggal', 'No Ref', 'Keterangan', 'Debit', 'Kredit', 'Saldo'];
|
||||
|
||||
$this->isTable = true;
|
||||
$this->setTableHeader($header, $this->colWidths);
|
||||
|
||||
// Header halaman pertama
|
||||
$this->_drawTableHeader();
|
||||
|
||||
$startX = ($this->GetPageWidth() - array_sum($this->colWidths)) / 2;
|
||||
$lineH = 5;
|
||||
|
||||
$this->SetFont('Arial', '', 9);
|
||||
|
||||
if (empty($result)) {
|
||||
$this->SetX($startX);
|
||||
$this->SetFont('Arial', 'B', 10);
|
||||
$this->Cell(array_sum($this->colWidths), 8, 'Tidak ada data', 1, 1, 'C');
|
||||
$this->isTable = false;
|
||||
return;
|
||||
}
|
||||
|
||||
$fill = false;
|
||||
|
||||
// Batas bawah A4 Portrait (297mm) dikurangi area footer
|
||||
$pageBreakTrigger = 277;
|
||||
|
||||
foreach ($result as $row) {
|
||||
|
||||
// Hitung tinggi baris berdasar No Ref & Keterangan (keduanya MultiCell)
|
||||
$nbRef = $this->NbLines($this->colWidths[1], $row['no_ref']);
|
||||
$nbKet = $this->NbLines($this->colWidths[2], $row['keterangan']);
|
||||
$nbLines = max(1, $nbRef, $nbKet);
|
||||
$rowH = $nbLines * $lineH;
|
||||
|
||||
// Ganti halaman jika mendekati batas bawah
|
||||
if ($this->GetY() + $rowH > $pageBreakTrigger) {
|
||||
$this->AddPage('P', 'A4');
|
||||
}
|
||||
|
||||
if ($fill) {
|
||||
$this->SetFillColor(245, 245, 245);
|
||||
}
|
||||
|
||||
$x = $startX;
|
||||
$y = $this->GetY();
|
||||
|
||||
// Tanggal
|
||||
$this->SetX($x);
|
||||
$this->Cell($this->colWidths[0], $rowH, date('d-m-Y', strtotime($row['tanggal'])), 1, 0, 'C', $fill);
|
||||
|
||||
// No Ref (MultiCell)
|
||||
$xRef = $this->GetX();
|
||||
$offsetRef = ($rowH - $nbRef * $lineH) / 2;
|
||||
// gambar border kotak dulu
|
||||
$this->Rect($xRef, $y, $this->colWidths[1], $rowH, $fill ? 'DF' : 'D');
|
||||
$this->SetXY($xRef, $y + $offsetRef);
|
||||
$this->MultiCell($this->colWidths[1], $lineH, $row['no_ref'], 0, 'C', false);
|
||||
|
||||
// Keterangan (MultiCell)
|
||||
$xKet = $xRef + $this->colWidths[1];
|
||||
$offsetKet = ($rowH - $nbKet * $lineH) / 2;
|
||||
$this->Rect($xKet, $y, $this->colWidths[2], $rowH, $fill ? 'DF' : 'D');
|
||||
$this->SetXY($xKet, $y + $offsetKet);
|
||||
$this->MultiCell($this->colWidths[2], $lineH, $row['keterangan'], 0, 'L', false);
|
||||
|
||||
// Kolom angka
|
||||
$this->SetXY($xKet + $this->colWidths[2], $y);
|
||||
$this->Cell($this->colWidths[3], $rowH, number_format($row['debit'], 2, ',', '.'), 1, 0, 'R', $fill);
|
||||
$this->Cell($this->colWidths[4], $rowH, number_format($row['kredit'], 2, ',', '.'), 1, 0, 'R', $fill);
|
||||
$this->Cell($this->colWidths[5], $rowH, number_format($row['saldo'], 2, ',', '.'), 1, 0, 'R', $fill);
|
||||
|
||||
$this->SetXY($x, $y + $rowH);
|
||||
|
||||
$fill = !$fill;
|
||||
}
|
||||
|
||||
$this->isTable = false;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// BARIS TOTAL
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
public function BukuBesarTotal($totalDebit, $totalKredit, $saldo)
|
||||
{
|
||||
$startX = ($this->GetPageWidth() - array_sum($this->colWidths)) / 2;
|
||||
$labelWidth = $this->colWidths[0] + $this->colWidths[1] + $this->colWidths[2];
|
||||
|
||||
$this->SetX($startX);
|
||||
$this->SetFillColor(255, 204, 102);
|
||||
$this->SetFont('Arial', 'B', 9);
|
||||
|
||||
$this->Cell($labelWidth, 8, 'TOTAL', 1, 0, 'C', true);
|
||||
$this->Cell($this->colWidths[3], 8, number_format($totalDebit, 2, ',', '.'), 1, 0, 'R', true);
|
||||
$this->Cell($this->colWidths[4], 8, number_format($totalKredit, 2, ',', '.'), 1, 0, 'R', true);
|
||||
$this->Cell($this->colWidths[5], 8, number_format($saldo, 2, ',', '.'), 1, 1, 'R', true);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// TANDA TANGAN
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
public function Pembuat($nama, $jabatan = '')
|
||||
{
|
||||
$signWidth = 70;
|
||||
$signX = 125; // disesuaikan untuk A4 Portrait (210mm)
|
||||
|
||||
$this->Ln(10);
|
||||
$this->SetX($signX);
|
||||
$this->SetFont('Arial', '', 10);
|
||||
$this->Cell($signWidth, 6, 'Mengetahui,', 0, 1, 'C');
|
||||
|
||||
$this->Ln(10);
|
||||
$this->SetX($signX);
|
||||
$this->SetFont('Arial', 'B', 10);
|
||||
$this->Cell($signWidth, 6, $nama, 0, 1, 'C');
|
||||
|
||||
if (!empty($jabatan)) {
|
||||
$this->SetX($signX);
|
||||
$this->SetFont('Arial', '', 9);
|
||||
$this->Cell($signWidth, 5, '(' . $jabatan . ')', 0, 1, 'C');
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// HELPER: Hitung jumlah baris MultiCell
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
private function NbLines($w, $txt)
|
||||
{
|
||||
$cw = &$this->CurrentFont['cw'];
|
||||
if ($w == 0) {
|
||||
$w = $this->w - $this->rMargin - $this->x;
|
||||
}
|
||||
$wmax = ($w - 2 * $this->cMargin) * 1000 / $this->FontSize;
|
||||
$s = str_replace("\r", '', (string)$txt);
|
||||
$nb = strlen($s);
|
||||
if ($nb > 0 && $s[$nb - 1] == "\n") {
|
||||
$nb--;
|
||||
}
|
||||
$sep = -1;
|
||||
$i = 0; $j = 0; $l = 0; $nl = 1;
|
||||
while ($i < $nb) {
|
||||
$c = $s[$i];
|
||||
if ($c == "\n") {
|
||||
$i++; $sep = -1; $j = $i; $l = 0; $nl++;
|
||||
continue;
|
||||
}
|
||||
if ($c == ' ') {
|
||||
$sep = $i;
|
||||
}
|
||||
$l += isset($cw[ord($c)]) ? $cw[ord($c)] : 600;
|
||||
if ($l > $wmax) {
|
||||
if ($sep == -1) {
|
||||
if ($i == $j) $i++;
|
||||
} else {
|
||||
$i = $sep + 1;
|
||||
}
|
||||
$sep = -1; $j = $i; $l = 0; $nl++;
|
||||
} else {
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
return $nl;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
<?php
|
||||
|
||||
require_once(APPPATH.'third_party/fpdf/fpdf.php');
|
||||
|
||||
class PDF extends FPDF
|
||||
{
|
||||
private $nama;
|
||||
private $alamat;
|
||||
private $telepon;
|
||||
private $logo;
|
||||
|
||||
private $isTable = false;
|
||||
private $tableWidth = [];
|
||||
private $tableHeader = [];
|
||||
|
||||
// Lebar kertas A4 Portrait = 210mm, margin kiri+kanan = 10+10 = 20mm
|
||||
// Area cetak efektif = 190mm
|
||||
private $pageWidth = 190;
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// SETTER
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
public function setHeaderData($nama, $alamat, $telepon, $logo = '')
|
||||
{
|
||||
$this->nama = $nama;
|
||||
$this->alamat = $alamat;
|
||||
$this->telepon = $telepon;
|
||||
$this->logo = $logo;
|
||||
}
|
||||
|
||||
public function setTableHeader($header, $width)
|
||||
{
|
||||
$this->tableHeader = $header;
|
||||
$this->tableWidth = $width;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// HEADER HALAMAN
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
function Header()
|
||||
{
|
||||
// Logo (opsional)
|
||||
if (!empty($this->logo) && file_exists($this->logo)) {
|
||||
$this->Image($this->logo, 10, 8, 18);
|
||||
$this->SetXY(31, 10);
|
||||
} else {
|
||||
$this->SetXY(10, 10);
|
||||
}
|
||||
|
||||
// Nama perusahaan
|
||||
$this->SetFont('Arial', 'B', 12);
|
||||
$this->Cell(0, 6, $this->nama, 0, 1, 'L');
|
||||
|
||||
// Alamat
|
||||
$this->SetFont('Arial', '', 9);
|
||||
$x = !empty($this->logo) && file_exists($this->logo) ? 31 : 10;
|
||||
$this->SetX($x);
|
||||
$this->Cell(0, 5, $this->alamat, 0, 1, 'L');
|
||||
|
||||
$this->SetX($x);
|
||||
$this->Cell(0, 5, 'Telp : ' . $this->telepon, 0, 1, 'L');
|
||||
|
||||
// Garis pemisah header (A4 Portrait: dari x=10 s/d x=200)
|
||||
$this->Ln(2);
|
||||
$this->SetLineWidth(0.6);
|
||||
$this->Line(10, $this->GetY(), 200, $this->GetY());
|
||||
$this->SetLineWidth(0.2);
|
||||
$this->Line(10, $this->GetY() + 1, 200, $this->GetY() + 1);
|
||||
|
||||
$this->Ln(5);
|
||||
|
||||
// Judul laporan
|
||||
$this->SetFont('Arial', 'B', 14);
|
||||
$this->SetTextColor(245, 140, 0);
|
||||
$this->Cell(0, 8, 'LAPORAN DATA BARANG', 0, 1, 'C');
|
||||
$this->SetTextColor(0, 0, 0);
|
||||
|
||||
// Tanggal cetak
|
||||
$this->SetFont('Arial', '', 9);
|
||||
$this->Cell(0, 5, 'DIcetak Pada : ' . date('d-m-Y') . ' - ' . date('H:i:s'), 0, 1, 'C');
|
||||
|
||||
$this->Ln(3);
|
||||
|
||||
// Header tabel (muncul otomatis saat ganti halaman)
|
||||
if ($this->isTable && !empty($this->tableHeader)) {
|
||||
$this->_drawTableHeader();
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// FOOTER HALAMAN
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
function Footer()
|
||||
{
|
||||
$this->SetY(-10);
|
||||
$this->SetFont('Arial', 'I', 8);
|
||||
$this->Cell(0, 5, 'Halaman ' . $this->PageNo(), 0, 0, 'C');
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// HELPER: Gambar baris header tabel
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
private function _drawTableHeader()
|
||||
{
|
||||
$this->SetFillColor(245, 140, 0);
|
||||
$this->SetTextColor(255, 255, 255);
|
||||
$this->SetFont('Arial', 'B', 8);
|
||||
|
||||
foreach ($this->tableHeader as $k => $v) {
|
||||
$this->Cell($this->tableWidth[$k], 7, $v, 1, 0, 'C', true);
|
||||
}
|
||||
|
||||
$this->SetTextColor(0, 0, 0);
|
||||
$this->Ln();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// TABEL DATA BARANG
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
public function ItemsTable($data)
|
||||
{
|
||||
/*
|
||||
* Total lebar kolom = 190mm (pas A4 Portrait, margin 10mm kiri & kanan)
|
||||
*
|
||||
* NO : 8
|
||||
* TGL BELI : 22
|
||||
* KODE : 22
|
||||
* NAMA BARANG : 48 ← kolom terpanjang
|
||||
* GUDANG : 35
|
||||
* STOK : 13
|
||||
* HARGA BELI : 21
|
||||
* HARGA JUAL : 21
|
||||
* ----
|
||||
* TOTAL : 190
|
||||
*/
|
||||
$w = [8, 22, 22, 48, 35, 13, 21, 21];
|
||||
|
||||
$header = [
|
||||
'NO',
|
||||
'TGL BELI',
|
||||
'KODE',
|
||||
'NAMA BARANG',
|
||||
'GUDANG',
|
||||
'STOK',
|
||||
'HARGA BELI',
|
||||
'HARGA JUAL',
|
||||
];
|
||||
|
||||
// Daftarkan SEBELUM AddPage() agar Header() sudah tahu isTable=true
|
||||
// ketika halaman berikutnya dibuat (termasuk halaman ke-2, 3, dst.)
|
||||
$this->isTable = true;
|
||||
$this->setTableHeader($header, $w);
|
||||
|
||||
// Gambar header tabel di halaman PERTAMA secara eksplisit,
|
||||
// karena saat AddPage() awal dipanggil isTable masih false.
|
||||
$this->_drawTableHeader();
|
||||
|
||||
// ---- Baris data ----
|
||||
$this->SetFont('Arial', '', 8);
|
||||
$no = 1;
|
||||
|
||||
foreach ($data as $row) {
|
||||
|
||||
// Ganti halaman sebelum batas bawah (A4 Portrait: tinggi efektif ~267mm)
|
||||
if ($this->GetY() > 262) {
|
||||
$this->AddPage('P', 'A4');
|
||||
// _drawTableHeader() sudah dipanggil otomatis oleh Header()
|
||||
}
|
||||
|
||||
// Warna baris selang-seling
|
||||
if ($no % 2 === 0) {
|
||||
$this->SetFillColor(255, 245, 230);
|
||||
$fill = true;
|
||||
} else {
|
||||
$fill = false;
|
||||
}
|
||||
|
||||
$this->Cell($w[0], 6, $no++, 1, 0, 'C', $fill);
|
||||
$this->Cell($w[1], 6, date('d-m-Y', strtotime($row->tanggal_pembelian)), 1, 0, 'C', $fill);
|
||||
$this->Cell($w[2], 6, $row->kode_detail, 1, 0, 'C', $fill);
|
||||
$this->Cell($w[3], 6, mb_substr($row->nama_barang, 0, 45), 1, 0, 'L', $fill);
|
||||
$this->Cell($w[4], 6, mb_substr($row->gudang ?: '-', 0, 30), 1, 0, 'L', $fill);
|
||||
$this->Cell($w[5], 6, number_format($row->stok), 1, 0, 'R', $fill);
|
||||
$this->Cell($w[6], 6, number_format($row->harga_beli, 0, ',', '.'), 1, 0, 'R', $fill);
|
||||
$this->Cell($w[7], 6, number_format($row->harga_jual, 0, ',', '.'), 1, 1, 'R', $fill);
|
||||
}
|
||||
|
||||
$this->isTable = false;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// RINGKASAN & TANDA TANGAN
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
public function TotalBarang($jumlah)
|
||||
{
|
||||
$this->Ln(4);
|
||||
$this->SetFont('Arial', 'B', 10);
|
||||
$this->Cell(0, 7, 'Total Data Barang : ' . $jumlah, 0, 1, 'R');
|
||||
}
|
||||
|
||||
// public function Pembuat($nama)
|
||||
// {
|
||||
// $this->Ln(12);
|
||||
// $this->SetFont('Arial', '', 10);
|
||||
// $this->Cell(0, 6, 'Mengetahui,', 0, 1, 'R');
|
||||
// $this->Ln(18);
|
||||
// $this->SetFont('Arial', 'B', 10);
|
||||
// $this->Cell(0, 6, $nama, 0, 1, 'R');
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,345 @@
|
||||
<?php
|
||||
|
||||
require_once(APPPATH . 'third_party/fpdf/fpdf.php');
|
||||
|
||||
class PDF extends FPDF
|
||||
{
|
||||
// ----------------------------------------------------------------
|
||||
// PROPERTI HEADER
|
||||
// ----------------------------------------------------------------
|
||||
private $nama;
|
||||
private $alamat;
|
||||
private $logo;
|
||||
private $periodeStr = '';
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// KONFIGURASI TABEL
|
||||
// ----------------------------------------------------------------
|
||||
// A4 Portrait efektif = 190mm, margin 10+10
|
||||
// Kolom: Kode 20 | Nama Akun 120 | Saldo 50 → TOTAL 190
|
||||
private $colWidths = [20, 120, 50];
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// SETTER
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
public function setHeaderData($nama, $alamat, $logo = '')
|
||||
{
|
||||
$this->nama = $nama;
|
||||
$this->alamat = $alamat;
|
||||
$this->logo = $logo;
|
||||
}
|
||||
|
||||
public function setInfoLaporan($periodeStr)
|
||||
{
|
||||
$this->periodeStr = $periodeStr;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// HEADER HALAMAN
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
function Header()
|
||||
{
|
||||
// Logo (opsional)
|
||||
if (!empty($this->logo) && file_exists($this->logo)) {
|
||||
$this->Image($this->logo, 10, 6, 18);
|
||||
$this->SetXY(31, 10);
|
||||
} else {
|
||||
$this->SetXY(10, 10);
|
||||
}
|
||||
|
||||
// Nama perusahaan
|
||||
$this->SetFont('Arial', 'B', 12);
|
||||
$this->Cell(0, 6, $this->nama, 0, 1, 'L');
|
||||
|
||||
// Alamat
|
||||
$this->SetFont('Arial', '', 9);
|
||||
$x = !empty($this->logo) && file_exists($this->logo) ? 31 : 10;
|
||||
$this->SetX($x);
|
||||
$this->Cell(0, 5, $this->alamat, 0, 1, 'L');
|
||||
|
||||
// Garis pemisah (A4 Portrait: x=10 s/d x=200)
|
||||
$this->Ln(2);
|
||||
$this->SetLineWidth(0.6);
|
||||
$this->Line(10, $this->GetY(), 200, $this->GetY());
|
||||
$this->SetLineWidth(0.2);
|
||||
$this->Line(10, $this->GetY() + 1, 200, $this->GetY() + 1);
|
||||
$this->Ln(5);
|
||||
|
||||
// Judul
|
||||
$this->SetFont('Arial', 'B', 13);
|
||||
$this->SetTextColor(245, 140, 0);
|
||||
$this->Cell(0, 7, 'LAPORAN LABA RUGI', 0, 1, 'C');
|
||||
$this->SetTextColor(0, 0, 0);
|
||||
|
||||
// Periode
|
||||
$this->SetFont('Arial', '', 10);
|
||||
$this->Cell(0, 5, $this->periodeStr, 0, 1, 'C');
|
||||
|
||||
// Tanggal cetak
|
||||
$this->SetFont('Arial', '', 9);
|
||||
$this->Cell(0, 5, 'Di Cetak Pada : ' . date('d-m-Y') . ' Jam ' . date('H:i:s'), 0, 1, 'C');
|
||||
|
||||
$this->Ln(4);
|
||||
|
||||
// Header kolom tabel
|
||||
$this->_drawTableHeader();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// FOOTER HALAMAN
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
function Footer()
|
||||
{
|
||||
$this->SetY(-10);
|
||||
$this->SetFont('Arial', 'I', 8);
|
||||
$this->Cell(0, 5, 'Halaman ' . $this->PageNo(), 0, 0, 'C');
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// HELPER: posisi X awal — TABEL RATA TENGAH KERTAS
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
private function _startX()
|
||||
{
|
||||
$tableW = array_sum($this->colWidths);
|
||||
return ($this->GetPageWidth() - $tableW) / 2;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// HELPER: Gambar header kolom tabel
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
private function _drawTableHeader()
|
||||
{
|
||||
$startX = $this->_startX();
|
||||
|
||||
$this->SetX($startX);
|
||||
$this->SetFillColor(245, 140, 0);
|
||||
$this->SetTextColor(255, 255, 255);
|
||||
$this->SetFont('Arial', 'B', 9);
|
||||
|
||||
$this->Cell($this->colWidths[0], 8, 'Kode', 1, 0, 'C', true);
|
||||
$this->Cell($this->colWidths[1], 8, 'Nama Akun', 1, 0, 'C', true);
|
||||
$this->Cell($this->colWidths[2], 8, 'Saldo', 1, 1, 'C', true);
|
||||
|
||||
$this->SetTextColor(0, 0, 0);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// HELPER: Gambar baris judul section (PENDAPATAN / BEBAN)
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
private function _drawSectionTitle($title)
|
||||
{
|
||||
if ($this->GetY() + 8 > 270) {
|
||||
$this->AddPage('P', 'A4');
|
||||
}
|
||||
|
||||
$startX = $this->_startX();
|
||||
$tableW = array_sum($this->colWidths);
|
||||
|
||||
$this->SetX($startX);
|
||||
$this->SetFillColor(255, 224, 178);
|
||||
$this->SetFont('Arial', 'B', 9);
|
||||
$this->Cell($tableW, 7, $title, 1, 1, 'L', true);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// HELPER: Gambar baris detail akun (dengan dukungan multiline Nama Akun)
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
private function _drawRow($kode, $namaAkun, $saldo)
|
||||
{
|
||||
$lineH = 6;
|
||||
|
||||
$nbLines = $this->NbLines($this->colWidths[1], $namaAkun);
|
||||
$rowH = max(1, $nbLines) * $lineH;
|
||||
|
||||
if ($this->GetY() + $rowH > 270) {
|
||||
$this->AddPage('P', 'A4');
|
||||
}
|
||||
|
||||
$startX = $this->_startX();
|
||||
$y = $this->GetY();
|
||||
|
||||
$this->SetFont('Arial', '', 9);
|
||||
$this->SetXY($startX, $y);
|
||||
$this->Cell($this->colWidths[0], $rowH, $kode, 1, 0, 'C');
|
||||
|
||||
$xNama = $this->GetX();
|
||||
$this->MultiCell($this->colWidths[1], $lineH, $namaAkun, 1, 'L');
|
||||
|
||||
$this->SetXY($xNama + $this->colWidths[1], $y);
|
||||
$this->Cell($this->colWidths[2], $rowH, number_format($saldo, 0, ',', '.'), 1, 0, 'R');
|
||||
|
||||
$this->SetXY($startX, $y + $rowH);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// HELPER: Gambar baris total section
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
private function _drawSectionTotal($label, $total)
|
||||
{
|
||||
if ($this->GetY() + 7 > 270) {
|
||||
$this->AddPage('P', 'A4');
|
||||
}
|
||||
|
||||
$startX = $this->_startX();
|
||||
|
||||
$this->SetX($startX);
|
||||
$this->SetFillColor(255, 224, 178);
|
||||
$this->SetFont('Arial', 'B', 9);
|
||||
$this->Cell($this->colWidths[0] + $this->colWidths[1], 7, $label, 1, 0, 'L', true);
|
||||
$this->Cell($this->colWidths[2], 7, number_format($total, 0, ',', '.'), 1, 1, 'R', true);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// TABEL LABA RUGI (PENDAPATAN, BEBAN, masing-masing dengan rincian akun)
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
public function LabaRugiTable($pendapatan, $beban, $totalPendapatan, $totalBeban)
|
||||
{
|
||||
// ---- SECTION PENDAPATAN ----
|
||||
$this->_drawSectionTitle('PENDAPATAN');
|
||||
|
||||
if (empty($pendapatan)) {
|
||||
$this->_drawRow('-', 'Tidak ada data', 0);
|
||||
} else {
|
||||
foreach ($pendapatan as $row) {
|
||||
$this->_drawRow($row['kode'], $row['nama'], $row['saldo']);
|
||||
}
|
||||
}
|
||||
|
||||
$this->_drawSectionTotal('TOTAL PENDAPATAN', $totalPendapatan);
|
||||
|
||||
// ---- SECTION BEBAN ----
|
||||
$this->AddPage('P', 'A4');
|
||||
$this->_drawSectionTitle('BEBAN');
|
||||
|
||||
if (empty($beban)) {
|
||||
$this->_drawRow('-', 'Tidak ada data', 0);
|
||||
} else {
|
||||
foreach ($beban as $row) {
|
||||
$this->_drawRow($row['kode'], $row['nama'], $row['saldo']);
|
||||
}
|
||||
}
|
||||
|
||||
$this->_drawSectionTotal('TOTAL BEBAN', $totalBeban);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// RINGKASAN: LABA / RUGI BERSIH
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
public function LabaRugiRingkasan($laba)
|
||||
{
|
||||
if ($this->GetY() + 9 > 270) {
|
||||
$this->AddPage('P', 'A4');
|
||||
}
|
||||
|
||||
$startX = $this->_startX();
|
||||
$tableW = array_sum($this->colWidths);
|
||||
$labelW = $tableW - $this->colWidths[2];
|
||||
|
||||
$this->Ln(2);
|
||||
$this->SetX($startX);
|
||||
$this->SetFont('Arial', 'B', 10);
|
||||
|
||||
if ($laba >= 0) {
|
||||
$this->SetFillColor(198, 239, 206); // hijau, laba
|
||||
$label = 'LABA BERSIH';
|
||||
} else {
|
||||
$this->SetFillColor(255, 199, 206); // merah, rugi
|
||||
$label = 'RUGI BERSIH';
|
||||
}
|
||||
|
||||
$this->Cell($labelW, 8, $label, 1, 0, 'L', true);
|
||||
$this->Cell($this->colWidths[2], 8, number_format($laba, 0, ',', '.'), 1, 1, 'R', true);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// TANDA TANGAN
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
public function Pembuat($nama, $jabatan = '')
|
||||
{
|
||||
$startX = $this->_startX();
|
||||
$tableW = array_sum($this->colWidths);
|
||||
$signWidth = 70;
|
||||
$signX = $startX + $tableW - $signWidth; // sejajar sisi kanan tabel
|
||||
|
||||
$this->Ln(10);
|
||||
$this->SetX($signX);
|
||||
$this->SetFont('Arial', '', 10);
|
||||
$this->Cell($signWidth, 6, 'Mengetahui,', 0, 1, 'R');
|
||||
|
||||
$this->Ln(10);
|
||||
$this->SetX($signX);
|
||||
$this->SetFont('Arial', 'B', 10);
|
||||
$this->Cell($signWidth, 6, $nama, 0, 1, 'R');
|
||||
|
||||
if (!empty($jabatan)) {
|
||||
$this->SetX($signX);
|
||||
$this->SetFont('Arial', '', 9);
|
||||
$this->Cell($signWidth, 5, '(' . $jabatan . ')', 0, 1, 'R');
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// HELPER: Hitung jumlah baris yang dibutuhkan MultiCell
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
private function NbLines($w, $txt)
|
||||
{
|
||||
$cw = &$this->CurrentFont['cw'];
|
||||
if ($w == 0) {
|
||||
$w = $this->w - $this->rMargin - $this->x;
|
||||
}
|
||||
$wmax = ($w - 2 * $this->cMargin) * 1000 / $this->FontSize;
|
||||
$s = str_replace("\r", '', $txt);
|
||||
$nb = strlen($s);
|
||||
if ($nb > 0 && $s[$nb - 1] == "\n") {
|
||||
$nb--;
|
||||
}
|
||||
$sep = -1;
|
||||
$i = 0;
|
||||
$j = 0;
|
||||
$l = 0;
|
||||
$nl = 1;
|
||||
while ($i < $nb) {
|
||||
$c = $s[$i];
|
||||
if ($c == "\n") {
|
||||
$i++;
|
||||
$sep = -1;
|
||||
$j = $i;
|
||||
$l = 0;
|
||||
$nl++;
|
||||
continue;
|
||||
}
|
||||
if ($c == ' ') {
|
||||
$sep = $i;
|
||||
}
|
||||
$l += isset($cw[ord($c)]) ? $cw[ord($c)] : 600;
|
||||
if ($l > $wmax) {
|
||||
if ($sep == -1) {
|
||||
if ($i == $j) {
|
||||
$i++;
|
||||
}
|
||||
} else {
|
||||
$i = $sep + 1;
|
||||
}
|
||||
$sep = -1;
|
||||
$j = $i;
|
||||
$l = 0;
|
||||
$nl++;
|
||||
} else {
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
return $nl;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,296 @@
|
||||
<?php
|
||||
require_once(APPPATH . 'third_party/fpdf/fpdf.php');
|
||||
|
||||
class PDF extends FPDF
|
||||
{
|
||||
private $nama;
|
||||
private $alamat;
|
||||
private $logo;
|
||||
private $periodeStr = '';
|
||||
|
||||
private $colWidths = [15, 47, 23];
|
||||
private $gap = 10;
|
||||
|
||||
public function setHeaderData($nama, $alamat, $logo = '')
|
||||
{
|
||||
$this->nama = $nama;
|
||||
$this->alamat = $alamat;
|
||||
$this->logo = $logo;
|
||||
}
|
||||
|
||||
public function setInfoLaporan($periodeStr)
|
||||
{
|
||||
$this->periodeStr = $periodeStr;
|
||||
}
|
||||
|
||||
function Header()
|
||||
{
|
||||
if (!empty($this->logo) && file_exists($this->logo)) {
|
||||
$this->Image($this->logo, 10, 6, 18);
|
||||
$this->SetXY(31, 10);
|
||||
} else {
|
||||
$this->SetXY(10, 10);
|
||||
}
|
||||
|
||||
$this->SetFont('Arial', 'B', 12);
|
||||
$this->Cell(0, 6, $this->nama, 0, 1, 'L');
|
||||
|
||||
$this->SetFont('Arial', '', 9);
|
||||
$x = !empty($this->logo) && file_exists($this->logo) ? 31 : 10;
|
||||
$this->SetX($x);
|
||||
$this->Cell(0, 5, $this->alamat, 0, 1, 'L');
|
||||
|
||||
$this->Ln(2);
|
||||
$this->SetLineWidth(0.6);
|
||||
$this->Line(10, $this->GetY(), 200, $this->GetY());
|
||||
$this->SetLineWidth(0.2);
|
||||
$this->Line(10, $this->GetY() + 1, 200, $this->GetY() + 1);
|
||||
$this->Ln(5);
|
||||
|
||||
$this->SetFont('Arial', 'B', 13);
|
||||
$this->SetTextColor(245, 140, 0);
|
||||
$this->Cell(0, 7, 'LAPORAN NERACA', 0, 1, 'C');
|
||||
$this->SetTextColor(0, 0, 0);
|
||||
|
||||
$this->SetFont('Arial', '', 10);
|
||||
$this->Cell(0, 5, $this->periodeStr, 0, 1, 'C');
|
||||
|
||||
$this->SetFont('Arial', '', 9);
|
||||
$this->Cell(0, 5, 'Di Cetak Pada : ' . date('d-m-Y') . ' - ' . date('H:i:s'), 0, 1, 'C');
|
||||
|
||||
$this->Ln(4);
|
||||
$this->_drawSectionHeader();
|
||||
}
|
||||
|
||||
function Footer()
|
||||
{
|
||||
$this->SetY(-10);
|
||||
$this->SetFont('Arial', 'I', 8);
|
||||
$this->Cell(0, 5, 'Halaman ' . $this->PageNo(), 0, 0, 'C');
|
||||
}
|
||||
|
||||
private function _drawSectionHeader()
|
||||
{
|
||||
$startX = $this->_startX();
|
||||
$tableW = array_sum($this->colWidths);
|
||||
|
||||
$this->SetX($startX);
|
||||
$this->SetFillColor(245, 140, 0);
|
||||
$this->SetTextColor(255, 255, 255);
|
||||
$this->SetFont('Arial', 'B', 10);
|
||||
$this->Cell($tableW, 7, 'ACTIVA', 1, 0, 'C', true);
|
||||
|
||||
$this->SetX($startX + $tableW + $this->gap);
|
||||
$this->Cell($tableW, 7, 'PASIVA', 1, 1, 'C', true);
|
||||
|
||||
$this->SetFont('Arial', 'B', 8);
|
||||
$this->SetFillColor(255, 224, 178);
|
||||
$this->SetTextColor(0, 0, 0);
|
||||
|
||||
$this->SetX($startX);
|
||||
$this->Cell($this->colWidths[0], 6, 'Kode', 1, 0, 'C', true);
|
||||
$this->Cell($this->colWidths[1], 6, 'Nama Akun', 1, 0, 'C', true);
|
||||
$this->Cell($this->colWidths[2], 6, 'Saldo', 1, 0, 'C', true);
|
||||
|
||||
$this->SetX($startX + $tableW + $this->gap);
|
||||
$this->Cell($this->colWidths[0], 6, 'Kode', 1, 0, 'C', true);
|
||||
$this->Cell($this->colWidths[1], 6, 'Nama Akun', 1, 0, 'C', true);
|
||||
$this->Cell($this->colWidths[2], 6, 'Saldo', 1, 1, 'C', true);
|
||||
}
|
||||
|
||||
private function _startX()
|
||||
{
|
||||
$totalW = (array_sum($this->colWidths) * 2) + $this->gap;
|
||||
return ($this->GetPageWidth() - $totalW) / 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hitung jumlah baris yang dibutuhkan MultiCell untuk teks tertentu
|
||||
* pada lebar kolom $w. Diadaptasi dari contoh resmi FPDF.
|
||||
*/
|
||||
private function _nbLines($w, $txt)
|
||||
{
|
||||
if ($txt === null || $txt === '') return 1;
|
||||
$cw = &$this->CurrentFont['cw'];
|
||||
if ($w == 0) {
|
||||
$w = $this->w - $this->rMargin - $this->x;
|
||||
}
|
||||
$wmax = ($w - 2 * $this->cMargin) * 1000 / $this->FontSize;
|
||||
$s = str_replace("\r", '', (string)$txt);
|
||||
$nb = strlen($s);
|
||||
if ($nb > 0 && $s[$nb - 1] == "\n") $nb--;
|
||||
$sep = -1; $i = 0; $j = 0; $l = 0; $nl = 1;
|
||||
while ($i < $nb) {
|
||||
$c = $s[$i];
|
||||
if ($c == "\n") { $i++; $sep = -1; $j = $i; $l = 0; $nl++; continue; }
|
||||
if ($c == ' ') $sep = $i;
|
||||
$l += isset($cw[$c]) ? $cw[$c] : 0;
|
||||
if ($l > $wmax) {
|
||||
if ($sep == -1) {
|
||||
if ($i == $j) $i++;
|
||||
} else {
|
||||
$i = $sep + 1;
|
||||
}
|
||||
$sep = -1; $j = $i; $l = 0; $nl++;
|
||||
} else {
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
return $nl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render satu baris (kiri+kanan) dengan tinggi sama,
|
||||
* memakai MultiCell untuk kolom Nama Akun.
|
||||
*/
|
||||
private function _drawRow($activaRow, $pasivaRow, $startX, $rightX, $lineH)
|
||||
{
|
||||
$wNama = $this->colWidths[1];
|
||||
|
||||
$nlA = $activaRow ? $this->_nbLines($wNama, $activaRow['nama']) : 1;
|
||||
$nlB = $pasivaRow ? $this->_nbLines($wNama, $pasivaRow['nama']) : 1;
|
||||
$nl = max($nlA, $nlB, 1);
|
||||
$h = $lineH * $nl;
|
||||
|
||||
// page break manual agar baris tidak terpotong
|
||||
if ($this->GetY() + $h > $this->PageBreakTrigger) {
|
||||
$this->AddPage($this->CurOrientation);
|
||||
}
|
||||
|
||||
$y = $this->GetY();
|
||||
|
||||
// ---------- ACTIVA ----------
|
||||
$this->SetXY($startX, $y);
|
||||
if ($activaRow) {
|
||||
// Kode (auto vertical-center karena pakai Cell dengan tinggi $h)
|
||||
$this->Cell($this->colWidths[0], $h, $activaRow['kode'], 1, 0, 'C');
|
||||
|
||||
// Nama (MultiCell) — vertical center manual
|
||||
$xNama = $this->GetX();
|
||||
$this->Rect($xNama, $y, $wNama, $h);
|
||||
$nLines = $this->_nbLines($wNama, $activaRow['nama']);
|
||||
$offsetY = ($h - $lineH * $nLines) / 2;
|
||||
$this->SetXY($xNama, $y + $offsetY);
|
||||
$this->MultiCell($wNama, $lineH, $activaRow['nama'], 0, 'L');
|
||||
|
||||
// Saldo
|
||||
$this->SetXY($xNama + $wNama, $y);
|
||||
$this->Cell($this->colWidths[2], $h, number_format($activaRow['saldo'], 0, ',', '.'), 1, 0, 'R');
|
||||
} else {
|
||||
$this->Cell($this->colWidths[0], $h, '', 1, 0);
|
||||
$this->Cell($wNama, $h, '', 1, 0);
|
||||
$this->Cell($this->colWidths[2], $h, '', 1, 0);
|
||||
}
|
||||
|
||||
// ---------- PASIVA ----------
|
||||
$this->SetXY($rightX, $y);
|
||||
if ($pasivaRow) {
|
||||
$this->Cell($this->colWidths[0], $h, $pasivaRow['kode'], 1, 0, 'C');
|
||||
|
||||
$xNama = $this->GetX();
|
||||
$this->Rect($xNama, $y, $wNama, $h);
|
||||
$nLines = $this->_nbLines($wNama, $pasivaRow['nama']);
|
||||
$offsetY = ($h - $lineH * $nLines) / 2;
|
||||
$this->SetXY($xNama, $y + $offsetY);
|
||||
$this->MultiCell($wNama, $lineH, $pasivaRow['nama'], 0, 'L');
|
||||
|
||||
$this->SetXY($xNama + $wNama, $y);
|
||||
$this->Cell($this->colWidths[2], $h, number_format($pasivaRow['saldo'], 0, ',', '.'), 1, 0, 'R');
|
||||
} else {
|
||||
$this->Cell($this->colWidths[0], $h, '', 1, 0);
|
||||
$this->Cell($wNama, $h, '', 1, 0);
|
||||
$this->Cell($this->colWidths[2], $h, '', 1, 0);
|
||||
}
|
||||
|
||||
// pindah ke baris berikutnya
|
||||
$this->SetXY($startX, $y + $h);
|
||||
}
|
||||
|
||||
|
||||
public function NeracaTable($activa, $pasiva, $totalActiva, $totalPasiva)
|
||||
{
|
||||
$startX = $this->_startX();
|
||||
$tableW = array_sum($this->colWidths);
|
||||
$rightX = $startX + $tableW + $this->gap;
|
||||
$lineH = 5;
|
||||
|
||||
$this->SetFont('Arial', '', 8);
|
||||
|
||||
$maxRow = max(count($activa), count($pasiva));
|
||||
for ($i = 0; $i < $maxRow; $i++) {
|
||||
$this->_drawRow(
|
||||
isset($activa[$i]) ? $activa[$i] : null,
|
||||
isset($pasiva[$i]) ? $pasiva[$i] : null,
|
||||
$startX, $rightX, $lineH
|
||||
);
|
||||
}
|
||||
|
||||
// ---- BARIS TOTAL ----
|
||||
if ($this->GetY() + 8 > $this->PageBreakTrigger) {
|
||||
$this->AddPage('P', 'A4');
|
||||
}
|
||||
|
||||
$y = $this->GetY();
|
||||
$this->SetFont('Arial', 'B', 8);
|
||||
$this->SetFillColor(255, 204, 102);
|
||||
|
||||
$this->SetXY($startX, $y);
|
||||
$this->Cell($this->colWidths[0] + $this->colWidths[1], 7, 'TOTAL ACTIVA', 1, 0, 'C', true);
|
||||
$this->Cell($this->colWidths[2], 7, number_format($totalActiva, 0, ',', '.'), 1, 0, 'R', true);
|
||||
|
||||
$this->SetXY($rightX, $y);
|
||||
$this->Cell($this->colWidths[0] + $this->colWidths[1], 7, 'TOTAL PASIVA', 1, 0, 'C', true);
|
||||
$this->Cell($this->colWidths[2], 7, number_format($totalPasiva, 0, ',', '.'), 1, 1, 'R', true);
|
||||
}
|
||||
|
||||
public function NeracaRingkasan($laba, $selisih)
|
||||
{
|
||||
$startX = $this->_startX();
|
||||
$tableW = array_sum($this->colWidths) * 2 + $this->gap;
|
||||
$labelW = $tableW - 40;
|
||||
|
||||
$this->Ln(4);
|
||||
|
||||
$this->SetX($startX);
|
||||
$this->SetFont('Arial', 'B', 9);
|
||||
$this->Cell($labelW, 7, 'Laba Rugi Berjalan', 1, 0, 'L');
|
||||
$this->SetFont('Arial', '', 9);
|
||||
$this->Cell(40, 7, number_format($laba, 0, ',', '.'), 1, 1, 'R');
|
||||
|
||||
$this->SetX($startX);
|
||||
$this->SetFont('Arial', 'B', 9);
|
||||
if (abs($selisih) < 1) {
|
||||
$this->SetFillColor(198, 239, 206);
|
||||
$label = 'Selisih (Balance)';
|
||||
} else {
|
||||
$this->SetFillColor(255, 199, 206);
|
||||
$label = 'Selisih (Tidak Balance)';
|
||||
}
|
||||
$this->Cell($labelW, 7, $label, 1, 0, 'L', true);
|
||||
$this->Cell(40, 7, number_format($selisih, 0, ',', '.'), 1, 1, 'R', true);
|
||||
}
|
||||
|
||||
public function Pembuat($nama, $jabatan = '')
|
||||
{
|
||||
$startX = $this->_startX();
|
||||
$tableW = array_sum($this->colWidths) * 2 + $this->gap;
|
||||
$signWidth = 70;
|
||||
$signX = $startX + $tableW - $signWidth;
|
||||
|
||||
$this->Ln(15);
|
||||
$this->SetX($signX);
|
||||
$this->SetFont('Arial', '', 10);
|
||||
$this->Cell($signWidth, 6, 'Mengetahui,', 0, 1, 'R');
|
||||
|
||||
$this->Ln(18);
|
||||
$this->SetX($signX);
|
||||
$this->SetFont('Arial', 'B', 10);
|
||||
$this->Cell($signWidth, 6, $nama, 0, 1, 'R');
|
||||
|
||||
if (!empty($jabatan)) {
|
||||
$this->SetX($signX);
|
||||
$this->SetFont('Arial', '', 9);
|
||||
$this->Cell($signWidth, 5, '(' . $jabatan . ')', 0, 1, 'R');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,313 @@
|
||||
<?php
|
||||
|
||||
require_once(APPPATH . 'third_party/fpdf/fpdf.php');
|
||||
|
||||
class PDF extends FPDF
|
||||
{
|
||||
// ----------------------------------------------------------------
|
||||
// PROPERTI HEADER
|
||||
// ----------------------------------------------------------------
|
||||
private $nama;
|
||||
private $alamat;
|
||||
private $logo;
|
||||
private $periodeStr = '';
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// KONFIGURASI TABEL
|
||||
// ----------------------------------------------------------------
|
||||
// A4 Portrait efektif = 190mm, margin 10+10
|
||||
// Kolom: Kode 20 | Nama Akun 90 | Debit 40 | Kredit 40 → TOTAL 190
|
||||
private $colWidths = [20, 90, 40, 40];
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// SETTER
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
public function setHeaderData($nama, $alamat, $logo = '')
|
||||
{
|
||||
$this->nama = $nama;
|
||||
$this->alamat = $alamat;
|
||||
$this->logo = $logo;
|
||||
}
|
||||
|
||||
public function setInfoLaporan($periodeStr)
|
||||
{
|
||||
$this->periodeStr = $periodeStr;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// HEADER HALAMAN
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
function Header()
|
||||
{
|
||||
// Header lengkap hanya di halaman pertama
|
||||
if ($this->PageNo() === 1) {
|
||||
// Logo (opsional)
|
||||
if (!empty($this->logo) && file_exists($this->logo)) {
|
||||
$this->Image($this->logo, 10, 6, 18);
|
||||
$this->SetXY(31, 10);
|
||||
} else {
|
||||
$this->SetXY(10, 10);
|
||||
}
|
||||
|
||||
// Nama perusahaan
|
||||
$this->SetFont('Arial', 'B', 12);
|
||||
$this->Cell(0, 6, $this->nama, 0, 1, 'L');
|
||||
|
||||
// Alamat
|
||||
$this->SetFont('Arial', '', 9);
|
||||
$x = !empty($this->logo) && file_exists($this->logo) ? 31 : 10;
|
||||
$this->SetX($x);
|
||||
$this->Cell(0, 5, $this->alamat, 0, 1, 'L');
|
||||
|
||||
// Garis pemisah (A4 Portrait: x=10 s/d x=200)
|
||||
$this->Ln(2);
|
||||
$this->SetLineWidth(0.6);
|
||||
$this->Line(10, $this->GetY(), 200, $this->GetY());
|
||||
$this->SetLineWidth(0.2);
|
||||
$this->Line(10, $this->GetY() + 1, 200, $this->GetY() + 1);
|
||||
$this->Ln(5);
|
||||
|
||||
// Judul
|
||||
$this->SetFont('Arial', 'B', 13);
|
||||
$this->SetTextColor(245, 140, 0);
|
||||
$this->Cell(0, 7, 'LAPORAN KEUANGAN', 0, 1, 'C');
|
||||
$this->SetTextColor(0, 0, 0);
|
||||
|
||||
// Periode
|
||||
$this->SetFont('Arial', '', 10);
|
||||
$this->Cell(0, 5, $this->periodeStr, 0, 1, 'C');
|
||||
|
||||
// Tanggal cetak
|
||||
$this->SetFont('Arial', '', 9);
|
||||
$this->Cell(0, 5, 'Di Cetak Pada : ' . date('d-m-Y') . ' - ' . date('H:i:s'), 0, 1, 'C');
|
||||
|
||||
$this->Ln(4);
|
||||
} else {
|
||||
// Halaman berikutnya tanpa header laporan
|
||||
$this->SetY(10);
|
||||
}
|
||||
|
||||
// Header kolom tabel tetap tampil di setiap halaman
|
||||
$this->_drawTableHeader();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// FOOTER HALAMAN
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
function Footer()
|
||||
{
|
||||
$this->SetY(-10);
|
||||
$this->SetFont('Arial', 'I', 8);
|
||||
$this->Cell(0, 5, 'Halaman ' . $this->PageNo(), 0, 0, 'C');
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// HELPER: posisi X awal — TABEL RATA TENGAH KERTAS
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
private function _startX()
|
||||
{
|
||||
$tableW = array_sum($this->colWidths);
|
||||
return ($this->GetPageWidth() - $tableW) / 2;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// HELPER: Gambar header kolom tabel
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
private function _drawTableHeader()
|
||||
{
|
||||
$startX = $this->_startX();
|
||||
|
||||
$this->SetX($startX);
|
||||
$this->SetFillColor(245, 140, 0);
|
||||
$this->SetTextColor(255, 255, 255);
|
||||
$this->SetFont('Arial', 'B', 9);
|
||||
|
||||
$this->Cell($this->colWidths[0], 8, 'Kode', 1, 0, 'C', true);
|
||||
$this->Cell($this->colWidths[1], 8, 'Nama Akun', 1, 0, 'C', true);
|
||||
$this->Cell($this->colWidths[2], 8, 'Debit', 1, 0, 'C', true);
|
||||
$this->Cell($this->colWidths[3], 8, 'Kredit', 1, 1, 'C', true);
|
||||
|
||||
$this->SetTextColor(0, 0, 0);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// TABEL NERACA SALDO (mendukung multiline Nama Akun)
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
public function NeracaSaldoTable($rows, $totalDebit, $totalKredit)
|
||||
{
|
||||
$startX = $this->_startX();
|
||||
$lineH = 6;
|
||||
|
||||
$this->SetFont('Arial', '', 9);
|
||||
|
||||
if (empty($rows)) {
|
||||
$this->SetX($startX);
|
||||
$this->SetFont('Arial', 'B', 10);
|
||||
$this->Cell(array_sum($this->colWidths), 8, 'Tidak ada data', 1, 1, 'C');
|
||||
} else {
|
||||
foreach ($rows as $row) {
|
||||
$namaAkun = isset($row->nama_akun) ? $row->nama_akun : (isset($row['nama_akun']) ? $row['nama_akun'] : '');
|
||||
$kodeAkun = isset($row->kode_akun) ? $row->kode_akun : (isset($row['kode_akun']) ? $row['kode_akun'] : '');
|
||||
$debit = isset($row->debit) ? (float)$row->debit : (isset($row['debit']) ? (float)$row['debit'] : 0);
|
||||
$kredit = isset($row->kredit) ? (float)$row->kredit : (isset($row['kredit']) ? (float)$row['kredit'] : 0);
|
||||
|
||||
$nbLines = $this->NbLines($this->colWidths[1], $namaAkun);
|
||||
$rowH = max(1, $nbLines) * $lineH;
|
||||
|
||||
if ($this->GetY() + $rowH > 270) {
|
||||
$this->AddPage('P', 'A4');
|
||||
}
|
||||
|
||||
$y = $this->GetY();
|
||||
|
||||
$this->SetFont('Arial', '', 9);
|
||||
$this->SetXY($startX, $y);
|
||||
$this->Cell($this->colWidths[0], $rowH, $kodeAkun, 1, 0, 'C');
|
||||
|
||||
$xNama = $this->GetX();
|
||||
$this->MultiCell($this->colWidths[1], $lineH, $namaAkun, 1, 'L');
|
||||
|
||||
$this->SetXY($xNama + $this->colWidths[1], $y);
|
||||
$this->Cell($this->colWidths[2], $rowH, $debit > 0 ? number_format($debit, 0, ',', '.') : '-', 1, 0, 'R');
|
||||
$this->Cell($this->colWidths[3], $rowH, $kredit > 0 ? number_format($kredit, 0, ',', '.') : '-', 1, 0, 'R');
|
||||
|
||||
$this->SetXY($startX, $y + $rowH);
|
||||
}
|
||||
}
|
||||
|
||||
// ---- BARIS TOTAL ----
|
||||
if ($this->GetY() + 8 > 270) {
|
||||
$this->AddPage('P', 'A4');
|
||||
}
|
||||
|
||||
$this->SetX($startX);
|
||||
$this->SetFillColor(255, 204, 102);
|
||||
$this->SetFont('Arial', 'B', 9);
|
||||
|
||||
$this->Cell($this->colWidths[0] + $this->colWidths[1], 8, 'TOTAL', 1, 0, 'C', true);
|
||||
$this->Cell($this->colWidths[2], 8, number_format($totalDebit, 0, ',', '.'), 1, 0, 'R', true);
|
||||
$this->Cell($this->colWidths[3], 8, number_format($totalKredit, 0, ',', '.'), 1, 1, 'R', true);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// RINGKASAN: STATUS BALANCE
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
public function NeracaSaldoRingkasan($totalDebit, $totalKredit)
|
||||
{
|
||||
if ($this->GetY() + 9 > 270) {
|
||||
$this->AddPage('P', 'A4');
|
||||
}
|
||||
|
||||
$startX = $this->_startX();
|
||||
$tableW = array_sum($this->colWidths);
|
||||
$labelW = $tableW - $this->colWidths[3];
|
||||
$selisih = $totalDebit - $totalKredit;
|
||||
|
||||
$this->Ln(2);
|
||||
$this->SetX($startX);
|
||||
$this->SetFont('Arial', 'B', 10);
|
||||
|
||||
if (abs($selisih) < 1) {
|
||||
$this->SetFillColor(198, 239, 206); // hijau, balance
|
||||
$label = 'SELISIH (BALANCE)';
|
||||
} else {
|
||||
$this->SetFillColor(255, 199, 206); // merah, tidak balance
|
||||
$label = 'SELISIH (TIDAK BALANCE)';
|
||||
}
|
||||
|
||||
$this->Cell($labelW, 8, $label, 1, 0, 'L', true);
|
||||
$this->Cell($this->colWidths[3], 8, number_format($selisih, 0, ',', '.'), 1, 1, 'R', true);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// TANDA TANGAN
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
public function Pembuat($nama, $jabatan = '')
|
||||
{
|
||||
$startX = $this->_startX();
|
||||
$tableW = array_sum($this->colWidths);
|
||||
$signWidth = 70;
|
||||
$signX = $startX + $tableW - $signWidth; // sejajar sisi kanan tabel
|
||||
|
||||
$this->Ln(3);
|
||||
// $this->SetX($signX);
|
||||
$this->SetFont('Arial', 'I', 9);
|
||||
$this->Cell($signWidth, 6, '*Mengetahui, (Finance Departement)', 0, 1, 'L');
|
||||
// $this->Cell($signWidth, 6, $nama, 0, 1, 'L');
|
||||
// if (!empty($jabatan)) {
|
||||
// $this->Cell($signWidth, 5, '(' . $jabatan . ')', 0, 1, 'L');
|
||||
// }
|
||||
// $this->Ln(0);
|
||||
// $this->SetX($signX);
|
||||
// $this->SetFont('Arial', 'B', 10);
|
||||
// $this->Cell($signWidth, 6, $nama, 0, 1, 'R');
|
||||
|
||||
// if (!empty($jabatan)) {
|
||||
// $this->SetX($signX);
|
||||
// $this->SetFont('Arial', '', 9);
|
||||
// $this->Cell($signWidth, 5, '(' . $jabatan . ')', 0, 1, 'R');
|
||||
// }
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// HELPER: Hitung jumlah baris yang dibutuhkan MultiCell
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
private function NbLines($w, $txt)
|
||||
{
|
||||
$cw = &$this->CurrentFont['cw'];
|
||||
if ($w == 0) {
|
||||
$w = $this->w - $this->rMargin - $this->x;
|
||||
}
|
||||
$wmax = ($w - 2 * $this->cMargin) * 1000 / $this->FontSize;
|
||||
$s = str_replace("\r", '', $txt);
|
||||
$nb = strlen($s);
|
||||
if ($nb > 0 && $s[$nb - 1] == "\n") {
|
||||
$nb--;
|
||||
}
|
||||
$sep = -1;
|
||||
$i = 0;
|
||||
$j = 0;
|
||||
$l = 0;
|
||||
$nl = 1;
|
||||
while ($i < $nb) {
|
||||
$c = $s[$i];
|
||||
if ($c == "\n") {
|
||||
$i++;
|
||||
$sep = -1;
|
||||
$j = $i;
|
||||
$l = 0;
|
||||
$nl++;
|
||||
continue;
|
||||
}
|
||||
if ($c == ' ') {
|
||||
$sep = $i;
|
||||
}
|
||||
$l += isset($cw[ord($c)]) ? $cw[ord($c)] : 600;
|
||||
if ($l > $wmax) {
|
||||
if ($sep == -1) {
|
||||
if ($i == $j) {
|
||||
$i++;
|
||||
}
|
||||
} else {
|
||||
$i = $sep + 1;
|
||||
}
|
||||
$sep = -1;
|
||||
$j = $i;
|
||||
$l = 0;
|
||||
$nl++;
|
||||
} else {
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
return $nl;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,556 @@
|
||||
<?php
|
||||
defined('BASEPATH') OR exit('No direct script access allowed');
|
||||
|
||||
class PeralatanTeknisi_model extends CI_Model {
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function get_teknisi_list()
|
||||
{
|
||||
return $this->db
|
||||
->select('e.id, e.employee_code, e.full_name, p.position_name, d.department_name')
|
||||
->from('k_employees e')
|
||||
->join('k_positions p', 'p.id = e.position_id', 'left')
|
||||
->join('k_departments d', 'd.id = e.department_id', 'left')
|
||||
->where('(e.resign_date IS NULL OR e.resign_date = \'0000-00-00\')')
|
||||
->where_in('LOWER(p.position_name)', ['teknisi', 'noc'])
|
||||
->order_by('e.full_name', 'ASC')
|
||||
->get()
|
||||
->result();
|
||||
}
|
||||
|
||||
public function get_barang_dibawa($user_id = null, $status = 'active')
|
||||
{
|
||||
$this->db
|
||||
->select("
|
||||
it.barcode,
|
||||
it.user_id,
|
||||
it.item_id,
|
||||
it.status,
|
||||
it.created_at,
|
||||
ib.serial_number,
|
||||
i.nama_barang,
|
||||
i.kode_detail,
|
||||
ke.full_name as teknisi_name,
|
||||
ke.employee_code
|
||||
")
|
||||
->from('item_technician it')
|
||||
->join('item_barcodes ib', 'ib.barcode = it.barcode', 'left')
|
||||
->join('items i', 'i.id = it.item_id', 'left')
|
||||
->join('k_employees ke', 'ke.id = it.user_id', 'left');
|
||||
|
||||
if ($user_id) {
|
||||
$this->db->where('it.user_id', $user_id);
|
||||
}
|
||||
|
||||
if ($status && in_array($status, ['active', 'inactive'])) {
|
||||
$this->db->where('it.status', $status);
|
||||
}
|
||||
|
||||
$this->db->order_by('it.created_at', 'DESC');
|
||||
|
||||
return $this->db->get()->result();
|
||||
}
|
||||
|
||||
public function get_item_by_barcode($barcode)
|
||||
{
|
||||
$barcode = trim($barcode);
|
||||
if ($barcode == '') return null;
|
||||
|
||||
$row = $this->db
|
||||
->select('ib.id as barcode_id, ib.item_id, ib.barcode, ib.serial_number, ib.qty_sisa, ib.status as barcode_status, i.nama_barang, i.kode_detail')
|
||||
->from('item_barcodes ib')
|
||||
->join('items i', 'i.id = ib.item_id', 'left')
|
||||
->where('ib.barcode', $barcode)
|
||||
->where('ib.qty_sisa >', 0)
|
||||
->get()
|
||||
->row();
|
||||
|
||||
if (!$row) {
|
||||
$row = $this->db
|
||||
->select('ib.id as barcode_id, ib.item_id, ib.barcode, ib.serial_number, ib.qty_sisa, ib.status as barcode_status, i.nama_barang, i.kode_detail')
|
||||
->from('item_barcodes ib')
|
||||
->join('items i', 'i.id = ib.item_id', 'left')
|
||||
->where('ib.serial_number', $barcode)
|
||||
->where('ib.qty_sisa >', 0)
|
||||
->get()
|
||||
->row();
|
||||
}
|
||||
|
||||
return $row;
|
||||
}
|
||||
|
||||
public function get_current_teknisi($barcode)
|
||||
{
|
||||
return $this->db
|
||||
->select('it.user_id, ke.full_name, ke.employee_code')
|
||||
->from('item_technician it')
|
||||
->join('k_employees ke', 'ke.id = it.user_id', 'left')
|
||||
->where('it.barcode', $barcode)
|
||||
->where('it.status', 'active')
|
||||
->get()
|
||||
->row();
|
||||
}
|
||||
|
||||
public function batch_kembalikan(array $barcodes)
|
||||
{
|
||||
$now = date('Y-m-d H:i:s');
|
||||
$errors = [];
|
||||
|
||||
$this->db->trans_start();
|
||||
|
||||
foreach ($barcodes as $barcode) {
|
||||
$barcode = trim($barcode);
|
||||
if ($barcode == '') continue;
|
||||
|
||||
$item = $this->db
|
||||
->select('it.*, ib.id as barcode_id, tec.full_name as teknisi_name')
|
||||
->from('item_technician it')
|
||||
->join('item_barcodes ib', 'ib.barcode = it.barcode', 'left')
|
||||
->join('k_employees tec', 'tec.id = it.user_id', 'left')
|
||||
->where('it.barcode', $barcode)
|
||||
->where('it.status', 'active')
|
||||
->get()
|
||||
->row();
|
||||
|
||||
if (!$item) {
|
||||
$errors[] = "Barcode $barcode tidak ditemukan atau sudah dikembalikan";
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->db->insert('item_movements', [
|
||||
'item_id' => $item->item_id,
|
||||
'barcode_id' => $item->barcode_id,
|
||||
'qty' => 1,
|
||||
'from_type' => 'technician',
|
||||
'from_id' => $item->user_id,
|
||||
'to_type' => 'warehouse',
|
||||
'to_id' => null,
|
||||
'movement_type' => 'transfer',
|
||||
'notes' => 'Pengembalian dari: ' . $item->teknisi_name . ' ke gudang',
|
||||
'created_at' => $now
|
||||
]);
|
||||
|
||||
$this->db
|
||||
->where('barcode', $barcode)
|
||||
->where('status', 'active')
|
||||
->update('item_technician', [
|
||||
'status' => 'inactive',
|
||||
'deleted_at' => $now
|
||||
]);
|
||||
|
||||
if ($item->barcode_id) {
|
||||
$this->db
|
||||
->where('id', $item->barcode_id)
|
||||
->update('item_barcodes', [
|
||||
'status' => 'available'
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
$this->db->trans_complete();
|
||||
|
||||
if ($this->db->trans_status() === FALSE) {
|
||||
return ['status' => false, 'message' => 'Gagal memproses pengembalian'];
|
||||
}
|
||||
|
||||
$success_count = count($barcodes) - count($errors);
|
||||
$msg = "$success_count barang berhasil dikembalikan";
|
||||
if (count($errors) > 0) {
|
||||
$msg .= '. ' . implode(', ', $errors);
|
||||
}
|
||||
|
||||
return ['status' => true, 'message' => $msg, 'errors' => $errors];
|
||||
}
|
||||
|
||||
public function get_dates_by_employee_id($user_id)
|
||||
{
|
||||
return $this->db
|
||||
->select("DATE(it.created_at) as tanggal")
|
||||
->from('item_technician it')
|
||||
->where('it.user_id', $user_id)
|
||||
->where('it.status', 'active')
|
||||
->group_by("DATE(it.created_at)")
|
||||
->order_by("DATE(it.created_at)", "DESC")
|
||||
->get()
|
||||
->result();
|
||||
}
|
||||
|
||||
public function batch_dibawa(array $barcodes, $user_id)
|
||||
{
|
||||
$now = date('Y-m-d H:i:s');
|
||||
$errors = [];
|
||||
$skipped = 0;
|
||||
|
||||
$technician = $this->db
|
||||
->select('id, full_name, employee_code')
|
||||
->from('k_employees')
|
||||
->where('id', $user_id)
|
||||
->get()
|
||||
->row();
|
||||
|
||||
$this->db->trans_start();
|
||||
|
||||
foreach ($barcodes as $barcode) {
|
||||
$barcode = trim($barcode);
|
||||
if ($barcode == '') continue;
|
||||
|
||||
$barcode_row = $this->db
|
||||
->select('ib.id as barcode_id, ib.item_id, ib.barcode')
|
||||
->from('item_barcodes ib')
|
||||
->where('ib.barcode', $barcode)
|
||||
->where_not_in('ib.status', ['damaged', 'lost', 'returned_supplier', 'sold_out'])
|
||||
->get()
|
||||
->row();
|
||||
|
||||
if (!$barcode_row) {
|
||||
$errors[] = "Barcode $barcode tidak valid atau tidak tersedia";
|
||||
continue;
|
||||
}
|
||||
|
||||
$already = $this->db
|
||||
->where('barcode', $barcode_row->barcode)
|
||||
->where('status', 'active')
|
||||
->get('item_technician')
|
||||
->row();
|
||||
|
||||
if ($already) {
|
||||
if ($already->user_id == $user_id) {
|
||||
$skipped++;
|
||||
continue;
|
||||
}
|
||||
$errors[] = "Barang $barcode sedang dibawa teknisi lain";
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->db->insert('item_technician', [
|
||||
'user_id' => $user_id,
|
||||
'barcode' => $barcode_row->barcode,
|
||||
'item_id' => $barcode_row->item_id,
|
||||
'status' => 'active',
|
||||
'created_at' => $now
|
||||
]);
|
||||
|
||||
$last_movement = $this->db
|
||||
->where('barcode_id', $barcode_row->barcode_id)
|
||||
->order_by('id', 'DESC')
|
||||
->limit(1)
|
||||
->get('item_movements')
|
||||
->row();
|
||||
|
||||
$from_type = 'warehouse';
|
||||
$from_id = null;
|
||||
if ($last_movement) {
|
||||
$from_type = $last_movement->to_type;
|
||||
$from_id = $last_movement->to_id;
|
||||
}
|
||||
|
||||
$this->db->insert('item_movements', [
|
||||
'item_id' => $barcode_row->item_id,
|
||||
'barcode_id' => $barcode_row->barcode_id,
|
||||
'qty' => 1,
|
||||
'from_type' => $from_type,
|
||||
'from_id' => $from_id,
|
||||
'to_type' => 'technician',
|
||||
'to_id' => $user_id,
|
||||
'movement_type' => 'transfer',
|
||||
'notes' => 'Dibawa oleh: ' . $technician->full_name . ' dari gudang',
|
||||
'created_at' => $now
|
||||
]);
|
||||
|
||||
$this->db
|
||||
->where('id', $barcode_row->barcode_id)
|
||||
->update('item_barcodes', [
|
||||
'status' => 'installed'
|
||||
]);
|
||||
}
|
||||
|
||||
$this->db->trans_complete();
|
||||
|
||||
if ($this->db->trans_status() === FALSE) {
|
||||
return ['status' => false, 'message' => 'Gagal memproses penugasan barang'];
|
||||
}
|
||||
|
||||
$success_count = count($barcodes) - count($errors) - $skipped;
|
||||
$msg = "$success_count barang berhasil ditugaskan";
|
||||
if ($skipped > 0) {
|
||||
$msg .= ". $skipped barang sudah ditugaskan ke teknisi yang sama";
|
||||
}
|
||||
if (count($errors) > 0) {
|
||||
$msg .= '. ' . implode(', ', $errors);
|
||||
}
|
||||
|
||||
return ['status' => true, 'message' => $msg, 'errors' => $errors];
|
||||
}
|
||||
|
||||
public function get_teknisi_table_data($start, $length, $search, $order_col, $order_dir)
|
||||
{
|
||||
$sort_cols = [
|
||||
1 => 'e.full_name',
|
||||
2 => 'e.employee_code',
|
||||
3 => 'd.department_name',
|
||||
4 => 'item_count'
|
||||
];
|
||||
|
||||
$this->db
|
||||
->select("
|
||||
e.id,
|
||||
e.full_name,
|
||||
e.employee_code,
|
||||
p.position_name,
|
||||
d.department_name,
|
||||
(SELECT COUNT(*) FROM item_technician it WHERE it.user_id = e.id AND it.status = 'active') as item_count
|
||||
")
|
||||
->from('k_employees e')
|
||||
->join('k_positions p', 'p.id = e.position_id', 'left')
|
||||
->join('k_departments d', 'd.id = e.department_id', 'left')
|
||||
->where('(e.resign_date IS NULL OR e.resign_date = \'0000-00-00\')')
|
||||
->where_in('LOWER(p.position_name)', ['teknisi', 'noc']);
|
||||
|
||||
if (!empty($search)) {
|
||||
$this->db->group_start()
|
||||
->like('LOWER(e.full_name)', strtolower($search))
|
||||
->or_like('LOWER(e.employee_code)', strtolower($search))
|
||||
->or_like('LOWER(d.department_name)', strtolower($search))
|
||||
->group_end();
|
||||
}
|
||||
|
||||
if (isset($sort_cols[$order_col])) {
|
||||
$this->db->order_by($sort_cols[$order_col], $order_dir);
|
||||
} else {
|
||||
$this->db->order_by('e.full_name', 'ASC');
|
||||
}
|
||||
|
||||
if ($length > 0) {
|
||||
$this->db->limit($length, $start);
|
||||
}
|
||||
|
||||
return $this->db->get()->result();
|
||||
}
|
||||
|
||||
public function count_all_teknisi()
|
||||
{
|
||||
$this->db
|
||||
->from('k_employees e')
|
||||
->join('k_positions p', 'p.id = e.position_id', 'left')
|
||||
->where('(e.resign_date IS NULL OR e.resign_date = \'0000-00-00\')')
|
||||
->where_in('LOWER(p.position_name)', ['teknisi', 'noc']);
|
||||
|
||||
return $this->db->count_all_results();
|
||||
}
|
||||
|
||||
public function count_filtered_teknisi($search)
|
||||
{
|
||||
$this->db
|
||||
->from('k_employees e')
|
||||
->join('k_positions p', 'p.id = e.position_id', 'left')
|
||||
->join('k_departments d', 'd.id = e.department_id', 'left')
|
||||
->where('(e.resign_date IS NULL OR e.resign_date = \'0000-00-00\')')
|
||||
->where_in('LOWER(p.position_name)', ['teknisi', 'noc']);
|
||||
|
||||
if (!empty($search)) {
|
||||
$this->db->group_start()
|
||||
->like('LOWER(e.full_name)', strtolower($search))
|
||||
->or_like('LOWER(e.employee_code)', strtolower($search))
|
||||
->or_like('LOWER(d.department_name)', strtolower($search))
|
||||
->group_end();
|
||||
}
|
||||
|
||||
return $this->db->count_all_results();
|
||||
}
|
||||
|
||||
public function get_teknisi_info($user_id)
|
||||
{
|
||||
return $this->db
|
||||
->select('e.id, e.full_name, e.employee_code, p.position_name, d.department_name')
|
||||
->from('k_employees e')
|
||||
->join('k_positions p', 'p.id = e.position_id', 'left')
|
||||
->join('k_departments d', 'd.id = e.department_id', 'left')
|
||||
->where('e.id', $user_id)
|
||||
->get()
|
||||
->row();
|
||||
}
|
||||
|
||||
public function get_teknisi_items_data($user_id, $start, $length, $search, $order_col, $order_dir)
|
||||
{
|
||||
$sort_cols = [
|
||||
1 => 'it.created_at',
|
||||
2 => 'it.barcode',
|
||||
3 => 'i.nama_barang',
|
||||
4 => 'ib.serial_number',
|
||||
5 => 'it.status'
|
||||
];
|
||||
|
||||
$this->db
|
||||
->select("
|
||||
it.barcode,
|
||||
i.nama_barang,
|
||||
ib.serial_number,
|
||||
ib.qty_sisa,
|
||||
it.status,
|
||||
DATE_FORMAT(it.created_at, '%d/%m/%Y') as tanggal
|
||||
")
|
||||
->from('item_technician it')
|
||||
->join('items i', 'i.id = it.item_id', 'left')
|
||||
->join('item_barcodes ib', 'ib.barcode = it.barcode', 'left')
|
||||
->where('it.user_id', $user_id)
|
||||
->where('it.status', 'active');
|
||||
|
||||
if (!empty($search)) {
|
||||
$this->db->group_start()
|
||||
->like('it.barcode', $search)
|
||||
->or_like('LOWER(i.nama_barang)', strtolower($search))
|
||||
->or_like('ib.serial_number', $search)
|
||||
->group_end();
|
||||
}
|
||||
|
||||
if (isset($sort_cols[$order_col])) {
|
||||
$this->db->order_by($sort_cols[$order_col], $order_dir);
|
||||
} else {
|
||||
$this->db->order_by('it.created_at', 'DESC');
|
||||
}
|
||||
|
||||
if ($length > 0) {
|
||||
$this->db->limit($length, $start);
|
||||
}
|
||||
|
||||
return $this->db->get()->result();
|
||||
}
|
||||
|
||||
public function count_all_teknisi_items($user_id)
|
||||
{
|
||||
$this->db
|
||||
->from('item_technician it')
|
||||
->where('it.user_id', $user_id)
|
||||
->where('it.status', 'active');
|
||||
|
||||
return $this->db->count_all_results();
|
||||
}
|
||||
|
||||
public function count_filtered_teknisi_items($user_id, $search)
|
||||
{
|
||||
$this->db
|
||||
->from('item_technician it')
|
||||
->join('items i', 'i.id = it.item_id', 'left')
|
||||
->join('item_barcodes ib', 'ib.barcode = it.barcode', 'left')
|
||||
->where('it.user_id', $user_id)
|
||||
->where('it.status', 'active');
|
||||
|
||||
if (!empty($search)) {
|
||||
$this->db->group_start()
|
||||
->like('it.barcode', $search)
|
||||
->or_like('LOWER(i.nama_barang)', strtolower($search))
|
||||
->or_like('ib.serial_number', $search)
|
||||
->group_end();
|
||||
}
|
||||
|
||||
return $this->db->count_all_results();
|
||||
}
|
||||
|
||||
public function get_transfer_data()
|
||||
{
|
||||
return $this->db
|
||||
->select("
|
||||
it.barcode,
|
||||
i.nama_barang,
|
||||
ib.serial_number,
|
||||
from_ke.full_name as from_name,
|
||||
from_ke.employee_code as from_code,
|
||||
to_ke.full_name as to_name,
|
||||
to_ke.employee_code as to_code,
|
||||
im.created_at,
|
||||
im.movement_type
|
||||
")
|
||||
->from('item_movements im')
|
||||
->join('items i', 'i.id = im.item_id', 'left')
|
||||
->join('item_barcodes ib', 'ib.id = im.barcode_id', 'left')
|
||||
->join('k_employees from_ke', 'from_ke.id = im.from_id', 'left')
|
||||
->join('k_employees to_ke', 'to_ke.id = im.to_id', 'left')
|
||||
->where('im.movement_type', 'installation')
|
||||
->where('im.from_type', 'technician')
|
||||
->where('im.to_type', 'technician')
|
||||
->order_by('im.created_at', 'DESC')
|
||||
->limit(200)
|
||||
->get()
|
||||
->result();
|
||||
}
|
||||
|
||||
public function detail_by_barcode($barcode)
|
||||
{
|
||||
return $this->db
|
||||
->select("
|
||||
it.*,
|
||||
ib.serial_number,
|
||||
ib.qty_sisa,
|
||||
ib.status as barcode_status,
|
||||
i.nama_barang,
|
||||
i.kode_detail,
|
||||
ke.full_name as teknisi_name,
|
||||
ke.employee_code
|
||||
")
|
||||
->from('item_technician it')
|
||||
->join('item_barcodes ib', 'ib.barcode = it.barcode', 'left')
|
||||
->join('items i', 'i.id = it.item_id', 'left')
|
||||
->join('k_employees ke', 'ke.id = it.user_id', 'left')
|
||||
->where('it.barcode', $barcode)
|
||||
->get()
|
||||
->row();
|
||||
}
|
||||
|
||||
public function get_teknisi_items_full($user_id, $tanggal = null)
|
||||
{
|
||||
$this->db
|
||||
->select("
|
||||
it.barcode,
|
||||
i.nama_barang,
|
||||
ib.serial_number,
|
||||
ib.qty_sisa,
|
||||
it.status,
|
||||
DATE_FORMAT(it.created_at, '%d/%m/%Y') as tanggal
|
||||
")
|
||||
->from('item_technician it')
|
||||
->join('items i', 'i.id = it.item_id', 'left')
|
||||
->join('item_barcodes ib', 'ib.barcode = it.barcode', 'left')
|
||||
->where('it.user_id', $user_id)
|
||||
->where('it.status', 'active');
|
||||
|
||||
if ($tanggal AND $tanggal != 'all') {
|
||||
$this->db->where('DATE(it.created_at)', $tanggal);
|
||||
}
|
||||
|
||||
return $this->db
|
||||
->order_by('it.created_at', 'ASC')
|
||||
->get()
|
||||
->result();
|
||||
}
|
||||
|
||||
public function generate_no_faktur()
|
||||
{
|
||||
return 'GRJN/K/' . date('d/m/Y');
|
||||
}
|
||||
|
||||
public function get_log_barang()
|
||||
{
|
||||
return $this->db
|
||||
->select("
|
||||
it.barcode,
|
||||
it.item_id,
|
||||
it.user_id,
|
||||
it.status,
|
||||
it.created_at,
|
||||
it.deleted_at,
|
||||
ke.full_name as teknisi_name,
|
||||
i.kode_detail as item_code,
|
||||
i.nama_barang
|
||||
")
|
||||
->from('item_technician it')
|
||||
->join('k_employees ke', 'ke.id = it.user_id', 'left')
|
||||
->join('items i', 'i.id = it.item_id', 'left')
|
||||
->order_by('it.created_at', 'DESC')
|
||||
->get()
|
||||
->result();
|
||||
}
|
||||
}
|
||||
@@ -16,9 +16,10 @@
|
||||
<tr>
|
||||
<th>No</th>
|
||||
<th>Tanggal</th>
|
||||
<th>Kode</th>
|
||||
<th>Nama</th>
|
||||
<th style="min-width: 80px;">Kode</th>
|
||||
<th style="min-width: 80px;">Nama</th>
|
||||
<th>Lokasi Asset</th>
|
||||
<th style="max-width: 150px;">Keterangan</th>
|
||||
<th>Nilai</th>
|
||||
<th>Masa</th>
|
||||
<th>Susut/Bulan</th>
|
||||
@@ -56,9 +57,10 @@
|
||||
<input type="text" id="nama_asset" class="form-control">
|
||||
|
||||
<label class="mt-2">Keterangan</label>
|
||||
<input type="text" id="keterangan" class="form-control">
|
||||
<textarea rows="3" id="keterangan" class="form-control"></textarea>
|
||||
<!-- <input type="text" id="keterangan" class="form-control"> -->
|
||||
|
||||
<label class="mt-2">Harga Per Unit</label>
|
||||
<label class="mt-2">Harga</label>
|
||||
<input type="text" id="harga" class="form-control format-rupiah">
|
||||
|
||||
<label class="mt-2">Tanggal</label>
|
||||
@@ -99,7 +101,8 @@
|
||||
<select id="edit_lokasi_asset" class="form-control"></select>
|
||||
|
||||
<label class="mt-2">Keterangan</label>
|
||||
<input type="text" id="edit_keterangan" class="form-control">
|
||||
<!-- <input type="text" id="edit_keterangan" class="form-control"> -->
|
||||
<textarea rows="3" id="edit_keterangan" class="form-control"></textarea>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-primary" id="btnUpdate">Update</button>
|
||||
@@ -146,7 +149,7 @@
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<input type="hidden" id="s_item_id">
|
||||
<input type="hidden" id="s_harga">
|
||||
<!-- <input type="hidden" id="s_harga"> -->
|
||||
|
||||
<label class="mt-2">Akun Asset (Debit)</label>
|
||||
<select id="s_account_asset" class="form-control"></select>
|
||||
@@ -155,14 +158,33 @@
|
||||
<select id="s_lokasi_asset" class="form-control"></select>
|
||||
|
||||
<label>Pilih Barang</label>
|
||||
<select id="s_item" class="form-control"></select>
|
||||
<select id="s_item" class="form-control select-search"></select>
|
||||
|
||||
<label class="mt-2">Stok Tersedia</label>
|
||||
<input type="text" id="s_stok" class="form-control" readonly>
|
||||
<div class="mb-2 d-none" id="wrap_barcode_select">
|
||||
<label for="barcode_select" class="form-label">Barcode / QR ID</label>
|
||||
<select id="barcode_select" class="form-control select-search"></select>
|
||||
</div>
|
||||
|
||||
<div id="stokSisaQty"></div>
|
||||
|
||||
<label class="mt-2">Harga Barang</label>
|
||||
<input type="text" id="s_harga" class="form-control format-rupiah" disabled>
|
||||
|
||||
<div class="mb-0 d-none" id="wrap_qty">
|
||||
<label class="mt-2">Qty</label>
|
||||
<input type="number" id="s_qty" placeholder="1" class="form-control">
|
||||
|
||||
<label class="mt-2">Total Harga</label>
|
||||
<input type="text" id="s_total_harga" class="form-control format-rupiah" disabled>
|
||||
</div>
|
||||
|
||||
<label class="mt-2">Tanggal</label>
|
||||
<input type="date" id="s_tanggal" class="form-control">
|
||||
|
||||
<label class="mt-2">Keterangan</label>
|
||||
<textarea rows="3" id="s_keterangan" class="form-control"></textarea>
|
||||
<!-- <input type="text" id="s_keterangan" class="form-control"> -->
|
||||
|
||||
<hr>
|
||||
|
||||
<label>Masa Manfaat (bulan)</label>
|
||||
@@ -224,6 +246,10 @@ function formatRupiah(angka){
|
||||
return angka.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ".");
|
||||
}
|
||||
|
||||
function parseNumber(val){
|
||||
return parseInt((val || '').toString().replace(/\D/g,'')) || 0;
|
||||
}
|
||||
|
||||
function setSelectValue(selector, value){
|
||||
setTimeout(()=>{
|
||||
$(selector).val(value).trigger('change');
|
||||
@@ -269,7 +295,7 @@ function loadItems(){
|
||||
$.get("<?= base_url('asset/get_items'); ?>",res=>{
|
||||
let opt='<option value="">-- PILIH BARANG --</option>';
|
||||
res.forEach(i=>{
|
||||
opt += `<option value="${i.id}" data-harga="${i.harga_beli}" data-stok="${i.stok}" data-nama="${i.nama_barang}">
|
||||
opt += `<option value="${i.id}" data-id="${i.id}" data-harga="${i.harga_beli}" data-stok="${i.stok}" data-nama="${i.nama_barang}" data-type="${i.tracking_type}">
|
||||
${i.kode_detail} - ${i.nama_barang} - ( Stok: ${i.stok})
|
||||
</option>`;
|
||||
});
|
||||
@@ -282,20 +308,81 @@ function hitungSusut(){
|
||||
let harga = parseInt($('#harga').val().replace(/\D/g,'')) || 0;
|
||||
let residu = parseInt($('#residu').val().replace(/\D/g,'')) || 0;
|
||||
let masa = parseInt($('#masa').val()) || 1;
|
||||
let susut = (harga - residu) / masa;
|
||||
$('#susut').val(formatRupiah(Math.round(susut)));
|
||||
|
||||
let maxResidu = harga / 100 * 80;
|
||||
|
||||
if (residu > maxResidu) {
|
||||
residu = maxResidu;
|
||||
$('#residu').val(maxResidu).trigger('input');
|
||||
}
|
||||
|
||||
let susut = (harga - residu) / masa;
|
||||
susut = Math.max(0, susut);
|
||||
$('#susut').val(susut);
|
||||
}
|
||||
|
||||
// ================= LOAD BARCODE ITEMS =================
|
||||
// function loadItemBarcodes(item_id){
|
||||
// return $.get("<?= base_url('items/get_list_barcode_id/'); ?>" + item_id, function(res){
|
||||
// if(res && res.length){
|
||||
// let opt = '<option value="">Pilih Item</option>';
|
||||
// res.forEach(i=>{
|
||||
// opt += `<option value="${i.barcode_id}">
|
||||
// ${i.nama_barang} - ${i.barcode} - ${i.serial_number}
|
||||
// </option>`;
|
||||
// });
|
||||
// $('#barcode_select').html(opt);
|
||||
// $('#wrap_barcode_select').removeClass('d-none');
|
||||
// } else {
|
||||
// $('#wrap_barcode_select').addClass('d-none');
|
||||
// }
|
||||
// }, 'json');
|
||||
// }
|
||||
|
||||
function loadItemBarcodes(item_id, selectedBarcode = null){
|
||||
return $.get("<?= base_url('items/get_list_barcode_id/'); ?>" + item_id, function(res){
|
||||
if(res && res.length){
|
||||
let opt = '<option value="">Pilih Barcode</option>';
|
||||
res.forEach(i=>{
|
||||
opt += `
|
||||
<option value="${i.barcode_id}" data-qty_sisa="${i.qty_sisa}">
|
||||
${i.nama_barang} - ${i.barcode} - ${i.serial_number}
|
||||
</option>
|
||||
`;
|
||||
});
|
||||
$('#barcode_select').html(opt);
|
||||
if(selectedBarcode){
|
||||
$('#barcode_select').val(selectedBarcode);
|
||||
}
|
||||
$('#wrap_barcode_select').removeClass('d-none');
|
||||
|
||||
// setMode('item');
|
||||
}else{
|
||||
$('#wrap_barcode_select').addClass('d-none');
|
||||
// setMode('manual');
|
||||
}
|
||||
},'json');
|
||||
}
|
||||
|
||||
|
||||
$('#masa, #residu, #harga').on('keyup change',hitungSusut);
|
||||
|
||||
// ================= HITUNG SUSUT STOCK =================
|
||||
$('#s_masa, #s_residu').on('keyup change',function(){
|
||||
let harga = parseInt($('#s_harga').val()) || 0;
|
||||
let residu = parseInt($('#s_residu').val().replace(/\D/g,'')) || 0;
|
||||
function hitungSusutStock(){
|
||||
let trackingType = ($('#s_item option:selected').data('type') || '').toString().trim().toUpperCase();
|
||||
let hargaDasar = trackingType === 'QTY' ? parseNumber($('#s_total_harga').val()) : parseNumber($('#s_harga').val());
|
||||
let residu = parseNumber($('#s_residu').val());
|
||||
let masa = parseInt($('#s_masa').val()) || 1;
|
||||
let susut = (harga - residu) / masa;
|
||||
$('#s_susut').val(formatRupiah(Math.round(susut)));
|
||||
});
|
||||
let maxResidu = hargaDasar / 100 * 80;
|
||||
|
||||
if (residu > maxResidu) {
|
||||
residu = maxResidu;
|
||||
$('#s_residu').val(maxResidu).trigger('input');
|
||||
}
|
||||
let susut = (hargaDasar - residu) / masa;
|
||||
susut = Math.max(0, susut);
|
||||
$('#s_susut').val(susut);
|
||||
}
|
||||
$('#s_masa, #s_residu').on('keyup change', hitungSusutStock);
|
||||
|
||||
// ================= TAMBAH ASSET MANUAL =================
|
||||
$('.btn-add-asset').click(function(){
|
||||
@@ -303,26 +390,77 @@ $('.btn-add-asset').click(function(){
|
||||
$('#modalTitleAsset').text('Tambah Asset');
|
||||
$('#modalAsset input[type="text"], #modalAsset input[type="date"], #modalAsset input[type="number"], #modalAsset select').val('');
|
||||
$('#id_asset').val('');
|
||||
$('#stokSisaQty').html('');
|
||||
loadKas(); loadAsset(); loadLokasiAsset();
|
||||
$('#modalAsset').modal('show');
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
// ================= DETAIL & HISTORY =================
|
||||
$(document).on('click','.btn-detail',function(){
|
||||
let id = $(this).data('id');
|
||||
|
||||
$.get("<?= base_url('asset/detail/'); ?>"+id, function(asset){
|
||||
$('#detailContent').html(`
|
||||
<table class="table table-borderless">
|
||||
<tr><td><strong>Kode Asset</strong></td><td>: <strong>${asset.kode_asset || '-'}</strong></td></tr>
|
||||
<tr><td><strong>Nama Asset</strong></td><td>: <strong>${asset.nama_asset}</strong></td></tr>
|
||||
<tr><td><strong>Lokasi</strong></td><td>: <strong>${asset.nama_lokasi || '-'}</strong></td></tr>
|
||||
<tr><td><strong>Harga/Unit</strong></td><td>: <strong>Rp ${formatRupiah(asset.harga_per_unit)}</strong></td></tr>
|
||||
<tr><td><strong>Nilai Buku</strong></td><td>: <strong>Rp ${formatRupiah(asset.nilai_buku)}</strong></td></tr>
|
||||
<tr><td><strong>Masa Manfaat</strong></td><td>: <strong>${asset.masa_manfaat} bulan</strong></td></tr>
|
||||
<tr><td><strong>Sumber</strong></td><td>: <strong>${asset.sumber}</strong></td></tr>
|
||||
<tr><td><strong>Keterangan</strong></td><td>: <strong>${asset.keterangan || '-'}</strong></td></tr>
|
||||
<div class="row">
|
||||
|
||||
<div class="col-md-6">
|
||||
<table class="table table-borderless table-sm">
|
||||
<tr>
|
||||
<td width="45%"><strong>Kode Asset</strong></td>
|
||||
<td>: <strong>${asset.kode_asset || '-'}</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Nama Asset</strong></td>
|
||||
<td>: <strong>${asset.nama_asset}</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Lokasi</strong></td>
|
||||
<td>: <strong>${asset.nama_lokasi || '-'}</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Harga/Unit</strong></td>
|
||||
<td>: <strong>Rp ${formatRupiah(asset.harga_per_unit)}</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Qty</strong></td>
|
||||
<td>: <strong>${asset.qty}</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Nilai Perolehan</strong></td>
|
||||
<td>: <strong>Rp ${formatRupiah(asset.nilai_perolehan)}</strong></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<table class="table table-borderless table-sm">
|
||||
<tr>
|
||||
<td width="45%"><strong>Akumulasi Penyusutan</strong></td>
|
||||
<td>: <strong>Rp ${formatRupiah(asset.akumulasi_penyusutan || 0)}</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Nilai Buku</strong></td>
|
||||
<td>: <strong>Rp ${formatRupiah(asset.nilai_buku)}</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Masa Manfaat</strong></td>
|
||||
<td>: <strong>${asset.masa_manfaat} bulan</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Sumber</strong></td>
|
||||
<td>: <strong>${asset.sumber}</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Keterangan</strong></td>
|
||||
<td>: <strong>${asset.keterangan || '-'}</strong></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
`);
|
||||
|
||||
// Load history
|
||||
@@ -336,7 +474,7 @@ $(document).on('click','.btn-detail',function(){
|
||||
h.tipe=='penambahan' ? 'bg-info' :
|
||||
h.tipe=='pengurangan' ? 'bg-warning' : 'bg-secondary';
|
||||
rows += `<tr>
|
||||
<td>${h.created_at}</td>
|
||||
<td>${h.tanggal_dibuat}</td>
|
||||
<td><span class="badge ${badgeClass}">${h.tipe}</span></td>
|
||||
<td>Rp ${formatRupiah(h.nilai || 0)}</td>
|
||||
<td>${h.keterangan}</td>
|
||||
@@ -465,35 +603,157 @@ $('#btnSave').click(function(){
|
||||
});
|
||||
|
||||
// ================= FROM STOCK =================
|
||||
$('.btn-from-stock').click(function(){
|
||||
function resetStockForm(){
|
||||
$('#modalAssetStock input[type="text"], #modalAssetStock input[type="date"], #modalAssetStock input[type="number"], #modalAssetStock select').val('');
|
||||
$('#s_item_id, #s_harga').val('');
|
||||
$('#s_item_id, #s_harga, #s_total_harga, #s_susut, #s_keterangan').val('');
|
||||
$('#s_qty').val(1);
|
||||
$('#stokSisaQty').html('');
|
||||
$('#wrap_qty').addClass('d-none');
|
||||
$('#wrap_barcode_select').removeClass('d-none');
|
||||
$('#barcode_select').html('<option value="">Loading...</option>');
|
||||
}
|
||||
|
||||
$('.btn-from-stock').click(function(){
|
||||
resetStockForm();
|
||||
loadItems();
|
||||
loadAsset();
|
||||
loadLokasiAsset();
|
||||
$('#modalAssetStock').modal('show');
|
||||
});
|
||||
|
||||
// ================= PILIH ITEM STOCK =================
|
||||
$(document).on('change','#s_item',function(){
|
||||
|
||||
$('#barcode_select').on('change', function(){
|
||||
let selectedItem = $('#s_item option:selected');
|
||||
let type = (selectedItem.data('type') || '').toString().trim().toUpperCase();
|
||||
let selected = $(this).find(':selected');
|
||||
let harga = selected.data('harga') || 0;
|
||||
let stok = selected.data('stok') || 0;
|
||||
$('#s_item_id').val($(this).val());
|
||||
let qty_sisa = parseFloat(selected.data('qty_sisa')) || 0;
|
||||
|
||||
if($(this).val()){
|
||||
$('#stokSisaQty').html('<h6 class="text-success ml-4">Stok : ' + qty_sisa + '</h6><hr>');
|
||||
}else{
|
||||
$('#stokSisaQty').html('');
|
||||
}
|
||||
|
||||
// perlakuan qty setelah barcode dipilih, berdasarkan tracking_type
|
||||
if(type === 'UNIT'){
|
||||
$('#stokSisaQty').html('');
|
||||
$('#wrap_qty').addClass('d-none');
|
||||
$('#s_qty').removeAttr('max').val(1);
|
||||
$('#s_total_harga').val(parseNumber($('#s_harga').val()));
|
||||
hitungSusutStock();
|
||||
}else{
|
||||
$('#wrap_qty').removeClass('d-none');
|
||||
$('#s_qty').attr('min', 1).attr('max', qty_sisa > 0 ? qty_sisa : 1).val('');
|
||||
$('#s_total_harga').val('');
|
||||
}
|
||||
});
|
||||
|
||||
$('#s_item').on('change', function(){
|
||||
let selected = $(this).find(':selected');
|
||||
let item_id = selected.data('id');
|
||||
let type = selected.data('type');
|
||||
let harga = parseInt(selected.data('harga')) || 0;
|
||||
let stok = parseFloat(selected.data('stok')) || 0;
|
||||
|
||||
$('#s_item_id').val(item_id || '');
|
||||
$('#s_harga').val(harga);
|
||||
$('#s_stok').val(stok);
|
||||
$('#s_total_harga').val(harga);
|
||||
$('#stokSisaQty').html('');
|
||||
$('#s_qty').val(1).removeAttr('max');
|
||||
|
||||
$('#wrap_qty').addClass('d-none');
|
||||
$('#wrap_barcode_select').addClass('d-none');
|
||||
$('#barcode_select').html('<option value="">Pilih Barcode</option>');
|
||||
|
||||
if(!item_id){
|
||||
return;
|
||||
}
|
||||
|
||||
let normalizedType = (type || '').toString().trim().toUpperCase();
|
||||
// barcode tetap harus muncul untuk ITEM maupun QTY
|
||||
loadItemBarcodes(item_id, null).done(function(res){
|
||||
if(res && res.length){
|
||||
$('#wrap_barcode_select').removeClass('d-none');
|
||||
}else{
|
||||
$('#wrap_barcode_select').addClass('d-none');
|
||||
$('#barcode_select').html('<option value="">Barcode tidak tersedia</option>');
|
||||
}
|
||||
|
||||
// sebelum pilih barcode: qty & total belum ditentukan
|
||||
$('#wrap_qty').addClass('d-none');
|
||||
$('#s_qty').removeAttr('max').val('');
|
||||
$('#s_total_harga').val('');
|
||||
}).fail(function(){
|
||||
$('#wrap_barcode_select').addClass('d-none');
|
||||
$('#barcode_select').html('<option value="">Gagal load barcode</option>');
|
||||
$('#wrap_qty').addClass('d-none');
|
||||
$('#s_qty').removeAttr('max').val('');
|
||||
$('#s_total_harga').val('');
|
||||
});
|
||||
});
|
||||
|
||||
$('#s_qty').on('keyup change',function(){
|
||||
let selectedItem = $('#s_item option:selected');
|
||||
let type = (selectedItem.data('type') || '').toString().trim().toUpperCase();
|
||||
let qtyRaw = $('#s_qty').val();
|
||||
let qty = parseInt(qtyRaw);
|
||||
let harga = parseNumber($('#s_harga').val());
|
||||
let max = parseFloat($('#s_qty').attr('max')) || 0;
|
||||
|
||||
if(type === 'UNIT'){
|
||||
$('#wrap_qty').addClass('d-none');
|
||||
$('#s_qty').val(1);
|
||||
$('#s_total_harga').val(harga);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!qtyRaw || isNaN(qty) || qty < 1){
|
||||
$('#s_total_harga').val('');
|
||||
return;
|
||||
}
|
||||
|
||||
if(max > 0 && qty > max){
|
||||
qty = max;
|
||||
Swal.fire('Warning','Stok tidak cukup! Max: ' + max,'warning');
|
||||
}
|
||||
|
||||
let total = harga * qty;
|
||||
$('#s_qty').val(qty);
|
||||
$('#s_total_harga').val(total);
|
||||
hitungSusutStock();
|
||||
});
|
||||
|
||||
// ================= SAVE FROM STOCK =================
|
||||
$('#btnSaveStock').click(function(){
|
||||
let btn = $(this);
|
||||
let trackingType = ($('#s_item option:selected').data('type') || '').toString().trim().toUpperCase();
|
||||
let maxQty = parseFloat($('#s_qty').attr('max')) || 0;
|
||||
let qtyRaw = $('#s_qty').val();
|
||||
let qty = parseInt(qtyRaw);
|
||||
|
||||
if(trackingType === 'UNIT'){
|
||||
qty = 1;
|
||||
}else{
|
||||
if(!qtyRaw || isNaN(qty) || qty < 1){
|
||||
Swal.fire('Error','Qty wajib diisi untuk item tracking QTY','error');
|
||||
return;
|
||||
}
|
||||
if(maxQty > 0 && qty > maxQty){
|
||||
Swal.fire('Error','Qty melebihi stok tersedia','error');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let data = {
|
||||
item_id: $('#s_item').val(),
|
||||
s_tanggal: $('#s_tanggal').val(),
|
||||
s_masa: $('#s_masa').val(),
|
||||
s_residu: $('#s_residu').val(),
|
||||
s_lokasi_asset: $('#s_lokasi_asset').val(),
|
||||
account_asset: $('#s_account_asset').val()
|
||||
account_asset: $('#s_account_asset').val(),
|
||||
s_keterangan: $('#s_keterangan').val(),
|
||||
barcode_id: $('#barcode_select').val(),
|
||||
qty: qty
|
||||
};
|
||||
|
||||
// Validasi
|
||||
@@ -502,6 +762,15 @@ $('#btnSaveStock').click(function(){
|
||||
return;
|
||||
}
|
||||
|
||||
if(!data.barcode_id){
|
||||
Swal.fire('Error','Barcode wajib dipilih','error');
|
||||
return;
|
||||
}
|
||||
|
||||
if(trackingType === 'UNIT'){
|
||||
data.qty = 1;
|
||||
}
|
||||
|
||||
btn.prop('disabled', true).html('Menyimpan...');
|
||||
|
||||
$.post("<?= base_url('asset/save_from_stock'); ?>",data,function(res){
|
||||
@@ -523,5 +792,6 @@ $('#btnSaveStock').click(function(){
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
</script>
|
||||
@@ -13,19 +13,23 @@
|
||||
<select id="account_id" class="form-control select-search"></select>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div class="col-md-2">
|
||||
<label>Dari</label>
|
||||
<input type="date" id="start_date" class="form-control">
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div class="col-md-2">
|
||||
<label>Sampai</label>
|
||||
<input type="date" id="end_date" class="form-control">
|
||||
</div>
|
||||
|
||||
<div class="col-md-2 d-flex align-items-end">
|
||||
|
||||
<div class="col-md-4 d-flex align-items-end gap-2">
|
||||
<button class="btn btn-primary w-100" id="btnFilter">Filter</button>
|
||||
<button type="button" class="btn btn-danger w-100" id="generate_pdf">
|
||||
Generate PDF</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="table-responsive">
|
||||
@@ -92,10 +96,61 @@ $(function(){
|
||||
},'json');
|
||||
}
|
||||
|
||||
|
||||
// FILTER
|
||||
$('#btnFilter').click(function(){
|
||||
loadData();
|
||||
});
|
||||
|
||||
|
||||
// Prevent double submit
|
||||
$(document).on('submit', 'form', function(e){
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
|
||||
// GENERATE PDF
|
||||
$('#generate_pdf').on('click', function () {
|
||||
|
||||
const now = new Date();
|
||||
const today = now.getFullYear() + '-'
|
||||
+ String(now.getMonth() + 1).padStart(2, '0') + '-'
|
||||
+ String(now.getDate()).padStart(2, '0');
|
||||
|
||||
const account_id = $('#account_id').val();
|
||||
const start_date = $('#start_date').val() || '0000-00-00';
|
||||
const end_date = $('#end_date').val() || today;
|
||||
|
||||
let btn = $(this);
|
||||
|
||||
btn.prop('disabled', true).html('Generate PDF...');
|
||||
|
||||
$.ajax({
|
||||
url: "<?= base_url('Generatebuku/generatepdf'); ?>"
|
||||
+ '/' + account_id
|
||||
+ '/' + start_date
|
||||
+ '/' + end_date,
|
||||
|
||||
type: "GET",
|
||||
xhrFields: {
|
||||
responseType: 'blob' // penting untuk PDF
|
||||
},
|
||||
success: function (response) {
|
||||
|
||||
let blob = new Blob([response], { type: 'application/pdf' });
|
||||
let url = window.URL.createObjectURL(blob);
|
||||
|
||||
// buka di tab baru
|
||||
window.open(url, '_blank');
|
||||
|
||||
},
|
||||
error: function () {
|
||||
Swal.fire('Error', 'Gagal generate PDF', 'error');
|
||||
},
|
||||
complete: function(){
|
||||
btn.prop('disabled', false).html('Generate PDF');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,431 @@
|
||||
<style>
|
||||
.calendar-grid{
|
||||
display:grid;
|
||||
grid-template-columns:repeat(7,1fr);
|
||||
gap:8px;
|
||||
}
|
||||
|
||||
.calendar-card{
|
||||
min-height:60px;
|
||||
font-size:12px;
|
||||
}
|
||||
|
||||
.calendar-card.empty{
|
||||
visibility:hidden;
|
||||
}
|
||||
|
||||
.calendar-card .badge{
|
||||
font-size:10px;
|
||||
}
|
||||
|
||||
.badge-custom {
|
||||
background-color: #ff8874;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.badge-custom1{
|
||||
background-color: #27F549;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.badge-custom2 {
|
||||
background-color: #CB21FF;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.badge-custom3 {
|
||||
background-color: #94A600;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.badge-custom4 {
|
||||
background-color: #8200C7;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<div class="container mt-4">
|
||||
|
||||
<div class="card border-0 shadow-sm rounded-4">
|
||||
|
||||
<div class="card-body p-4">
|
||||
|
||||
<div class="d-flex justify-content-end align-items-center mb-3">
|
||||
<h7 class="mb-0">Tanggal</h7>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
|
||||
|
||||
<h5 class="mb-0">
|
||||
Monitoring Absensi Bulanan
|
||||
</h5>
|
||||
|
||||
<input type="month"
|
||||
id="filterMonth"
|
||||
class="form-control"
|
||||
style="max-width:220px"
|
||||
value="<?= date('Y-m') ?>">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="table-responsive">
|
||||
|
||||
<table id="tableAttendance"
|
||||
class="table modern-table align-middle w-100">
|
||||
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>No</th>
|
||||
<th>Kode</th>
|
||||
<th>Nama</th>
|
||||
<th>Periode</th>
|
||||
<th>Status</th>
|
||||
<th>Aksi</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- MODAL DETAIL -->
|
||||
<div class="modal fade" id="modalDetail">
|
||||
<div class="modal-dialog modal-xl modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header bg-warning text-white">
|
||||
<h5>Detail Absensi Bulanan</h5>
|
||||
<button class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<table class="table mb-3">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th width="180">No</th>
|
||||
<td><span id="d_id"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Kode Karyawan</th>
|
||||
<td><span id="d_employee_code"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Nama Karyawan</th>
|
||||
<td><span id="d_full_name"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Periode</th>
|
||||
<td><span id="d_month"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Status</th>
|
||||
<td><span id="d_status"></span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Score</th>
|
||||
<td><span id="d_score"></span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<table class="table mb-3">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th width="100"><span class="badge bg-success">Hadir</span></th>
|
||||
<td id="d_present_count"></td>
|
||||
<th width="180">Hari</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><span class="badge bg-info">Sakit</span></th>
|
||||
<td id="d_sick_count"></td>
|
||||
<th width="180">Hari</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><span class="badge bg-primary">Izin</span></th>
|
||||
<td id="d_total_permit"></td>
|
||||
<th width="180">Hari</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><span class="badge bg-danger">Alpha</span></th>
|
||||
<td id="d_absent_count"></td>
|
||||
<th width="180">Hari</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><span class="badge bg-warning">Terlambat</span></th>
|
||||
<td id="d_late_count"></td>
|
||||
<th width="180">Menit</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><span class="badge badge-custom">Total Early Leave Minutes</span></th>
|
||||
<td id="d_total_early_leave_minutes"></td>
|
||||
<th width="180">Menit</th>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<table class="table mb-3">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th width="100"><span class="badge badge-custom1">Total Hari Kerja</span></th>
|
||||
<td id="d_total_hari_kerja"></td>
|
||||
<th width="180">Hari</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><span class="badge bg-dark">Cuti</span></th>
|
||||
<td id="d_total_annual"></td>
|
||||
<th width="180">Hari</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><span class="badge bg-secondary">Total Jam Kerja</span></th>
|
||||
<td id="d_total_work_hours"></td>
|
||||
<th width="180">Jam</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><span class="badge badge-custom2">Total Late Minutes</span></th>
|
||||
<td id="d_total_late_minutes"></td>
|
||||
<th width="180">Menit</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><span class="badge badge-custom4">Total Overtime Hours</span></th>
|
||||
<td id="d_total_overtime_hours"></td>
|
||||
<th width="180">Jam</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><span class="badge badge-custom3">Total Alpha</span></th>
|
||||
<td id="d_total_alpha"></td>
|
||||
<th width="180">Hari</th>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="border-top my-3"></div>
|
||||
<div id="detailContent"> // VIEW DETAIL ABSENSI BULANAN (DI-GENERATE DARI JS)
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
$(function(){
|
||||
|
||||
let table = $('#tableAttendance').DataTable({
|
||||
ajax:{
|
||||
url:`<?= base_url(); ?>attendancemonthly/get_data`,
|
||||
data:function(d){
|
||||
d.date = $('#filterMonth').val();
|
||||
},
|
||||
dataSrc:'data'
|
||||
},
|
||||
columns:[
|
||||
{data:0},
|
||||
{data:1},
|
||||
{data:2},
|
||||
{data:3},
|
||||
{data:4},
|
||||
{data:5}
|
||||
|
||||
]
|
||||
});
|
||||
|
||||
$('#filterMonth').change(function(){
|
||||
table.ajax.reload();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
$(function () {
|
||||
|
||||
$(document).on('click', '.btn-detail', function () {
|
||||
|
||||
let id = $(this).data('id');
|
||||
let month = $(this).data('month');
|
||||
|
||||
$.get(
|
||||
`<?= base_url(); ?>attendancemonthly/detail/${id}/${month}`,
|
||||
function (res) {
|
||||
|
||||
// Header
|
||||
$('#d_id').text(res.header.id);
|
||||
$('#d_employee_code').text(res.header.employee_code);
|
||||
$('#d_full_name').text(res.header.full_name);
|
||||
$('#d_month').text(res.header.month);
|
||||
$('#d_status').text(res.header.status);
|
||||
|
||||
// Header - perhitungan
|
||||
$('#d_total_hari_kerja').text(res.header.total_hari_kerja);
|
||||
$('#d_present_count').text(res.header.total_present);
|
||||
$('#d_total_hadir').text(res.header.total_hadir);
|
||||
$('#d_late_count').text(res.header.total_late);
|
||||
$('#d_total_permit').text(res.header.total_permit);
|
||||
|
||||
|
||||
$('#d_sick_count').text(res.header.total_sick);
|
||||
$('#d_total_overtime_hours').text(res.header.total_overtime_hours);
|
||||
$('#d_total_work_hours').text(res.header.total_work_hours);
|
||||
$('#d_total_annual').text(res.header.total_annual);
|
||||
$('#d_total_late_minutes').text(res.header.total_late_minutes);
|
||||
$('#d_total_early_leave_minutes').text(res.header.total_early_leave_minutes);
|
||||
|
||||
$('#d_score').text(res.header.score);
|
||||
$('#d_leave_count').text(res.header.total_leave);
|
||||
$('#d_absent_count').text(res.header.total_alpha);
|
||||
$('#d_late_count').text(res.header.total_late);
|
||||
$('#d_total_alpha').text(res.header.total_alpha);
|
||||
|
||||
|
||||
// Mapping data absensi berdasarkan tanggal
|
||||
let attendanceMap = {};
|
||||
|
||||
$.each(res.detail, function (i, row) {
|
||||
attendanceMap[row.attendance_date] = row;
|
||||
});
|
||||
|
||||
// Ambil bulan dari header
|
||||
let monthDate = new Date(res.header.month);
|
||||
let year = monthDate.getFullYear();
|
||||
let month = monthDate.getMonth();
|
||||
|
||||
// Hari pertama bulan
|
||||
let firstDay = new Date(year, month, 1);
|
||||
|
||||
// Jumlah hari dalam bulan
|
||||
let daysInMonth = new Date(year, month + 1, 0).getDate();
|
||||
|
||||
// Konversi agar minggu dimulai Senin
|
||||
let startDay = firstDay.getDay();
|
||||
startDay = startDay === 0 ? 6 : startDay - 1;
|
||||
|
||||
let html = `
|
||||
<div class="calendar-wrapper">
|
||||
|
||||
<div class="row g-1 fw-bold text-center mb-2">
|
||||
<div class="col">Sen</div>
|
||||
<div class="col">Sel</div>
|
||||
<div class="col">Rab</div>
|
||||
<div class="col">Kam</div>
|
||||
<div class="col">Jum</div>
|
||||
<div class="col">Sab</div>
|
||||
<div class="col">Min</div>
|
||||
</div>
|
||||
|
||||
<div class="calendar-grid">
|
||||
`;
|
||||
|
||||
// Kosong sebelum tanggal 1
|
||||
for (let i = 0; i < startDay; i++) {
|
||||
html += `<div class="calendar-card empty"></div>`;
|
||||
}
|
||||
|
||||
// Isi tanggal
|
||||
for (let day = 1; day <= daysInMonth; day++) {
|
||||
|
||||
let dateStr =
|
||||
`${year}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
|
||||
|
||||
let data = attendanceMap[dateStr];
|
||||
|
||||
let badge = '';
|
||||
let cardClass = '';
|
||||
|
||||
if (data) {
|
||||
|
||||
switch (data.attendance_status) {
|
||||
case 'present':
|
||||
badge = '<span class="badge bg-success">Hadir</span>';
|
||||
cardClass = 'border-success';
|
||||
break;
|
||||
|
||||
case 'late':
|
||||
badge = '<span class="badge bg-warning">Terlambat</span>';
|
||||
cardClass = 'border-warning';
|
||||
break;
|
||||
|
||||
case 'alpha':
|
||||
badge = '<span class="badge bg-danger">Alpha</span>';
|
||||
cardClass = 'border-danger';
|
||||
break;
|
||||
|
||||
case 'sick':
|
||||
badge = '<span class="badge bg-info">Sakit</span>';
|
||||
cardClass = 'border-info';
|
||||
break;
|
||||
|
||||
case 'permit':
|
||||
badge = '<span class="badge bg-secondary">Izin</span>';
|
||||
cardClass = 'border-secondary';
|
||||
break;
|
||||
|
||||
case 'annual':
|
||||
badge = '<span class="badge bg-dark">Cuti</span>';
|
||||
cardClass = 'border-dark';
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
html += `
|
||||
<div class="calendar-card card ${cardClass}">
|
||||
<div class="card-body p-2">
|
||||
<div class="row gx-0">
|
||||
<div class="col-auto">
|
||||
<div class="fw-bold" style="font-size: 16px;">${day}</div>
|
||||
</div>
|
||||
<div class="col text-end">
|
||||
<div class="m-0">${badge}</div>
|
||||
</div>
|
||||
</div>
|
||||
<small class="text-muted d-block mt-1" style="font-size:15px;">
|
||||
${data.shift_name}
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
} else {
|
||||
|
||||
html += `
|
||||
<div class="calendar-card card">
|
||||
<div class="card-body p-2">
|
||||
<div class="fw-bold" style="font-size: 16px;">${day}</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
html += `
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
$('#detailContent').html(html);
|
||||
|
||||
|
||||
// Bootstrap 5
|
||||
let modal = new bootstrap.Modal(document.getElementById('modalDetail'));
|
||||
modal.show();
|
||||
|
||||
},
|
||||
'json'
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
@@ -89,7 +89,7 @@
|
||||
|
||||
<!-- MODAL (TIDAK DIUBAH STYLE) -->
|
||||
<div class="modal fade" id="modalItem">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-dialog modal-dialog-centered modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header bg-warning text-white">
|
||||
<h5 class="modal-title" id="modalTitle">Item Invoice</h5>
|
||||
@@ -98,9 +98,6 @@
|
||||
|
||||
<div class="modal-body">
|
||||
<input type="hidden" id="item_id">
|
||||
<div id="item_mode_label" class="alert alert-secondary py-1 small mb-3">
|
||||
Mode: Manual Input
|
||||
</div>
|
||||
|
||||
<div class="mb-2">
|
||||
<label for="account_id" class="form-label">Tujuan Neraca (Account)</label>
|
||||
@@ -124,7 +121,14 @@
|
||||
<select id="item_select" class="form-control select-search"></select>
|
||||
</div>
|
||||
|
||||
<div class="mb-2">
|
||||
<div class="mb-2 d-none" id="wrap_barcode_select">
|
||||
<label for="barcode_select" class="form-label">Barcode / QR ID</label>
|
||||
<select id="barcode_select" class="form-control select-search"></select>
|
||||
</div>
|
||||
|
||||
<div id="stokSisaQty"></div>
|
||||
|
||||
<div class="mb-2" id="wrap_qty_item">
|
||||
<label for="qty" class="form-label">Qty</label>
|
||||
<input type="number" id="qty" class="form-control" min="0">
|
||||
</div>
|
||||
@@ -225,9 +229,12 @@ $(function(){
|
||||
|
||||
// ================= RESET UI =================
|
||||
function resetForm(){
|
||||
// $('#form_scan_barang').addClass('d-none');
|
||||
$('#wrap_warehouse').addClass('d-none');
|
||||
$('#wrap_item_select').addClass('d-none');
|
||||
$('#wrap_nama_item').removeClass('d-none');
|
||||
$('#wrap_qty_item').removeClass('d-none');
|
||||
$('#wrap_barcode_select').addClass('d-none');
|
||||
|
||||
$('#warehouse_id').html('<option value="">Pilih Gudang</option>');
|
||||
$('#item_select').html('<option value="">Pilih Item</option>');
|
||||
@@ -237,10 +244,12 @@ $(function(){
|
||||
$('#qty').val('');
|
||||
$('#harga').val('');
|
||||
$('#keterangan').val('');
|
||||
$('#barcode_select').val('');
|
||||
|
||||
$('#is_cicilan').prop('checked', false);
|
||||
$('#wrap_cicilan').addClass('d-none');
|
||||
$('#tenor').val('');
|
||||
$('#stokSisaQty').html('');
|
||||
|
||||
setMode('manual');
|
||||
}
|
||||
@@ -270,12 +279,15 @@ $(function(){
|
||||
|
||||
$.get("<?= base_url('invoices/check_items_by_account/'); ?>" + account_id, function(res){
|
||||
if(res.has_item){
|
||||
// $('#form_scan_barang').removeClass('d-none');
|
||||
$('#wrap_warehouse').removeClass('d-none');
|
||||
$('#wrap_nama_item').addClass('d-none');
|
||||
$('#wrap_qty_item').addClass('d-none');
|
||||
loadWarehouse();
|
||||
setMode('warehouse');
|
||||
} else {
|
||||
$('#wrap_nama_item').removeClass('d-none');
|
||||
$('#wrap_qty_item').removeClass('d-none');
|
||||
setMode('manual');
|
||||
}
|
||||
}, 'json').fail(function(){
|
||||
@@ -305,6 +317,7 @@ $(function(){
|
||||
|
||||
if(!warehouse_id){
|
||||
$('#wrap_nama_item').removeClass('d-none');
|
||||
$('#wrap_qty_item').removeClass('d-none');
|
||||
setMode('manual');
|
||||
return;
|
||||
}
|
||||
@@ -328,10 +341,111 @@ $(function(){
|
||||
$('#item_select').html(opt);
|
||||
$('#wrap_item_select').removeClass('d-none');
|
||||
$('#wrap_nama_item').addClass('d-none');
|
||||
$('#wrap_qty_item').addClass('d-none');
|
||||
setMode('item');
|
||||
} else {
|
||||
$('#wrap_item_select').addClass('d-none');
|
||||
$('#wrap_nama_item').removeClass('d-none');
|
||||
$('#wrap_qty_item').removeClass('d-none');
|
||||
setMode('manual');
|
||||
}
|
||||
}, 'json');
|
||||
}
|
||||
|
||||
$('#item_select').on('change', function(){
|
||||
let item_id = $(this).val();
|
||||
|
||||
$('#wrap_barcode_select').addClass('d-none');
|
||||
$('#barcode_select').html('<option value="">Loading...</option>');
|
||||
|
||||
if(!item_id){
|
||||
$('#wrap_barcode_select').removeClass('d-none');
|
||||
setMode('manual');
|
||||
return;
|
||||
}
|
||||
|
||||
// loadItemBarcodes(item_id);
|
||||
loadItemBarcodes(
|
||||
item_id,
|
||||
isEditing ? editData.barcode_id : null
|
||||
);
|
||||
});
|
||||
|
||||
$('#barcode_select').on('change', function(){
|
||||
|
||||
let barcode_id = $(this).val();
|
||||
|
||||
let selected = $(this).find(':selected');
|
||||
let qty_sisa = parseFloat(selected.data('qty_sisa')) || 0;
|
||||
|
||||
// console.log("QTY : " + qty_sisa);
|
||||
if (qty_sisa > 1 ) {
|
||||
$('#stokSisaQty').html('<h6 class="text-success ml-4">Stok : ' + qty_sisa + '</h6><hr>');
|
||||
$('#qty').val('');
|
||||
$('#wrap_qty_item').removeClass('d-none');
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// $(document).on('keyup','#qty',function(){
|
||||
// hitungTotal();
|
||||
// });
|
||||
|
||||
// function hitungTotal(){
|
||||
// let qty_sisa = 1000;
|
||||
// $('#stokSisaQty').html('<h6 class="text-success ml-4">Stok : ' + qty_sisa + '</h6><hr>');
|
||||
// }
|
||||
|
||||
|
||||
|
||||
// ================= LOAD BARCODE ITEMS =================
|
||||
// function loadItemBarcodes(item_id){
|
||||
// return $.get("<?= base_url('items/get_list_barcode_id/'); ?>" + item_id, function(res){
|
||||
// if(res && res.length){
|
||||
// let opt = '<option value="">Pilih Item</option>';
|
||||
// res.forEach(i=>{
|
||||
// opt += `<option value="${i.barcode_id}">
|
||||
// ${i.nama_barang} - ${i.barcode} - ${i.serial_number}
|
||||
// </option>`;
|
||||
// });
|
||||
// $('#barcode_select').html(opt);
|
||||
// $('#wrap_barcode_select').removeClass('d-none');
|
||||
|
||||
// // set QTY jadi 1
|
||||
// let qty = Number($('#qty').val());
|
||||
// $('#qty').val(qty + 1);
|
||||
|
||||
// setMode('item');
|
||||
// } else {
|
||||
// $('#wrap_barcode_select').addClass('d-none');
|
||||
// // $('#wrap_barcode_select').removeClass('d-none');
|
||||
// setMode('manual');
|
||||
// }
|
||||
// }, 'json');
|
||||
// }
|
||||
|
||||
function loadItemBarcodes(item_id, selectedBarcode = null){
|
||||
return $.get("<?= base_url('items/get_list_barcode_id/'); ?>" + item_id, function(res){
|
||||
if(res && res.length){
|
||||
let opt = '<option value="">Pilih Barcode</option>';
|
||||
res.forEach(i=>{
|
||||
opt += `
|
||||
<option value="${i.barcode_id}" data-qty_sisa="${i.qty_sisa}">
|
||||
${i.nama_barang} - ${i.barcode} - ${i.serial_number}
|
||||
</option>
|
||||
`;
|
||||
});
|
||||
$('#barcode_select').html(opt);
|
||||
if(selectedBarcode){
|
||||
$('#barcode_select').val(selectedBarcode);
|
||||
}
|
||||
$('#wrap_barcode_select').removeClass('d-none');
|
||||
if(!isEditing){
|
||||
$('#qty').val(1);
|
||||
}
|
||||
setMode('item');
|
||||
}else{
|
||||
$('#wrap_barcode_select').addClass('d-none');
|
||||
setMode('manual');
|
||||
}
|
||||
},'json');
|
||||
@@ -347,8 +461,8 @@ $(function(){
|
||||
// ================= STOK CHECK =================
|
||||
$('#qty').on('input', function(){
|
||||
let qty = parseFloat($(this).val()) || 0;
|
||||
let selected = $('#item_select option:selected');
|
||||
let stok = selected.length ? parseFloat(selected.data('stok')) || 0 : 0;
|
||||
let selected = $('#barcode_select option:selected');
|
||||
let stok = selected.length ? parseFloat(selected.data('qty_sisa')) || 0 : 0;
|
||||
|
||||
if(stok > 0 && qty > stok){
|
||||
Swal.fire('Warning','Stok tidak cukup! Max: ' + stok,'warning');
|
||||
@@ -476,25 +590,7 @@ $(function(){
|
||||
let btnAction = '';
|
||||
if(v.status == 'draft'){
|
||||
|
||||
let btnEditTable = '';
|
||||
if(v.is_cicilan == 0) {
|
||||
btnEditTable = `
|
||||
<button class="btn btn-sm btn-secondary btn-editItem"
|
||||
data-id="${v.id}"
|
||||
data-tanggal_input="${v.tanggal}"
|
||||
data-account="${v.account_id}"
|
||||
data-item="${v.nama_item}"
|
||||
data-qty="${v.qty}"
|
||||
data-harga="${v.harga}"
|
||||
data-warehouse="${v.warehouse_id || ''}"
|
||||
data-item_id="${v.items_id || ''}"
|
||||
data-keterangan="${v.keterangan || ''}"
|
||||
data-is_cicilan="${v.is_cicilan || 0}">
|
||||
Edit
|
||||
</button>`;
|
||||
}
|
||||
btnAction = `
|
||||
${btnEditTable}
|
||||
<button class="btn btn-sm btn-danger btn-delete" data-id="${v.id}">
|
||||
Hapus
|
||||
</button>
|
||||
@@ -509,7 +605,7 @@ $(function(){
|
||||
</td>
|
||||
<td>${no++}</td>
|
||||
<td>${formatTglIndo(v.tanggal)}</td>
|
||||
<td>${v.nama_item || '-'}</td>
|
||||
<td>${v.nama_item || '-'}<br><small class="text-secondary"><small></small><i>${v.barcode || ''}</i></small></td>
|
||||
<td>${v.qty || 0}</td>
|
||||
<td>${formatNumber(v.harga)}</td>
|
||||
<td>${formatNumber(subtotal)}</td>
|
||||
@@ -563,53 +659,6 @@ $(function(){
|
||||
modal.show();
|
||||
});
|
||||
|
||||
// ================= EDIT =================
|
||||
$(document).on('click','.btn-editItem', function(){
|
||||
action = "edit";
|
||||
edit_id = $(this).data('id');
|
||||
isEditing = true;
|
||||
editData = $(this).data();
|
||||
|
||||
$('#modalTitle').text('Edit Item');
|
||||
resetForm();
|
||||
|
||||
// Set basic fields first
|
||||
$('#account_id').val(editData.account).trigger('change');
|
||||
|
||||
// Wait for account change to complete, then set other fields
|
||||
setTimeout(() => {
|
||||
if(editData.warehouse){
|
||||
$('#warehouse_id').val(editData.warehouse).trigger('change');
|
||||
|
||||
// gak ada checkbox cicilan
|
||||
$('#is_cicilan_group').addClass('d-none');
|
||||
|
||||
// Wait for warehouse items to load
|
||||
setTimeout(() => {
|
||||
$('#item_select').val(editData.item_id).trigger('change');
|
||||
}, 500);
|
||||
} else {
|
||||
$('#nama_item').val(editData.item);
|
||||
}
|
||||
|
||||
|
||||
$('#qty').val(editData.qty);
|
||||
$('#harga').val(editData.harga);
|
||||
$('#tanggal_input').val(editData.tanggal_input);
|
||||
$('#keterangan').val(editData.keterangan);
|
||||
|
||||
}, 500);
|
||||
|
||||
// if(editData.is_cicilan == 1){
|
||||
// $('#qty').prop('disabled', true);
|
||||
// $('#harga').prop('disabled', true);
|
||||
// $('#is_cicilan').prop('disabled', true);
|
||||
// $('#tenor').prop('disabled', true);
|
||||
// }
|
||||
|
||||
modal.show();
|
||||
});
|
||||
|
||||
// ================= SAVE =================
|
||||
|
||||
// ================= SAVE - FIXED SESUAI STYLE ANDA =================
|
||||
@@ -642,6 +691,7 @@ $(function(){
|
||||
qty: parseFloat($('#qty').val()) || 0,
|
||||
harga: parseFloat($('#harga').val()) || 0,
|
||||
keterangan: $('#keterangan').val().trim(),
|
||||
barcode_id: $('#barcode_select').val() || 0,
|
||||
|
||||
is_cicilan: $('#is_cicilan').is(':checked') ? 1 : 0,
|
||||
tenor: parseInt($('#tenor').val()) || 0,
|
||||
@@ -706,7 +756,7 @@ $(function(){
|
||||
btn.prop('disabled', true).html('Menghapus...');
|
||||
|
||||
$.get({
|
||||
url: "<?= base_url('invoices/delete_item/'); ?>" + id,
|
||||
url: "<?= base_url('invoices/delete_item/'); ?>" + id + "/" + `<?= $no_invoice ?>`,
|
||||
dataType: 'json' // ✅ FIXED
|
||||
})
|
||||
.done(function(res){
|
||||
@@ -1160,5 +1210,4 @@ $(function(){
|
||||
$(this).addClass('table-secondary');
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
@@ -365,7 +365,7 @@ $('.btn-add-bayar').click(function(){
|
||||
|
||||
$('#tableBayar tbody').append(`
|
||||
<tr>
|
||||
<td><select class="form-control account_id">${accountOptionsAssets}</select></td>
|
||||
<td style="width: 60%;"><select class="form-control account_id select-search">${accountOptionsAssets}</select></td>
|
||||
<td><input type="text" class="form-control debit text-end format-rupiah"></td>
|
||||
<td><button class="btn btn-danger btn-remove">X</button></td>
|
||||
</tr>
|
||||
|
||||
@@ -0,0 +1,641 @@
|
||||
<style>
|
||||
body {
|
||||
background: #f5f7fb;
|
||||
}
|
||||
|
||||
/* ===========================
|
||||
CARD
|
||||
=========================== */
|
||||
.main-card,
|
||||
.scanner-card,
|
||||
.table-card {
|
||||
border: none;
|
||||
border-radius: 18px;
|
||||
overflow: hidden;
|
||||
/*box-shadow: 0 8px 25px rgba(0, 0, 0, .08);*/
|
||||
}
|
||||
|
||||
/* ===========================
|
||||
HEADER
|
||||
=========================== */
|
||||
.header-gradient {
|
||||
background: linear-gradient(135deg, #f59e0b, #d97706);
|
||||
color: #fff;
|
||||
padding: 22px 28px;
|
||||
}
|
||||
|
||||
.info-box {
|
||||
background: rgba(255,255,255,.18);
|
||||
border-radius: 12px;
|
||||
padding: 12px 16px;
|
||||
}
|
||||
|
||||
.progress {
|
||||
height: 8px;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
/* ===========================
|
||||
SCANNER
|
||||
=========================== */
|
||||
.barcode-avatar {
|
||||
width: 110px;
|
||||
height: 110px;
|
||||
margin: auto;
|
||||
border-radius: 50%;
|
||||
background: #fff7e6;
|
||||
color: #f59e0b;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 45px;
|
||||
}
|
||||
|
||||
.barcode-code {
|
||||
font-size: 22px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
.form-control-lg {
|
||||
height: 60px;
|
||||
font-size: 22px;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
border-color: #f59e0b;
|
||||
box-shadow: 0 0 0 .2rem rgba(245, 158, 11, .2);
|
||||
}
|
||||
|
||||
.scan-note {
|
||||
color: #6c757d;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.camera-btn {
|
||||
height: 55px;
|
||||
border-radius: 12px;
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
/* ===========================
|
||||
TABLE
|
||||
=========================== */
|
||||
.table thead th {
|
||||
background: #d97706;
|
||||
color: #fff;
|
||||
border: none;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.table tbody td {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.table tbody tr:hover {
|
||||
background: #fffaf0;
|
||||
}
|
||||
|
||||
/* ===========================
|
||||
BADGE
|
||||
=========================== */
|
||||
.badge-success-soft {
|
||||
display: inline-block;
|
||||
padding: 6px 14px;
|
||||
border-radius: 30px;
|
||||
background: #d1fae5;
|
||||
color: #065f46;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.badge-danger-soft {
|
||||
display: inline-block;
|
||||
padding: 6px 14px;
|
||||
border-radius: 30px;
|
||||
background: #fee2e2;
|
||||
color: #991b1b;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.badge-warning-soft {
|
||||
display: inline-block;
|
||||
padding: 6px 14px;
|
||||
border-radius: 30px;
|
||||
background: #fef3c7;
|
||||
color: #92400e;
|
||||
font-weight: 600;
|
||||
}
|
||||
#reader{
|
||||
width:100%;
|
||||
min-height:280px;
|
||||
border-radius:12px;
|
||||
overflow:hidden;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="container mt-4">
|
||||
<div class="card main-card">
|
||||
<!-- HEADER -->
|
||||
<div class="header-gradient">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-md-8">
|
||||
<h3 class="mb-1">
|
||||
<i class="fa fa-barcode"></i>
|
||||
Input Serial Number
|
||||
</h3>
|
||||
<div class="small">
|
||||
Silahkan scan Serial Number perangkat.
|
||||
Data akan otomatis tersimpan.
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 text-right">
|
||||
<div class="info-box">
|
||||
<div class="small">
|
||||
Progress
|
||||
</div>
|
||||
<h5 class="font-weight-bold mb-2" id="progress_text">
|
||||
0 / 0 Barcode
|
||||
</h5>
|
||||
<div class="progress">
|
||||
<div class="progress-bar bg-success" style="width:35%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- BODY -->
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<!-- LEFT -->
|
||||
<div class="col-lg-4 mb-4">
|
||||
<div class="card scanner-card">
|
||||
<div class="card-body">
|
||||
<div class="barcode-avatar">
|
||||
<i class="bi bi-qr-code-scan"></i>
|
||||
</div>
|
||||
<div class="text-center mt-3">
|
||||
<div class="barcode-code">
|
||||
ONU-250626-0007
|
||||
</div>
|
||||
<div class="scan-note mt-2">
|
||||
Barcode yang sedang menunggu Serial Number
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<center>
|
||||
<label class="font-weight-bold">
|
||||
Serial Number
|
||||
</label>
|
||||
<!-- <input
|
||||
type="text"
|
||||
id="serial_number"
|
||||
class="form-control form-control-lg text-center"
|
||||
placeholder="Scan Serial Number"
|
||||
autocomplete="off"
|
||||
autofocus
|
||||
>
|
||||
<small class="scan-note">
|
||||
Scanner akan otomatis menyimpan.
|
||||
</small>
|
||||
<button class="btn btn-warning btn-block camera-btn mt-4">
|
||||
<i class="fa fa-camera mr-2"></i>
|
||||
Scan dengan Kamera
|
||||
</button> -->
|
||||
<div id="scannerArea">
|
||||
|
||||
<input
|
||||
type="text"
|
||||
id="serial_number"
|
||||
class="form-control form-control-lg text-center"
|
||||
placeholder="Scan Serial Number"
|
||||
autocomplete="off"
|
||||
autofocus>
|
||||
|
||||
<small class="scan-note">
|
||||
Scanner akan otomatis menyimpan.
|
||||
</small>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-warning btn-block camera-btn mt-4"
|
||||
id="btnCamera">
|
||||
|
||||
<i class="fa fa-camera mr-2"></i>
|
||||
Scan dengan Kamera
|
||||
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="cameraArea" style="display:none;">
|
||||
|
||||
<div id="reader"></div>
|
||||
|
||||
<small class="scan-note d-block mt-2">
|
||||
Arahkan QR Code ke kamera.
|
||||
</small>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-secondary btn-block mt-3"
|
||||
id="btnCloseCamera">
|
||||
|
||||
Tutup Kamera
|
||||
|
||||
</button>
|
||||
|
||||
</div>
|
||||
</center>
|
||||
<div class="alert alert-warning mt-3 mb-0 text-center">
|
||||
<i class="fa fa-check-circle"></i>
|
||||
Auto Save Aktif
|
||||
</div>
|
||||
<input type="hidden" id="barcode_id">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- RIGHT -->
|
||||
<div class="col-lg-8">
|
||||
<div class="card table-card">
|
||||
<div class="card-header bg-white">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0">
|
||||
Daftar Barcode
|
||||
</h5>
|
||||
<span class="badge badge-warning px-3 py-2">
|
||||
20 Data
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
|
||||
<table class="table table-hover mb-0" id="tableBarcode">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="60">No</th>
|
||||
<th>Barcode</th>
|
||||
<th>Serial Number</th>
|
||||
<th width="120">Status</th>
|
||||
<th width="80" class="text-center">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>
|
||||
<strong></strong>
|
||||
</td>
|
||||
<td></td>
|
||||
<td>
|
||||
<span class="badge-success-soft">
|
||||
<i class="fa fa-check mr-1"></i>
|
||||
|
||||
</span>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<button class="btn btn-sm btn-outline-danger">
|
||||
<i class="fa fa-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
let ITEM_ID = `<?= $item_id; ?>` ;
|
||||
let isSaving = false;
|
||||
|
||||
$(document).ready(function () {
|
||||
loadData();
|
||||
$("#serial_number").focus();
|
||||
$("#serial_number").on("keypress", function (e) {
|
||||
if (e.which == 13) {
|
||||
e.preventDefault();
|
||||
if (!isSaving) {
|
||||
saveSerial();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function loadData() {
|
||||
$.getJSON( "<?= base_url(); ?>" + "items/load_data_barcode/" + ITEM_ID)
|
||||
.done(function (r) {
|
||||
render(r);
|
||||
})
|
||||
.fail(function () {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Oops...',
|
||||
text: 'Gagal mengambil data barcode.'
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function saveSerial() {
|
||||
let serial = $("#serial_number").val().trim();
|
||||
if (serial == "") {
|
||||
$("#serial_number").focus();
|
||||
return;
|
||||
}
|
||||
isSaving = true;
|
||||
$("#serial_number")
|
||||
.prop("disabled", true);
|
||||
$.ajax({
|
||||
url: "<?= base_url(); ?>" + "items/save_serial_number",
|
||||
type: "POST",
|
||||
dataType: "json",
|
||||
data: {
|
||||
barcode_id: $("#barcode_id").val(),
|
||||
serial_number: serial
|
||||
},
|
||||
success: function (r) {
|
||||
isSaving = false;
|
||||
render(r);
|
||||
},
|
||||
error: function () {
|
||||
isSaving = false;
|
||||
$("#serial_number")
|
||||
.prop("disabled", false)
|
||||
.focus();
|
||||
Swal.fire({
|
||||
icon: "error",
|
||||
title: "Error",
|
||||
text: "Terjadi kesalahan pada server."
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function render(r) {
|
||||
|
||||
if (!r.status) {
|
||||
$("#serial_number")
|
||||
.val("")
|
||||
.prop("disabled", false)
|
||||
.focus();
|
||||
|
||||
Swal.fire({
|
||||
icon: "error",
|
||||
title: "Oops...",
|
||||
text: r.message
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// =======================
|
||||
// Semua SN sudah selesai
|
||||
// =======================
|
||||
|
||||
if (r.next == null) {
|
||||
|
||||
$("#barcode_id").val("");
|
||||
$(".barcode-code").html("SELESAI");
|
||||
|
||||
$(".progress-bar").css("width", "100%");
|
||||
$("#progress_text").html(r.done + " / " + r.total);
|
||||
|
||||
if (r.item_status == "draft") {
|
||||
|
||||
$("#serial_number")
|
||||
.val("")
|
||||
.prop("disabled", false)
|
||||
.attr("placeholder", "Semua SN selesai. Edit melalui daftar di bawah jika ada yang salah.")
|
||||
.blur();
|
||||
|
||||
Swal.fire({
|
||||
icon: "success",
|
||||
title: "Selesai",
|
||||
text: "Semua Serial Number telah diinput. Karena status barang masih Draft, Anda masih dapat mengubah Serial Number melalui daftar di bawah."
|
||||
});
|
||||
|
||||
} else {
|
||||
|
||||
$("#serial_number")
|
||||
.val("")
|
||||
.prop("disabled", true);
|
||||
|
||||
Swal.fire({
|
||||
icon: "success",
|
||||
title: "Selesai",
|
||||
text: "Semua Serial Number berhasil diinput."
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
$("#barcode_id").val(r.next.id);
|
||||
$(".barcode-code").text(r.next.barcode);
|
||||
|
||||
$("#progress_text").text(r.done + " / " + r.total);
|
||||
|
||||
$(".progress-bar").css("width", r.percent + "%");
|
||||
|
||||
$("#serial_number")
|
||||
.val("")
|
||||
.prop("disabled", false)
|
||||
.attr("placeholder", "Scan Serial Number")
|
||||
.focus();
|
||||
|
||||
}
|
||||
|
||||
// =======================
|
||||
// Table
|
||||
// =======================
|
||||
|
||||
let html = "";
|
||||
|
||||
$.each(r.list, function (i, v) {
|
||||
|
||||
html += "<tr>";
|
||||
html += "<td>" + (i + 1) + "</td>";
|
||||
html += "<td><strong>" + v.barcode + "</strong></td>";
|
||||
html += "<td>" + (v.serial_number ? v.serial_number : "-") + "</td>";
|
||||
|
||||
html += "<td>";
|
||||
|
||||
if (v.serial_number) {
|
||||
html += '<span class="badge-success-soft"><i class="fa fa-check mr-1"></i>Selesai</span>';
|
||||
} else {
|
||||
html += '<span class="badge-warning-soft">Menunggu</span>';
|
||||
}
|
||||
|
||||
html += "</td>";
|
||||
|
||||
html += '<td class="text-center">';
|
||||
|
||||
if (r.item_status == "draft") {
|
||||
|
||||
html += '<button class="btn btn-sm btn-outline-danger btnDelete" data-id="' + v.id + '">Delete</button>';
|
||||
|
||||
} else {
|
||||
|
||||
html += '<button class="btn btn-sm btn-outline-secondary" disabled>Locked</button>';
|
||||
|
||||
}
|
||||
|
||||
html += "</td>";
|
||||
html += "</tr>";
|
||||
|
||||
});
|
||||
|
||||
$("#tableBarcode tbody").html(html);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// function render(r) {
|
||||
// if (!r.status) {
|
||||
// $("#serial_number")
|
||||
// .val("")
|
||||
// .prop("disabled", false)
|
||||
// .focus();
|
||||
// Swal.fire({
|
||||
// icon: "error",
|
||||
// title: "Oops...",
|
||||
// text: r.message
|
||||
// });
|
||||
// return;
|
||||
// }
|
||||
// if (r.next == null) {
|
||||
// $("#barcode_id").val("");
|
||||
// $(".barcode-code").html("SELESAI");
|
||||
// $("#serial_number")
|
||||
// .val("")
|
||||
// .prop("disabled", true);
|
||||
// $(".progress-bar")
|
||||
// .css("width", "100%");
|
||||
// $("#progress_text").html(r.done + " / " + r.total);
|
||||
// Swal.fire({
|
||||
// icon: "success",
|
||||
// title: "Selesai",
|
||||
// text: "Semua Serial Number berhasil diinput."
|
||||
// });
|
||||
// return;
|
||||
// }
|
||||
// // Barcode Aktif
|
||||
// $("#barcode_id").val(r.next.id);
|
||||
// $(".barcode-code").text(r.next.barcode);
|
||||
// // Progress
|
||||
// $("#progress_text").text(r.done + " / " + r.total);
|
||||
// $(".progress-bar")
|
||||
// .css("width", r.percent + "%");
|
||||
// // Table
|
||||
// let html = "";
|
||||
// $.each(r.list, function (i, v) {
|
||||
// html += "<tr>";
|
||||
// html += "<td>" + (i + 1) + "</td>";
|
||||
// html += "<td><strong>" + v.barcode + "</strong></td>";
|
||||
// html += "<td>" + (v.serial_number ? v.serial_number : "-") + "</td>";
|
||||
// html += "<td>";
|
||||
// if (v.serial_number) {
|
||||
// html += '<span class="badge-success-soft"><i class="fa fa-check mr-1"></i>Selesai</span>';
|
||||
// } else {
|
||||
// html += '<span class="badge-warning-soft">Menunggu</span>';
|
||||
// }
|
||||
// html += "</td>";
|
||||
// html += '<td class="text-center">';
|
||||
// html += '<button class="btn btn-sm btn-outline-danger btnDelete" data-id="' + v.id + '">';
|
||||
// html += 'Delete';
|
||||
// html += '</button>';
|
||||
// html += '</td>';
|
||||
// html += "</tr>";
|
||||
// });
|
||||
// $("#tableBarcode tbody").html(html);
|
||||
// // Reset Input
|
||||
// $("#serial_number")
|
||||
// .val("")
|
||||
// .prop("disabled", false)
|
||||
// .focus();
|
||||
// }
|
||||
|
||||
$(document).on("click", ".btnDelete", function () {
|
||||
let id = $(this).data("id");
|
||||
Swal.fire({
|
||||
title: "Hapus Serial Number?",
|
||||
icon: "warning",
|
||||
showCancelButton: true,
|
||||
confirmButtonText: "Ya",
|
||||
cancelButtonText: "Batal"
|
||||
}).then(function (result) {
|
||||
if (!result.isConfirmed) return;
|
||||
$.post(
|
||||
"<?= base_url(); ?>" + "items/delete_serial_number",
|
||||
{
|
||||
barcode_id: id
|
||||
},
|
||||
function (r) {
|
||||
render(r);
|
||||
},
|
||||
"json"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
let html5QrCode=null;
|
||||
|
||||
$("#btnCamera").click(function(){
|
||||
|
||||
$("#scannerArea").hide();
|
||||
$("#cameraArea").show();
|
||||
|
||||
html5QrCode=new Html5Qrcode("reader");
|
||||
|
||||
html5QrCode.start(
|
||||
{
|
||||
facingMode:"environment"
|
||||
},
|
||||
{
|
||||
fps:10,
|
||||
qrbox:220
|
||||
},
|
||||
function(decodedText){
|
||||
|
||||
html5QrCode.stop().then(function(){
|
||||
|
||||
$("#cameraArea").hide();
|
||||
$("#scannerArea").show();
|
||||
|
||||
$("#serial_number")
|
||||
.val(decodedText);
|
||||
|
||||
saveSerial();
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
$("#btnCloseCamera").click(function(){
|
||||
|
||||
if(html5QrCode){
|
||||
|
||||
html5QrCode.stop().then(function(){
|
||||
|
||||
$("#cameraArea").hide();
|
||||
$("#scannerArea").show();
|
||||
|
||||
$("#serial_number").focus();
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
@@ -34,7 +34,7 @@
|
||||
|
||||
<!-- MODAL -->
|
||||
<div class="modal fade" id="modalItem">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-dialog modal-dialog-centered modal-xl">
|
||||
<div class="modal-content">
|
||||
|
||||
<div class="modal-header bg-warning text-white">
|
||||
@@ -45,39 +45,146 @@
|
||||
|
||||
<input type="hidden" id="id">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div id="group_tanggal">
|
||||
<label>Tanggal Pembelian</label>
|
||||
<input type="date" id="tanggal_beli" class="form-control">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div id="group_kode">
|
||||
<label class="mt-2">Kode Barang</label>
|
||||
<select id="kode_barang" class="form-control select-search"></select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<label>Nama Barang</label>
|
||||
<input type="text" id="nama_barang" class="form-control">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-2">
|
||||
<div id="group_qty">
|
||||
<label class="mt-2">Jumlah Barang</label>
|
||||
<input type="number" id="qty" class="form-control">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<div id="group_tracking_qty">
|
||||
<label class="mt-2">Jml / (Roll, Box, Dus)</label>
|
||||
<input type="number" id="tracking_qty" class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="mt-2">Harga Beli</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">Rp</span>
|
||||
<input type="text" id="harga_beli" class="form-control format-rupiah">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="mt-2">Harga Jual</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">Rp</span>
|
||||
<input type="text" id="harga_jual" class="form-control format-rupiah">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="group_warehouse">
|
||||
<label class="mt-2">Gudang</label>
|
||||
<select id="warehouse_id" class="form-control"></select>
|
||||
</div>
|
||||
|
||||
<div id="group_account">
|
||||
<!-- <div class="row">
|
||||
<label class="mt-2">Akun Kas</label>
|
||||
<div class="col-md-7">
|
||||
<div id="group_account">
|
||||
<select id="account_kas" class="form-control"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">Rp</span>
|
||||
<input type="text" id="account[]" class="form-control format-rupiah">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn btn-warning btn-sm mt-4">Tambah Account</button> -->
|
||||
|
||||
<hr>
|
||||
<label class="font-weight-bold">
|
||||
Pembayaran
|
||||
</label>
|
||||
<div id="accountContainer">
|
||||
<div class="row account-row mb-2">
|
||||
<div class="col-md-6">
|
||||
<select
|
||||
name="account_kas[]"
|
||||
class="form-control account_kas">
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">Rp</span>
|
||||
<input
|
||||
type="text"
|
||||
name="nominal[]"
|
||||
class="form-control nominal format-rupiah">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-danger btn-remove-account w-100"
|
||||
disabled>
|
||||
Hapus
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<button
|
||||
type="button"
|
||||
id="btnTambahAccount"
|
||||
class="btn btn-warning btn-sm">
|
||||
+ Tambah Account
|
||||
</button>
|
||||
</div>
|
||||
<div class="mt-3">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<!-- total qty * harga beli -->
|
||||
<h5>
|
||||
Total :
|
||||
<span id="totalHppBarang">
|
||||
Rp 0
|
||||
</span>
|
||||
</h5>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<!-- total accounting -->
|
||||
<h5>
|
||||
Total :
|
||||
<span id="totalAccount">
|
||||
Rp 0
|
||||
</span>
|
||||
</h5>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<!-- balance accounting + total hpp barang -->
|
||||
<h5>
|
||||
Total :
|
||||
<span id="totalAll">
|
||||
Rp 0
|
||||
</span>
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
@@ -89,59 +196,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- MODAL BARANAG KELUAR -->
|
||||
<div class="modal fade" id="modalKeluar">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
|
||||
<div class="modal-header bg-danger text-white">
|
||||
<h5>Barang Keluar</h5>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
|
||||
<div id="group_account_biaya">
|
||||
<label class="mt-2">Akun Biaya</label>
|
||||
<select id="account_biaya" class="form-control"></select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label>Tanggal</label>
|
||||
<input type="date" id="tanggal_keluar" class="form-control">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="mt-2">Gudang</label>
|
||||
<select id="warehouse_id_keluar" class="form-control"></select>
|
||||
</div>
|
||||
|
||||
<div class="d-none" id="group_barang_keluar">
|
||||
<label class="mt-2">Barang</label>
|
||||
<select id="barang_id" class="form-control select-search"></select>
|
||||
</div>
|
||||
|
||||
<div class="d-none" id="group_qty_keluar">
|
||||
<label class="mt-2">Jumlah Barang</label>
|
||||
<input type="number" id="qty_keluar" class="form-control">
|
||||
</div>
|
||||
|
||||
<div class="mb-2 d-none" id="group_keterangan">
|
||||
<label for="keterangan" class="form-label">Keterangan</label>
|
||||
<textarea class="form-control" id="keterangan_keluar" rows="3"></textarea>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-danger" id="btnKeluar">Keluarkan</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<!-- MODAL DETAIL -->
|
||||
<div class="modal fade" id="modalDetail">
|
||||
<div class="modal-dialog modal-lg">
|
||||
@@ -158,46 +212,13 @@
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="modal fade" id="modalAdjust">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
|
||||
<div class="modal-header bg-warning">
|
||||
<h5>Penyesuaian Stok</h5>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
|
||||
<input type="hidden" id="adjust_item_id">
|
||||
|
||||
<label>Qty Adjustment</label>
|
||||
<input type="number" id="adjust_qty" class="form-control">
|
||||
<small>Gunakan minus (-) jika mengurangi</small>
|
||||
|
||||
<label class="mt-2">Gudang</label>
|
||||
<select id="adjust_warehouse" class="form-control"></select>
|
||||
|
||||
<label class="mt-2">Keterangan</label>
|
||||
<input type="text" id="adjust_ket" class="form-control">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-warning" id="btnAdjust">Simpan</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<script>
|
||||
|
||||
$(function(){
|
||||
|
||||
let action = 'add';
|
||||
let selectedItemId = null;
|
||||
let accountOption = '';
|
||||
|
||||
// ================= DATATABLE =================
|
||||
let table = $('#tableItems').DataTable({
|
||||
@@ -210,24 +231,26 @@ let table = $('#tableItems').DataTable({
|
||||
});
|
||||
|
||||
// ================= LOAD AKUN KAS =================
|
||||
// function loadKas(){
|
||||
// $.get("<?= base_url('items/get_accounts'); ?>",res=>{
|
||||
// let opt='<option value="">-- PILIH KAS --</option>';
|
||||
// res.forEach(a=>{
|
||||
// opt += `<option value="${a.id}">${a.nama_akun}</option>`;
|
||||
// });
|
||||
// $('#account_kas').html(opt);
|
||||
// },'json');
|
||||
// }
|
||||
function loadKas(){
|
||||
$.get("<?= base_url('items/get_accounts'); ?>",res=>{
|
||||
let opt='<option value="">-- PILIH KAS --</option>';
|
||||
res.forEach(a=>{
|
||||
opt += `<option value="${a.id}">${a.nama_akun}</option>`;
|
||||
$.get("<?= base_url('items/get_accounts'); ?>",function(res){
|
||||
accountOption = '<option value="">-- PILIH KAS --</option>';
|
||||
res.forEach(function(a){
|
||||
accountOption += `
|
||||
<option value="${a.id}">
|
||||
${a.nama_akun}
|
||||
</option>
|
||||
`;
|
||||
});
|
||||
$('#account_kas').html(opt);
|
||||
},'json');
|
||||
}
|
||||
|
||||
// ================= LOAD AKUN KAS =================
|
||||
function loadKasOut(){
|
||||
$.get("<?= base_url('items/get_accounts_biaya'); ?>",res=>{
|
||||
let opt='<option value="">-- PILIH KAS --</option>';
|
||||
res.forEach(a=>{
|
||||
opt += `<option value="${a.id}">${a.nama_akun}</option>`;
|
||||
});
|
||||
$('#account_biaya').html(opt);
|
||||
$('.account_kas').html(accountOption);
|
||||
},'json');
|
||||
}
|
||||
|
||||
@@ -241,27 +264,105 @@ function loadGudang(){
|
||||
$('#warehouse_id, #adjust_warehouse').html(opt);
|
||||
},'json');
|
||||
}
|
||||
// ================= LOAD GUDANG =================
|
||||
function loadGudangList(){
|
||||
$.get("<?= base_url('warehouses/list'); ?>",res=>{
|
||||
let opt='<option value="">-- PILIH GUDANG --</option>';
|
||||
res.forEach(w=>{
|
||||
opt += `<option value="${w.id}">${w.nama}</option>`;
|
||||
|
||||
function loadKode() {
|
||||
$.get("<?= base_url('kodebarang/list'); ?>", function(res) {
|
||||
|
||||
let opt = '<option value="">-- PILIH KODE --</option>';
|
||||
|
||||
res.forEach(function(a){
|
||||
opt += `
|
||||
<option value="${a.id}"
|
||||
data-type="${a.tracking_type}">
|
||||
${a.kode_barang} - ${a.nama}
|
||||
</option>
|
||||
`;
|
||||
});
|
||||
$('#warehouse_id_keluar').html(opt);
|
||||
|
||||
$('#kode_barang').html(opt).trigger('change');
|
||||
}, 'json');
|
||||
}
|
||||
|
||||
function loadKode(){
|
||||
$.get("<?= base_url('kodebarang/list'); ?>",res=>{
|
||||
let opt='<option value="">-- PILIH KODE --</option>';
|
||||
res.forEach(a=>{
|
||||
opt += `<option value="${a.id}">${a.kode_barang} - ${a.nama}</option>`;
|
||||
});
|
||||
$('#kode_barang').html(opt);
|
||||
},'json');
|
||||
function addAccountRow(){
|
||||
let html = `
|
||||
<div class="row account-row mb-2">
|
||||
<div class="col-md-6">
|
||||
<select
|
||||
name="account_kas[]"
|
||||
class="form-control account_kas">
|
||||
${accountOption}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">
|
||||
Rp
|
||||
</span>
|
||||
<input
|
||||
type="text"
|
||||
name="nominal[]"
|
||||
class="form-control nominal format-rupiah">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-sm btn-danger btn-remove-account w-100">
|
||||
Hapus
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
$('#accountContainer').append(html);
|
||||
hitungTotal();
|
||||
}
|
||||
|
||||
$(document).on('change', '#kode_barang', function () {
|
||||
|
||||
let type = $(this).find(':selected').data('type');
|
||||
|
||||
if(type == 'QTY'){
|
||||
$('#group_tracking_qty').show();
|
||||
}else{
|
||||
$('#group_tracking_qty').hide();
|
||||
$('#tracking_qty').val('');
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
$('#btnTambahAccount').click(function(){
|
||||
addAccountRow();
|
||||
});
|
||||
|
||||
$(document).on('click','.btn-remove-account',function(){
|
||||
$(this).closest('.account-row').remove();
|
||||
hitungTotal();
|
||||
});
|
||||
|
||||
// function hitungTotal(){
|
||||
// let total = 0;
|
||||
// $('.nominal').each(function(){
|
||||
// let angka = $(this)
|
||||
// .val()
|
||||
// .replace(/\./g,'')
|
||||
// .replace(/,/g,'');
|
||||
// total += Number(angka || 0);
|
||||
// });
|
||||
// $('#totalAccount').text(formatRupiah(total.toString()));
|
||||
// }
|
||||
|
||||
$(document).on('keyup','.nominal',function(){
|
||||
hitungTotal();
|
||||
});
|
||||
|
||||
$(document).on('keyup','#harga_beli',function(){
|
||||
hitungTotal();
|
||||
});
|
||||
|
||||
$(document).on('keyup change','#qty',function(){
|
||||
hitungTotal();
|
||||
});
|
||||
|
||||
// ================= RESET FORM =================
|
||||
function resetForm(){
|
||||
$('#id').val('');
|
||||
@@ -270,6 +371,44 @@ function resetForm(){
|
||||
$('#harga_beli').val('');
|
||||
$('#harga_jual').val('');
|
||||
$('#tanggal_beli').val('');
|
||||
$('#tracking_qty').val('');
|
||||
$('#group_tracking_qty').hide();
|
||||
$('#accountContainer').html(`
|
||||
<div class="row account-row mb-2">
|
||||
<div class="col-md-6">
|
||||
<select
|
||||
name="account_kas[]"
|
||||
class="form-control account_kas">
|
||||
${accountOption}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">Rp</span>
|
||||
<input
|
||||
type="text"
|
||||
name="nominal[]"
|
||||
class="form-control nominal format-rupiah">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<button
|
||||
class="btn btn-sm btn-danger btn-remove-account w-100"
|
||||
disabled>
|
||||
Hapus
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`);
|
||||
$('#totalAccount').text('Rp 0');
|
||||
$('#totalHppBarang').text('Rp 0');
|
||||
|
||||
$('#totalAll')
|
||||
.text('Balance')
|
||||
.removeClass('text-danger')
|
||||
.addClass('text-success');
|
||||
|
||||
$('#btnSimpan').prop('disabled',true);
|
||||
}
|
||||
|
||||
// ================= INIT LOAD =================
|
||||
@@ -324,6 +463,40 @@ $('#btnSimpan').click(function(){
|
||||
|
||||
let btn = $(this);
|
||||
|
||||
let qtys = parseFloat($('#qty').val()) || 0;
|
||||
|
||||
let totalHpp = qtys * rupiahToNumber($('#harga_beli').val());
|
||||
|
||||
let totalAccount = 0;
|
||||
|
||||
$('.nominal').each(function(){
|
||||
|
||||
totalAccount += rupiahToNumber($(this).val());
|
||||
|
||||
});
|
||||
|
||||
if(totalHpp != totalAccount){
|
||||
|
||||
Swal.fire({
|
||||
icon:'warning',
|
||||
title:'Belum Balance',
|
||||
text:'Total pembayaran harus sama dengan Total Harga Beli.'
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
let account = [];
|
||||
$('.account-row').each(function(){
|
||||
account.push({
|
||||
account_id: $(this).find('.account_kas').val(),
|
||||
nominal: $(this)
|
||||
.find('.nominal')
|
||||
.val()
|
||||
});
|
||||
});
|
||||
// data.accounts = account;
|
||||
|
||||
let data = {
|
||||
id: $('#id').val(),
|
||||
kode_id: $('#kode_barang').val(),
|
||||
@@ -332,8 +505,10 @@ $('#btnSimpan').click(function(){
|
||||
harga_beli: $('#harga_beli').val(),
|
||||
harga_jual: $('#harga_jual').val(),
|
||||
warehouse_id: $('#warehouse_id').val(),
|
||||
account_kas: $('#account_kas').val(),
|
||||
// account_kas: $('#account_kas').val(),
|
||||
account_kas: account,
|
||||
tanggal_beli: $('#tanggal_beli').val(),
|
||||
tracking_qty: $('#tracking_qty').val(),
|
||||
status: `draft`
|
||||
};
|
||||
|
||||
@@ -384,122 +559,74 @@ $('#btnSimpan').click(function(){
|
||||
|
||||
});
|
||||
|
||||
// =====================================================
|
||||
// ================= START URUSAN KELUAR ===============
|
||||
// =====================================================
|
||||
|
||||
// ================= RESET FORM KELUAR =================
|
||||
function resetFormKeluar(){
|
||||
$('#tanggal_keluar').val('');
|
||||
$('#qty_keluar').val('');
|
||||
$('#keterangan_keluar').val('');
|
||||
}
|
||||
// ================= PRINT BARCODE =================
|
||||
$(document).on('click', '.btn-print', function () {
|
||||
|
||||
loadKasOut();
|
||||
loadGudangList();
|
||||
// ================= BARANG KELUAR ================= account_biaya warehouse_id barang_id
|
||||
$('.btn-out').click(function(){
|
||||
// action='add';
|
||||
resetFormKeluar();
|
||||
let id = $(this).data('id');
|
||||
|
||||
$('#warehouse_id').closest('div').show();
|
||||
$('#account_biaya').closest('div').show();
|
||||
$('#modalKeluar').modal('show');
|
||||
Swal.fire({
|
||||
title: 'Cetak Barcode',
|
||||
html: `
|
||||
<div class="text-left">
|
||||
|
||||
loadGudangList();
|
||||
loadKasOut();
|
||||
});
|
||||
<div class="form-group">
|
||||
<label for="print_column" class="font-weight-bold">
|
||||
Jumlah Kolom
|
||||
</label>
|
||||
<select id="print_column" class="form-control">
|
||||
<option value="1">1 Kolom</option>
|
||||
<option value="2">2 Kolom</option>
|
||||
<option value="3" selected>3 Kolom (Rekomendasi)</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
$('#warehouse_id_keluar').on('change', function(){
|
||||
let warehouse_id = $(this).val();
|
||||
<div class="form-group mb-0">
|
||||
<label for="duplicat" class="font-weight-bold">
|
||||
Jumlah Cetak per Barcode
|
||||
</label>
|
||||
<select id="duplicat" class="form-control">
|
||||
<option value="1" selected>1 Label</option>
|
||||
<option value="2">2 Label</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
// $('#wrap_item_select').addClass('d-none');
|
||||
$('#barang_id').html('<option value="">Loading...</option>');
|
||||
</div>
|
||||
`,
|
||||
icon: 'question',
|
||||
showCancelButton: true,
|
||||
confirmButtonText: 'Cetak',
|
||||
cancelButtonText: 'Batal',
|
||||
confirmButtonColor: '#f0ad4e',
|
||||
preConfirm: () => {
|
||||
|
||||
loadItems(warehouse_id);
|
||||
});
|
||||
let column = $('#print_column').val();
|
||||
let duplicat = $('#duplicat').val();
|
||||
|
||||
// ================= ITEMS =================
|
||||
function loadItems(warehouse_id){
|
||||
return $.get("<?= base_url('items/get_items_by_wh_id/'); ?>" + warehouse_id, function(res){
|
||||
if(res && res.length){
|
||||
let opt = '<option value="">-- PILIH BARANG --</option>';
|
||||
res.forEach(i=>{
|
||||
opt += `<option value="${i.id}">
|
||||
${i.kode_detail} - ${i.nama_barang} (Stok: ${i.stok})
|
||||
</option>`;
|
||||
});
|
||||
$('#barang_id').html(opt);
|
||||
$('#group_barang_keluar').removeClass('d-none');
|
||||
$('#group_qty_keluar').removeClass('d-none');
|
||||
$('#group_keterangan').removeClass('d-none');
|
||||
} else {
|
||||
$('#barang_id').html(opt);
|
||||
}
|
||||
}, 'json');
|
||||
}
|
||||
|
||||
// ================= SAVE =================
|
||||
$('#btnKeluar').click(function(){
|
||||
|
||||
let btn = $(this);
|
||||
|
||||
let data = {
|
||||
account_biaya: $('#account_biaya').val(),
|
||||
tanggal_keluar: $('#tanggal_keluar').val(),
|
||||
warehouse_id_keluar: $('#warehouse_id_keluar').val(),
|
||||
barang_id: $('#barang_id').val(),
|
||||
qty_keluar: $('#qty_keluar').val(),
|
||||
keterangan_keluar: $('#keterangan_keluar').val()
|
||||
return {
|
||||
column: column,
|
||||
duplicat: duplicat
|
||||
};
|
||||
|
||||
let url = "<?= base_url('items/keluarkan'); ?>";
|
||||
|
||||
// 🔥 loading state
|
||||
btn.prop('disabled', true).html('Menyimpan...');
|
||||
|
||||
$.post(url, data, function(res){
|
||||
|
||||
if(res.status){
|
||||
|
||||
$('#modalItem').modal('hide');
|
||||
table.ajax.reload(null,false);
|
||||
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: 'Berhasil',
|
||||
text: res.message || 'Data berhasil disimpan',
|
||||
timer: 1500,
|
||||
showConfirmButton: false
|
||||
});
|
||||
|
||||
}else{
|
||||
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Gagal',
|
||||
text: res.message || 'Terjadi kesalahan'
|
||||
});
|
||||
}
|
||||
|
||||
},'json')
|
||||
.fail(function(){
|
||||
}).then((result) => {
|
||||
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Error',
|
||||
text: 'Koneksi ke server gagal'
|
||||
});
|
||||
if (!result.isConfirmed) {
|
||||
return;
|
||||
}
|
||||
|
||||
window.open(
|
||||
"<?= base_url('items/print_barcode/'); ?>" +
|
||||
id +
|
||||
"?column=" + result.value.column +
|
||||
"&duplicat=" + result.value.duplicat,
|
||||
"_blank"
|
||||
);
|
||||
|
||||
})
|
||||
.always(function(){
|
||||
btn.prop('disabled', false).html('Keluarkan');
|
||||
});
|
||||
|
||||
});
|
||||
// =====================================================
|
||||
// ================= END URUSAN KELUAR ===============
|
||||
// =====================================================
|
||||
|
||||
// ================= DELETE =================
|
||||
$(document).on('click','.btn-delete',function(){
|
||||
@@ -680,78 +807,75 @@ $(document).on('click','.btn-detail',function(){
|
||||
|
||||
});
|
||||
|
||||
// ================= OPEN ADJUST =================
|
||||
$(document).on('click','.btn-adjust',function(){
|
||||
function rupiahToNumber(value){
|
||||
|
||||
let id = $(this).data('id');
|
||||
if(!value) return 0;
|
||||
|
||||
let s = value.toString().trim();
|
||||
s = s.replace(/\.(?=\d{3}(\.|$))/g, '');
|
||||
s = s.replace(/,/g, '');
|
||||
return parseFloat(s) || 0;
|
||||
|
||||
if(!id){
|
||||
alert('Pilih item dari tabel (gunakan tombol Adjust di baris)');
|
||||
return;
|
||||
}
|
||||
|
||||
selectedItemId = id;
|
||||
$('#adjust_item_id').val(id);
|
||||
$('#adjust_qty').val('');
|
||||
$('#adjust_ket').val('');
|
||||
function hitungTotal(){
|
||||
|
||||
// ==========================
|
||||
// HPP BARANG
|
||||
// ==========================
|
||||
|
||||
let qty = parseFloat($('#qty').val()) || 0;
|
||||
let hargaBeli = rupiahToNumber($('#harga_beli').val());
|
||||
|
||||
let totalHpp = qty * hargaBeli;
|
||||
|
||||
|
||||
// ==========================
|
||||
// ACCOUNTING
|
||||
// ==========================
|
||||
|
||||
let totalAccount = 0;
|
||||
|
||||
$('.nominal').each(function(){
|
||||
|
||||
totalAccount += rupiahToNumber($(this).val());
|
||||
|
||||
$('#modalAdjust').modal('show');
|
||||
});
|
||||
|
||||
// ================= SAVE ADJUST =================
|
||||
$('#btnAdjust').click(function(){
|
||||
|
||||
let btn = $(this);
|
||||
// ==========================
|
||||
// BALANCE
|
||||
// ==========================
|
||||
|
||||
let data = {
|
||||
item_id: $('#adjust_item_id').val(),
|
||||
qty: $('#adjust_qty').val(),
|
||||
warehouse_id: $('#adjust_warehouse').val(),
|
||||
keterangan: $('#adjust_ket').val()
|
||||
};
|
||||
let balance = totalAccount - totalHpp;
|
||||
|
||||
// 🔥 loading state
|
||||
btn.prop('disabled', true).html('Menyimpan...');
|
||||
|
||||
$.post("<?= base_url('items/adjust'); ?>", data, function(res){
|
||||
$('#totalHppBarang').text(formatRupiah(totalHpp.toString()));
|
||||
$('#totalAccount').text(formatRupiah(totalAccount.toString()));
|
||||
|
||||
if(res.status){
|
||||
if(balance == 0){
|
||||
|
||||
$('#modalAdjust').modal('hide');
|
||||
table.ajax.reload(null,false);
|
||||
$('#totalAll')
|
||||
.removeClass('text-danger')
|
||||
.addClass('text-success')
|
||||
.text('Balance');
|
||||
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: 'Berhasil',
|
||||
text: res.message || 'Stok berhasil disesuaikan',
|
||||
timer: 1500,
|
||||
showConfirmButton: false
|
||||
});
|
||||
$('#btnSimpan').prop('disabled',false);
|
||||
|
||||
}else{
|
||||
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Gagal',
|
||||
text: res.message || 'Gagal melakukan penyesuaian'
|
||||
});
|
||||
let prefix = balance > 0 ? '+' : '-';
|
||||
|
||||
$('#totalAll')
|
||||
.removeClass('text-success')
|
||||
.addClass('text-danger')
|
||||
.text(prefix + ' ' + formatRupiah(Math.abs(balance).toString()));
|
||||
|
||||
$('#btnSimpan').prop('disabled',true);
|
||||
|
||||
}
|
||||
|
||||
},'json')
|
||||
.fail(function(){
|
||||
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Error',
|
||||
text: 'Koneksi ke server gagal'
|
||||
});
|
||||
|
||||
})
|
||||
.always(function(){
|
||||
btn.prop('disabled', false).html('Simpan');
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,406 @@
|
||||
<style>
|
||||
body {
|
||||
background: #f5f7fb;
|
||||
}
|
||||
|
||||
/* ===========================
|
||||
CARD
|
||||
=========================== */
|
||||
.main-card,
|
||||
.scanner-card,
|
||||
.table-card {
|
||||
border: none;
|
||||
border-radius: 18px;
|
||||
overflow: hidden;
|
||||
/*box-shadow: 0 8px 25px rgba(0, 0, 0, .08);*/
|
||||
}
|
||||
|
||||
/* ===========================
|
||||
HEADER
|
||||
=========================== */
|
||||
.header-gradient {
|
||||
background: linear-gradient(135deg, #f59e0b, #d97706);
|
||||
color: #fff;
|
||||
padding: 22px 28px;
|
||||
}
|
||||
|
||||
.info-box {
|
||||
background: rgba(255,255,255,.18);
|
||||
border-radius: 12px;
|
||||
padding: 12px 16px;
|
||||
}
|
||||
|
||||
.progress {
|
||||
height: 8px;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
/* ===========================
|
||||
SCANNER
|
||||
=========================== */
|
||||
.barcode-avatar {
|
||||
width: 110px;
|
||||
height: 110px;
|
||||
margin: auto;
|
||||
border-radius: 50%;
|
||||
background: #fff7e6;
|
||||
color: #f59e0b;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 45px;
|
||||
}
|
||||
|
||||
.barcode-code {
|
||||
font-size: 22px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
.form-control-lg {
|
||||
height: 60px;
|
||||
font-size: 22px;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
border-color: #f59e0b;
|
||||
box-shadow: 0 0 0 .2rem rgba(245, 158, 11, .2);
|
||||
}
|
||||
|
||||
.scan-note {
|
||||
color: #6c757d;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.camera-btn {
|
||||
height: 55px;
|
||||
border-radius: 12px;
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
/* ===========================
|
||||
TABLE
|
||||
=========================== */
|
||||
.table thead th {
|
||||
background: #d97706;
|
||||
color: #fff;
|
||||
border: none;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.table tbody td {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.table tbody tr:hover {
|
||||
background: #fffaf0;
|
||||
}
|
||||
|
||||
/* ===========================
|
||||
BADGE
|
||||
=========================== */
|
||||
.badge-success-soft {
|
||||
display: inline-block;
|
||||
padding: 6px 14px;
|
||||
border-radius: 30px;
|
||||
background: #d1fae5;
|
||||
color: #065f46;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.badge-danger-soft {
|
||||
display: inline-block;
|
||||
padding: 6px 14px;
|
||||
border-radius: 30px;
|
||||
background: #fee2e2;
|
||||
color: #991b1b;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.badge-warning-soft {
|
||||
display: inline-block;
|
||||
padding: 6px 14px;
|
||||
border-radius: 30px;
|
||||
background: #fef3c7;
|
||||
color: #92400e;
|
||||
font-weight: 600;
|
||||
}
|
||||
#reader{
|
||||
width:100%;
|
||||
min-height:280px;
|
||||
border-radius:12px;
|
||||
overflow:hidden;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="container mt-4">
|
||||
<div class="card main-card">
|
||||
<!-- HEADER -->
|
||||
<div class="header-gradient">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-md-8">
|
||||
<h3 class="mb-1">
|
||||
<i class="fa fa-barcode"></i>
|
||||
Riwayat Perpindahan Barang
|
||||
</h3>
|
||||
<div class="small">
|
||||
Silahkan scan / input QR id Barang
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 text-right">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- BODY -->
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<!-- LEFT -->
|
||||
<div class="col-lg-4 mb-4">
|
||||
<div class="card scanner-card">
|
||||
<div class="card-body">
|
||||
<div class="barcode-avatar">
|
||||
<i class="bi bi-qr-code-scan"></i>
|
||||
</div>
|
||||
<div class="text-center mt-3">
|
||||
<div class="scan-note mt-2">
|
||||
Scan QR ID
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
|
||||
<center>
|
||||
<div id="scannerArea">
|
||||
|
||||
<input
|
||||
type="text"
|
||||
id="serial_number"
|
||||
class="form-control form-control-lg text-center"
|
||||
placeholder="Scan / Input QR ID"
|
||||
autocomplete="off"
|
||||
autofocus>
|
||||
|
||||
<small class="scan-note">
|
||||
Scanner akan otomatis memanggil riwayat.
|
||||
</small>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-warning btn-block camera-btn mt-4"
|
||||
id="btnCamera">
|
||||
|
||||
<i class="fa fa-camera mr-2"></i>
|
||||
Scan dengan Kamera
|
||||
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="cameraArea" style="display:none;">
|
||||
|
||||
<div id="reader"></div>
|
||||
|
||||
<small class="scan-note d-block mt-2">
|
||||
Arahkan QR Code ke kamera.
|
||||
</small>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-secondary btn-block mt-3"
|
||||
id="btnCloseCamera">
|
||||
|
||||
Tutup Kamera
|
||||
|
||||
</button>
|
||||
|
||||
</div>
|
||||
</center>
|
||||
<input type="hidden" id="barcode_id">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- RIGHT -->
|
||||
<div class="col-lg-8">
|
||||
<div class="card table-card">
|
||||
<div class="card-header bg-white">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0">
|
||||
Riwayat Perpindahan Barang
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body border-bottom" id="itemInfo">
|
||||
Belum ada data
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
|
||||
<table class="table table-hover mb-0" id="tableBarcode">
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="60">No</th>
|
||||
<th>Date</th>
|
||||
<th>Type</th>
|
||||
<th>From</th>
|
||||
<th>To</th>
|
||||
<th>NOtes</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
$("#serial_number").keypress(function(e){
|
||||
if(e.which==13){
|
||||
searchHistory();
|
||||
}
|
||||
});
|
||||
function searchHistory(){
|
||||
let barcode=$("#serial_number").val().trim();
|
||||
if(barcode=="") return;
|
||||
$.ajax({
|
||||
url: `<?= base_url("items/scan_history"); ?>`,
|
||||
type:"POST",
|
||||
dataType:"json",
|
||||
data:{
|
||||
barcode:barcode
|
||||
},
|
||||
beforeSend:function(){
|
||||
$("#serial_number").prop("disabled",true);
|
||||
},
|
||||
success:function(r){
|
||||
$("#serial_number")
|
||||
.prop("disabled",false)
|
||||
.val("")
|
||||
.focus();
|
||||
if(!r.status){
|
||||
Swal.fire({
|
||||
icon:"error",
|
||||
text:r.message
|
||||
});
|
||||
return;
|
||||
}
|
||||
renderItem(r.item);
|
||||
renderHistory(r.history);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function renderItem(item){
|
||||
let html='';
|
||||
|
||||
html+='<div class="row">';
|
||||
|
||||
html+=' <div class="col-md-6">';
|
||||
|
||||
html+=' <h4 class="mb-3"><b>'+item.nama_barang+'</b></h4>';
|
||||
|
||||
html+=' <div class="row mb-2">';
|
||||
html+=' <div class="col-4 font-weight-bold">Kode</div>';
|
||||
html+=' <div class="col-1">:</div>';
|
||||
html+=' <div class="col-7">'+item.kode_detail+'</div>';
|
||||
html+=' </div>';
|
||||
|
||||
html+=' <div class="row mb-2">';
|
||||
html+=' <div class="col-4 font-weight-bold">Barcode</div>';
|
||||
html+=' <div class="col-1">:</div>';
|
||||
html+=' <div class="col-7">'+item.barcode+'</div>';
|
||||
html+=' </div>';
|
||||
|
||||
html+=' </div>';
|
||||
|
||||
html+=' <div class="col-md-6">';
|
||||
|
||||
html+=' <div class="row mb-2">';
|
||||
html+=' <div class="col-4 font-weight-bold">Serial Number</div>';
|
||||
html+=' <div class="col-1">:</div>';
|
||||
html+=' <div class="col-7">'+(item.serial_number ? item.serial_number : '-')+'</div>';
|
||||
html+=' </div>';
|
||||
|
||||
html+=' <div class="row mb-2">';
|
||||
html+=' <div class="col-4 font-weight-bold">Status</div>';
|
||||
html+=' <div class="col-1">:</div>';
|
||||
html+=' <div class="col-7">'+item.status+'</div>';
|
||||
html+=' </div>';
|
||||
|
||||
html+=' <div class="row mb-2">';
|
||||
html+=' <div class="col-4 font-weight-bold">Qty</div>';
|
||||
html+=' <div class="col-1">:</div>';
|
||||
html+=' <div class="col-7">'+item.qty_sisa+' / '+item.qty_awal+'</div>';
|
||||
html+=' </div>';
|
||||
|
||||
html+=' </div>';
|
||||
|
||||
html+='</div>';
|
||||
|
||||
$("#itemInfo").html(html);
|
||||
}
|
||||
|
||||
function renderHistory(list){
|
||||
let html='';
|
||||
$.each(list,function(i,v){
|
||||
html+="<tr>";
|
||||
html+="<td>"+(i+1)+"</td>";
|
||||
html+="<td>"+v.created_at+"</td>";
|
||||
html+="<td>"+v.movement_type+"</td>";
|
||||
html+="<td>"+(v.from_warehouse??v.from_type)+"</td>";
|
||||
html+="<td>"+(v.to_warehouse??v.to_type)+"</td>";
|
||||
html+="<td>"+v.notes+"</td>";
|
||||
html+="</tr>";
|
||||
});
|
||||
$("#tableBarcode tbody").html(html);
|
||||
}
|
||||
|
||||
|
||||
let html5QrCode=null;
|
||||
$("#btnCamera").click(function(){
|
||||
$("#scannerArea").hide();
|
||||
$("#cameraArea").show();
|
||||
html5QrCode=new Html5Qrcode("reader");
|
||||
html5QrCode.start(
|
||||
{
|
||||
facingMode:"environment"
|
||||
},
|
||||
{
|
||||
fps:10,
|
||||
qrbox:220
|
||||
},
|
||||
function(decodedText){
|
||||
html5QrCode.stop().then(function(){
|
||||
$("#cameraArea").hide();
|
||||
$("#scannerArea").show();
|
||||
$("#serial_number")
|
||||
.val(decodedText);
|
||||
saveSerial();
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
$("#btnCloseCamera").click(function(){
|
||||
if(html5QrCode){
|
||||
html5QrCode.stop().then(function(){
|
||||
$("#cameraArea").hide();
|
||||
$("#scannerArea").show();
|
||||
$("#serial_number").focus();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
@@ -6,7 +6,7 @@
|
||||
<h5>Data Barang</h5>
|
||||
<div class="text-end">
|
||||
<button class="btn btn-add bg-danger btn-out mr-2">Pengeluaran</button>
|
||||
<button class="btn btn-warning btn-add btn-add-item mr-2">Tambah Barang</button>
|
||||
<!-- <button class="btn btn-warning btn-add btn-add-item mr-2">Tambah Barang</button> -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -92,7 +92,7 @@
|
||||
|
||||
<!-- MODAL BARANAG KELUAR -->
|
||||
<div class="modal fade" id="modalKeluar">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-dialog modal-dialog-centered modal-lg">
|
||||
<div class="modal-content">
|
||||
|
||||
<div class="modal-header bg-danger text-white">
|
||||
@@ -101,6 +101,57 @@
|
||||
|
||||
<div class="modal-body">
|
||||
|
||||
<center>
|
||||
<div id="scannerArea">
|
||||
|
||||
<input
|
||||
type="text"
|
||||
id="serial_number"
|
||||
class="form-control form-control-lg text-center"
|
||||
placeholder="Scan Barcode / Serial Number / Nama Barang"
|
||||
autocomplete="off"
|
||||
autofocus>
|
||||
|
||||
<small class="scan-note">
|
||||
Scan atau ketik Barcode / Serial Number / Nama Barang, lalu Enter.
|
||||
</small><br>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-warning btn-block camera-btn mt-4"
|
||||
id="btnCamera">
|
||||
|
||||
<i class="fa fa-camera mr-2"></i>
|
||||
Scan dengan Kamera
|
||||
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="cameraArea" style="display:none;">
|
||||
|
||||
<div id="reader"></div>
|
||||
|
||||
<small class="scan-note d-block mt-2">
|
||||
Arahkan QR Code ke kamera.
|
||||
</small>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-secondary btn-block mt-3"
|
||||
id="btnCloseCamera">
|
||||
|
||||
Tutup Kamera
|
||||
|
||||
</button>
|
||||
|
||||
</div>
|
||||
</center>
|
||||
<div style="max-height: 200px; overflow: auto;">
|
||||
<div class="mt-2" id="tableBarcode"></div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div id="group_account_biaya">
|
||||
<label class="mt-2">Akun Biaya</label>
|
||||
<select id="account_biaya" class="form-control"></select>
|
||||
@@ -111,7 +162,7 @@
|
||||
<input type="date" id="tanggal_keluar" class="form-control">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<!-- <div>
|
||||
<label class="mt-2">Gudang</label>
|
||||
<select id="warehouse_id_keluar" class="form-control"></select>
|
||||
</div>
|
||||
@@ -124,9 +175,8 @@
|
||||
<div class="d-none" id="group_qty_keluar">
|
||||
<label class="mt-2">Jumlah Barang</label>
|
||||
<input type="number" id="qty_keluar" class="form-control">
|
||||
</div>
|
||||
|
||||
<div class="mb-2 d-none" id="group_keterangan">
|
||||
</div> -->
|
||||
<div class="mb-2" id="group_keterangan">
|
||||
<label for="keterangan" class="form-label">Keterangan</label>
|
||||
<textarea class="form-control" id="keterangan_keluar" rows="3"></textarea>
|
||||
</div>
|
||||
@@ -195,6 +245,290 @@
|
||||
|
||||
<script>
|
||||
|
||||
// URUSAN BARCODE INPUT DAN CAMSCANNER
|
||||
let daftarBarang = [];
|
||||
let isSaving = false;
|
||||
let searchTimer = null;
|
||||
|
||||
$(document).ready(function () {
|
||||
$("#serial_number").focus();
|
||||
|
||||
$("#serial_number").on("keypress", function(e){
|
||||
if(e.which==13){
|
||||
e.preventDefault();
|
||||
if(searchTimer){
|
||||
clearTimeout(searchTimer);
|
||||
}
|
||||
if(!isSaving){
|
||||
getBarcodeId(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$("#serial_number").on("input", function(){
|
||||
let keyword = ($(this).val() || "").trim();
|
||||
|
||||
if(searchTimer){
|
||||
clearTimeout(searchTimer);
|
||||
}
|
||||
|
||||
if(keyword === ""){
|
||||
daftarBarang = [];
|
||||
renderTable();
|
||||
return;
|
||||
}
|
||||
|
||||
searchTimer = setTimeout(function(){
|
||||
if(!isSaving){
|
||||
getBarcodeId(false);
|
||||
}
|
||||
}, 300);
|
||||
});
|
||||
});
|
||||
|
||||
function getBarcodeId(isScanner = false){
|
||||
let barcode = ($("#serial_number").val() || "").trim();
|
||||
if(barcode==""){
|
||||
return;
|
||||
}
|
||||
|
||||
$.post(
|
||||
"<?=base_url('items/get_barcode_item');?>",
|
||||
{ barcode: barcode },
|
||||
function(r){
|
||||
if(!r.status){
|
||||
Swal.fire({
|
||||
icon:"error",
|
||||
title:"Oops...",
|
||||
text:r.message
|
||||
});
|
||||
$("#serial_number").val("").focus();
|
||||
return;
|
||||
}
|
||||
|
||||
let rows = Array.isArray(r.data) ? r.data : [];
|
||||
if(rows.length === 0){
|
||||
Swal.fire({
|
||||
icon:"warning",
|
||||
title:"Data kosong",
|
||||
text:"Tidak ada barang yang cocok."
|
||||
});
|
||||
$("#serial_number").val("").focus();
|
||||
return;
|
||||
}
|
||||
|
||||
daftarBarang = rows;
|
||||
renderTable();
|
||||
|
||||
if(isScanner){
|
||||
$("#serial_number").val("").focus();
|
||||
}
|
||||
},
|
||||
"json"
|
||||
);
|
||||
}
|
||||
|
||||
function renderTable() {
|
||||
|
||||
let html = '';
|
||||
|
||||
html += '<div class="table-responsive">';
|
||||
html += '<table class="table table-bordered table-hover table-sm mb-0">';
|
||||
html += '<thead>';
|
||||
html += '<tr>';
|
||||
html += '<th width="50">No</th>';
|
||||
html += '<th width="180">Barcode</th>';
|
||||
html += '<th width="120">Kode Barang</th>';
|
||||
html += '<th>Nama Barang</th>';
|
||||
html += '<th width="180">Serial Number</th>';
|
||||
html += '<th width="70" class="text-center">Pilih</th>';
|
||||
html += '</tr>';
|
||||
html += '</thead>';
|
||||
html += '<tbody>';
|
||||
|
||||
if (daftarBarang.length == 0) {
|
||||
|
||||
html += `
|
||||
<tr>
|
||||
<td colspan="6" class="text-center text-muted py-4">
|
||||
Belum ada barang yang discan.
|
||||
</td>
|
||||
</tr>
|
||||
`;
|
||||
|
||||
} else {
|
||||
|
||||
$.each(daftarBarang, function (i, v) {
|
||||
|
||||
html += `
|
||||
<tr>
|
||||
<td>${i + 1}</td>
|
||||
<td><strong>${v.barcode}</strong></td>
|
||||
<td>${v.kode_detail}</td>
|
||||
<td>${v.nama_barang}</td>
|
||||
<td>${v.serial_number ? v.serial_number : '-'}</td>
|
||||
|
||||
<td class="text-center align-middle">
|
||||
<div class="form-check d-flex justify-content-center mb-0">
|
||||
<input
|
||||
class="form-check-input pilihBarang"
|
||||
type="checkbox"
|
||||
value="${v.barcode_id}"
|
||||
data-barcode="${v.barcode}">
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
`;
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
html += '</tbody>';
|
||||
html += '</table>';
|
||||
html += '</div>';
|
||||
|
||||
$('#tableBarcode').html(html);
|
||||
|
||||
if (daftarBarang.length === 1) {
|
||||
$(".pilihBarang").prop("checked", false);
|
||||
$(".pilihBarang").first().prop("checked", true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// function renderTable(){
|
||||
|
||||
// let html='';
|
||||
|
||||
// html+='<div class="table-responsive">';
|
||||
// html+='<table class="table table-bordered table-hover table-sm mb-0">';
|
||||
// html+='<thead>';
|
||||
// html+='<tr>';
|
||||
// html+='<th width="50">No</th>';
|
||||
// html+='<th width="180">Barcode</th>';
|
||||
// html+='<th width="150">Kode Barang</th>';
|
||||
// html+='<th>Nama Barang</th>';
|
||||
// html+='<th width="180">Serial Number</th>';
|
||||
// html+='<th width="80" class="text-center">Action</th>';
|
||||
// html+='</tr>';
|
||||
// html+='</thead>';
|
||||
// html+='<tbody>';
|
||||
|
||||
// if(daftarBarang.length==0){
|
||||
|
||||
// html+='<tr>';
|
||||
// html+='<td colspan="6" class="text-center text-muted py-4">';
|
||||
// html+='Belum ada barcode yang discan.';
|
||||
// html+='</td>';
|
||||
// html+='</tr>';
|
||||
|
||||
// }else{
|
||||
|
||||
// $.each(daftarBarang,function(i,v){
|
||||
|
||||
// html+='<tr>';
|
||||
// html+='<td>'+(i+1)+'</td>';
|
||||
// html+='<td><strong>'+v.barcode+'</strong></td>';
|
||||
// html+='<td>'+v.kode_detail+'</td>';
|
||||
// html+='<td>'+v.nama_barang+'</td>';
|
||||
// html+='<td>'+(v.serial_number?v.serial_number:'-')+'</td>';
|
||||
// html+='<td class="text-center">';
|
||||
// html+='<button class="btn btn-sm btn-outline-danger btnRemove" data-id="'+v.barcode_id+'">';
|
||||
// html+='<i class="bi bi-trash"></i>';
|
||||
// html+='</button>';
|
||||
// html+='</td>';
|
||||
// html+='</tr>';
|
||||
|
||||
// });
|
||||
|
||||
// }
|
||||
|
||||
// html+='</tbody>';
|
||||
// html+='</table>';
|
||||
// html+='</div>';
|
||||
|
||||
// $("#tableBarcode").html(html);
|
||||
|
||||
// }
|
||||
|
||||
|
||||
$(document).on("change", ".pilihBarang", function(){
|
||||
if($(this).is(":checked")){
|
||||
$(".pilihBarang").not(this).prop("checked", false);
|
||||
}
|
||||
});
|
||||
|
||||
function getSelectedBarcodeRows(){
|
||||
let selectedIds = $(".pilihBarang:checked").map(function(){
|
||||
return String($(this).val());
|
||||
}).get();
|
||||
|
||||
if(selectedIds.length === 0){
|
||||
return [];
|
||||
}
|
||||
|
||||
return daftarBarang.filter(function(v){
|
||||
return selectedIds.indexOf(String(v.barcode_id)) !== -1;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
let html5QrCode=null;
|
||||
|
||||
$("#btnCamera").click(function(){
|
||||
|
||||
$("#scannerArea").hide();
|
||||
$("#cameraArea").show();
|
||||
|
||||
html5QrCode=new Html5Qrcode("reader");
|
||||
|
||||
html5QrCode.start(
|
||||
{
|
||||
facingMode:"environment"
|
||||
},
|
||||
{
|
||||
fps:10,
|
||||
qrbox:220
|
||||
},
|
||||
function(decodedText){
|
||||
|
||||
html5QrCode.stop().then(function(){
|
||||
|
||||
$("#cameraArea").hide();
|
||||
$("#scannerArea").show();
|
||||
|
||||
$("#serial_number")
|
||||
.val(decodedText);
|
||||
|
||||
getBarcodeId();
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
$("#btnCloseCamera").click(function(){
|
||||
|
||||
if(html5QrCode){
|
||||
|
||||
html5QrCode.stop().then(function(){
|
||||
|
||||
$("#cameraArea").hide();
|
||||
$("#scannerArea").show();
|
||||
|
||||
$("#serial_number").focus();
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
// URUSAN BARCODE INPUT DAN CAMSCANNER
|
||||
|
||||
|
||||
$(function(){
|
||||
|
||||
let action = 'add';
|
||||
@@ -278,20 +612,20 @@ loadGudang();
|
||||
loadKas();
|
||||
loadKode();
|
||||
|
||||
// ================= ADD =================
|
||||
$('.btn-add-item').click(function(){
|
||||
action='add';
|
||||
resetForm();
|
||||
// // ================= ADD =================
|
||||
// $('.btn-add-item').click(function(){
|
||||
// action='add';
|
||||
// resetForm();
|
||||
|
||||
$('#qty').closest('div').show();
|
||||
$('#kode_barang').closest('div').show();
|
||||
$('#warehouse_id').closest('div').show();
|
||||
$('#account_kas').closest('div').show();
|
||||
$('#modalItem').modal('show');
|
||||
loadGudang();
|
||||
loadKas();
|
||||
loadKode();
|
||||
});
|
||||
// $('#qty').closest('div').show();
|
||||
// $('#kode_barang').closest('div').show();
|
||||
// $('#warehouse_id').closest('div').show();
|
||||
// $('#account_kas').closest('div').show();
|
||||
// $('#modalItem').modal('show');
|
||||
// loadGudang();
|
||||
// loadKas();
|
||||
// loadKode();
|
||||
// });
|
||||
|
||||
// ================= EDIT =================
|
||||
$(document).on('click','.btn-editItem',function(){
|
||||
@@ -392,8 +726,9 @@ $('#btnSimpan').click(function(){
|
||||
// ================= RESET FORM KELUAR =================
|
||||
function resetFormKeluar(){
|
||||
$('#tanggal_keluar').val('');
|
||||
$('#qty_keluar').val('');
|
||||
$('#keterangan_keluar').val('');
|
||||
daftarBarang = [];
|
||||
renderTable();
|
||||
}
|
||||
|
||||
loadKasOut();
|
||||
@@ -411,6 +746,12 @@ $('.btn-out').click(function(){
|
||||
loadKasOut();
|
||||
});
|
||||
|
||||
$('#modalKeluar').on('shown.bs.modal', function () {
|
||||
setTimeout(function () {
|
||||
$('#serial_number').trigger('focus').select();
|
||||
}, 100);
|
||||
});
|
||||
|
||||
$('#warehouse_id_keluar').on('change', function(){
|
||||
let warehouse_id = $(this).val();
|
||||
|
||||
@@ -444,28 +785,41 @@ function loadItems(warehouse_id){
|
||||
$('#btnKeluar').click(function(){
|
||||
|
||||
let btn = $(this);
|
||||
let selectedRows = getSelectedBarcodeRows();
|
||||
|
||||
if(selectedRows.length !== 1){
|
||||
Swal.fire({
|
||||
icon: 'warning',
|
||||
title: 'Pilih 1 barang',
|
||||
text: 'Pengeluaran hanya boleh 1 barcode item per proses.'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
let selected = selectedRows[0];
|
||||
|
||||
let data = {
|
||||
account_biaya: $('#account_biaya').val(),
|
||||
tanggal_keluar: $('#tanggal_keluar').val(),
|
||||
warehouse_id_keluar: $('#warehouse_id_keluar').val(),
|
||||
barang_id: $('#barang_id').val(),
|
||||
qty_keluar: $('#qty_keluar').val(),
|
||||
keterangan_keluar: $('#keterangan_keluar').val()
|
||||
keterangan_keluar: $('#keterangan_keluar').val(),
|
||||
selected_barcode: selected.barcode_id
|
||||
};
|
||||
|
||||
let url = "<?= base_url('items/keluarkan'); ?>";
|
||||
|
||||
// 🔥 loading state
|
||||
btn.prop('disabled', true).html('Menyimpan...');
|
||||
|
||||
$.post(url, data, function(res){
|
||||
|
||||
if(res.status){
|
||||
|
||||
$('#modalItem').modal('hide');
|
||||
$('#modalKeluar').modal('hide');
|
||||
table.ajax.reload(null,false);
|
||||
|
||||
daftarBarang = [];
|
||||
renderTable();
|
||||
$("#serial_number").val("");
|
||||
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: 'Berhasil',
|
||||
@@ -502,68 +856,68 @@ $('#btnKeluar').click(function(){
|
||||
// ================= END URUSAN KELUAR ===============
|
||||
// =====================================================
|
||||
|
||||
// ================= DELETE =================
|
||||
$(document).on('click','.btn-delete',function(){
|
||||
// // ================= DELETE =================
|
||||
// $(document).on('click','.btn-delete',function(){
|
||||
|
||||
let id = $(this).data('id');
|
||||
let btn = $(this);
|
||||
// let id = $(this).data('id');
|
||||
// let btn = $(this);
|
||||
|
||||
Swal.fire({
|
||||
title: 'Hapus data?',
|
||||
text: "Data tidak bisa dikembalikan!",
|
||||
icon: 'warning',
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: '#d33',
|
||||
cancelButtonColor: '#3085d6',
|
||||
confirmButtonText: 'Ya, hapus!',
|
||||
cancelButtonText: 'Batal'
|
||||
}).then((result) => {
|
||||
// Swal.fire({
|
||||
// title: 'Hapus data?',
|
||||
// text: "Data tidak bisa dikembalikan!",
|
||||
// icon: 'warning',
|
||||
// showCancelButton: true,
|
||||
// confirmButtonColor: '#d33',
|
||||
// cancelButtonColor: '#3085d6',
|
||||
// confirmButtonText: 'Ya, hapus!',
|
||||
// cancelButtonText: 'Batal'
|
||||
// }).then((result) => {
|
||||
|
||||
if(result.isConfirmed){
|
||||
// if(result.isConfirmed){
|
||||
|
||||
btn.prop('disabled', true).html('Menghapus...');
|
||||
// btn.prop('disabled', true).html('Menghapus...');
|
||||
|
||||
$.get("<?= base_url('items/delete/'); ?>"+id,function(res){
|
||||
// $.get("<?= base_url('items/delete/'); ?>"+id,function(res){
|
||||
|
||||
if(res.status){
|
||||
// if(res.status){
|
||||
|
||||
table.ajax.reload(null,false);
|
||||
// table.ajax.reload(null,false);
|
||||
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: 'Berhasil',
|
||||
text: res.message || 'Data berhasil dihapus',
|
||||
timer: 1500,
|
||||
showConfirmButton: false
|
||||
});
|
||||
// Swal.fire({
|
||||
// icon: 'success',
|
||||
// title: 'Berhasil',
|
||||
// text: res.message || 'Data berhasil dihapus',
|
||||
// timer: 1500,
|
||||
// showConfirmButton: false
|
||||
// });
|
||||
|
||||
}else{
|
||||
// }else{
|
||||
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Gagal',
|
||||
text: res.message || 'Gagal menghapus data'
|
||||
});
|
||||
}
|
||||
// Swal.fire({
|
||||
// icon: 'error',
|
||||
// title: 'Gagal',
|
||||
// text: res.message || 'Gagal menghapus data'
|
||||
// });
|
||||
// }
|
||||
|
||||
},'json')
|
||||
.fail(function(){
|
||||
// },'json')
|
||||
// .fail(function(){
|
||||
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Error',
|
||||
text: 'Koneksi ke server gagal'
|
||||
});
|
||||
// Swal.fire({
|
||||
// icon: 'error',
|
||||
// title: 'Error',
|
||||
// text: 'Koneksi ke server gagal'
|
||||
// });
|
||||
|
||||
})
|
||||
.always(function(){
|
||||
btn.prop('disabled', false).html('Hapus');
|
||||
});
|
||||
// })
|
||||
// .always(function(){
|
||||
// btn.prop('disabled', false).html('Hapus');
|
||||
// });
|
||||
|
||||
}
|
||||
});
|
||||
// }
|
||||
// });
|
||||
|
||||
});
|
||||
// });
|
||||
|
||||
// ================= DETAIL =================
|
||||
$(document).on('click','.btn-detail',function(){
|
||||
@@ -681,5 +1035,73 @@ $('#btnAdjust').click(function(){
|
||||
|
||||
});
|
||||
|
||||
// ================= PRINT BARCODE =================
|
||||
$(document).on('click', '.btn-print', function () {
|
||||
|
||||
let id = $(this).data('id');
|
||||
|
||||
Swal.fire({
|
||||
title: 'Cetak Barcode',
|
||||
html: `
|
||||
<div class="text-left">
|
||||
|
||||
<div class="form-group">
|
||||
<label for="print_column" class="font-weight-bold">
|
||||
Jumlah Kolom
|
||||
</label>
|
||||
<select id="print_column" class="form-control">
|
||||
<option value="1">1 Kolom</option>
|
||||
<option value="2">2 Kolom</option>
|
||||
<option value="3" selected>3 Kolom (Rekomendasi)</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group mb-0">
|
||||
<label for="duplicat" class="font-weight-bold">
|
||||
Jumlah Cetak per Barcode
|
||||
</label>
|
||||
<select id="duplicat" class="form-control">
|
||||
<option value="1" selected>1 Label</option>
|
||||
<option value="2">2 Label</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
`,
|
||||
icon: 'question',
|
||||
showCancelButton: true,
|
||||
confirmButtonText: 'Cetak',
|
||||
cancelButtonText: 'Batal',
|
||||
confirmButtonColor: '#f0ad4e',
|
||||
preConfirm: () => {
|
||||
|
||||
let column = $('#print_column').val();
|
||||
let duplicat = $('#duplicat').val();
|
||||
|
||||
return {
|
||||
column: column,
|
||||
duplicat: duplicat
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}).then((result) => {
|
||||
|
||||
if (!result.isConfirmed) {
|
||||
return;
|
||||
}
|
||||
|
||||
window.open(
|
||||
"<?= base_url('items/print_barcode/'); ?>" +
|
||||
id +
|
||||
"?column=" + result.value.column +
|
||||
"&duplicat=" + result.value.duplicat,
|
||||
"_blank"
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
@@ -398,17 +398,21 @@ $('#btnSimpan').click(function(){
|
||||
return;
|
||||
}
|
||||
|
||||
if(totalDebit === 0 && totalKredit === 0){
|
||||
Swal.fire('Warning','Harap Isi Semua Kolom!','warning'); // ✅ FIX add all collumn must be filled
|
||||
return;
|
||||
}
|
||||
|
||||
if($('#keterangan').val().trim() === ''){
|
||||
Swal.fire('Warning','Keterangan tidak boleh kosong!','warning');
|
||||
return;
|
||||
}
|
||||
|
||||
if(!$('#tanggal').val()){
|
||||
Swal.fire('Warning','Tanggal wajib diisi!','warning'); // ✅ FIX tambah validasi tanggal
|
||||
Swal.fire('Warning','Tanggal tidak boleh kosong!','warning');
|
||||
return;
|
||||
}
|
||||
|
||||
if(!$('#keterangan').val()){
|
||||
Swal.fire('Warning','Keterangan wajib diisi!','warning'); // ✅ FIX tambah validasi keterangan
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// lanjut simpan
|
||||
$.post("<?= base_url('jurnal/save'); ?>", {
|
||||
tanggal: $('#tanggal').val(),
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
<th>Kode</th>
|
||||
<th>Nama</th>
|
||||
<th>Limit Stok</th>
|
||||
<th>Tracking Type</th>
|
||||
<th>Need Serial</th>
|
||||
<th>Unit</th>
|
||||
<th>Aksi</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -49,6 +52,25 @@
|
||||
<label>Limit Stok</label>
|
||||
<input type="number" id="limit_stock" class="form-control">
|
||||
|
||||
<label class="mt-2">Tracking Type</label>
|
||||
<select id="tracking_type" class="form-control">
|
||||
<option value="UNIT">UNIT</option>
|
||||
<option value="QTY">QTY</option>
|
||||
</select>
|
||||
|
||||
<label class="mt-2">Need Serial Number</label>
|
||||
<select id="need_serial_number" class="form-control">
|
||||
<option value="false">Tidak</option>
|
||||
<option value="true">Ya</option>
|
||||
</select>
|
||||
|
||||
<label class="mt-2">Unit</label>
|
||||
<select id="unit" class="form-control">
|
||||
<option value="PCS">PCS</option>
|
||||
<option value="Meter">Meter</option>
|
||||
<option value="Lembar">Lembar</option>
|
||||
</select>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
@@ -77,6 +99,9 @@ $(function(){
|
||||
$('#kode').val('');
|
||||
$('#nama').val('');
|
||||
$('#limit_stock').val('');
|
||||
$('#tracking_type').val('UNIT');
|
||||
$('#need_serial_number').val('false');
|
||||
$('#unit').val('PCS');
|
||||
}
|
||||
|
||||
// ADD
|
||||
@@ -96,6 +121,9 @@ $(function(){
|
||||
$('#kode').val(res.kode_barang);
|
||||
$('#nama').val(res.nama);
|
||||
$('#limit_stock').val(res.limit_stock);
|
||||
$('#tracking_type').val((res.tracking_type || 'UNIT').toUpperCase());
|
||||
$('#need_serial_number').val((res.need_serial_number || 'false').toLowerCase());
|
||||
$('#unit').val(res.unit || 'PCS');
|
||||
|
||||
action = 'edit';
|
||||
$('#modalKodebarang').modal('show');
|
||||
@@ -110,11 +138,19 @@ $(function(){
|
||||
id: $('#id').val(),
|
||||
kode: $('#kode').val(),
|
||||
nama: $('#nama').val(),
|
||||
limit_stock: $('#limit_stock').val()
|
||||
limit_stock: $('#limit_stock').val(),
|
||||
tracking_type: $('#tracking_type').val(),
|
||||
need_serial_number: $('#need_serial_number').val(),
|
||||
unit: $('#unit').val()
|
||||
};
|
||||
|
||||
if(!data.kode || !data.nama || !data.limit_stock){
|
||||
Swal.fire('Warning','Nama gudang wajib diisi','warning');
|
||||
if(!data.kode || !data.nama){
|
||||
Swal.fire('Warning','Kode dan nama barang wajib diisi','warning');
|
||||
return;
|
||||
}
|
||||
|
||||
if(data.limit_stock === '' || parseInt(data.limit_stock) < 0){
|
||||
Swal.fire('Warning','Limit stok wajib diisi dan tidak boleh negatif','warning');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -141,7 +177,7 @@ $(function(){
|
||||
let id = $(this).data('id');
|
||||
|
||||
Swal.fire({
|
||||
title:'Hapus gudang?',
|
||||
title:'Hapus kode barang?',
|
||||
text:'Pastikan tidak dipakai item',
|
||||
icon:'warning',
|
||||
showCancelButton:true
|
||||
|
||||
@@ -16,10 +16,13 @@
|
||||
<input type="date" id="tanggal_sampai" class="form-control">
|
||||
</div>
|
||||
|
||||
<div class="col-md-4 d-flex align-items-end">
|
||||
<div class="col-md-4 d-flex align-items-end gap-2">
|
||||
<button type="submit" class="btn btn-warning w-100">
|
||||
Tampilkan
|
||||
</button>
|
||||
<button type="button" class="btn btn-danger w-100" id="generate_pdf">
|
||||
Generate PDF
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
@@ -168,5 +171,42 @@ $(function(){
|
||||
loadData(dari, sampai);
|
||||
});
|
||||
|
||||
|
||||
|
||||
$('#generate_pdf').on('click', function () {
|
||||
|
||||
const tanggal_dari = $('#tanggal_dari').val() || '0000-00-00';
|
||||
const tanggal_sampai = $('#tanggal_sampai').val() || '0000-00-00';
|
||||
|
||||
let btn = $(this);
|
||||
|
||||
|
||||
btn.prop('disabled', true).html('Generate PDF...');
|
||||
|
||||
$.ajax({
|
||||
url: "<?= base_url('generatelabarugi/generatepdf'); ?>"
|
||||
+ '/' + tanggal_dari
|
||||
+ '/' + tanggal_sampai,
|
||||
|
||||
type: "GET",
|
||||
xhrFields: {
|
||||
responseType: 'blob' // penting untuk PDF
|
||||
},
|
||||
success: function (response) {
|
||||
let blob = new Blob([response], { type: 'application/pdf' });
|
||||
let url = window.URL.createObjectURL(blob);
|
||||
|
||||
// buka di tab baru
|
||||
window.open(url, '_blank');
|
||||
|
||||
},
|
||||
error: function () {
|
||||
Swal.fire('Error', 'Gagal generate PDF', 'error');
|
||||
},
|
||||
complete: function(){
|
||||
btn.prop('disabled', false).html('Generate PDF');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@@ -18,10 +18,12 @@
|
||||
<input type="date" name="tanggal_sampai" id="tanggal_sampai" class="form-control">
|
||||
</div>
|
||||
|
||||
<div class="col-md-4 d-flex align-items-end">
|
||||
<div class="col-md-4 d-flex align-items-end gap-2">
|
||||
<button type="submit" class="btn btn-warning w-100">
|
||||
Tampilkan
|
||||
</button>
|
||||
</button><br>
|
||||
<button type="button" class="btn btn-danger w-100" id="generate_pdf">
|
||||
<i class="fa fa-print"></i>Generate PDF</button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
@@ -76,6 +78,7 @@
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
$(function(){
|
||||
|
||||
function loadData(tanggal_dari = '', tanggal_sampai = ''){
|
||||
@@ -85,6 +88,7 @@ $(function(){
|
||||
tanggal_sampai: tanggal_sampai
|
||||
}, function(res){
|
||||
|
||||
|
||||
let act = '', pas = '';
|
||||
|
||||
// ================= ACTIVA
|
||||
@@ -134,18 +138,64 @@ $(function(){
|
||||
},'json');
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ================= LOAD AWAL
|
||||
loadData();
|
||||
|
||||
|
||||
// ================= SUBMIT FILTER
|
||||
$('#filterForm').on('submit', function(e){
|
||||
e.preventDefault();
|
||||
|
||||
|
||||
let dari = $('#tanggal_dari').val();
|
||||
let sampai = $('#tanggal_sampai').val();
|
||||
|
||||
loadData(dari, sampai);
|
||||
});
|
||||
|
||||
|
||||
// Prevent double submit
|
||||
$(document).on('submit', 'form', function(e){
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
|
||||
$('#generate_pdf').on('click', function () {
|
||||
|
||||
const tanggal_dari = $('#tanggal_dari').val() || '0000-00-00';
|
||||
const tanggal_sampai = $('#tanggal_sampai').val() || '0000-00-00';
|
||||
|
||||
let btn = $(this);
|
||||
|
||||
|
||||
btn.prop('disabled', true).html('Generate PDF...');
|
||||
|
||||
$.ajax({
|
||||
url: "<?= base_url('generateneraca/generatepdf'); ?>"
|
||||
+ '/' + tanggal_dari
|
||||
+ '/' + tanggal_sampai,
|
||||
|
||||
type: "GET",
|
||||
xhrFields: {
|
||||
responseType: 'blob' // penting untuk PDF
|
||||
},
|
||||
success: function (response) {
|
||||
let blob = new Blob([response], { type: 'application/pdf' });
|
||||
let url = window.URL.createObjectURL(blob);
|
||||
|
||||
// buka di tab baru
|
||||
window.open(url, '_blank');
|
||||
|
||||
},
|
||||
error: function () {
|
||||
Swal.fire('Error', 'Gagal generate PDF', 'error');
|
||||
},
|
||||
complete: function(){
|
||||
btn.prop('disabled', false).html('Generate PDF');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@@ -3,20 +3,49 @@
|
||||
<div class="card-body">
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h4 class="mb-0 fw-bold">Neraca Saldo</h4>
|
||||
<h4 class="mb-0 fw-bold">Laporan Keuangan</h4>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Tanggal Awal</label>
|
||||
<input type="date" id="tanggal_awal" class="form-control"
|
||||
value="<?= date('Y-m-01'); ?>">
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Tanggal Akhir</label>
|
||||
<input type="date" id="tanggal_akhir" class="form-control"
|
||||
value="<?= date('Y-m-t'); ?>">
|
||||
</div>
|
||||
|
||||
<div class="col-md-2 d-flex align-items-end gap-2">
|
||||
<button class="btn btn-warning w-100" id="btnFilter">
|
||||
Tampilkan
|
||||
</button>
|
||||
<button type="button" class="btn btn-danger" id="generate_pdf">
|
||||
PDF
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-striped table-hover align-middle">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th style="width: 120px;">Kode</th>
|
||||
<th width="120">Kode</th>
|
||||
<th>Akun</th>
|
||||
<th class="text-end">Debit</th>
|
||||
<th class="text-end">Kredit</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody id="tb"></tbody>
|
||||
|
||||
<tfoot class="table-light">
|
||||
<tr class="fw-bold">
|
||||
<th colspan="2" class="text-end">Total</th>
|
||||
@@ -24,6 +53,7 @@
|
||||
<th class="text-end text-danger" id="tk"></th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@@ -34,15 +64,29 @@
|
||||
<script>
|
||||
$(function () {
|
||||
|
||||
$.get("<?= base_url('neracasaldo/get_data'); ?>", function(res){
|
||||
loadData();
|
||||
|
||||
$('#btnFilter').click(function () {
|
||||
loadData();
|
||||
});
|
||||
|
||||
function loadData() {
|
||||
|
||||
$.get("<?= base_url('neracasaldo/get_data'); ?>", {
|
||||
|
||||
tanggal_awal: $('#tanggal_awal').val(),
|
||||
tanggal_akhir: $('#tanggal_akhir').val()
|
||||
|
||||
}, function (res) {
|
||||
|
||||
let html = '';
|
||||
let td = 0, tk = 0;
|
||||
let td = 0;
|
||||
let tk = 0;
|
||||
|
||||
res.forEach(r => {
|
||||
|
||||
let debit = parseFloat(r.saldo_debit) || 0;
|
||||
let kredit = parseFloat(r.saldo_kredit) || 0;
|
||||
let debit = parseFloat(r.debit) || 0;
|
||||
let kredit = parseFloat(r.kredit) || 0;
|
||||
|
||||
td += debit;
|
||||
tk += kredit;
|
||||
@@ -53,7 +97,8 @@ $(function(){
|
||||
<td>${r.nama_akun}</td>
|
||||
<td class="text-end">${debit.toLocaleString()}</td>
|
||||
<td class="text-end">${kredit.toLocaleString()}</td>
|
||||
</tr>`;
|
||||
</tr>
|
||||
`;
|
||||
});
|
||||
|
||||
$('#tb').html(html);
|
||||
@@ -62,5 +107,26 @@ $(function(){
|
||||
|
||||
}, 'json');
|
||||
|
||||
}
|
||||
|
||||
$('#generate_pdf').click(function () {
|
||||
|
||||
let btn = $(this);
|
||||
|
||||
btn.prop('disabled', true).text('Generate PDF...');
|
||||
|
||||
window.open(
|
||||
"<?= base_url('generateneracasaldo/generatepdf'); ?>" +
|
||||
"?tanggal_awal=" + $('#tanggal_awal').val() +
|
||||
"&tanggal_akhir=" + $('#tanggal_akhir').val(),
|
||||
"_blank"
|
||||
);
|
||||
|
||||
setTimeout(function () {
|
||||
btn.prop('disabled', false).text('Generate PDF');
|
||||
}, 1000);
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
@@ -38,6 +38,8 @@
|
||||
<link href="https://cdn.jsdelivr.net/npm/select2@4.0.13/dist/css/select2.min.css" rel="stylesheet" />
|
||||
<script src="https://cdn.jsdelivr.net/npm/select2@4.0.13/dist/js/select2.min.js"></script>
|
||||
|
||||
<!-- Camera Scanner -->
|
||||
<script src="https://unpkg.com/html5-qrcode"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -82,15 +84,15 @@
|
||||
<i class="bi bi-graph-up"></i> Laporan
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a class="dropdown-item <?= ($active_menu == 'neraca_saldo') ? 'active' : '' ?>" href="<?= base_url('neracasaldo'); ?>">
|
||||
<i class="bi bi-clipboard-data me-2"></i> Neraca Saldo</a>
|
||||
</li>
|
||||
<li><a class="dropdown-item <?= ($active_menu == 'laba_rugi') ? 'active' : '' ?>" href="<?= base_url('labarugi'); ?>">
|
||||
<i class="bi bi-graph-up-arrow me-2"></i> Laba Rugi</a>
|
||||
</li>
|
||||
<li><a class="dropdown-item <?= ($active_menu == 'neraca') ? 'active' : '' ?>" href="<?= base_url('neraca'); ?>">
|
||||
<i class="bi bi-bar-chart-line me-2"></i> Neraca</a>
|
||||
</li>
|
||||
<li><a class="dropdown-item <?= ($active_menu == 'neraca_saldo') ? 'active' : '' ?>" href="<?= base_url('neracasaldo'); ?>">
|
||||
<i class="bi bi-clipboard-data me-2"></i> Laporan Harian</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
@@ -156,7 +158,7 @@
|
||||
</li>
|
||||
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle <?= in_array($active_menu, ['warehouses','items', 'kode_barang', 'draft_items']) ? 'active' : '' ?>" href="#" data-bs-toggle="dropdown">
|
||||
<a class="nav-link dropdown-toggle <?= in_array($active_menu, ['warehouses','items', 'kode_barang', 'draft_items', 'peralatan_teknisi']) ? 'active' : '' ?>" href="#" data-bs-toggle="dropdown">
|
||||
<i class="bi bi-box-seam"></i> Gudang
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
@@ -178,11 +180,23 @@
|
||||
<i class="bi bi-box me-2"></i> Daftar Barang
|
||||
</a>
|
||||
</li><hr class="dropdown-divider">
|
||||
<li>
|
||||
<a class="dropdown-item <?= ($active_menu == 'item_historys') ? 'active' : '' ?>"
|
||||
href="<?= base_url('items/item_barcodes_history'); ?>">
|
||||
<i class="bi bi-clock me-2"></i> History Barang
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="dropdown-item <?= ($active_menu == 'kode_barang') ? 'active' : '' ?>"
|
||||
href="<?= base_url('kodebarang'); ?>">
|
||||
<i class="bi bi-code me-2"></i> Daftar Kode Barang
|
||||
</a>
|
||||
</li><hr class="dropdown-divider">
|
||||
<li>
|
||||
<a class="dropdown-item <?= ($active_menu == 'peralatan_teknisi') ? 'active' : '' ?>"
|
||||
href="<?= base_url('teknisi'); ?>">
|
||||
<i class="bi bi-tools me-2"></i> Peralatan Teknisi
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
@@ -258,17 +272,24 @@
|
||||
</li>
|
||||
|
||||
<li>
|
||||
|
||||
<a class="dropdown-item rounded-3 <?= ($active_menu == 'attendance_monitoring') ? 'active' : '' ?>"
|
||||
href="<?= base_url('attendancemonitoring'); ?>">
|
||||
|
||||
<i class="bi bi-fingerprint me-2"></i>
|
||||
Monitoring Absensi
|
||||
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="dropdown-item rounded-3 <?= ($active_menu == 'attendance_monthly') ? 'active' : '' ?>"
|
||||
href="<?= base_url('attendancemonthly'); ?>">
|
||||
|
||||
<i class="bi bi-calendar2-date me-2"></i>
|
||||
Absensi Bulanan
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
<hr class="dropdown-divider">
|
||||
<li>
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,155 @@
|
||||
<link rel="stylesheet" href="https://cdn.datatables.net/1.13.6/css/dataTables.bootstrap5.min.css">
|
||||
<link rel="stylesheet" href="https://cdn.datatables.net/responsive/2.4.1/css/responsive.bootstrap5.min.css">
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--tech-primary: #ff9800;
|
||||
--tech-primary-light: #fff3e0;
|
||||
--tech-primary-dark: #ff9800;
|
||||
}
|
||||
|
||||
body {
|
||||
background: #f5f7fb;
|
||||
}
|
||||
|
||||
.tech-card {
|
||||
border: none;
|
||||
border-radius: 14px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.tech-card .card-header-custom {
|
||||
background: linear-gradient(135deg, var(--tech-primary), var(--tech-primary-dark));
|
||||
color: #fff;
|
||||
padding: 14px 20px;
|
||||
font-weight: 600;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.tech-card .card-body {
|
||||
padding: 18px;
|
||||
}
|
||||
|
||||
.table-tech thead th {
|
||||
background: #f8f9fa;
|
||||
font-weight: 600;
|
||||
font-size: 12px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .4px;
|
||||
color: #495057;
|
||||
border-bottom: 2px solid #ffcc80;
|
||||
padding: 10px 8px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.table-tech tbody td {
|
||||
padding: 8px;
|
||||
vertical-align: middle;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.table-tech td .badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 65px;
|
||||
padding: 0.35em 0.65em;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.table-tech tbody tr:hover {
|
||||
background: #fff3e0;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="container-fluid px-3 mt-3">
|
||||
|
||||
<div class="d-flex align-items-center gap-2 mb-3">
|
||||
<div class="bg-warning text-white p-2 rounded-3 d-flex align-items-center justify-content-center" style="width:38px;height:38px;">
|
||||
<i class="bi bi-journal-text fs-5"></i>
|
||||
</div>
|
||||
<div class="flex-grow-1">
|
||||
<h5 class="mb-0 fw-bold" style="color:#1a1a2e;">Log Barang Teknisi</h5>
|
||||
<small class="text-muted">Riwayat barang yang pernah dibawa oleh teknisi</small>
|
||||
</div>
|
||||
<div>
|
||||
<a href="<?= base_url('teknisi'); ?>" class="btn btn-secondary">
|
||||
<i class="bi bi-arrow-left me-1"></i> Kembali
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card tech-card">
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-tech table-striped w-100" id="tblLogBarang">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>No</th>
|
||||
<th>Nama Teknisi</th>
|
||||
<th>Kode Barang</th>
|
||||
<th>Barcode</th>
|
||||
<th>Nama Barang</th>
|
||||
<th>Tanggal Dibawa</th>
|
||||
<th>Tanggal Kembali</th>
|
||||
<th>Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
$(function(){
|
||||
$('#tblLogBarang').DataTable({
|
||||
processing: true,
|
||||
serverSide: false,
|
||||
ajax: {
|
||||
url: "<?= base_url('teknisi/get_log_data'); ?>",
|
||||
dataSrc: 'data'
|
||||
},
|
||||
columns: [
|
||||
{ data: 0 },
|
||||
{ data: 1 },
|
||||
{ data: 2 },
|
||||
{ data: 3 },
|
||||
{ data: 4 },
|
||||
{ data: 5 },
|
||||
{ data: 6 },
|
||||
{ data: 7 }
|
||||
],
|
||||
pageLength: 25,
|
||||
lengthMenu: [
|
||||
[25, 50, 100, -1],
|
||||
[25, 50, 100, 'All']
|
||||
],
|
||||
responsive: true,
|
||||
searching: true,
|
||||
paging: true,
|
||||
ordering: true,
|
||||
info: true,
|
||||
language: {
|
||||
search: "Cari:",
|
||||
lengthMenu: "Tampilkan _MENU_ data",
|
||||
info: "Menampilkan _START_ - _END_ dari _TOTAL_ data",
|
||||
zeroRecords: "Tidak ada data",
|
||||
paginate: {
|
||||
first: "Awal",
|
||||
last: "Akhir",
|
||||
next: "»",
|
||||
previous: "«"
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 19 KiB |
Reference in New Issue
Block a user