Initial commit

This commit is contained in:
root
2026-05-26 08:07:45 +00:00
commit a234e55e64
4263 changed files with 855927 additions and 0 deletions
+15
View File
@@ -0,0 +1,15 @@
# top-most EditorConfig file
root = true
# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
insert_final_newline = true
# Matches multiple files with brace expansion notation
# Set default charset
[*]
charset = utf-8
# Tab indentation (no size specified)
indent_style = tab
+30
View File
@@ -0,0 +1,30 @@
.DS_Store
application/cache/*
!application/cache/index.html
application/logs/*
!application/logs/index.html
!application/*/.htaccess
tests/mocks/database/ci_test.sqlite
user_guide_src/build/*
user_guide_src/cilexer/build/*
user_guide_src/cilexer/dist/*
user_guide_src/cilexer/pycilexer.egg-info/*
# IDE Files
#-------------------------
/nbproject/
.idea/*
## Sublime Text cache files
*.tmlanguage.cache
*.tmPreferences.cache
*.stTheme.cache
*.sublime-workspace
*.sublime-project
/tests/tests/
/tests/results/
+9
View File
@@ -0,0 +1,9 @@
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
# Redirect requests to index.php
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?/$1 [L]
</IfModule>
+6
View File
@@ -0,0 +1,6 @@
<IfModule authz_core_module>
Require all denied
</IfModule>
<IfModule !authz_core_module>
Deny from all
</IfModule>
+11
View File
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<title>403 Forbidden</title>
</head>
<body>
<p>Directory access is forbidden.</p>
</body>
</html>
+135
View File
@@ -0,0 +1,135 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
/*
| -------------------------------------------------------------------
| AUTO-LOADER
| -------------------------------------------------------------------
| This file specifies which systems should be loaded by default.
|
| In order to keep the framework as light-weight as possible only the
| absolute minimal resources are loaded by default. For example,
| the database is not connected to automatically since no assumption
| is made regarding whether you intend to use it. This file lets
| you globally define which systems you would like loaded with every
| request.
|
| -------------------------------------------------------------------
| Instructions
| -------------------------------------------------------------------
|
| These are the things you can load automatically:
|
| 1. Packages
| 2. Libraries
| 3. Drivers
| 4. Helper files
| 5. Custom config files
| 6. Language files
| 7. Models
|
*/
/*
| -------------------------------------------------------------------
| Auto-load Packages
| -------------------------------------------------------------------
| Prototype:
|
| $autoload['packages'] = array(APPPATH.'third_party', '/usr/local/shared');
|
*/
$autoload['packages'] = array();
/*
| -------------------------------------------------------------------
| Auto-load Libraries
| -------------------------------------------------------------------
| These are the classes located in system/libraries/ or your
| application/libraries/ directory, with the addition of the
| 'database' library, which is somewhat of a special case.
|
| Prototype:
|
| $autoload['libraries'] = array('database', 'email', 'session');
|
| You can also supply an alternative library name to be assigned
| in the controller:
|
| $autoload['libraries'] = array('user_agent' => 'ua');
*/
$autoload['libraries'] = array('session', 'database');
/*
| -------------------------------------------------------------------
| Auto-load Drivers
| -------------------------------------------------------------------
| These classes are located in system/libraries/ or in your
| application/libraries/ directory, but are also placed inside their
| own subdirectory and they extend the CI_Driver_Library class. They
| offer multiple interchangeable driver options.
|
| Prototype:
|
| $autoload['drivers'] = array('cache');
|
| You can also supply an alternative property name to be assigned in
| the controller:
|
| $autoload['drivers'] = array('cache' => 'cch');
|
*/
$autoload['drivers'] = array();
/*
| -------------------------------------------------------------------
| Auto-load Helper Files
| -------------------------------------------------------------------
| Prototype:
|
| $autoload['helper'] = array('url', 'file');
*/
$autoload['helper'] = array('url', 'file', 'permission', 'activity', 'indo_date');
/*
| -------------------------------------------------------------------
| Auto-load Config files
| -------------------------------------------------------------------
| Prototype:
|
| $autoload['config'] = array('config1', 'config2');
|
| NOTE: This item is intended for use ONLY if you have created custom
| config files. Otherwise, leave it blank.
|
*/
$autoload['config'] = array();
/*
| -------------------------------------------------------------------
| Auto-load Language files
| -------------------------------------------------------------------
| Prototype:
|
| $autoload['language'] = array('lang1', 'lang2');
|
| NOTE: Do not include the "_lang" part of your file. For example
| "codeigniter_lang.php" would be referenced as array('codeigniter');
|
*/
$autoload['language'] = array();
/*
| -------------------------------------------------------------------
| Auto-load Models
| -------------------------------------------------------------------
| Prototype:
|
| $autoload['model'] = array('first_model', 'second_model');
|
| You can also supply an alternative model name to be assigned
| in the controller:
|
| $autoload['model'] = array('first_model' => 'first');
*/
$autoload['model'] = array();
+533
View File
@@ -0,0 +1,533 @@
<?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/keuangan/';
$config['base_url'] = 'https://accounting.radiq.my.id/';
/*
|--------------------------------------------------------------------------
| 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'] = '';
+85
View File
@@ -0,0 +1,85 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
/*
|--------------------------------------------------------------------------
| Display Debug backtrace
|--------------------------------------------------------------------------
|
| If set to TRUE, a backtrace will be displayed along with php errors. If
| error_reporting is disabled, the backtrace will not display, regardless
| of this setting
|
*/
defined('SHOW_DEBUG_BACKTRACE') OR define('SHOW_DEBUG_BACKTRACE', TRUE);
/*
|--------------------------------------------------------------------------
| File and Directory Modes
|--------------------------------------------------------------------------
|
| These prefs are used when checking and setting modes when working
| with the file system. The defaults are fine on servers with proper
| security, but you may wish (or even need) to change the values in
| certain environments (Apache running a separate process for each
| user, PHP under CGI with Apache suEXEC, etc.). Octal values should
| always be used to set the mode correctly.
|
*/
defined('FILE_READ_MODE') OR define('FILE_READ_MODE', 0644);
defined('FILE_WRITE_MODE') OR define('FILE_WRITE_MODE', 0666);
defined('DIR_READ_MODE') OR define('DIR_READ_MODE', 0755);
defined('DIR_WRITE_MODE') OR define('DIR_WRITE_MODE', 0755);
/*
|--------------------------------------------------------------------------
| File Stream Modes
|--------------------------------------------------------------------------
|
| These modes are used when working with fopen()/popen()
|
*/
defined('FOPEN_READ') OR define('FOPEN_READ', 'rb');
defined('FOPEN_READ_WRITE') OR define('FOPEN_READ_WRITE', 'r+b');
defined('FOPEN_WRITE_CREATE_DESTRUCTIVE') OR define('FOPEN_WRITE_CREATE_DESTRUCTIVE', 'wb'); // truncates existing file data, use with care
defined('FOPEN_READ_WRITE_CREATE_DESTRUCTIVE') OR define('FOPEN_READ_WRITE_CREATE_DESTRUCTIVE', 'w+b'); // truncates existing file data, use with care
defined('FOPEN_WRITE_CREATE') OR define('FOPEN_WRITE_CREATE', 'ab');
defined('FOPEN_READ_WRITE_CREATE') OR define('FOPEN_READ_WRITE_CREATE', 'a+b');
defined('FOPEN_WRITE_CREATE_STRICT') OR define('FOPEN_WRITE_CREATE_STRICT', 'xb');
defined('FOPEN_READ_WRITE_CREATE_STRICT') OR define('FOPEN_READ_WRITE_CREATE_STRICT', 'x+b');
/*
|--------------------------------------------------------------------------
| Exit Status Codes
|--------------------------------------------------------------------------
|
| Used to indicate the conditions under which the script is exit()ing.
| While there is no universal standard for error codes, there are some
| broad conventions. Three such conventions are mentioned below, for
| those who wish to make use of them. The CodeIgniter defaults were
| chosen for the least overlap with these conventions, while still
| leaving room for others to be defined in future versions and user
| applications.
|
| The three main conventions used for determining exit status codes
| are as follows:
|
| Standard C/C++ Library (stdlibc):
| http://www.gnu.org/software/libc/manual/html_node/Exit-Status.html
| (This link also contains other GNU-specific conventions)
| BSD sysexits.h:
| http://www.gsp.com/cgi-bin/man.cgi?section=3&topic=sysexits
| Bash scripting:
| http://tldp.org/LDP/abs/html/exitcodes.html
|
*/
defined('EXIT_SUCCESS') OR define('EXIT_SUCCESS', 0); // no errors
defined('EXIT_ERROR') OR define('EXIT_ERROR', 1); // generic error
defined('EXIT_CONFIG') OR define('EXIT_CONFIG', 3); // configuration error
defined('EXIT_UNKNOWN_FILE') OR define('EXIT_UNKNOWN_FILE', 4); // file not found
defined('EXIT_UNKNOWN_CLASS') OR define('EXIT_UNKNOWN_CLASS', 5); // unknown class
defined('EXIT_UNKNOWN_METHOD') OR define('EXIT_UNKNOWN_METHOD', 6); // unknown class member
defined('EXIT_USER_INPUT') OR define('EXIT_USER_INPUT', 7); // invalid user input
defined('EXIT_DATABASE') OR define('EXIT_DATABASE', 8); // database error
defined('EXIT__AUTO_MIN') OR define('EXIT__AUTO_MIN', 9); // lowest automatically-assigned error code
defined('EXIT__AUTO_MAX') OR define('EXIT__AUTO_MAX', 125); // highest automatically-assigned error code
+98
View File
@@ -0,0 +1,98 @@
<?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' => 3309, // port PostgreSQL
'username' => 'keuangan',
'password' => '@P4ssw0rd',
'database' => 'keuangan',
'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
);
+24
View File
@@ -0,0 +1,24 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
$_doctypes = array(
'xhtml11' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">',
'xhtml1-strict' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">',
'xhtml1-trans' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">',
'xhtml1-frame' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">',
'xhtml-basic11' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN" "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd">',
'html5' => '<!DOCTYPE html>',
'html4-strict' => '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">',
'html4-trans' => '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">',
'html4-frame' => '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">',
'mathml1' => '<!DOCTYPE math SYSTEM "http://www.w3.org/Math/DTD/mathml1/mathml.dtd">',
'mathml2' => '<!DOCTYPE math PUBLIC "-//W3C//DTD MathML 2.0//EN" "http://www.w3.org/Math/DTD/mathml2/mathml2.dtd">',
'svg10' => '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">',
'svg11' => '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">',
'svg11-basic' => '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1 Basic//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-basic.dtd">',
'svg11-tiny' => '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1 Tiny//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-tiny.dtd">',
'xhtml-math-svg-xh' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN" "http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd">',
'xhtml-math-svg-sh' => '<!DOCTYPE svg:svg PUBLIC "-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN" "http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd">',
'xhtml-rdfa-1' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.0//EN" "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd">',
'xhtml-rdfa-2' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.1//EN" "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-2.dtd">'
);
+114
View File
@@ -0,0 +1,114 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
/*
| -------------------------------------------------------------------
| Foreign Characters
| -------------------------------------------------------------------
| This file contains an array of foreign characters for transliteration
| conversion used by the Text helper
|
*/
$foreign_characters = array(
'/ä|æ|ǽ/' => 'ae',
'/ö|œ/' => 'oe',
'/ü/' => 'ue',
'/Ä/' => 'Ae',
'/Ü/' => 'Ue',
'/Ö/' => 'Oe',
'/À|Á|Â|Ã|Ä|Å|Ǻ|Ā|Ă|Ą|Ǎ|Α|Ά|Ả|Ạ|Ầ|Ẫ|Ẩ|Ậ|Ằ|Ắ|Ẵ|Ẳ|Ặ|А/' => 'A',
'/à|á|â|ã|å|ǻ|ā|ă|ą|ǎ|ª|α|ά|ả|ạ|ầ|ấ|ẫ|ẩ|ậ|ằ|ắ|ẵ|ẳ|ặ|а/' => 'a',
'/Б/' => 'B',
'/б/' => 'b',
'/Ç|Ć|Ĉ|Ċ|Č/' => 'C',
'/ç|ć|ĉ|ċ|č/' => 'c',
'/Д|Δ/' => 'D',
'/д|δ/' => 'd',
'/Ð|Ď|Đ/' => 'Dj',
'/ð|ď|đ/' => 'dj',
'/È|É|Ê|Ë|Ē|Ĕ|Ė|Ę|Ě|Ε|Έ|Ẽ|Ẻ|Ẹ|Ề|Ế|Ễ|Ể|Ệ|Е|Э/' => 'E',
'/è|é|ê|ë|ē|ĕ|ė|ę|ě|έ|ε|ẽ|ẻ|ẹ|ề|ế|ễ|ể|ệ|е|э/' => 'e',
'/Ф/' => 'F',
'/ф/' => 'f',
'/Ĝ|Ğ|Ġ|Ģ|Γ|Г|Ґ/' => 'G',
'/ĝ|ğ|ġ|ģ|γ|г|ґ/' => 'g',
'/Ĥ|Ħ/' => 'H',
'/ĥ|ħ/' => 'h',
'/Ì|Í|Î|Ï|Ĩ|Ī|Ĭ|Ǐ|Į|İ|Η|Ή|Ί|Ι|Ϊ|Ỉ|Ị|И|Ы/' => 'I',
'/ì|í|î|ï|ĩ|ī|ĭ|ǐ|į|ı|η|ή|ί|ι|ϊ|ỉ|ị|и|ы|ї/' => 'i',
'/Ĵ/' => 'J',
'/ĵ/' => 'j',
'/Θ/' => 'TH',
'/θ/' => 'th',
'/Ķ|Κ|К/' => 'K',
'/ķ|κ|к/' => 'k',
'/Ĺ|Ļ|Ľ|Ŀ|Ł|Λ|Л/' => 'L',
'/ĺ|ļ|ľ|ŀ|ł|λ|л/' => 'l',
'/М/' => 'M',
'/м/' => 'm',
'/Ñ|Ń|Ņ|Ň|Ν|Н/' => 'N',
'/ñ|ń|ņ|ň|ʼn|ν|н/' => 'n',
'/Ò|Ó|Ô|Õ|Ō|Ŏ|Ǒ|Ő|Ơ|Ø|Ǿ|Ο|Ό|Ω|Ώ|Ỏ|Ọ|Ồ|Ố|Ỗ|Ổ|Ộ|Ờ|Ớ|Ỡ|Ở|Ợ|О/' => 'O',
'/ò|ó|ô|õ|ō|ŏ|ǒ|ő|ơ|ø|ǿ|º|ο|ό|ω|ώ|ỏ|ọ|ồ|ố|ỗ|ổ|ộ|ờ|ớ|ỡ|ở|ợ|о/' => 'o',
'/П/' => 'P',
'/п/' => 'p',
'/Ŕ|Ŗ|Ř|Ρ|Р/' => 'R',
'/ŕ|ŗ|ř|ρ|р/' => 'r',
'/Ś|Ŝ|Ş|Ș|Š|Σ|С/' => 'S',
'/ś|ŝ|ş|ș|š|ſ|σ|ς|с/' => 's',
'/Ț|Ţ|Ť|Ŧ|Τ|Т/' => 'T',
'/ț|ţ|ť|ŧ|τ|т/' => 't',
'/Þ|þ/' => 'th',
'/Ù|Ú|Û|Ũ|Ū|Ŭ|Ů|Ű|Ų|Ư|Ǔ|Ǖ|Ǘ|Ǚ|Ǜ|Ũ|Ủ|Ụ|Ừ|Ứ|Ữ|Ử|Ự|У/' => 'U',
'/ù|ú|û|ũ|ū|ŭ|ů|ű|ų|ư|ǔ|ǖ|ǘ|ǚ|ǜ|υ|ύ|ϋ|ủ|ụ|ừ|ứ|ữ|ử|ự|у/' => 'u',
'/Ƴ|Ɏ|Ỵ|Ẏ|Ӳ|Ӯ|Ў|Ý|Ÿ|Ŷ|Υ|Ύ|Ϋ|Ỳ|Ỹ|Ỷ|Ỵ|Й/' => 'Y',
'/ẙ|ʏ|ƴ|ɏ|ỵ|ẏ|ӳ|ӯ|ў|ý|ÿ|ŷ|ỳ|ỹ|ỷ|ỵ|й/' => 'y',
'/В/' => 'V',
'/в/' => 'v',
'/Ŵ/' => 'W',
'/ŵ/' => 'w',
'/Φ/' => 'F',
'/φ/' => 'f',
'/Χ/' => 'CH',
'/χ/' => 'ch',
'/Ź|Ż|Ž|Ζ|З/' => 'Z',
'/ź|ż|ž|ζ|з/' => 'z',
'/Æ|Ǽ/' => 'AE',
'/ß/' => 'ss',
'/IJ/' => 'IJ',
'/ij/' => 'ij',
'/Œ/' => 'OE',
'/ƒ/' => 'f',
'/Ξ/' => 'KS',
'/ξ/' => 'ks',
'/Π/' => 'P',
'/π/' => 'p',
'/Β/' => 'V',
'/β/' => 'v',
'/Μ/' => 'M',
'/μ/' => 'm',
'/Ψ/' => 'PS',
'/ψ/' => 'ps',
'/Ё/' => 'Yo',
'/ё/' => 'yo',
'/Є/' => 'Ye',
'/є/' => 'ye',
'/Ї/' => 'Yi',
'/Ж/' => 'Zh',
'/ж/' => 'zh',
'/Х/' => 'Kh',
'/х/' => 'kh',
'/Ц/' => 'Ts',
'/ц/' => 'ts',
'/Ч/' => 'Ch',
'/ч/' => 'ch',
'/Ш/' => 'Sh',
'/ш/' => 'sh',
'/Щ/' => 'Shch',
'/щ/' => 'shch',
'/Ъ|ъ|Ь|ь/' => '',
'/Ю/' => 'Yu',
'/ю/' => 'yu',
'/Я/' => 'Ya',
'/я/' => 'ya'
);
+13
View File
@@ -0,0 +1,13 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
/*
| -------------------------------------------------------------------------
| Hooks
| -------------------------------------------------------------------------
| This file lets you define "hooks" to extend CI without hacking the core
| files. Please see the user guide for info:
|
| https://codeigniter.com/userguide3/general/hooks.html
|
*/
+11
View File
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<title>403 Forbidden</title>
</head>
<body>
<p>Directory access is forbidden.</p>
</body>
</html>
+19
View File
@@ -0,0 +1,19 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
/*
| -------------------------------------------------------------------------
| Memcached settings
| -------------------------------------------------------------------------
| Your Memcached servers can be specified below.
|
| See: https://codeigniter.com/userguide3/libraries/caching.html#memcached
|
*/
$config = array(
'default' => array(
'hostname' => '127.0.0.1',
'port' => '11211',
'weight' => '1',
),
);
+84
View File
@@ -0,0 +1,84 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
/*
|--------------------------------------------------------------------------
| Enable/Disable Migrations
|--------------------------------------------------------------------------
|
| Migrations are disabled by default for security reasons.
| You should enable migrations whenever you intend to do a schema migration
| and disable it back when you're done.
|
*/
$config['migration_enabled'] = FALSE;
/*
|--------------------------------------------------------------------------
| Migration Type
|--------------------------------------------------------------------------
|
| Migration file names may be based on a sequential identifier or on
| a timestamp. Options are:
|
| 'sequential' = Sequential migration naming (001_add_blog.php)
| 'timestamp' = Timestamp migration naming (20121031104401_add_blog.php)
| Use timestamp format YYYYMMDDHHIISS.
|
| Note: If this configuration value is missing the Migration library
| defaults to 'sequential' for backward compatibility with CI2.
|
*/
$config['migration_type'] = 'timestamp';
/*
|--------------------------------------------------------------------------
| Migrations table
|--------------------------------------------------------------------------
|
| This is the name of the table that will store the current migrations state.
| When migrations runs it will store in a database table which migration
| level the system is at. It then compares the migration level in this
| table to the $config['migration_version'] if they are not the same it
| will migrate up. This must be set.
|
*/
$config['migration_table'] = 'migrations';
/*
|--------------------------------------------------------------------------
| Auto Migrate To Latest
|--------------------------------------------------------------------------
|
| If this is set to TRUE when you load the migrations class and have
| $config['migration_enabled'] set to TRUE the system will auto migrate
| to your latest migration (whatever $config['migration_version'] is
| set to). This way you do not have to call migrations anywhere else
| in your code to have the latest migration.
|
*/
$config['migration_auto_latest'] = FALSE;
/*
|--------------------------------------------------------------------------
| Migrations version
|--------------------------------------------------------------------------
|
| This is used to set migration version that the file system should be on.
| If you run $this->migration->current() this is the version that schema will
| be upgraded / downgraded to.
|
*/
$config['migration_version'] = 0;
/*
|--------------------------------------------------------------------------
| Migrations Path
|--------------------------------------------------------------------------
|
| Path to your migrations folder.
| Typically, it will be within your application path.
| Also, writing permission is required within the migrations path.
|
*/
$config['migration_path'] = APPPATH.'migrations/';
+186
View File
@@ -0,0 +1,186 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
/*
| -------------------------------------------------------------------
| MIME TYPES
| -------------------------------------------------------------------
| This file contains an array of mime types. It is used by the
| Upload class to help identify allowed file types.
|
*/
return array(
'hqx' => array('application/mac-binhex40', 'application/mac-binhex', 'application/x-binhex40', 'application/x-mac-binhex40'),
'cpt' => 'application/mac-compactpro',
'csv' => array('text/x-comma-separated-values', 'text/comma-separated-values', 'application/octet-stream', 'application/vnd.ms-excel', 'application/x-csv', 'text/x-csv', 'text/csv', 'application/csv', 'application/excel', 'application/vnd.msexcel', 'text/plain'),
'bin' => array('application/macbinary', 'application/mac-binary', 'application/octet-stream', 'application/x-binary', 'application/x-macbinary'),
'dms' => 'application/octet-stream',
'lha' => 'application/octet-stream',
'lzh' => 'application/octet-stream',
'exe' => array('application/octet-stream', 'application/x-msdownload'),
'class' => 'application/octet-stream',
'psd' => array('application/x-photoshop', 'image/vnd.adobe.photoshop'),
'so' => 'application/octet-stream',
'sea' => 'application/octet-stream',
'dll' => 'application/octet-stream',
'oda' => 'application/oda',
'pdf' => array('application/pdf', 'application/force-download', 'application/x-download', 'binary/octet-stream'),
'ai' => array('application/pdf', 'application/postscript'),
'eps' => 'application/postscript',
'ps' => 'application/postscript',
'smi' => 'application/smil',
'smil' => 'application/smil',
'mif' => 'application/vnd.mif',
'xls' => array('application/vnd.ms-excel', 'application/msexcel', 'application/x-msexcel', 'application/x-ms-excel', 'application/x-excel', 'application/x-dos_ms_excel', 'application/xls', 'application/x-xls', 'application/excel', 'application/download', 'application/vnd.ms-office', 'application/msword'),
'ppt' => array('application/powerpoint', 'application/vnd.ms-powerpoint', 'application/vnd.ms-office', 'application/msword'),
'pptx' => array('application/vnd.openxmlformats-officedocument.presentationml.presentation', 'application/x-zip', 'application/zip'),
'wbxml' => 'application/wbxml',
'wmlc' => 'application/wmlc',
'dcr' => 'application/x-director',
'dir' => 'application/x-director',
'dxr' => 'application/x-director',
'dvi' => 'application/x-dvi',
'gtar' => 'application/x-gtar',
'gz' => 'application/x-gzip',
'gzip' => 'application/x-gzip',
'php' => array('application/x-httpd-php', 'application/php', 'application/x-php', 'text/php', 'text/x-php', 'application/x-httpd-php-source'),
'php4' => 'application/x-httpd-php',
'php3' => 'application/x-httpd-php',
'phtml' => 'application/x-httpd-php',
'phps' => 'application/x-httpd-php-source',
'js' => array('application/x-javascript', 'text/plain'),
'swf' => 'application/x-shockwave-flash',
'sit' => 'application/x-stuffit',
'tar' => 'application/x-tar',
'tgz' => array('application/x-tar', 'application/x-gzip-compressed'),
'z' => 'application/x-compress',
'xhtml' => 'application/xhtml+xml',
'xht' => 'application/xhtml+xml',
'zip' => array('application/x-zip', 'application/zip', 'application/x-zip-compressed', 'application/s-compressed', 'multipart/x-zip'),
'rar' => array('application/x-rar', 'application/rar', 'application/x-rar-compressed'),
'mid' => 'audio/midi',
'midi' => 'audio/midi',
'mpga' => 'audio/mpeg',
'mp2' => 'audio/mpeg',
'mp3' => array('audio/mpeg', 'audio/mpg', 'audio/mpeg3', 'audio/mp3'),
'aif' => array('audio/x-aiff', 'audio/aiff'),
'aiff' => array('audio/x-aiff', 'audio/aiff'),
'aifc' => 'audio/x-aiff',
'ram' => 'audio/x-pn-realaudio',
'rm' => 'audio/x-pn-realaudio',
'rpm' => 'audio/x-pn-realaudio-plugin',
'ra' => 'audio/x-realaudio',
'rv' => 'video/vnd.rn-realvideo',
'wav' => array('audio/x-wav', 'audio/wave', 'audio/wav'),
'bmp' => array('image/bmp', 'image/x-bmp', 'image/x-bitmap', 'image/x-xbitmap', 'image/x-win-bitmap', 'image/x-windows-bmp', 'image/ms-bmp', 'image/x-ms-bmp', 'application/bmp', 'application/x-bmp', 'application/x-win-bitmap'),
'gif' => 'image/gif',
'jpeg' => array('image/jpeg', 'image/pjpeg'),
'jpg' => array('image/jpeg', 'image/pjpeg'),
'jpe' => array('image/jpeg', 'image/pjpeg'),
'jp2' => array('image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'),
'j2k' => array('image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'),
'jpf' => array('image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'),
'jpg2' => array('image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'),
'jpx' => array('image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'),
'jpm' => array('image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'),
'mj2' => array('image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'),
'mjp2' => array('image/jp2', 'video/mj2', 'image/jpx', 'image/jpm'),
'png' => array('image/png', 'image/x-png'),
'tiff' => 'image/tiff',
'tif' => 'image/tiff',
'heic' => 'image/heic',
'heif' => 'image/heif',
'css' => array('text/css', 'text/plain'),
'html' => array('text/html', 'text/plain'),
'htm' => array('text/html', 'text/plain'),
'shtml' => array('text/html', 'text/plain'),
'txt' => 'text/plain',
'text' => 'text/plain',
'log' => array('text/plain', 'text/x-log'),
'rtx' => 'text/richtext',
'rtf' => 'text/rtf',
'xml' => array('application/xml', 'text/xml', 'text/plain'),
'xsl' => array('application/xml', 'text/xsl', 'text/xml'),
'mpeg' => 'video/mpeg',
'mpg' => 'video/mpeg',
'mpe' => 'video/mpeg',
'qt' => 'video/quicktime',
'mov' => 'video/quicktime',
'avi' => array('video/x-msvideo', 'video/msvideo', 'video/avi', 'application/x-troff-msvideo'),
'movie' => 'video/x-sgi-movie',
'doc' => array('application/msword', 'application/vnd.ms-office'),
'docx' => array('application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/zip', 'application/msword', 'application/x-zip'),
'dot' => array('application/msword', 'application/vnd.ms-office'),
'dotx' => array('application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/zip', 'application/msword'),
'xlsx' => array('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/zip', 'application/vnd.ms-excel', 'application/msword', 'application/x-zip'),
'word' => array('application/msword', 'application/octet-stream'),
'xl' => 'application/excel',
'eml' => 'message/rfc822',
'json' => array('application/json', 'text/json'),
'pem' => array('application/x-x509-user-cert', 'application/x-pem-file', 'application/octet-stream'),
'p10' => array('application/x-pkcs10', 'application/pkcs10'),
'p12' => 'application/x-pkcs12',
'p7a' => 'application/x-pkcs7-signature',
'p7c' => array('application/pkcs7-mime', 'application/x-pkcs7-mime'),
'p7m' => array('application/pkcs7-mime', 'application/x-pkcs7-mime'),
'p7r' => 'application/x-pkcs7-certreqresp',
'p7s' => 'application/pkcs7-signature',
'crt' => array('application/x-x509-ca-cert', 'application/x-x509-user-cert', 'application/pkix-cert'),
'crl' => array('application/pkix-crl', 'application/pkcs-crl'),
'der' => 'application/x-x509-ca-cert',
'kdb' => 'application/octet-stream',
'pgp' => 'application/pgp',
'gpg' => 'application/gpg-keys',
'sst' => 'application/octet-stream',
'csr' => 'application/octet-stream',
'rsa' => 'application/x-pkcs7',
'cer' => array('application/pkix-cert', 'application/x-x509-ca-cert'),
'3g2' => 'video/3gpp2',
'3gp' => array('video/3gp', 'video/3gpp'),
'mp4' => 'video/mp4',
'm4a' => 'audio/x-m4a',
'f4v' => array('video/mp4', 'video/x-f4v'),
'flv' => 'video/x-flv',
'webm' => 'video/webm',
'aac' => array('audio/x-aac', 'audio/aac'),
'm4u' => 'application/vnd.mpegurl',
'm3u' => 'text/plain',
'xspf' => 'application/xspf+xml',
'vlc' => 'application/videolan',
'wmv' => array('video/x-ms-wmv', 'video/x-ms-asf'),
'au' => 'audio/x-au',
'ac3' => 'audio/ac3',
'flac' => 'audio/x-flac',
'ogg' => array('audio/ogg', 'video/ogg', 'application/ogg'),
'kmz' => array('application/vnd.google-earth.kmz', 'application/zip', 'application/x-zip'),
'kml' => array('application/vnd.google-earth.kml+xml', 'application/xml', 'text/xml'),
'ics' => 'text/calendar',
'ical' => 'text/calendar',
'zsh' => 'text/x-scriptzsh',
'7z' => array('application/x-7z-compressed', 'application/x-compressed', 'application/x-zip-compressed', 'application/zip', 'multipart/x-zip'),
'7zip' => array('application/x-7z-compressed', 'application/x-compressed', 'application/x-zip-compressed', 'application/zip', 'multipart/x-zip'),
'cdr' => array('application/cdr', 'application/coreldraw', 'application/x-cdr', 'application/x-coreldraw', 'image/cdr', 'image/x-cdr', 'zz-application/zz-winassoc-cdr'),
'wma' => array('audio/x-ms-wma', 'video/x-ms-asf'),
'jar' => array('application/java-archive', 'application/x-java-application', 'application/x-jar', 'application/x-compressed'),
'svg' => array('image/svg+xml', 'image/svg', 'application/xml', 'text/xml'),
'vcf' => 'text/x-vcard',
'srt' => array('text/srt', 'text/plain'),
'vtt' => array('text/vtt', 'text/plain'),
'ico' => array('image/x-icon', 'image/x-ico', 'image/vnd.microsoft.icon'),
'odc' => 'application/vnd.oasis.opendocument.chart',
'otc' => 'application/vnd.oasis.opendocument.chart-template',
'odf' => 'application/vnd.oasis.opendocument.formula',
'otf' => 'application/vnd.oasis.opendocument.formula-template',
'odg' => 'application/vnd.oasis.opendocument.graphics',
'otg' => 'application/vnd.oasis.opendocument.graphics-template',
'odi' => 'application/vnd.oasis.opendocument.image',
'oti' => 'application/vnd.oasis.opendocument.image-template',
'odp' => 'application/vnd.oasis.opendocument.presentation',
'otp' => 'application/vnd.oasis.opendocument.presentation-template',
'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
'ots' => 'application/vnd.oasis.opendocument.spreadsheet-template',
'odt' => 'application/vnd.oasis.opendocument.text',
'odm' => 'application/vnd.oasis.opendocument.text-master',
'ott' => 'application/vnd.oasis.opendocument.text-template',
'oth' => 'application/vnd.oasis.opendocument.text-web'
);
+14
View File
@@ -0,0 +1,14 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
/*
| -------------------------------------------------------------------------
| Profiler Sections
| -------------------------------------------------------------------------
| This file lets you determine whether or not various sections of Profiler
| data are displayed when the Profiler is enabled.
| Please see the user guide for info:
|
| https://codeigniter.com/userguide3/general/profiling.html
|
*/
+58
View File
@@ -0,0 +1,58 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
/*
| -------------------------------------------------------------------------
| URI ROUTING
| -------------------------------------------------------------------------
| This file lets you re-map URI requests to specific controller functions.
|
| Typically there is a one-to-one relationship between a URL string
| and its corresponding controller class/method. The segments in a
| URL normally follow this pattern:
|
| example.com/class/method/id/
|
| In some instances, however, you may want to remap this relationship
| so that a different class/function is called than the one
| corresponding to the URL.
|
| Please see the user guide for complete details:
|
| https://codeigniter.com/userguide3/general/routing.html
|
| -------------------------------------------------------------------------
| RESERVED ROUTES
| -------------------------------------------------------------------------
|
| There are three reserved routes:
|
| $route['default_controller'] = 'welcome';
|
| This route indicates which controller class should be loaded if the
| URI contains no data. In the above example, the "welcome" class
| would be loaded.
|
| $route['404_override'] = 'errors/page_missing';
|
| This route will tell the Router which controller/method to use if those
| provided in the URL cannot be matched to a valid route.
|
| $route['translate_uri_dashes'] = FALSE;
|
| This is not exactly a route, but allows you to automatically route
| controller and method names that contain dashes. '-' isn't a valid
| class or method name character, so it requires translation.
| When you set this option to TRUE, it will replace ALL dashes in the
| controller and method URI segments.
|
| Examples: my-controller/index -> my_controller/index
| my-controller/my-method -> my_controller/my_method
*/
$route['default_controller'] = 'dashboard';
$route['404_override'] = '';
$route['translate_uri_dashes'] = FALSE;
// untuk webhook absensi
$route['iclock/cdata'] = 'AttendanceWebhook/cdata';
+64
View File
@@ -0,0 +1,64 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
/*
| -------------------------------------------------------------------
| SMILEYS
| -------------------------------------------------------------------
| This file contains an array of smileys for use with the emoticon helper.
| Individual images can be used to replace multiple smileys. For example:
| :-) and :) use the same image replacement.
|
| Please see user guide for more info:
| https://codeigniter.com/userguide3/helpers/smiley_helper.html
|
*/
$smileys = array(
// smiley image name width height alt
':-)' => array('grin.gif', '19', '19', 'grin'),
':lol:' => array('lol.gif', '19', '19', 'LOL'),
':cheese:' => array('cheese.gif', '19', '19', 'cheese'),
':)' => array('smile.gif', '19', '19', 'smile'),
';-)' => array('wink.gif', '19', '19', 'wink'),
';)' => array('wink.gif', '19', '19', 'wink'),
':smirk:' => array('smirk.gif', '19', '19', 'smirk'),
':roll:' => array('rolleyes.gif', '19', '19', 'rolleyes'),
':-S' => array('confused.gif', '19', '19', 'confused'),
':wow:' => array('surprise.gif', '19', '19', 'surprised'),
':bug:' => array('bigsurprise.gif', '19', '19', 'big surprise'),
':-P' => array('tongue_laugh.gif', '19', '19', 'tongue laugh'),
'%-P' => array('tongue_rolleye.gif', '19', '19', 'tongue rolleye'),
';-P' => array('tongue_wink.gif', '19', '19', 'tongue wink'),
':P' => array('raspberry.gif', '19', '19', 'raspberry'),
':blank:' => array('blank.gif', '19', '19', 'blank stare'),
':long:' => array('longface.gif', '19', '19', 'long face'),
':ohh:' => array('ohh.gif', '19', '19', 'ohh'),
':grrr:' => array('grrr.gif', '19', '19', 'grrr'),
':gulp:' => array('gulp.gif', '19', '19', 'gulp'),
'8-/' => array('ohoh.gif', '19', '19', 'oh oh'),
':down:' => array('downer.gif', '19', '19', 'downer'),
':red:' => array('embarrassed.gif', '19', '19', 'red face'),
':sick:' => array('sick.gif', '19', '19', 'sick'),
':shut:' => array('shuteye.gif', '19', '19', 'shut eye'),
':-/' => array('hmm.gif', '19', '19', 'hmmm'),
'>:(' => array('mad.gif', '19', '19', 'mad'),
':mad:' => array('mad.gif', '19', '19', 'mad'),
'>:-(' => array('angry.gif', '19', '19', 'angry'),
':angry:' => array('angry.gif', '19', '19', 'angry'),
':zip:' => array('zip.gif', '19', '19', 'zipper'),
':kiss:' => array('kiss.gif', '19', '19', 'kiss'),
':ahhh:' => array('shock.gif', '19', '19', 'shock'),
':coolsmile:' => array('shade_smile.gif', '19', '19', 'cool smile'),
':coolsmirk:' => array('shade_smirk.gif', '19', '19', 'cool smirk'),
':coolgrin:' => array('shade_grin.gif', '19', '19', 'cool grin'),
':coolhmm:' => array('shade_hmm.gif', '19', '19', 'cool hmm'),
':coolmad:' => array('shade_mad.gif', '19', '19', 'cool mad'),
':coolcheese:' => array('shade_cheese.gif', '19', '19', 'cool cheese'),
':vampire:' => array('vampire.gif', '19', '19', 'vampire'),
':snake:' => array('snake.gif', '19', '19', 'snake'),
':exclaim:' => array('exclaim.gif', '19', '19', 'exclaim'),
':question:' => array('question.gif', '19', '19', 'question')
);
+222
View File
@@ -0,0 +1,222 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
/*
| -------------------------------------------------------------------
| USER AGENT TYPES
| -------------------------------------------------------------------
| This file contains four arrays of user agent data. It is used by the
| User Agent Class to help identify browser, platform, robot, and
| mobile device data. The array keys are used to identify the device
| and the array values are used to set the actual name of the item.
*/
$platforms = array(
'windows nt 10.0' => 'Windows 10',
'windows nt 6.3' => 'Windows 8.1',
'windows nt 6.2' => 'Windows 8',
'windows nt 6.1' => 'Windows 7',
'windows nt 6.0' => 'Windows Vista',
'windows nt 5.2' => 'Windows 2003',
'windows nt 5.1' => 'Windows XP',
'windows nt 5.0' => 'Windows 2000',
'windows nt 4.0' => 'Windows NT 4.0',
'winnt4.0' => 'Windows NT 4.0',
'winnt 4.0' => 'Windows NT',
'winnt' => 'Windows NT',
'windows 98' => 'Windows 98',
'win98' => 'Windows 98',
'windows 95' => 'Windows 95',
'win95' => 'Windows 95',
'windows phone' => 'Windows Phone',
'windows' => 'Unknown Windows OS',
'android' => 'Android',
'blackberry' => 'BlackBerry',
'iphone' => 'iOS',
'ipad' => 'iOS',
'ipod' => 'iOS',
'os x' => 'Mac OS X',
'ppc mac' => 'Power PC Mac',
'freebsd' => 'FreeBSD',
'ppc' => 'Macintosh',
'linux' => 'Linux',
'debian' => 'Debian',
'sunos' => 'Sun Solaris',
'beos' => 'BeOS',
'apachebench' => 'ApacheBench',
'aix' => 'AIX',
'irix' => 'Irix',
'osf' => 'DEC OSF',
'hp-ux' => 'HP-UX',
'netbsd' => 'NetBSD',
'bsdi' => 'BSDi',
'openbsd' => 'OpenBSD',
'gnu' => 'GNU/Linux',
'unix' => 'Unknown Unix OS',
'symbian' => 'Symbian OS'
);
// The order of this array should NOT be changed. Many browsers return
// multiple browser types so we want to identify the sub-type first.
$browsers = array(
'OPR' => 'Opera',
'Flock' => 'Flock',
'Edge' => 'Edge',
'Chrome' => 'Chrome',
// Opera 10+ always reports Opera/9.80 and appends Version/<real version> to the user agent string
'Opera.*?Version' => 'Opera',
'Opera' => 'Opera',
'MSIE' => 'Internet Explorer',
'Internet Explorer' => 'Internet Explorer',
'Trident.* rv' => 'Internet Explorer',
'Shiira' => 'Shiira',
'Firefox' => 'Firefox',
'Chimera' => 'Chimera',
'Phoenix' => 'Phoenix',
'Firebird' => 'Firebird',
'Camino' => 'Camino',
'Netscape' => 'Netscape',
'OmniWeb' => 'OmniWeb',
'Safari' => 'Safari',
'Mozilla' => 'Mozilla',
'Konqueror' => 'Konqueror',
'icab' => 'iCab',
'Lynx' => 'Lynx',
'Links' => 'Links',
'hotjava' => 'HotJava',
'amaya' => 'Amaya',
'IBrowse' => 'IBrowse',
'Maxthon' => 'Maxthon',
'Ubuntu' => 'Ubuntu Web Browser'
);
$mobiles = array(
// legacy array, old values commented out
'mobileexplorer' => 'Mobile Explorer',
// 'openwave' => 'Open Wave',
// 'opera mini' => 'Opera Mini',
// 'operamini' => 'Opera Mini',
// 'elaine' => 'Palm',
'palmsource' => 'Palm',
// 'digital paths' => 'Palm',
// 'avantgo' => 'Avantgo',
// 'xiino' => 'Xiino',
'palmscape' => 'Palmscape',
// 'nokia' => 'Nokia',
// 'ericsson' => 'Ericsson',
// 'blackberry' => 'BlackBerry',
// 'motorola' => 'Motorola'
// Phones and Manufacturers
'motorola' => 'Motorola',
'nokia' => 'Nokia',
'nexus' => 'Nexus',
'palm' => 'Palm',
'iphone' => 'Apple iPhone',
'ipad' => 'iPad',
'ipod' => 'Apple iPod Touch',
'sony' => 'Sony Ericsson',
'ericsson' => 'Sony Ericsson',
'blackberry' => 'BlackBerry',
'cocoon' => 'O2 Cocoon',
'blazer' => 'Treo',
'lg' => 'LG',
'amoi' => 'Amoi',
'xda' => 'XDA',
'mda' => 'MDA',
'vario' => 'Vario',
'htc' => 'HTC',
'samsung' => 'Samsung',
'sharp' => 'Sharp',
'sie-' => 'Siemens',
'alcatel' => 'Alcatel',
'benq' => 'BenQ',
'ipaq' => 'HP iPaq',
'mot-' => 'Motorola',
'playstation portable' => 'PlayStation Portable',
'playstation 3' => 'PlayStation 3',
'playstation vita' => 'PlayStation Vita',
'hiptop' => 'Danger Hiptop',
'nec-' => 'NEC',
'panasonic' => 'Panasonic',
'philips' => 'Philips',
'sagem' => 'Sagem',
'sanyo' => 'Sanyo',
'spv' => 'SPV',
'zte' => 'ZTE',
'sendo' => 'Sendo',
'nintendo dsi' => 'Nintendo DSi',
'nintendo ds' => 'Nintendo DS',
'nintendo 3ds' => 'Nintendo 3DS',
'wii' => 'Nintendo Wii',
'open web' => 'Open Web',
'openweb' => 'OpenWeb',
'meizu' => 'Meizu',
'huawei' => 'Huawei',
'xiaomi' => 'Xiaomi',
'oppo' => 'Oppo',
'vivo' => 'Vivo',
'infinix' => 'Infinix',
// Operating Systems
'android' => 'Android',
'symbian' => 'Symbian',
'SymbianOS' => 'SymbianOS',
'elaine' => 'Palm',
'series60' => 'Symbian S60',
'windows ce' => 'Windows CE',
// Browsers
'obigo' => 'Obigo',
'netfront' => 'Netfront Browser',
'openwave' => 'Openwave Browser',
'mobilexplorer' => 'Mobile Explorer',
'operamini' => 'Opera Mini',
'opera mini' => 'Opera Mini',
'opera mobi' => 'Opera Mobile',
'fennec' => 'Firefox Mobile',
// Other
'digital paths' => 'Digital Paths',
'avantgo' => 'AvantGo',
'xiino' => 'Xiino',
'novarra' => 'Novarra Transcoder',
'vodafone' => 'Vodafone',
'docomo' => 'NTT DoCoMo',
'o2' => 'O2',
// Fallback
'mobile' => 'Generic Mobile',
'wireless' => 'Generic Mobile',
'j2me' => 'Generic Mobile',
'midp' => 'Generic Mobile',
'cldc' => 'Generic Mobile',
'up.link' => 'Generic Mobile',
'up.browser' => 'Generic Mobile',
'smartphone' => 'Generic Mobile',
'cellphone' => 'Generic Mobile'
);
// There are hundreds of bots but these are the most common.
$robots = array(
'googlebot' => 'Googlebot',
'msnbot' => 'MSNBot',
'baiduspider' => 'Baiduspider',
'bingbot' => 'Bing',
'slurp' => 'Inktomi Slurp',
'yahoo' => 'Yahoo',
'ask jeeves' => 'Ask Jeeves',
'fastcrawler' => 'FastCrawler',
'infoseek' => 'InfoSeek Robot 1.0',
'lycos' => 'Lycos',
'yandex' => 'YandexBot',
'mediapartners-google' => 'MediaPartners Google',
'CRAZYWEBCRAWLER' => 'Crazy Webcrawler',
'adsbot-google' => 'AdsBot Google',
'feedfetcher-google' => 'Feedfetcher Google',
'curious george' => 'Curious George',
'ia_archiver' => 'Alexa Crawler',
'MJ12bot' => 'Majestic-12',
'Uptimebot' => 'Uptimebot',
'UptimeRobot' => 'UptimeRobot'
);
+168
View File
@@ -0,0 +1,168 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class Accounts 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" => "accounts",
"parent_accounts" => $this->db->get('accounts')->result()
];
$this->load->view('partials/header', $data);
$this->load->view('accounts/layout', $data);
$this->load->view('partials/footer');
}
public function get_data()
{
$this->dm->setTable('accounts')
->setColumn(
[null,'kode_akun','nama_akun','tipe','posisi','kategori',null],
['kode_akun','nama_akun','tipe'],
['kode_akun'=>'ASC']
);
$list = $this->dm->get_datatables();
$data = [];
$no = $_POST['start'];
foreach ($list as $row) {
$no++;
$data[] = [
$no,
$row->kode_akun,
$row->nama_akun,
ucfirst($row->tipe),
ucfirst($row->posisi),
ucfirst($row->kategori),
'
<button class="btn btn-sm btn-warning btn-edit" data-id="'.$row->id.'">Edit</button>
<button class="btn btn-sm btn-danger btn-delete" data-id="'.$row->id.'">Delete</button>
'
];
}
echo json_encode([
"draw" => $_POST['draw'],
"recordsTotal" => $this->dm->count_all(),
"recordsFiltered" => $this->dm->count_filtered(),
"data" => $data
]);
}
// ================= SAVE
public function save()
{
$tipe = $this->input->post('tipe');
$data = [
'kode_akun' => $this->input->post('kode_akun'),
'nama_akun' => $this->input->post('nama_akun'),
'tipe' => $tipe,
'posisi' => in_array($tipe, ['asset','expense']) ? 'debit' : 'kredit',
'kategori' => in_array($tipe, ['asset','liability','equity']) ? 'neraca' : 'laba_rugi',
'parent_id' => $this->input->post('parent_id') ?: NULL
];
$insert = $this->dm->setTable('accounts')->insert($data);
if($insert){
log_activity(
'Accounts',
'create',
'Menambah akun: '.$data['kode_akun'].' - '.$data['nama_akun']
);
}
echo json_encode([
'status' => $insert ? true : false,
'message' => $insert ? 'Data berhasil disimpan' : 'Gagal simpan'
]);
}
// ================= UPDATE
public function update()
{
$id = $this->input->post('id');
$tipe = $this->input->post('tipe');
$data = [
'kode_akun' => $this->input->post('kode_akun'),
'nama_akun' => $this->input->post('nama_akun'),
'tipe' => $tipe,
'posisi' => in_array($tipe, ['asset','expense']) ? 'debit' : 'kredit',
'kategori' => in_array($tipe, ['asset','liability','equity']) ? 'neraca' : 'laba_rugi',
'parent_id' => $this->input->post('parent_id') ?: NULL
];
$update = $this->dm->setTable('accounts')->update($id, $data);
if($update){
log_activity(
'Accounts',
'update',
'Update akun ID '.$id.' menjadi: '.$data['kode_akun'].' - '.$data['nama_akun']
);
}
echo json_encode([
'status' => $update ? true : false,
'message' => $update ? 'Data berhasil diupdate' : 'Gagal update'
]);
}
public function detail($id)
{
$data = $this->dm->setTable('accounts')->find($id);
echo json_encode($data);
}
public function delete($id)
{
// cek apakah jadi parent
$child = $this->db->where('parent_id',$id)->count_all_results('accounts');
if($child > 0){
echo json_encode([
'status'=>false,
'message'=>'Tidak bisa hapus, akun punya child'
]);
return;
}
$delete = $this->dm->setTable('accounts')->delete($id);
if($delete){
log_activity(
'Accounts',
'delete',
'Menghapus akun ID '.$id,
'success'
);
}
echo json_encode([
'status' => $delete ? true : false,
'message' => $delete ? 'Data berhasil dihapus' : 'Gagal hapus'
]);
}
}
+343
View File
@@ -0,0 +1,343 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class Asset extends CI_Controller {
public function __construct(){
parent::__construct();
$this->load->database();
}
public function index(){
$data = ["active_menu" => "asset"];
$this->load->view('partials/header', $data);
$this->load->view('asset/layout');
$this->load->view('partials/footer');
}
// ================= GET DATA =================
public function get_data(){
$data = $this->db
->select('assets.*, lokasi_asset.nama as nama_lokasi')
->from('assets')
->join('lokasi_asset', 'lokasi_asset.id = assets.lokasi_asset_id', 'left')
->order_by('assets.id','DESC')
->get()
->result();
$no = 1;
$result = [];
foreach($data as $d){
$result[] = [
$no++,
$d->tanggal_perolehan,
$d->kode_asset ?? '-',
$d->nama_asset,
$d->nama_lokasi ?? '-',
number_format($d->nilai_perolehan),
$d->masa_manfaat . ' bln',
number_format($d->penyusutan_per_bulan),
$d->created_at,
'
<button class="btn btn-sm btn-info btn-detail mb-1 ml-1" data-id="'.$d->id.'"><i class="bi bi-eye"></i></button>
<button class="btn btn-sm btn-warning btn-edit mb-1 ml-1" data-id="'.$d->id.'">Edit</button>
<button class="btn btn-sm btn-danger btn-delete mb-1 ml-1" data-id="'.$d->id.'">Hapus</button>
'
];
}
echo json_encode(['data'=>$result]);
}
// ================= DETAIL ASSET + HISTORY =================
public function detail($id)
{
$data = $this->db
->select('a.*, la.nama AS nama_lokasi') // ambil field tambahan dari lokasi_asset
->from('assets a')
->join('lokasi_asset la', 'a.lokasi_asset_id = la.id', 'left')
->where('a.id', $id)
->get()
->row();
echo json_encode($data);
}
public function get_history($asset_id){
$data = $this->db
->where('asset_id', $asset_id)
->order_by('created_at', 'DESC')
->get('asset_mutations')
->result();
echo json_encode($data);
}
// ================= SAVE MANUAL (1 UNIT) =================
public function save(){
$harga = preg_replace('/\D/','',$this->input->post('harga'));
$residu = preg_replace('/\D/','',$this->input->post('residu'));
$masa = (int)$this->input->post('masa');
$account_debit_id = (int)$this->input->post('account_asset');
$account_kredit_id = (int)$this->input->post('account_kas');
$lokasi_asset_id = (int)$this->input->post('lokasi_asset');
$tanggal = $this->input->post('tanggal');
$nama_asset = $this->input->post('nama_asset');
$keterangan = $this->input->post('keterangan');
$nilai_perolehan = $harga * 1;
$penyusutan = ($harga - $residu) / ($masa ?: 1);
$this->db->trans_start();
$data = [
'kode_asset' => 'AST-'.time(),
'nama_asset' => $nama_asset,
'harga_per_unit' => $harga,
'nilai_perolehan' => $nilai_perolehan,
'tanggal_perolehan' => $tanggal,
'masa_manfaat' => $masa,
'nilai_residu' => $residu,
'penyusutan_per_bulan' => $penyusutan,
'nilai_buku' => $nilai_perolehan,
'sumber' => 'manual',
'account_debit_id' => $account_debit_id,
'account_kredit_id' => $account_kredit_id,
'lokasi_asset_id' => $lokasi_asset_id,
'keterangan' => $keterangan
];
$this->db->insert('assets',$data);
$asset_id = $this->db->insert_id();
// Journal
$this->db->insert('journals',[
'tanggal' => $tanggal,
'no_ref' => 'AST-'.date('YmdHis').'-'.$asset_id,
'keterangan' => 'Penambahan Aset '.$nama_asset,
'ref_type' => 'assets',
'ref_id' => $asset_id,
'created_by' => $this->session->userdata('user_id')
]);
$jid = $this->db->insert_id();
$this->db->insert('journal_details',[
'journal_id' => $jid,
'account_id' => $account_debit_id,
'debit' => $nilai_perolehan,
'kredit' => 0
]);
$this->db->insert('journal_details',[
'journal_id' => $jid,
'account_id' => $account_kredit_id,
'debit' => 0,
'kredit' => $nilai_perolehan
]);
// MUTATION PEROLEHAN
$this->db->insert('asset_mutations',[
'asset_id' => $asset_id,
'tipe' => 'perolehan',
'nilai' => $nilai_perolehan,
'keterangan' => 'Perolehan manual: '.$keterangan
]);
$this->db->trans_complete();
echo json_encode(['status'=>true]);
}
// ================= SAVE FROM STOCK =================
public function save_from_stock(){
$item_id = $this->input->post('item_id');
$account_debit_id = (int)$this->input->post('account_asset');
$lokasi_asset_id = (int)$this->input->post('s_lokasi_asset');
$masa = (int)$this->input->post('s_masa');
$residu = preg_replace('/\D/','',$this->input->post('s_residu'));
$tanggal = $this->input->post('s_tanggal');
$item = $this->db->get_where('items',['id'=>$item_id])->row();
if(!$item){
echo json_encode(['status'=>false,'message'=>'Item tidak ditemukan']);
return;
}
if($item->stok < 1){
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);
$this->db->trans_start();
// INSERT ASSET
$this->db->insert('assets',[
'item_id' => $item_id,
'kode_asset' => 'AST-'.time(),
'nama_asset' => $nama_asset,
'harga_per_unit' => $harga,
'nilai_perolehan' => $nilai_perolehan,
'tanggal_perolehan' => $tanggal,
'masa_manfaat' => $masa,
'nilai_residu' => $residu,
'penyusutan_per_bulan' => $penyusutan,
'nilai_buku' => $nilai_perolehan,
'sumber' => 'gudang',
'account_debit_id' => $account_debit_id,
'account_kredit_id' => 21, // Persediaan
'lokasi_asset_id' => $lokasi_asset_id
]);
$asset_id = $this->db->insert_id();
// Journal
$this->db->insert('journals',[
'tanggal' => $tanggal,
'no_ref' => 'AST-'.date('YmdHis').'-'.$asset_id,
'keterangan' => 'Penambahan Aset dari Gudang '.$nama_asset,
'ref_type' => 'assets',
'ref_id' => $asset_id,
'created_by' => $this->session->userdata('user_id')
]);
$jid = $this->db->insert_id();
// Debit Asset
$this->db->insert('journal_details',[
'journal_id' => $jid,
'account_id' => $account_debit_id,
'debit' => $nilai_perolehan,
'kredit' => 0
]);
// Kredit Persediaan
$this->db->insert('journal_details',[
'journal_id' => $jid,
'account_id' => 21,
'debit' => 0,
'kredit' => $nilai_perolehan
]);
// UPDATE STOK ITEM (kurangi 1)
$this->db->where('id', $item_id)
->set('stok', 'stok-1', FALSE)
->update('items');
// MUTATION DARI GUDANG
$this->db->insert('asset_mutations',[
'asset_id' => $asset_id,
'tipe' => 'perolehan',
'nilai' => $nilai_perolehan,
'keterangan' => 'Perolehan dari gudang: '.$nama_asset
]);
$this->db->trans_complete();
echo json_encode(['status'=>true]);
}
// ================= UPDATE =================
public function update(){
$id = $this->input->post('id');
$nama_asset = $this->input->post('nama_asset');
$lokasi_asset_id = (int)$this->input->post('lokasi_asset');
$keterangan = $this->input->post('keterangan');
$this->db->trans_start();
$data = [
'nama_asset' => $nama_asset,
'lokasi_asset_id' => $lokasi_asset_id,
'keterangan' => $keterangan
];
$this->db->where('id',$id);
$this->db->update('assets',$data);
// MUTATION EDIT
$this->db->insert('asset_mutations',[
'asset_id' => $id,
'tipe' => 'penambahan',
'nilai' => 0,
'keterangan' => 'Edit asset: '.$nama_asset
]);
$this->db->trans_complete();
echo json_encode(['status'=>true]);
}
// ================= DELETE =================
public function delete(){
$id = $this->input->post('id');
$alasan = $this->input->post('alasan');
$keterangan = $this->input->post('keterangan');
$asset = $this->db->get_where('assets',['id'=>$id])->row();
$this->db->trans_start();
// Hapus journal
$journals = $this->db->get_where('journals', ['ref_type'=>'assets', 'ref_id'=>$id])->result();
foreach($journals as $j){
$this->db->delete('journal_details', ['journal_id'=>$j->id]);
$this->db->delete('journals', ['id'=>$j->id]);
}
// Hapus asset
$this->db->delete('assets',['id'=>$id]);
// MUTATION DELETE
$this->db->insert('asset_mutations',[
'asset_id' => $id,
'tipe' => 'pengurangan',
'nilai' => $asset->nilai_buku,
'keterangan' => 'Penghapusan ('.$alasan.'): '.$keterangan
]);
$this->db->trans_complete();
echo json_encode(['status'=>true]);
}
// ================= GET DATA SELECT =================
public function get_accounts_kas(){
$data = $this->db
->where_in('sub_tipe', ['kas', 'hutang'])
->order_by('kode_akun', 'ASC')
->get('accounts')
->result();
echo json_encode($data);
}
public function get_accounts_asset(){
$data = $this->db
->group_start()
->like('nama_akun', 'Aset', 'after')
->group_end()
->order_by('kode_akun', 'ASC')
->get('accounts')
->result();
echo json_encode($data);
}
public function get_items(){
$data = $this->db
->where('stok > 0')
->get('items')
->result();
echo json_encode($data);
}
public function get_lokasi_asset(){
$data = $this->db
->get('lokasi_asset')
->result();
echo json_encode($data);
}
}
@@ -0,0 +1,85 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class AttendanceWebhook extends CI_Controller {
public function __construct()
{
parent::__construct();
date_default_timezone_set('Asia/Jakarta');
}
public function cdata()
{
// =========================
// AMBIL SN MESIN
// =========================
$sn = $this->input->get('SN');
// =========================
// RAW BODY DARI MESIN
// =========================
$body = file_get_contents('php://input');
// =========================
// LOG DIRECTORY
// =========================
$logDir = APPPATH . 'logs/fingerprint/';
if (!is_dir($logDir)) {
mkdir($logDir, 0777, true);
}
// =========================
// FILE LOG
// =========================
$logFile = $logDir . date('Y-m-d') . '.log';
// =========================
// FORMAT LOG HEADER
// =========================
$logText = "\n==================================================\n";
$logText .= "TIME : " . date('Y-m-d H:i:s') . "\n";
$logText .= "SN : " . $sn . "\n";
$logText .= "IP : " . $this->input->ip_address() . "\n";
$logText .= "DATA : \n";
// =========================
// PARSE BARIS ABSENSI
// =========================
$lines = explode("\n", trim($body));
foreach ($lines as $line) {
$line = trim($line);
if (empty($line)) {
continue;
}
$logText .= $line . "\n";
// contoh parsing
$data = explode("\t", $line);
/*
hasil biasanya:
[0] => PIN/User ID
[1] => Datetime
[2] => Status
*/
}
// =========================
// SIMPAN LOG
// =========================
file_put_contents($logFile, $logText, FILE_APPEND);
// =========================
// RESPONSE KE MESIN
// =========================
echo "OK";
}
}
@@ -0,0 +1,65 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class Attendancemonitoring extends CI_Controller {
public function __construct()
{
parent::__construct();
$this->load->database();
}
public function index()
{
$data = [
"active_menu" => "attendance_monitoring"
];
$this->load->view('partials/header', $data);
$this->load->view('employees/attendance_monitoring');
$this->load->view('partials/footer');
}
public function get_data()
{
$date = $this->input->get('date');
if(!$date){
$date = date('Y-m-d');
}
$data = $this->db->query("
SELECT
a.*,
e.employee_code,
e.full_name,
s.shift_name
FROM k_attendances a
LEFT JOIN k_employees e ON e.id = a.employee_id
LEFT JOIN k_shifts s ON s.id = a.shift_id
WHERE a.attendance_date = '$date'
ORDER BY a.checkin_time DESC
")->result();
$rows = [];
$no = 1;
foreach($data as $d){
$rows[] = [
$no++,
$d->employee_code,
$d->full_name,
$d->shift_name,
$d->checkin_time,
$d->checkout_time,
$d->attendance_status
];
}
echo json_encode([
'data' => $rows
]);
}
}
+87
View File
@@ -0,0 +1,87 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class Auth extends CI_Controller {
public function __construct()
{
parent::__construct();
}
public function index()
{
if ($this->session->userdata('logged_in')) {
redirect('dashboard');
}
$this->load->view('auth/login');
}
public function process_login()
{
$username = $this->input->post('username');
$password = $this->input->post('password');
$user = $this->db->get_where('v_users', ['username' => $username])->row();
if ($user && password_verify($password, $user->password)) {
// Decode permissions
$permissions = json_decode($user->permissions, true);
if (!is_array($permissions)) {
$permissions = [];
}
$session_data = [
'user_id' => $user->id,
'nama' => $user->nama,
'username' => $user->username,
'role' => $user->nama_role,
'permissions' => $permissions,
'logged_in' => TRUE
];
$this->session->set_userdata($session_data);
// 🔥 LOG LOGIN BERHASIL
log_activity(
'Auth',
'login',
'User '.$user->username.' berhasil login',
'success'
);
redirect('dashboard');
} else {
// 🔥 LOG LOGIN GAGAL
log_activity(
'Auth',
'login_failed',
'Login gagal untuk username: '.$username,
'failed'
);
$this->session->set_flashdata('error', 'Username atau Password salah!');
redirect('auth');
}
}
public function logout()
{
$username = $this->session->userdata('username');
// 🔥 LOG LOGOUT (SEBELUM DESTROY SESSION)
log_activity(
'Auth',
'logout',
'User '.$username.' logout',
'success'
);
$this->session->sess_destroy();
redirect('auth');
}
}
?>
+290
View File
@@ -0,0 +1,290 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class Basejurnal extends CI_Controller {
public function __construct()
{
parent::__construct();
$this->load->model('DynamicModel', 'dm');
if (!$this->session->userdata('logged_in')) {
redirect('auth');
}
}
// ================= INDEX
public function index()
{
if($this->session->userdata('role') != 'Admin'){
show_error('Akses ditolak', 403);
}
$data = [
"active_menu" => "basejurnal"
];
$this->load->view('partials/header', $data);
$this->load->view('basejurnal/layout', $data);
$this->load->view('partials/footer');
}
// ================= DATATABLE
public function get_data()
{
$this->dm->setTable('base_journal')
->setColumn(
[null,'kode','nama','deskripsi','is_active',null],
['kode','nama'],
['id'=>'DESC']
);
$list = $this->dm->get_datatables();
$data = [];
$no = $_POST['start'];
foreach ($list as $row) {
$no++;
$status = $row->is_active ? 'Aktif' : 'Nonaktif';
$data[] = [
$no,
$row->kode,
$row->nama,
$row->deskripsi,
$status,
'
<button class="btn btn-sm btn-warning btn-edit" data-id="'.$row->id.'">Edit</button>
<button class="btn btn-sm btn-danger btn-delete" data-id="'.$row->id.'">Hapus</button>
'
];
}
echo json_encode([
"draw" => $_POST['draw'],
"recordsTotal" => $this->dm->count_all(),
"recordsFiltered" => $this->dm->count_filtered(),
"data" => $data
]);
}
public function save()
{
$this->db->trans_start();
$kode = trim($this->input->post('kode'));
$nama = trim($this->input->post('nama'));
if(empty($kode) || empty($nama)){
echo json_encode([
'status' => false,
'message' => 'Kode & Nama wajib diisi'
]);
return;
}
$header = [
'kode' => $kode,
'nama' => $nama,
'deskripsi' => $this->input->post('deskripsi'),
'is_active' => 1
];
$this->dm->setTable('base_journal')->insert($header);
$base_id = $this->db->insert_id();
$accounts = $this->input->post('account_id');
$posisi = $this->input->post('posisi');
if(!empty($accounts)){
$detail = [];
foreach ($accounts as $i => $acc) {
if(empty($acc)) continue;
$detail[] = [
'base_journal_id' => $base_id,
'account_id' => $acc,
'posisi' => $posisi[$i] ?? 'debit',
'urutan' => $i + 1
];
}
if(!empty($detail)){
$this->db->insert_batch('base_journal_detail', $detail);
}
}
$this->db->trans_complete();
if (!$this->db->trans_status()) {
echo json_encode([
'status' => false,
'message' => 'Gagal simpan data'
]);
return;
}
log_activity(
'base_journal',
'create',
'Membuat base journal kode: '.$kode,
'success'
);
echo json_encode([
'status' => true,
'message' => 'Data berhasil disimpan'
]);
}
public function update()
{
$id = $this->input->post('id');
if(empty($id)){
echo json_encode([
'status' => false,
'message' => 'ID tidak valid'
]);
return;
}
$this->db->trans_start();
$header = [
'kode' => trim($this->input->post('kode')),
'nama' => trim($this->input->post('nama')),
'deskripsi' => $this->input->post('deskripsi'),
];
$this->dm->setTable('base_journal')->update($id, $header);
// 🔥 DELETE OLD DETAIL
$this->db->where('base_journal_id', $id)->delete('base_journal_detail');
// 🔥 INSERT NEW DETAIL
$accounts = $this->input->post('account_id');
$posisi = $this->input->post('posisi');
if(!empty($accounts)){
$detail = [];
foreach ($accounts as $i => $acc) {
if(empty($acc)) continue;
$detail[] = [
'base_journal_id' => $id,
'account_id' => $acc,
'posisi' => $posisi[$i] ?? 'debit',
'urutan' => $i + 1
];
}
if(!empty($detail)){
$this->db->insert_batch('base_journal_detail', $detail);
}
}
$this->db->trans_complete();
if (!$this->db->trans_status()) {
echo json_encode([
'status' => false,
'message' => 'Gagal update data'
]);
return;
}
log_activity(
'base_journal',
'update',
'Mengubah base journal ID: '.$id.' (kode: '.$header['kode'].')',
'success'
);
echo json_encode([
'status' => true,
'message' => 'Data berhasil diupdate'
]);
}
// ================= DETAIL
public function detail($id)
{
$header = $this->dm->setTable('base_journal')->find($id);
$detail = $this->db
->select('d.*, a.nama_akun as account_name')
->from('base_journal_detail d')
->join('accounts a','a.id = d.account_id','left') // <-- GANTI DI SINI
->where('d.base_journal_id',$id)
->order_by('d.urutan','ASC')
->get()
->result();
echo json_encode([
'header' => $header,
'detail' => $detail
]);
}
public function delete($id = null)
{
// set response JSON
$this->output->set_content_type('application/json');
if (empty($id)) {
echo json_encode([
'status' => false,
'message' => 'ID tidak valid'
]);
return;
}
$this->db->trans_start();
// hapus detail dulu
$this->db->where('base_journal_id', $id)->delete('base_journal_detail');
// hapus header
$delete = $this->dm->setTable('base_journal')->delete($id);
$this->db->trans_complete();
if (!$this->db->trans_status() || !$delete) {
echo json_encode([
'status' => false,
'message' => 'Gagal hapus data'
]);
return;
}
// ================= LOG ACTIVITY =================
log_activity(
'base_journal',
'delete',
'Menghapus base journal ID: '.$id,
'success'
);
echo json_encode([
'status' => true,
'message' => 'Data berhasil dihapus'
]);
}
public function get_list_account()
{
$data = $this->db->select('id, nama_akun, kode_akun')
->from('accounts') // sesuaikan tabel kamu
->order_by('kode_akun','ASC')
->get()
->result();
echo json_encode($data);
}
}
+108
View File
@@ -0,0 +1,108 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class Bukubesar 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" => "buku_besar"];
$this->load->view('partials/header', $data);
$this->load->view('buku_besar/layout');
$this->load->view('partials/footer');
}
// =========================
// GET ACCOUNTS (FILTER)
// =========================
public function get_accounts()
{
$data = $this->dm->setTable('accounts')
->order_by('kode_akun','ASC')
->get();
echo json_encode($data);
}
// =========================
// GET DATA BUKU BESAR
// =========================
public function get_data()
{
$account_id = $this->input->get('account_id');
$start = $this->input->get('start_date');
$end = $this->input->get('end_date');
$this->db->select('
journals.tanggal,
journals.no_ref,
journals.keterangan,
journal_details.debit,
journal_details.kredit,
accounts.nama_akun,
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 ($account_id) {
$this->db->where('journal_details.account_id', $account_id);
}
if ($start && $end) {
$this->db->where('journals.tanggal >=', $start);
$this->db->where('journals.tanggal <=', $end);
}
// 🔥 FILTER DEBIT/KREDIT TIDAK BOLEH 0 SEMUA
$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) {
// LOGIKA SALDO BERDASARKAN TIPE AKUN
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
];
}
echo json_encode($result);
}
}
+165
View File
@@ -0,0 +1,165 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class Cameraqrscan extends CI_Controller {
public function __construct()
{
parent::__construct();
$this->load->model('DynamicModel', 'dm');
// Cek apakah user sudah login
if (!$this->session->userdata('logged_in')) {
redirect('login');
}
}
public function index($type = 'masuk')
{
// pastikan hanya masuk/keluar
$type = strtolower($type);
if (!in_array($type, ['masuk', 'keluar'])) {
show_404();
return;
}
$data = [
"active_menu" => "",
"scan_type" => $type // passing ke view
];
$this->load->view('partials/header', $data);
$this->load->view('scan/qr_scan', $data);
$this->load->view('partials/footer');
}
public function get_data()
{
$qr_code = $this->input->post('qr_code');
$scan_type = $this->input->post('scan_type'); // masuk / keluar
if (!$qr_code) {
echo json_encode(['status' => false, 'message' => 'QR Code tidak ditemukan']);
return;
}
// ============================================================
// AMBIL DATA SISWA
// ============================================================
$siswa = $this->db->get_where('v_siswa', ['qr_id' => $qr_code])->row_array();
if (!$siswa) {
echo json_encode([
'status' => false,
'message' => 'Data siswa tidak ditemukan'
]);
return;
}
$id_siswa = $siswa['id'];
$id_kelas = $siswa['id_kelas'];
$id_kamar = $siswa['id_kamar'];
// ============================================================
// CEGAH DUPLIKAT 5 MENIT (PostgreSQL)
// ============================================================
// Hitung waktu 5 menit yang lalu
$datetimeNow = date('Y-m-d H:i:s');
$datetime5MenitLalu = date('Y-m-d H:i:s', strtotime('-5 minutes'));
$cek = $this->db->query("
SELECT id
FROM kunjungan
WHERE id_siswa = ?
AND created_at >= ?
ORDER BY created_at DESC
LIMIT 1
", [$id_siswa, $datetime5MenitLalu])->row_array();
if ($cek) {
echo json_encode([
'status' => true,
'data' => $siswa,
'scan_type' => $scan_type,
'message' => "Sudah tercatat dalam 5 menit terakhir"
]);
return;
}
// ============================================================
// AMBIL TEMPLATE PESAN
// ============================================================
$template = $this->db->get_where('template_pesan', ['jenis' => $scan_type])->row_array();
if (!$template) {
echo json_encode([
'status' => false,
'message' => 'Template pesan tidak ditemukan => ' . $scan_type
]);
return;
}
$pesan = $template['isi_template'];
$pesanType = $template['type'];
// ============================================================
// REPLACE TEMPLATE {{column}}
// ============================================================
foreach ($siswa as $key => $value) {
$pesan = str_replace('{{'.$key.'}}', $value, $pesan);
}
// Tambahan otomatis
$pesan = str_replace('{{tanggal}}', date('Y-m-d'), $pesan);
$pesan = str_replace('{{jam}}', date('H:i:s'), $pesan);
// ============================================================
// INSERT KUNJUNGAN
// ============================================================
$this->db->insert('kunjungan', [
'id_siswa' => $id_siswa,
'type' => $scan_type,
'id_users' => $this->session->userdata('user_id'),
'id_kelas' => $id_kelas,
'id_kamar' => $id_kamar,
'created_at' => date('Y-m-d H:i:s'),
'media_type' => 'qr'
]);
$new_id = $this->db->insert_id();
// ============================================================
// INSERT NOTIFIKASI WA
// ============================================================
$this->db->insert('notifikasi', [
'type' => $pesanType,
'pesan' => $pesan,
'status' => 'pending',
'nomor_whatsapp' => $siswa['nomor_whatsapp'],
'created_at' => date('Y-m-d H:i:s'),
]);
// ============================================================
// RESPONSE
// ============================================================
$info = ($scan_type === 'masuk')
? "Berhasil scan MASUK"
: "Berhasil scan KELUAR";
// tampilkan data lengkap dari v_kunjungan
$dataSiswa = $this->db->get_where('v_kunjungan_detail', ['id' => $new_id])->row();
echo json_encode([
'status' => true,
'data' => $dataSiswa,
'scan_type' => $scan_type,
'message' => $info
]);
}
}
+140
View File
@@ -0,0 +1,140 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class Cronjob extends CI_Controller {
public function __construct()
{
parent::__construct();
$this->load->database();
// ONLY CLI ACCESS
if (!$this->input->is_cli_request()) {
exit('No direct access allowed');
}
}
public function generate_insight()
{
$period = date('Y-m');
// =====================================================
// 🔥 REPLACE MODE (hapus data lama dulu)
// =====================================================
$this->db->where('period', $period);
$this->db->delete('ai_insights');
// =====================================================
// 1. FINANCE - OVERDUE INVOICE
// =====================================================
$overdue = $this->db->query("
SELECT COUNT(*) as total
FROM invoices
WHERE status != 'paid'
AND jatuh_tempo < CURDATE()
AND deleted_at IS NULL
")->row()->total;
if ($overdue > 0) {
$this->db->insert('ai_insights', [
'type' => 'finance',
'severity' => 'warning',
'message' => "$overdue invoice sudah melewati jatuh tempo",
'meta' => json_encode([
'overdue_count' => (int)$overdue
]),
'period' => $period
]);
}
// =====================================================
// 2. FINANCE - CASHFLOW BULAN INI
// =====================================================
$cash = $this->db->query("
SELECT COALESCE(SUM(jd.debit),0) as cash_in
FROM journal_details jd
JOIN journals j ON j.id = jd.journal_id
WHERE MONTH(j.tanggal) = MONTH(CURDATE())
AND YEAR(j.tanggal) = YEAR(CURDATE())
")->row()->cash_in;
if ($cash > 0) {
$this->db->insert('ai_insights', [
'type' => 'finance',
'severity' => ($cash > 10000000) ? 'info' : 'warning',
'message' => "Cashflow bulan ini Rp " . number_format($cash),
'meta' => json_encode([
'cash_in' => (float)$cash
]),
'period' => $period
]);
}
// =====================================================
// 3. STOCK - KRITIS
// =====================================================
$stocks = $this->db->query("
SELECT
i.kode_id,
k.nama_barang,
SUM(i.stok) as total_stok,
k.limit_stock
FROM items i
JOIN kode_barang k ON k.id = i.kode_id
GROUP BY i.kode_id, k.nama_barang, k.limit_stock
HAVING total_stok <= k.limit_stock
")->result();
foreach ($stocks as $s) {
$this->db->insert('ai_insights', [
'type' => 'stock',
'severity' => 'critical',
'message' => "Stok {$s->nama_barang} kritis ({$s->total_stok})",
'meta' => json_encode([
'kode_id' => $s->kode_id,
'item' => $s->nama_barang,
'stok' => (int)$s->total_stok,
'limit' => (int)$s->limit_stock
]),
'period' => $period
]);
}
// =====================================================
// 4. CUSTOMER - PAYMENT RISK
// =====================================================
$lateCustomers = $this->db->query("
SELECT
c.nama,
COUNT(i.id) as total_invoice
FROM invoices i
JOIN customers c ON c.id = i.customer_id
WHERE i.status != 'paid'
AND i.jatuh_tempo < CURDATE()
GROUP BY i.customer_id
HAVING total_invoice > 2
")->result();
foreach ($lateCustomers as $c) {
$this->db->insert('ai_insights', [
'type' => 'customer',
'severity' => 'warning',
'message' => "Customer {$c->nama} memiliki {$c->total_invoice} invoice overdue",
'meta' => json_encode([
'customer' => $c->nama,
'overdue_invoice' => (int)$c->total_invoice
]),
'period' => $period
]);
}
// =====================================================
// RESPONSE
// =====================================================
echo json_encode([
'status' => true,
'message' => 'AI Insight generated (REPLACE MODE)',
'period' => $period
]);
}
}
+150
View File
@@ -0,0 +1,150 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class Customers extends CI_Controller {
public function __construct()
{
parent::__construct();
$this->load->model('DynamicModel', 'dm');
if (!$this->session->userdata('logged_in')) {
redirect('auth');
}
}
public function index()
{
$data = ["active_menu" => "customers"];
$this->load->view('partials/header', $data);
$this->load->view('customers/layout');
$this->load->view('partials/footer');
}
// =========================
// DATATABLE
// =========================
public function get_data()
{
$this->dm->setTable('customers')
->setColumn(
[null,'nama','alamat','telp','email',null],
['nama','alamat','telp','email'],
['id'=>'DESC']
);
$list = $this->dm->get_datatables();
$data = [];
$no = $_POST['start'];
foreach ($list as $row) {
$no++;
$aksi = '
<button class="btn btn-sm btn-warning btn-edit" data-id="'.$row->id.'">Edit</button>
<button class="btn btn-sm btn-danger btn-delete" data-id="'.$row->id.'">Delete</button>
';
$data[] = [
$no,
$row->nama,
$row->alamat,
$row->telp,
$row->email,
$aksi
];
}
echo json_encode([
"draw" => $_POST['draw'],
"recordsTotal" => $this->dm->count_all(),
"recordsFiltered" => $this->dm->count_filtered(),
"data" => $data
]);
}
// =========================
// SAVE
// =========================
public function save()
{
$data = [
'nama' => $this->input->post('nama'),
'alamat' => $this->input->post('alamat'),
'telp' => $this->input->post('telp'),
'email' => $this->input->post('email')
];
if (!$data['nama']) {
echo json_encode(['status'=>false,'message'=>'Nama wajib diisi']);
return;
}
$this->dm->setTable('customers')->insert($data);
// ================= LOG ACTIVITY =================
log_activity(
'customers',
'create',
'Menambahkan customer: '.$data['nama'],
'success'
);
echo json_encode(['status'=>true,'message'=>'Data berhasil disimpan']);
}
// =========================
// DETAIL
// =========================
public function detail($id)
{
$data = $this->dm->setTable('customers')->get_by_id($id);
echo json_encode($data);
}
// =========================
// UPDATE
// =========================
public function update()
{
$id = $this->input->post('id');
$data = [
'nama' => $this->input->post('nama'),
'alamat' => $this->input->post('alamat'),
'telp' => $this->input->post('telp'),
'email' => $this->input->post('email')
];
$this->dm->setTable('customers')->update($id, $data);
// ================= LOG ACTIVITY =================
log_activity(
'customers',
'update',
'Mengubah customer ID: '.$id.' ('.$data['nama'].')',
'success'
);
echo json_encode(['status'=>true,'message'=>'Data berhasil diupdate']);
}
// =========================
// DELETE
// =========================
public function delete($id)
{
$this->dm->setTable('customers')->delete($id);
// ================= LOG ACTIVITY =================
log_activity(
'customers',
'delete',
'Menghapus customer: '.($customer->nama ?? 'ID '.$id),
'success'
);
echo json_encode(['status'=>true,'message'=>'Data berhasil dihapus']);
}
}
+279
View File
@@ -0,0 +1,279 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class Dashboard extends CI_Controller {
public function __construct()
{
parent::__construct();
$this->load->library('session');
// Cek apakah user sudah login
if (!$this->session->userdata('logged_in')) {
redirect('auth');
}
}
public function index()
{
$this->db->select('
accounts.id,
accounts.kode_akun,
accounts.nama_akun,
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','accounts.id = journal_details.account_id','left');
$this->db->where('accounts.kategori','laba_rugi');
$this->db->where('accounts.is_active',1);
$this->db->group_by('accounts.id');
$this->db->order_by('accounts.kode_akun','ASC');
$rows = $this->db->get()->result();
$revenue = 0;
$expense = 0;
foreach ($rows as $r) {
// 🔥 NORMALISASI SALDO BERDASARKAN POSISI
if ($r->tipe == 'revenue') {
$saldo = $r->kredit - $r->debit;
$revenue += $saldo;
}
if ($r->tipe == 'expense') {
$saldo = $r->debit - $r->kredit;
$expense += $saldo;
}
}
$summary = [
'total_revenue' => $revenue,
'total_expense' => $expense,
'net_profit' => $revenue - $expense
];
$today = date('Y-m');
$ai_insight = $this->db
->select('type, severity, message, meta, created_at')
->where('period', $today)
->order_by("
CASE severity
WHEN 'critical' THEN 1
WHEN 'warning' THEN 2
WHEN 'info' THEN 3
END
", '', false)
->order_by('created_at', 'DESC')
->limit(20)
->get('ai_insights')
->result();
$data = [
"active_menu" => "dashboard",
"summary" => $summary,
"ai_insight" => $ai_insight
];
$this->load->view('partials/header', $data);
$this->load->view('dashboard/layout', $data);
$this->load->view('partials/footer');
}
public function get_activity()
{
$this->db->select('activity_logs.*, users.nama');
$this->db->from('activity_logs');
$this->db->join('users','users.id = activity_logs.user_id','left');
if ($this->session->userdata('role') != 'Admin') {
$this->db->where('user_id',$this->session->userdata('user_id'));
}
$this->db->order_by('created_at','DESC');
$this->db->limit(10);
$data = $this->db->get()->result();
echo json_encode($data);
}
public function log_activity()
{
$data = [
"active_menu" => "dashboard"
];
$this->load->view('partials/header', $data);
$this->load->view('dashboard/activity_logs', $data);
$this->load->view('partials/footer');
}
public function get_activity_datatable()
{
$this->load->database();
$draw = $this->input->post('draw');
$start = $this->input->post('start');
$length = $this->input->post('length');
$search = $this->input->post('search')['value'] ?? '';
// =========================
// BASE QUERY
// =========================
$this->db->select('
activity_logs.*,
users.nama
');
$this->db->from('activity_logs');
$this->db->join('users', 'users.id = activity_logs.user_id', 'left');
// =========================
// SEARCH
// =========================
if (!empty($search)) {
$this->db->group_start();
$this->db->like('activity_logs.module', $search);
$this->db->or_like('activity_logs.action', $search);
$this->db->or_like('activity_logs.description', $search);
$this->db->or_like('activity_logs.status', $search);
$this->db->or_like('users.nama', $search);
$this->db->group_end();
}
// =========================
// COUNT FILTERED
// =========================
$filtered_query = clone $this->db;
$recordsFiltered = $filtered_query->count_all_results('', false);
// =========================
// ORDER
// =========================
$this->db->order_by('activity_logs.id', 'DESC');
// =========================
// LIMIT
// =========================
if ($length != -1) {
$this->db->limit($length, $start);
}
$query = $this->db->get();
$data = $query->result();
// =========================
// TOTAL RECORD
// =========================
$recordsTotal = $this->db->count_all('activity_logs');
// =========================
// FORMAT OUTPUT
// =========================
$result = [];
foreach ($data as $row) {
$result[] = [
'created_at' => $row->created_at,
'nama' => $row->nama ?? 'System',
'module' => $row->module,
'action' => $row->action,
'description' => $row->description,
'status' => $row->status,
'ip_address' => $row->ip_address
];
}
echo json_encode([
"draw" => intval($draw),
"recordsTotal" => $recordsTotal,
"recordsFiltered" => $recordsFiltered,
"data" => $result
]);
}
public function get_chart()
{
$year = $this->input->get('year');
// 🔥 DEFAULT: tahun berjalan
if (empty($year)) {
$year = date('Y');
}
$this->db->select('
journals.tanggal,
accounts.tipe,
SUM(journal_details.debit) as total_debit,
SUM(journal_details.kredit) as total_kredit
');
$this->db->from('journal_details');
$this->db->join('journals', 'journals.id = journal_details.journal_id', 'left');
$this->db->join('accounts', 'accounts.id = journal_details.account_id', 'left');
$this->db->where('accounts.kategori', 'laba_rugi');
$this->db->where('accounts.is_active', 1);
// 🔥 pakai tahun default / filter
$this->db->where('YEAR(journals.tanggal)', $year);
$this->db->group_by(['journals.tanggal', 'accounts.tipe']);
$this->db->order_by('journals.tanggal', 'ASC');
$rows = $this->db->get()->result();
$map = [];
foreach ($rows as $r) {
$date = $r->tanggal;
if (!isset($map[$date])) {
$map[$date] = [
'revenue' => 0,
'expense' => 0
];
}
if ($r->tipe == 'revenue') {
$map[$date]['revenue'] += ($r->total_kredit - $r->total_debit);
}
if ($r->tipe == 'expense') {
$map[$date]['expense'] += ($r->total_debit - $r->total_kredit);
}
}
$labels = [];
$revenue = [];
$expense = [];
foreach ($map as $date => $val) {
$labels[] = $date;
$revenue[] = (float)$val['revenue'];
$expense[] = (float)$val['expense'];
}
echo json_encode([
'year' => $year, // optional biar debugging enak
'labels' => $labels,
'revenue' => $revenue,
'expense' => $expense
]);
}
public function get_years()
{
$this->db->select('DISTINCT YEAR(tanggal) as year');
$this->db->from('journals');
$this->db->order_by('year', 'DESC');
$data = $this->db->get()->result();
echo json_encode($data);
}
}
+450
View File
@@ -0,0 +1,450 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class Employees extends CI_Controller {
public function __construct()
{
parent::__construct();
$this->load->database();
$this->load->helper(['url', 'file']);
date_default_timezone_set('Asia/Jakarta');
}
// =========================================================
// INDEX
// =========================================================
public function index()
{
$data = [
"active_menu" => "employees"
];
$data['departments'] = $this->db
->where('is_active',1)
->order_by('department_name','ASC')
->get('k_departments')
->result();
$data['positions'] = $this->db
->where('is_active',1)
->order_by('position_name','ASC')
->get('k_positions')
->result();
$data['branches'] = $this->db
->order_by('branch_name','ASC')
->get('k_branches')
->result();
$this->load->view('partials/header', $data);
$this->load->view('employees/employees', $data);
$this->load->view('partials/footer');
}
// =========================================================
// GET ALL
// =========================================================
public function getAll()
{
try {
$data = $this->db
->select("
e.*,
d.department_name,
p.position_name,
b.branch_name
")
->from('k_employees e')
->join(
'k_departments d',
'd.id = e.department_id',
'left'
)
->join(
'k_positions p',
'p.id = e.position_id',
'left'
)
->join(
'k_branches b',
'b.id = e.branch_id',
'left'
)
->order_by('e.department_id','ASC')
->get()
->result();
return $this->json([
'status' => true,
'data' => $data
]);
} catch (Exception $e) {
return $this->json([
'status' => false,
'message' => $e->getMessage(),
'data' => []
]);
}
}
// =========================================================
// DETAIL
// =========================================================
public function detail($id = null)
{
try {
$data = $this->db
->select("
e.*,
d.department_name,
p.position_name,
b.branch_name
")
->from('k_employees e')
->join(
'k_departments d',
'd.id = e.department_id',
'left'
)
->join(
'k_positions p',
'p.id = e.position_id',
'left'
)
->join(
'k_branches b',
'b.id = e.branch_id',
'left'
)
->where('e.id',$id)
->get()
->row();
if (!$data) {
return $this->json([
'status' => false,
'message' => 'Data tidak ditemukan'
]);
}
return $this->json([
'status' => true,
'data' => $data
]);
} catch (Exception $e) {
return $this->json([
'status' => false,
'message' => $e->getMessage()
]);
}
}
// =========================================================
// STORE
// =========================================================
public function store()
{
try {
if (empty(trim($this->input->post('full_name')))) {
return $this->json([
'status' => false,
'message'=> 'Nama wajib diisi'
]);
}
if (empty($this->input->post('join_date'))) {
return $this->json([
'status' => false,
'message'=> 'Tanggal masuk wajib diisi'
]);
}
$employee_code = $this->generateEmployeeCode();
$photo = $this->uploadPhoto();
$data = $this->prepareData();
$data['employee_code'] = $employee_code;
$data['photo'] = $photo;
$this->db->insert('k_employees',$data);
return $this->json([
'status' => true,
'message' => 'Berhasil simpan data'
]);
} catch (Exception $e) {
return $this->json([
'status' => false,
'message' => $e->getMessage()
]);
}
}
// =========================================================
// UPDATE
// =========================================================
public function update($id = null)
{
try {
$employee = $this->db
->get_where('k_employees',['id'=>$id])
->row();
if (!$employee) {
return $this->json([
'status' => false,
'message' => 'Data tidak ditemukan'
]);
}
$data = $this->prepareData();
if (!empty($_FILES['photo_file']['name'])) {
$photo = $this->uploadPhoto();
if (
!empty($employee->photo)
&&
file_exists(FCPATH . $employee->photo)
) {
unlink(FCPATH . $employee->photo);
}
$data['photo'] = $photo;
}
$this->db
->where('id',$id)
->update('k_employees',$data);
return $this->json([
'status' => true,
'message' => 'Berhasil update data'
]);
} catch (Exception $e) {
return $this->json([
'status' => false,
'message' => $e->getMessage()
]);
}
}
// =========================================================
// DELETE
// =========================================================
public function delete($id = null)
{
try {
$employee = $this->db
->get_where('k_employees',['id'=>$id])
->row();
if (!$employee) {
return $this->json([
'status' => false,
'message' => 'Data tidak ditemukan'
]);
}
if (
!empty($employee->photo)
&&
file_exists(FCPATH . $employee->photo)
) {
unlink(FCPATH . $employee->photo);
}
$this->db->delete('k_employees',[
'id'=>$id
]);
return $this->json([
'status' => true,
'message' => 'Berhasil hapus data'
]);
} catch (Exception $e) {
return $this->json([
'status' => false,
'message' => $e->getMessage()
]);
}
}
// =========================================================
// GENERATE CODE
// =========================================================
private function generateEmployeeCode()
{
$last = $this->db
->order_by('id','DESC')
->get('k_employees')
->row();
$number = $last ? $last->id + 1 : 1;
return 'RJN-' . str_pad($number,5,'0',STR_PAD_LEFT);
}
// =========================================================
// UPLOAD PHOTO
// =========================================================
private function uploadPhoto()
{
if (empty($_FILES['photo_file']['name'])) {
return null;
}
$path = FCPATH . 'uploads/karyawan/';
if (!is_dir($path)) {
mkdir($path,0777,true);
}
$config['upload_path'] = './uploads/karyawan/';
$config['allowed_types'] = 'jpg|jpeg|png|webp';
$config['max_size'] = 2048;
$config['encrypt_name'] = true;
$this->load->library('upload',$config);
if (!$this->upload->do_upload('photo_file')) {
throw new Exception(
strip_tags(
$this->upload->display_errors()
)
);
}
$upload = $this->upload->data();
return 'uploads/karyawan/' . $upload['file_name'];
}
// =========================================================
// PREPARE DATA
// =========================================================
private function prepareData()
{
return [
'nik' => trim($this->input->post('nik')),
'full_name' => trim(
$this->input->post('full_name')
),
'gender' => $this->input->post('gender'),
'birth_place' => trim(
$this->input->post('birth_place')
),
'birth_date' => $this->input->post('birth_date'),
'phone' => trim(
$this->input->post('phone')
),
'email' => trim(
$this->input->post('email')
),
'address' => trim(
$this->input->post('address')
),
'join_date' => $this->input->post('join_date'),
'resign_date' => $this->input->post('resign_date'),
'employment_status' => $this->input->post('employment_status'),
'marital_status' => $this->input->post('marital_status'),
'religion' => trim(
$this->input->post('religion')
),
'department_id' => $this->input->post('department_id'),
'position_id' => $this->input->post('position_id'),
'branch_id' => $this->input->post('branch_id'),
'basic_salary' => $this->input->post('basic_salary') ?: 0,
'bank_name' => trim(
$this->input->post('bank_name')
),
'bank_account_number' => trim(
$this->input->post('bank_account_number')
),
'bank_account_name' => trim(
$this->input->post('bank_account_name')
),
'bpjs_kesehatan_number' => trim(
$this->input->post('bpjs_kesehatan_number')
),
'bpjs_ketenagakerjaan_number' => trim(
$this->input->post('bpjs_ketenagakerjaan_number')
),
'fingerprint_user_id' => trim(
$this->input->post('fingerprint_user_id')
),
'is_active' => $this->input->post('is_active') ?: 1
];
}
// =========================================================
// JSON
// =========================================================
private function json($data)
{
return $this->output
->set_content_type('application/json')
->set_output(json_encode($data));
}
}
+188
View File
@@ -0,0 +1,188 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class Generateinvoice extends CI_Controller {
public function generate($invoice_id)
{
// ❗ WAJIB: hindari output lain sebelum PDF
ob_end_clean();
// =========================
// 🔥 AMBIL DATA PERUSAHAAN
// =========================
$invp = $this->db->get('pengaturan')->row_array(); // 🔥 FIX: tidak pakai GET server_id
if(!$invp){
show_error('Data perusahaan tidak ditemukan');
}
$nama = $invp['nama_perusahaan'];
$alamat = $invp['alamat_perusahaan'];
$telepon = $invp['telepon_perusahaan'];
$Keterangan = $invp['catatan_perusahaan'];
$Pembuat = $invp['nama_bendahara'];
$logo = base_url('uploads/img/logo-ljn.png');
// =========================
// 🔥 GET INVOICE + CUSTOMER
// =========================
$invi = $this->db->select('invoices.*, customers.nama, customers.alamat, customers.telp')
->from('invoices')
->join('customers', 'customers.id = invoices.customer_id', 'left')
->where('invoices.id', $invoice_id)
->get()
->row_array();
// =========================
// VALIDASI
// =========================
if(!$invi){
show_error('Invoice tidak ditemukan');
}
// =========================
// MAPPING DATA
// =========================
$customerAddress = $invi['alamat'] ?? '-';
$customerName = $invi['nama'] ?? '-';
$customerNumber = $invi['telp'] ?? '-';
$invoiceNumber = $invi['no_invoice'] ?? '-';
$invoiceDate = $invi['tanggal'] ?? '-'; // 🔥 sesuaikan field kamu
$invoiceLimit = $invi['jatuh_tempo'] ?? '-'; // 🔥 sesuaikan juga
// =========================
// 🔥 DETAIL ITEM
// =========================
$header = ['No','Tanggal','Item','Deskripsi','Qty','Harga', 'Subtotal'];
$data = [];
// 🔥 NOTE: sesuaikan tabel kamu (invoice_details / invoice_list)
// $detail = $this->db->get_where('invoice_details', [
// 'invoice_id' => $invoice_id
// ])->result_array();
$detail = $this->db
->where('invoice_id', $invoice_id)
->order_by('tanggal', 'ASC')
->get('invoice_details')
->result_array();
$no = 1;
foreach($detail as $d){
$data[] = [
$no++,
$d['tanggal'],
$d['nama_item'],
$d['keterangan'],
$d['qty'],
number_format($d['harga']),
number_format($d['subtotal'])
];
}
// =========================
// 🔥 TOTAL
// =========================
$total = (float)$invi['total'];
$this->db->select_sum('sisa_piutang');
$this->db->where('customer_id', $invi['customer_id']);
$this->db->where('status !=', 'paid');
$this->db->where('id !=', $invoice_id); // exclude invoice sekarang
$tunggakan = $this->db->get('invoices')->row()->sisa_piutang ?? 0;
// Ngambil Data Tunggakan bulan lalu jika ada.
$keteranganlain = 'Akumulasi Tunggakan Invoice Bulan Lalu';
$nom_lainnya = $tunggakan;
$subtotal = number_format($total);
$lainnya = number_format($nom_lainnya);
$totalAmount = number_format($total + $nom_lainnya);
$terbilang = trim($this->terbilang($total + $nom_lainnya)).' rupiah';
$filename = 'INV-'.$customerName.'-'.$invoiceNumber.'.pdf';
// =========================
// 🔥 LOAD FPDF
// =========================
require_once(APPPATH.'third_party/fpdf/fpdf.php');
require_once(APPPATH.'libraries/PDF_Invoice.php');
$pdf = new PDF();
// 🔥 FIX PATH LOGO (WAJIB)
$logo_path = FCPATH.'img/user/'.$logo;
if(!file_exists($logo_path)){
$logo_path = ''; // biar gak error kalau logo tidak ada
}
$pdf->setHeaderData($nama, $alamat, $telepon, $logo);
$pdf->AddPage();
$pdf->SetFont('Arial','',12);
$pdf->CustomerDetails(
$customerName,
$customerAddress,
$invoiceNumber,
$invoiceDate,
$customerNumber,
$invoiceLimit
);
$pdf->InvoiceTable($header, $data);
$pdf->Total($subtotal,$lainnya,$totalAmount,$terbilang,$keteranganlain);
$pdf->Keterangan($Keterangan);
$pdf->Pembuat($Pembuat);
// =========================
// 🔥 OUTPUT LANGSUNG (TANPA SIMPAN)
// =========================
$pdf->Output('I', $filename);
// =========================
// LOG ACTIVITY
// =========================
log_activity(
'invoice',
'generate',
'Generate invoice #' . $invoiceNumber . ' untuk ' . $customerName,
'success'
);
exit;
}
// =========================
// 🔥 TERBILANG
// =========================
private function terbilang($angka)
{
$angka = abs($angka);
$baca = ["", "satu", "dua", "tiga", "empat", "lima", "enam", "tujuh", "delapan", "sembilan", "sepuluh", "sebelas"];
if ($angka < 12) {
return " " . $baca[$angka];
} elseif ($angka < 20) {
return $this->terbilang($angka - 10) . " belas";
} elseif ($angka < 100) {
return $this->terbilang(intval($angka / 10)) . " puluh" . $this->terbilang($angka % 10);
} elseif ($angka < 200) {
return " seratus" . $this->terbilang($angka - 100);
} elseif ($angka < 1000) {
return $this->terbilang(intval($angka / 100)) . " ratus" . $this->terbilang($angka % 100);
} elseif ($angka < 2000) {
return " seribu" . $this->terbilang($angka - 1000);
} elseif ($angka < 1000000) {
return $this->terbilang(intval($angka / 1000)) . " ribu" . $this->terbilang($angka % 1000);
} elseif ($angka < 1000000000) {
return $this->terbilang(intval($angka / 1000000)) . " juta" . $this->terbilang($angka % 1000000);
}
return "";
}
}
@@ -0,0 +1,768 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class Generateschedule extends CI_Controller {
public function __construct()
{
parent::__construct();
$this->load->database();
date_default_timezone_set('Asia/Jakarta');
}
// =====================================================
// INDEX
// =====================================================
public function index()
{
$data = [
"active_menu" => "generate_schedule"
];
// ============================================
// POSITIONS
// ============================================
$data['positions'] = $this->db
->where('is_active',1)
->order_by('position_name','ASC')
->get('k_positions')
->result();
// ============================================
// SHIFTS
// ============================================
$data['shifts'] = $this->db
->order_by('shift_code','ASC')
->get('k_shifts')
->result();
$this->load->view('partials/header', $data);
$this->load->view('employees/generate_schedule', $data);
$this->load->view('partials/footer');
}
// =====================================================
// CALENDAR DATA
// =====================================================
public function calendar_data()
{
$month = (int)$this->input->get('month');
$year = (int)$this->input->get('year');
if(empty($month)){
$month = date('n');
}
if(empty($year)){
$year = date('Y');
}
$firstDate = $year . '-'
. str_pad($month,2,'0',STR_PAD_LEFT)
. '-01';
// ============================================
// START MONDAY
// ============================================
$startDate = date(
'Y-m-d',
strtotime('monday this week', strtotime($firstDate))
);
if(date('N', strtotime($firstDate)) == 1){
$startDate = $firstDate;
}
// ============================================
// END SUNDAY
// ============================================
$lastDate = date(
'Y-m-t',
strtotime($firstDate)
);
$endDate = date(
'Y-m-d',
strtotime('sunday this week', strtotime($lastDate))
);
$days = [];
$current = strtotime($startDate);
$end = strtotime($endDate);
while($current <= $end){
$date = date('Y-m-d', $current);
// ========================================
// GET SCHEDULE GROUP BY SHIFT
// ========================================
$this->db->select('
sh.id as shift_id,
sh.shift_name,
p.position_name,
COUNT(s.id) as total_employee
');
$this->db->from('k_employee_shift_schedules s');
$this->db->join(
'k_employees e',
'e.id = s.employee_id',
'left'
);
$this->db->join(
'k_positions p',
'p.id = e.position_id',
'left'
);
$this->db->join(
'k_shifts sh',
'sh.id = s.shift_id',
'left'
);
$this->db->where('DATE(s.start_datetime)', $date);
// ========================================
// GROUPING
// ========================================
$this->db->group_by('sh.id');
$this->db->group_by('p.id');
$this->db->order_by('sh.checkin_time','ASC');
$this->db->order_by('p.position_name','ASC');
$rows = $this->db->get()->result();
// ========================================
// FORMAT GROUPED
// ========================================
$grouped = [];
if($rows){
foreach($rows as $r){
$shiftId = $r->shift_id ?: 0;
if(!isset($grouped[$shiftId])){
$grouped[$shiftId] = [
'shift_id' => $r->shift_id,
'shift_name' => $r->shift_name ?: '-',
'positions' => []
];
}
$grouped[$shiftId]['positions'][] = [
'position_name' => $r->position_name ?: '-',
'total' => (int)$r->total_employee
];
}
}
$days[] = [
'date' => date('d', strtotime($date)),
'month' => date('M', strtotime($date)),
'day_name' => $this->translateDay(
date('l', strtotime($date))
),
'full_date' => tanggal_indo($date),
'full_date_raw' => $date,
'is_current_month' => (
date('m', strtotime($date))
==
str_pad($month,2,'0',STR_PAD_LEFT)
),
'shifts' => array_values($grouped)
];
$current = strtotime('+1 day', $current);
}
echo json_encode([
'days' => $days
]);
}
// =====================================================
// GET EMPLOYEE
// =====================================================
public function get_employees()
{
$position_id = $this->input->get('position_id');
$this->db->select('
e.id,
e.full_name,
e.fingerprint_user_id,
p.position_name
');
$this->db->from('k_employees e');
$this->db->join(
'k_positions p',
'p.id=e.position_id',
'left'
);
$this->db->where('e.is_active',1);
if(!empty($position_id)){
$this->db->where('e.position_id',$position_id);
}
$this->db->order_by('e.full_name','ASC');
$employees = $this->db->get()->result();
echo json_encode([
'data' => $employees
]);
}
// =====================================================
// SAVE MANUAL SCHEDULE
// =====================================================
public function save_manual_schedule()
{
try{
$employee_ids = $this->input->post('employee_ids');
$schedule_date = $this->input->post('schedule_date');
$shift_id = $this->input->post('shift_id');
// ============================================
// VALIDASI
// ============================================
if(empty($employee_ids)){
throw new Exception('Employee belum dipilih');
}
if(empty($schedule_date)){
throw new Exception('Tanggal wajib diisi');
}
if(empty($shift_id)){
throw new Exception('Shift wajib dipilih');
}
// ============================================
// GET SHIFT
// ============================================
$shift = $this->db
->where('id', $shift_id)
->get('k_shifts')
->row();
if(!$shift){
throw new Exception('Shift tidak ditemukan');
}
// ============================================
// BUILD START DATETIME
// ============================================
$start_datetime = date(
'Y-m-d H:i:s',
strtotime(
$schedule_date . ' ' . $shift->checkin_time
)
);
// ============================================
// BUILD END DATETIME
// ============================================
$end_datetime = date(
'Y-m-d H:i:s',
strtotime(
$schedule_date . ' ' . $shift->checkout_time
)
);
// ============================================
// JIKA SHIFT NYEBRANG HARI
// ============================================
$is_cross_day = 0;
if(
strtotime($shift->checkout_time)
<=
strtotime($shift->checkin_time)
){
$end_datetime = date(
'Y-m-d H:i:s',
strtotime('+1 day', strtotime($end_datetime))
);
$is_cross_day = 1;
}
// ============================================
// HITUNG TOTAL JAM KERJA
// ============================================
$work_hours = (
strtotime($end_datetime)
-
strtotime($start_datetime)
) / 3600;
// ============================================
// LOOP EMPLOYEE
// ============================================
foreach($employee_ids as $employee_id){
// ========================================
// CHECK EXIST
// ========================================
$exists = $this->db
->where('employee_id', $employee_id)
->where('schedule_date', $schedule_date)
->get('k_employee_shift_schedules')
->row();
// ========================================
// DATA SAVE
// ========================================
$saveData = [
'employee_id' => $employee_id,
'shift_id' => $shift_id,
'start_datetime' => $start_datetime,
'end_datetime' => $end_datetime,
'work_hours' => $work_hours,
'is_cross_day' => $is_cross_day,
'schedule_source' => 'manual',
'notes' => 'manual planner',
'updated_at' => date('Y-m-d H:i:s')
];
// ========================================
// UPDATE
// ========================================
if($exists){
$this->db
->where('id', $exists->id)
->update(
'k_employee_shift_schedules',
$saveData
);
// ========================================
// INSERT
// ========================================
} else {
$saveData['created_at'] = date('Y-m-d H:i:s');
$this->db->insert(
'k_employee_shift_schedules',
$saveData
);
}
}
echo json_encode([
'status' => true,
'message' => 'Jadwal berhasil disimpan'
]);
} catch(Exception $e){
echo json_encode([
'status' => false,
'message' => $e->getMessage()
]);
}
}
// =====================================================
// TRANSLATE DAY
// =====================================================
private function translateDay($day)
{
$days = [
'Sunday' => 'Minggu',
'Monday' => 'Senin',
'Tuesday' => 'Selasa',
'Wednesday' => 'Rabu',
'Thursday' => 'Kamis',
'Friday' => 'Jumat',
'Saturday' => 'Sabtu'
];
return isset($days[$day])
? $days[$day]
: $day;
}
// =====================================================
// DETAIL SCHEDULE
// =====================================================
public function detail_schedule()
{
$schedule_date = $this->input->get('schedule_date');
if(empty($schedule_date)){
echo json_encode([
'status' => false,
'message'=> 'Tanggal wajib diisi',
'data' => []
]);
return;
}
// ============================================
// GET DATA
// ============================================
$this->db->select('
s.id as schedule_id,
s.employee_id,
s.shift_id,
s.start_datetime,
s.end_datetime,
e.employee_code,
e.full_name,
e.fingerprint_user_id,
p.id as position_id,
p.position_name,
sh.shift_name,
sh.checkin_time,
sh.checkout_time,
sh.color
');
$this->db->from('k_employee_shift_schedules s');
$this->db->join(
'k_employees e',
'e.id=s.employee_id',
'left'
);
$this->db->join(
'k_positions p',
'p.id=e.position_id',
'left'
);
$this->db->join(
'k_shifts sh',
'sh.id=s.shift_id',
'left'
);
// ============================================
// FILTER DATE
// ============================================
$this->db->where(
'DATE(s.start_datetime)',
$schedule_date
);
$this->db->order_by('p.position_name','ASC');
$this->db->order_by('e.full_name','ASC');
$rows = $this->db->get()->result();
// ============================================
// GROUP POSITION
// ============================================
$grouped = [];
if($rows){
foreach($rows as $r){
$position_id = $r->position_id ?: 0;
// ====================================
// CREATE GROUP
// ====================================
if(!isset($grouped[$position_id])){
$grouped[$position_id] = [
'position_id' => $r->position_id,
'position_name' => $r->position_name ?: '-',
'employees' => []
];
}
// ====================================
// EMPLOYEE
// ====================================
$grouped[$position_id]['employees'][] = [
'schedule_id' => $r->schedule_id,
'employee_id' => $r->employee_id,
'full_name' => $r->full_name,
'employee_code' => $r->employee_code,
'fingerprint_user_id' => $r->fingerprint_user_id,
'shift_id' => $r->shift_id,
'shift_name' => $r->shift_name,
'checkin' => $r->checkin_time
? date(
'H:i',
strtotime($r->checkin_time)
)
: '-',
'checkout' => $r->checkout_time
? date(
'H:i',
strtotime($r->checkout_time)
)
: '-',
'start_datetime' => $r->start_datetime,
'end_datetime' => $r->end_datetime,
'color' => $r->color
];
}
}
echo json_encode([
'status' => true,
'data' => array_values($grouped)
]);
}
// =====================================================
// UPDATE SCHEDULE SHIFT
// =====================================================
public function update_schedule_shift()
{
try{
$schedule_id = $this->input->post('schedule_id');
$shift_id = $this->input->post('shift_id');
// ============================================
// VALIDASI
// ============================================
if(empty($schedule_id)){
throw new Exception('Schedule ID kosong');
}
if(empty($shift_id)){
throw new Exception('Shift wajib dipilih');
}
// ============================================
// CHECK SCHEDULE
// ============================================
$schedule = $this->db
->where('id', $schedule_id)
->get('k_employee_shift_schedules')
->row();
if(!$schedule){
throw new Exception('Schedule tidak ditemukan');
}
// ============================================
// CHECK SHIFT
// ============================================
$shift = $this->db
->where('id', $shift_id)
->get('k_shifts')
->row();
if(!$shift){
throw new Exception('Shift tidak ditemukan');
}
// ============================================
// AMBIL TANGGAL
// ============================================
$schedule_date = date(
'Y-m-d',
strtotime($schedule->start_datetime)
);
// ============================================
// BUILD START DATETIME
// ============================================
$start_datetime = date(
'Y-m-d H:i:s',
strtotime(
$schedule_date . ' ' . $shift->checkin_time
)
);
// ============================================
// BUILD END DATETIME
// ============================================
$end_datetime = date(
'Y-m-d H:i:s',
strtotime(
$schedule_date . ' ' . $shift->checkout_time
)
);
// ============================================
// CROSS DAY
// ============================================
$is_cross_day = 0;
if(
strtotime($shift->checkout_time)
<=
strtotime($shift->checkin_time)
){
$end_datetime = date(
'Y-m-d H:i:s',
strtotime('+1 day', strtotime($end_datetime))
);
$is_cross_day = 1;
}
// ============================================
// WORK HOURS
// ============================================
$work_hours = $shift->work_hours;
// ============================================
// UPDATE
// ============================================
$this->db
->where('id', $schedule_id)
->update(
'k_employee_shift_schedules',
[
'shift_id' => $shift_id,
'start_datetime' => $start_datetime,
'end_datetime' => $end_datetime,
'work_hours' => $work_hours,
'is_cross_day' => $is_cross_day,
'updated_at' => date('Y-m-d H:i:s')
]
);
echo json_encode([
'status' => true,
'message' => 'Shift berhasil diupdate'
]);
} catch(Exception $e){
echo json_encode([
'status' => false,
'message' => $e->getMessage()
]);
}
}
// =====================================================
// DELETE SCHEDULE
// =====================================================
public function delete_schedule()
{
try{
$schedule_id = $this->input->post('schedule_id');
// ============================================
// VALIDASI
// ============================================
if(empty($schedule_id)){
throw new Exception('Schedule ID kosong');
}
// ============================================
// CHECK DATA
// ============================================
$schedule = $this->db
->where('id', $schedule_id)
->get('k_employee_shift_schedules')
->row();
if(!$schedule){
throw new Exception('Schedule tidak ditemukan');
}
// ============================================
// DELETE
// ============================================
$this->db
->where('id', $schedule_id)
->delete('k_employee_shift_schedules');
echo json_encode([
'status' => true,
'message' => 'Schedule berhasil dihapus'
]);
} catch(Exception $e){
echo json_encode([
'status' => false,
'message' => $e->getMessage()
]);
}
}
}
+177
View File
@@ -0,0 +1,177 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class Holidays extends CI_Controller {
public function __construct()
{
parent::__construct();
$this->load->database();
}
// =========================================
// VIEW
// =========================================
public function index()
{
$data = [
"active_menu" => "holidays"
];
$this->load->view('partials/header', $data);
$this->load->view('employees/holidays');
$this->load->view('partials/footer');
}
// =========================================
// GET DATA
// =========================================
public function get_data()
{
$data = $this->db
->order_by('holiday_date','DESC')
->get('k_holidays')
->result();
$rows = [];
$no = 1;
foreach($data as $d){
$rows[] = [
$no++,
date('d M Y', strtotime($d->holiday_date)),
$d->holiday_name,
$d->is_national == 1
? '<span class="badge bg-success">Nasional</span>'
: '<span class="badge bg-secondary">Perusahaan</span>',
'
<button class="btn btn-warning btn-sm btn-edit"
data-id="'.$d->id.'">
Edit
</button>
<button class="btn btn-danger btn-sm btn-delete"
data-id="'.$d->id.'">
Delete
</button>
'
];
}
echo json_encode([
'data' => $rows
]);
}
// =========================================
// DETAIL
// =========================================
public function detail($id)
{
$data = $this->db
->get_where('k_holidays',['id'=>$id])
->row();
echo json_encode($data);
}
// =========================================
// SAVE
// =========================================
public function save()
{
$holiday_date = $this->input->post('holiday_date');
$holiday_name = $this->input->post('holiday_name');
$is_national = $this->input->post('is_national');
$cek = $this->db
->get_where('k_holidays',[
'holiday_date' => $holiday_date
])
->row();
if($cek){
echo json_encode([
'status' => false,
'message' => 'Tanggal holiday sudah ada'
]);
return;
}
$insert = [
'holiday_date' => $holiday_date,
'holiday_name' => $holiday_name,
'is_national' => $is_national
];
$this->db->insert('k_holidays',$insert);
echo json_encode([
'status' => true,
'message' => 'Holiday berhasil ditambahkan'
]);
}
// =========================================
// UPDATE
// =========================================
public function update()
{
$id = $this->input->post('id');
$holiday_date = $this->input->post('holiday_date');
$holiday_name = $this->input->post('holiday_name');
$is_national = $this->input->post('is_national');
$cek = $this->db
->where('holiday_date',$holiday_date)
->where('id !=',$id)
->get('k_holidays')
->row();
if($cek){
echo json_encode([
'status' => false,
'message' => 'Tanggal holiday sudah ada'
]);
return;
}
$update = [
'holiday_date' => $holiday_date,
'holiday_name' => $holiday_name,
'is_national' => $is_national
];
$this->db
->where('id',$id)
->update('k_holidays',$update);
echo json_encode([
'status' => true,
'message' => 'Holiday berhasil diupdate'
]);
}
// =========================================
// DELETE
// =========================================
public function delete($id)
{
$this->db
->where('id',$id)
->delete('k_holidays');
echo json_encode([
'status' => true,
'message' => 'Holiday berhasil dihapus'
]);
}
}
File diff suppressed because it is too large Load Diff
+795
View File
@@ -0,0 +1,795 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class Items extends CI_Controller {
public function __construct()
{
parent::__construct();
if (!$this->session->userdata('logged_in')) {
redirect('auth');
}
}
public function index()
{
$data = ["active_menu" => "items"];
$this->load->view('partials/header', $data);
$this->load->view('items/layout');
$this->load->view('partials/footer');
}
public function draft_items()
{
$data = ["active_menu" => "draft_items"];
$this->load->view('partials/header', $data);
$this->load->view('items/draft_items');
$this->load->view('partials/footer');
}
// =========================
// GET DATA (FIX)
// =========================
public function get_data()
{
$status = $this->input->get('s') ?? 'all';
$allowed = ['draft','active','all'];
if (!in_array($status, $allowed)) {
$status = 'all';
}
$this->db->select('
items.id,
items.nama_barang,
items.kode_detail,
items.harga_beli,
items.harga_jual,
items.tanggal_pembelian,
items.created_at,
items.stok,
items.status,
items.kode_id,
(
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
');
if ($status !== 'all') {
$this->db->where('items.status', $status);
}
// urutan custom
$this->db->order_by('(items.stok = 0)', 'ASC', false);
$this->db->order_by('items.tanggal_pembelian', 'ASC');
$this->db->from('items');
$data = $this->db->get()->result();
// $this->db->select('
// items.id,
// items.nama_barang,
// items.kode_detail,
// items.harga_beli,
// items.harga_jual,
// items.tanggal_pembelian,
// items.created_at,
// items.stok,
// items.status,
// items.kode_id,
// (
// 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
// ');
// if ($status !== 'all') {
// $this->db->where('items.status', $status);
// }
// // 🔥 filter utama per kode_id
// $this->db->where("
// (items.stok > 0)
// OR
// (
// items.stok = 0
// AND items.tanggal_pembelian = (
// SELECT MAX(i2.tanggal_pembelian)
// FROM items i2
// WHERE i2.stok = 0
// AND i2.kode_id = items.kode_id
// )
// )
// ", null, false);
// // urutan
// $this->db->order_by('items.stok = 0', 'ASC', false);
// $this->db->order_by('items.tanggal_pembelian', 'ASC');
// $this->db->from('items');
// $data = $this->db->get()->result();
$result = [];
$no = 1;
foreach($data as $row){
$btnDetail = '<button class="btn btn-info btn-sm btn-detail m-1" data-id="'.htmlspecialchars($row->id, ENT_QUOTES, 'UTF-8').'">Detail</button>';
$btnEdit = '<button class="btn btn-secondary btn-sm btn-editItem m-1" data-id="'.htmlspecialchars($row->id, ENT_QUOTES, 'UTF-8').'">Edit</button>';
$btnDelete = '<button class="btn btn-danger btn-sm btn-delete m-1" data-id="'.htmlspecialchars($row->id, ENT_QUOTES, 'UTF-8').'">Delete</button>';
if($row->status == 'draft'){
$btnPost = '<button class="btn btn-success btn-sm btn-updatePosting m-1" data-id="'.htmlspecialchars($row->id, ENT_QUOTES, 'UTF-8').'">Posting</button>';
$action = $btnDetail . $btnPost . $btnDelete;
} else {
if($this->session->userdata('role') == 'Admin') {
$btnAdjust = '<button class="btn btn-warning btn-sm btn-adjust m-1" data-id="'.htmlspecialchars($row->id, ENT_QUOTES, 'UTF-8').'">Adjust</button>';
} else {
$btnAdjust = '';
}
$action = $btnDetail . $btnEdit . $btnAdjust . $btnDelete;
}
$result[] = [
$no++,
$row->tanggal_pembelian,
$row->kode_detail,
$row->nama_barang,
$row->gudang ?? '-',
$row->stok,
number_format($row->harga_beli ?? 0, 2),
number_format($row->harga_jual ?? 0, 2),
$row->created_at,
$action
];
}
echo json_encode(["data"=>$result]);
}
// =========================
// DETAIL
// =========================
public function detail_simple($id)
{
echo json_encode(
$this->db->get_where('items',['id'=>$id])->row()
);
}
// =========================
// DETAIL
// =========================
public function detail($id)
{
$item = $this->db->get_where('items',['id'=>$id])->row();
$logs = $this->db
->select('stock_logs.*, warehouses.nama as gudang')
->join('warehouses','warehouses.id = stock_logs.warehouse_id','left')
->where('item_id',$id)
->order_by('stock_logs.id','DESC')
->get('stock_logs')
->result();
echo json_encode([
'item'=>$item,
'logs'=>$logs
]);
}
public function get_accounts()
{
$data = $this->db
->where_in('kode_akun', [101,102,201])
->order_by('kode_akun','ASC')
->get('accounts')
->result();
echo json_encode($data);
}
public function get_accounts_biaya()
{
$data = $this->db
->where(['tipe' => 'expense', 'posisi'=> 'debit', 'kategori'=> 'laba_rugi'])
->order_by('kode_akun','ASC')
->get('accounts')
->result();
echo json_encode($data);
}
// =========================
// SAVE (FIX TOTAL)
// =========================
public function save()
{
$nama_barang = $this->input->post('nama_barang');
$qty = (int)$this->input->post('qty');
$harga_beli = (float)$this->input->post('harga_beli');
$harga_jual = (float)$this->input->post('harga_jual');
$warehouse_id = $this->input->post('warehouse_id');
$account_kas = $this->input->post('account_kas');
$kode_id = $this->input->post('kode_id');
$tanggal_beli = $this->input->post('tanggal_beli');
$status = $this->input->post('status');
// ================= VALIDASI =================
if(!$nama_barang || $qty <= 0 || $harga_beli <= 0 || !$warehouse_id || !$account_kas){
echo json_encode([
'status'=>false,
'message'=>'Data tidak lengkap / tidak valid'
]);
return;
}
// ================= VALIDASI HARGA =================
if($harga_beli > $harga_jual){
echo json_encode([
'status'=>false,
'message'=>'Harga beli tidak boleh lebih besar dari harga jual'
]);
return;
}
$kode = $this->db->get_where('kode_barang',['id'=>$kode_id])->row();
$tanggal_kode = date('dmy', strtotime($tanggal_beli));
$kode_detail = $kode->kode_barang . '-' . $tanggal_kode;
$total = $qty * $harga_beli;
$this->db->trans_start();
// ================= INSERT ITEM =================
$this->db->insert('items',[
'stok' =>$qty,
'kode_detail' =>$kode_detail,
'kode_id' =>$kode_id,
'nama_barang' =>$nama_barang,
'harga_beli' =>$harga_beli,
'harga_jual' =>$harga_jual,
'tanggal_pembelian' => $tanggal_beli,
'status' => $status
]);
$item_id = $this->db->insert_id();
// ================= STOCK MASUK =================
$this->db->insert('stock_logs',[
'item_id'=>$item_id,
'warehouse_id'=>$warehouse_id,
'qty'=>$qty,
'tipe'=>'masuk',
'keterangan'=>'Pembelian awal',
'created_at'=>date('Y-m-d H:i:s')
]);
// ================= JURNAL =================
$no_ref = 'BLI-'.date('Ymd').'-'.$item_id;
$this->db->insert('journals',[
'tanggal'=>$tanggal_beli,
'no_ref'=>$no_ref,
'ref_type'=> 'new_items',
'ref_id'=>$item_id,
'keterangan'=>'Pembelian '.$nama_barang,
'created_by' => $this->session->userdata('user_id')
]);
$jid = $this->db->insert_id();
// 🔥 Debit Persediaan
$this->db->insert('journal_details',[
'journal_id'=>$jid,
'account_id'=>21, // persediaan
'debit'=>$total,
'kredit'=>0
]);
// 🔥 Kredit Kas (DINAMIS)
$this->db->insert('journal_details',[
'journal_id'=>$jid,
'account_id'=>$account_kas,
'debit'=>0,
'kredit'=>$total
]);
$this->db->trans_complete();
// ================= CEK TRANSAKSI =================
if ($this->db->trans_status() === FALSE){
echo json_encode([
'status'=>false,
'message'=>'Gagal simpan data'
]);
} else {
log_activity(
'items',
'create',
'Menambahkan item: ' . $nama_barang,
'success'
);
echo json_encode([
'status'=>true,
'message'=>'Berhasil tambah item'
]);
}
}
// =========================
// UPDATE (FIX AKUN)
// =========================
public function update()
{
$id = $this->input->post('id');
$nama_barang = $this->input->post('nama_barang');
$harga_baru = (float)$this->input->post('harga_beli');
$harga_jual = (float)$this->input->post('harga_jual');
$item = $this->db->get_where('items',['id'=>$id])->row();
if(!$item){
echo json_encode(['status'=>false,'message'=>'Item tidak ditemukan']);
return;
}
$this->db->trans_start();
$this->db->where('id',$id)->update('items',[
'nama_barang'=>$nama_barang,
'harga_beli'=>$harga_baru,
'harga_jual'=>$harga_jual
]);
if($item->harga_beli != $harga_baru){
$stok = $this->db->select('SUM(qty * IF(tipe="masuk",1,-1)) as stok')
->where('item_id',$id)
->get('stock_logs')
->row()->stok ?? 0;
$selisih = ($harga_baru - $item->harga_beli) * $stok;
if($selisih != 0){
$this->db->insert('journals',[
'tanggal'=>date('Y-m-d'),
'no_ref'=>'ADJ-'.date('YmdHis').'-'.$id,
'keterangan'=>'Penyesuaian harga '.$item->nama_barang,
'created_by' => $this->session->userdata('user_id')
]);
$jid = $this->db->insert_id();
if($selisih > 0){
// 🔥 naik → pendapatan
$this->db->insert_batch('journal_details',[
[
'journal_id'=>$jid,
'account_id'=>21,
'debit'=>$selisih,
'kredit'=>0
],
[
'journal_id'=>$jid,
'account_id'=>65, // ✅ BENAR
'debit'=>0,
'kredit'=>$selisih
]
]);
}else{
// 🔥 turun → beban
$this->db->insert_batch('journal_details',[
[
'journal_id'=>$jid,
'account_id'=>64, // ✅ BENAR
'debit'=>abs($selisih),
'kredit'=>0
],
[
'journal_id'=>$jid,
'account_id'=>21,
'debit'=>0,
'kredit'=>abs($selisih)
]
]);
}
}
}
$this->db->trans_complete();
log_activity(
'items',
'update',
'Update item: ' . $nama_barang,
'success'
);
$this->update_item_stok($id);
echo json_encode(['status'=>true,'message'=>'Berhasil update + adjustment']);
}
public function delete($id)
{
// cek item ada
$item = $this->db->get_where('items',['id'=>$id])->row();
if(!$item){
echo json_encode(['status'=>false,'message'=>'Item tidak ditemukan']);
return;
}
// cek ada transaksi stock
$cekStock = $this->db->where('item_id',$id)->count_all_results('stock_logs');
if($cekStock > 1){
echo json_encode([
'status'=>false,
'message'=>'Tidak bisa dihapus, sudah ada transaksi stok'
]);
return;
}
$this->db->trans_start();
// hapus item
$this->db->delete('stock_logs',['item_id'=>$id]);
$this->db->delete('items',['id'=>$id]);
$j = $this->db->get_where('journals', [
'ref_type'=>'new_items',
'ref_id'=>$id
])->result();
foreach($j as $row){
$this->db->delete('journal_details', ['journal_id'=>$row->id]);
$this->db->delete('journals', ['id'=>$row->id]);
}
$this->db->trans_complete();
if ($this->db->trans_status() === FALSE){
echo json_encode(['status'=>false,'message'=>'Gagal hapus']);
} else {
log_activity(
'items',
'delete',
'Hapus item ID: ' . $id,
'success'
);
echo json_encode(['status'=>true,'message'=>'Berhasil dihapus']);
}
}
public function posting()
{
$id = $this->input->post('id');
if(!$id){
echo json_encode([
'status' => false,
'message' => 'ID tidak valid'
]);
return;
}
// cek item ada
$item = $this->db->get_where('items', ['id' => $id])->row();
if(!$item){
echo json_encode([
'status' => false,
'message' => 'Item tidak ditemukan'
]);
return;
}
// cegah double posting
if($item->status == 'active'){
echo json_encode([
'status' => false,
'message' => 'Barang sudah diterima sebelumnya'
]);
return;
}
$this->db->trans_start();
// update status saja
$this->db->where('id', $id);
$this->db->update('items', [
'status' => 'active'
]);
$this->db->trans_complete();
if ($this->db->trans_status() === FALSE){
echo json_encode([
'status' => false,
'message' => 'Gagal memproses data'
]);
} else {
// log aktivitas
log_activity(
'items',
'posting',
'Barang diterima | ID: '.$id.' | Nama: '.$item->nama_barang,
'success'
);
echo json_encode([
'status' => true,
'message' => 'Barang sudah diterima dan stok siap digunakan'
]);
}
}
// =========================
// KELUAR BARANG (FIX TANPA stok column)
// =========================
public function keluarkan()
{
$account_biaya = (int)$this->input->post('account_biaya');
$tanggal_keluar = $this->input->post('tanggal_keluar');
$warehouse_id_keluar = $this->input->post('warehouse_id_keluar');
$barang_id = $this->input->post('barang_id');
$qty_keluar = (int)$this->input->post('qty_keluar');
$keterangan_keluar = $this->input->post('keterangan_keluar');
// =========================
// VALIDASI
// =========================
if(!$barang_id || !$warehouse_id_keluar || $qty_keluar <= 0){
echo json_encode(['status'=>false,'message'=>'Data tidak valid']);
return;
}
// =========================
// CEK ITEM
// =========================
$item = $this->db->get_where('items',['id'=>$barang_id])->row();
if(!$item){
echo json_encode(['status'=>false,'message'=>'Item tidak ditemukan']);
return;
}
// =========================
// HITUNG STOK
// =========================
$stok = $this->db->select('
COALESCE(SUM(qty * IF(tipe="masuk",1,-1)),0) as stok
')
->where('item_id',$barang_id)
->where('warehouse_id',$warehouse_id_keluar)
->get('stock_logs')
->row()->stok;
if($stok < $qty_keluar){
echo json_encode(['status'=>false,'message'=>'Stok tidak cukup']);
return;
}
$nilai = $qty_keluar * (float)$item->harga_beli;
// =========================
// TRANSACTION
// =========================
$this->db->trans_start();
// log keluar
$this->db->insert('stock_logs',[
'item_id'=>$barang_id,
'warehouse_id'=>$warehouse_id_keluar,
'qty'=>$qty_keluar,
'tipe'=>'keluar',
'keterangan'=>$keterangan_keluar
]);
$stock_log_id = $this->db->insert_id();
// jurnal
$this->db->insert('journals',[
'tanggal'=>$tanggal_keluar,
'no_ref'=>'GOUT-'.date('YmdHis').'-'.$barang_id,
'keterangan'=>'Pengeluaran Barang '.$item->nama_barang . ' ( ' . $keterangan_keluar . ' )',
'ref_type' =>'stock_logs',
'ref_id' => $stock_log_id,
'created_by' => $this->session->userdata('user_id')
]);
$jid = $this->db->insert_id();
// Debit biaya / HPP
$this->db->insert('journal_details',[
'journal_id'=>$jid,
'account_id'=>$account_biaya,
'debit'=>$nilai,
'kredit'=>0
]);
// Kredit persediaan
$this->db->insert('journal_details',[
'journal_id'=>$jid,
'account_id'=>21,
'debit'=>0,
'kredit'=>$nilai
]);
$this->db->trans_complete();
// =========================
// CEK TRANSACTION
// =========================
if (!$this->db->trans_status()) {
echo json_encode(['status'=>false,'message'=>'Gagal proses']);
return;
}
log_activity(
'items',
'stock_out',
'Keluar barang: ' . $item->nama_barang . ' qty: ' . $qty_keluar . ' ( ' . $keterangan_keluar . ' )',
'success'
);
$this->update_item_stok($barang_id);
echo json_encode(['status'=>true,'message'=>'Barang berhasil dikeluarkan']);
}
public function list()
{
echo json_encode(
$this->db->get('warehouses')->result()
);
}
public function adjust()
{
$item_id = $this->input->post('item_id');
$qty = (int)$this->input->post('qty');
$warehouse_id = $this->input->post('warehouse_id');
$ket = $this->input->post('keterangan');
if($qty == 0){
echo json_encode(['status'=>false,'message'=>'Qty tidak boleh 0']);
return;
}
$item = $this->db->get_where('items',['id'=>$item_id])->row();
$nilai = abs($qty) * $item->harga_beli;
$this->db->trans_start();
// 🔥 stock log
$this->db->insert('stock_logs',[
'item_id'=>$item_id,
'warehouse_id'=>$warehouse_id,
'qty'=>abs($qty),
'tipe'=> $qty > 0 ? 'masuk' : 'keluar',
'keterangan'=>'Adjustment: '.$ket
]);
// 🔥 jurnal
$this->db->insert('journals',[
'tanggal'=>date('Y-m-d'),
'no_ref'=>'ADJ-'.date('Ymd').'-'.$item_id,
'keterangan'=>'Penyesuaian '.$item->nama_barang,
'created_by' => $this->session->userdata('user_id')
]);
$jid = $this->db->insert_id();
if($qty > 0){
// stok naik
$this->db->insert_batch('journal_details',[
[
'journal_id'=>$jid,
'account_id'=>21, // persediaan
'debit'=>$nilai,
'kredit'=>0
],
[
'journal_id'=>$jid,
'account_id'=>65, // modal / selisih
'debit'=>0,
'kredit'=>$nilai
]
]);
}else{
// stok turun
$this->db->insert_batch('journal_details',[
[
'journal_id'=>$jid,
'account_id'=>64,
'debit'=>$nilai,
'kredit'=>0
],
[
'journal_id'=>$jid,
'account_id'=>21,
'debit'=>0,
'kredit'=>$nilai
]
]);
}
$this->db->trans_complete();
log_activity(
'items',
'adjust',
'Adjustment item ID: ' . $item_id . ' qty: ' . $qty,
'success'
);
$this->update_item_stok($item_id);
echo json_encode(['status'=>true,'message'=>'Adjustment berhasil']);
}
private function update_item_stok($item_id)
{
$stok = $this->db->query("
SELECT COALESCE(SUM(qty * IF(tipe='masuk',1,-1)),0) as stok
FROM stock_logs
WHERE item_id = ?
", [$item_id])->row()->stok;
$this->db->where('id', $item_id)
->update('items', [
'stok' => $stok
]);
}
public function get_items_by_wh_id($warehouse_id)
{
$this->db->select('
items.id,
items.nama_barang,
items.harga_jual,
items.kode_detail,
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
AND stock_logs.warehouse_id = '.$this->db->escape($warehouse_id),
'left');
$this->db->group_by('items.id');
// ✅ INI KUNCINYA
$this->db->having('stok >', 0);
$data = $this->db->get()->result();
echo json_encode($data);
}
}
+333
View File
@@ -0,0 +1,333 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class Jurnal 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" => "jurnal"];
$this->load->view('partials/header', $data);
$this->load->view('jurnal/layout');
$this->load->view('partials/footer');
}
// =========================
// GET DATA DATATABLE (SUDAH ADA TOTAL)
// =========================
public function get_data()
{
$draw = $_POST['draw'];
$start = $_POST['start'];
$length = $_POST['length'];
$search = $_POST['search']['value'];
// ================= TOTAL ALL
$totalAll = $this->db->count_all('journals');
// ================= QUERY FILTERED (HITUNG)
$this->db->from('journals');
if(!empty($search)){
$this->db->group_start();
$this->db->like('no_ref', $search);
$this->db->or_like('keterangan', $search);
$this->db->or_like('tanggal', $search);
$this->db->group_end();
}
$filtered = $this->db->count_all_results();
// ================= QUERY DATA (AMBIL DATA)
$this->db->select('
journals.id,
journals.tanggal,
journals.no_ref,
journals.keterangan,
COALESCE(SUM(journal_details.debit),0) as total_debit,
COALESCE(SUM(journal_details.kredit),0) as total_kredit
');
$this->db->from('journals');
$this->db->join('journal_details','journal_details.journal_id = journals.id','LEFT');
if(!empty($search)){
$this->db->group_start();
$this->db->like('journals.no_ref', $search);
$this->db->or_like('journals.keterangan', $search);
$this->db->or_like('journals.tanggal', $search);
$this->db->group_end();
}
$this->db->group_by('journals.id');
$this->db->order_by('journals.tanggal','DESC');
$this->db->order_by('journals.created_at','DESC');
$this->db->limit($length, $start);
$query = $this->db->get()->result();
// ================= FORMAT
$data = [];
$no = $start;
foreach ($query as $row) {
$no++;
if($this->session->userdata('role') != 'Admin'){
$aksi = '
<button class="btn btn-sm btn-info btn-detail" data-id="'.$row->id.'">Detail</button>
';
}
else {
$aksi = '
<button class="btn btn-sm btn-info btn-detail" data-id="'.$row->id.'">Detail</button>
<button class="btn btn-sm btn-danger btn-delete" data-id="'.$row->id.'">Delete</button>
';
}
$data[] = [
$no,
$row->tanggal,
$row->no_ref,
$row->keterangan,
number_format($row->total_debit,0,',','.'),
number_format($row->total_kredit,0,',','.'),
$aksi
];
}
echo json_encode([
"draw" => $draw,
"recordsTotal" => $totalAll,
"recordsFiltered" => $filtered,
"data" => $data
]);
}
// =========================
// GET ACCOUNTS
// =========================
public function get_accounts()
{
$data = $this->db->order_by('kode_akun','ASC')->get('accounts')->result();
echo json_encode($data);
}
// =========================
// SAVE
// =========================
public function save()
{
$tanggal = $this->input->post('tanggal');
$no_ref = 'JR-' . date('YmdHis');
$keterangan = $this->input->post('keterangan');
$accounts = $this->input->post('account_id');
$debit = $this->input->post('debit');
$kredit = $this->input->post('kredit');
if (array_sum($debit) != array_sum($kredit)) {
echo json_encode([
'status'=>false,
'message'=>'Debit & Kredit tidak balance!'
]);
return;
}
$this->db->trans_start();
$this->db->insert('journals', [
'tanggal' => $tanggal,
'no_ref' => $no_ref,
'keterangan' => $keterangan,
'created_by' => $this->session->userdata('user_id')
]);
$journal_id = $this->db->insert_id();
for ($i=0; $i<count($accounts); $i++) {
if ($accounts[$i] == '') continue;
$this->db->insert('journal_details', [
'journal_id' => $journal_id,
'account_id' => $accounts[$i],
'debit' => $debit[$i] ?: 0,
'kredit' => $kredit[$i] ?: 0
]);
}
$this->db->trans_complete();
if ($this->db->trans_status() === FALSE) {
echo json_encode([
'status'=>false,
'message'=>'Gagal simpan jurnal'
]);
return;
}
// ================= LOG ACTIVITY =================
log_activity(
'Jurnal',
'create',
'Membuat jurnal No Ref: '.$no_ref,
'success'
);
echo json_encode([
'status'=>true,
'message'=>'Jurnal berhasil disimpan'
]);
}
// =========================
// DETAIL
// =========================
public function detail($id)
{
$header = $this->db->get_where('journals',['id'=>$id])->row();
$detail = $this->db
->select('accounts.kode_akun, accounts.nama_akun, journal_details.*')
->from('journal_details')
->join('accounts','accounts.id = journal_details.account_id')
->where('journal_id',$id)
->get()->result();
// print_r($header);
$userid = $header->created_by;
$user = $this->db
->select('nama')
->where('id', $userid)
->get('users')
->row();
echo json_encode([
'header'=>$header,
'detail'=>$detail,
'user' =>$user
]);
}
// =========================
// DELETE
// =========================
public function delete($id)
{
$journal = $this->db->get_where('journals', ['id' => $id])->row();
// VALIDASI
if (!$journal) {
echo json_encode([
'status' => false,
'message' => 'Jurnal tidak ditemukan'
]);
return;
}
$this->db->trans_start();
// =========================
// HANDLE STOCK LOGS
// =========================
if ($journal->ref_type == 'stock_logs') {
$stock = $this->db
->select('item_id, warehouse_id')
->where('id', $journal->ref_id)
->get('stock_logs')
->row();
if ($stock) {
// hapus log stok
$this->db->delete('stock_logs', ['id' => $journal->ref_id]);
// update stok ulang (pakai item_id saja biar gak ganggu sistem lama)
$this->update_item_stok($stock->item_id);
}
}
// =========================
// DELETE JOURNAL
// =========================
$this->db->delete('journal_details', ['journal_id' => $id]);
$this->db->delete('journals', ['id' => $id]);
$this->db->trans_complete();
// =========================
// VALIDASI TRANSAKSI
// =========================
if (!$this->db->trans_status()) {
echo json_encode([
'status' => false,
'message' => 'Gagal hapus jurnal'
]);
return;
}
// ================= LOG ACTIVITY =================
log_activity(
'Jurnal',
'delete',
'Menghapus jurnal no_ref: ' . $journal->no_ref . ' ket: ' . $journal->keterangan,
'success'
);
echo json_encode([
'status' => true, // ✅ FIX
'message' => 'Jurnal berhasil dihapus',
'journal' => $journal->ref_type
]);
}
public function get_base_jurnal()
{
echo json_encode(
$this->db->order_by('kode','ASC')->get('base_journal')->result()
);
}
public function get_base_jurnal_detail($id)
{
echo json_encode(
$this->db->where('base_journal_id',$id)
->get('base_journal_detail')
->result()
);
}
private function update_item_stok($item_id)
{
$stok = $this->db->query("
SELECT COALESCE(SUM(qty * IF(tipe='masuk',1,-1)),0) as stok
FROM stock_logs
WHERE item_id = ?
", [$item_id])->row()->stok;
$this->db->where('id', $item_id)
->update('items', [
'stok' => $stok
]);
}
}
+155
View File
@@ -0,0 +1,155 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class Kodebarang extends CI_Controller {
public function __construct()
{
parent::__construct();
if (!$this->session->userdata('logged_in')) {
redirect('auth');
}
}
public function index()
{
$data = ["active_menu" => "kode_barang"];
$this->load->view('partials/header', $data);
$this->load->view('kode_barang/layout');
$this->load->view('partials/footer');
}
// =========================
// GET DATA (DATATABLE)
// =========================
public function get_data()
{
$data = $this->db->get('kode_barang')->result();
$result = [];
$no = 1;
foreach ($data as $row) {
$result[] = [
$no++,
$row->kode_barang,
$row->nama,
$row->limit_stock,
'
<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>
'
];
}
echo json_encode(["data"=>$result]);
}
// =========================
// DETAIL
// =========================
public function detail($id)
{
echo json_encode(
$this->db->get_where('kode_barang',['id'=>$id])->row()
);
}
// =========================
// SAVE
// =========================
public function save()
{
$nama = $this->input->post('nama');
$kode = $this->input->post('kode');
$limit_stock = $this->input->post('limit_stock');
if(!$nama){
echo json_encode(['status'=>false,'message'=>'Nama wajib diisi']);
return;
}
$this->db->insert('kode_barang', [
'kode_barang' => $kode,
'nama' => $nama,
'limit_stock' => $limit_stock
]);
log_activity(
'kode_barang',
'create',
'Menambahkan kode barang: ' . $nama,
'success'
);
echo json_encode(['status'=>true,'message'=>'Gudang berhasil ditambahkan']);
}
// =========================
// UPDATE
// =========================
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');
if(!$nama){
echo json_encode(['status'=>false,'message'=>'Nama wajib diisi']);
return;
}
$this->db->where('id',$id)->update('kode_barang', [
'kode_barang' => $kode,
'nama' => $nama,
'limit_stock' => $limit_stock
]);
log_activity(
'kode_barang',
'update',
'Update kode barang ID: ' . $id . ' nama: ' . $nama,
'success'
);
echo json_encode(['status'=>true,'message'=>'Gudang berhasil diupdate']);
}
// =========================
// DELETE
// =========================
public function delete($id)
{
// optional: cek relasi ke items
$cek = $this->db->get_where('items',['warehouse_id'=>$id])->num_rows();
if($cek > 0){
echo json_encode([
'status'=>false,
'message'=>'Gudang tidak bisa dihapus karena masih dipakai item'
]);
return;
}
$this->db->delete('kode_barang',['id'=>$id]);
log_activity(
'kode_barang',
'delete',
'Hapus gudang ID: ' . $id,
'success'
);
echo json_encode(['status'=>true,'message'=>'Gudang berhasil dihapus']);
}
public function list()
{
echo json_encode(
$this->db->get('kode_barang')->result()
);
}
}
+88
View File
@@ -0,0 +1,88 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class Labarugi 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');
}
public function get_data()
{
$tanggal_dari = $this->input->get('tanggal_dari');
$tanggal_sampai = $this->input->get('tanggal_sampai');
$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();
$revenue = 0;
$expense = 0;
foreach ($rows as $r) {
if ($r->tipe == 'revenue') {
$saldo = $r->kredit - $r->debit;
$revenue += $saldo;
}
if ($r->tipe == 'expense') {
$saldo = $r->debit - $r->kredit;
$expense += $saldo;
}
}
echo json_encode([
'data' => $rows,
'revenue' => $revenue,
'expense' => $expense,
'laba' => $revenue - $expense
]);
}
}
+284
View File
@@ -0,0 +1,284 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class Leaverequests extends CI_Controller {
public function __construct()
{
parent::__construct();
$this->load->database();
}
// ==========================================
// PAGE
// ==========================================
public function index()
{
$data = [
"active_menu" => "leave_requests"
];
$data['employees'] = $this->db
->where('is_active',1)
->order_by('full_name','ASC')
->get('k_employees')
->result();
$this->load->view('partials/header', $data);
$this->load->view('employees/leave_requests', $data);
$this->load->view('partials/footer');
}
// ==========================================
// DATATABLE
// ==========================================
public function get_data()
{
$this->db->select('
lr.*,
e.full_name,
e.fingerprint_user_id
');
$this->db->from('k_leave_requests lr');
$this->db->join(
'k_employees e',
'e.id=lr.employee_id',
'left'
);
$this->db->order_by('lr.id','DESC');
$list = $this->db->get()->result();
$data = [];
$no = 1;
foreach($list as $r){
// =====================================
// LEAVE TYPE
// =====================================
$leave_type = '-';
if($r->leave_type == 'annual'){
$leave_type = 'Cuti Tahunan';
}
if($r->leave_type == 'sick'){
$leave_type = 'Sakit';
}
if($r->leave_type == 'permit'){
$leave_type = 'Izin';
}
if($r->leave_type == 'maternity'){
$leave_type = 'Cuti Melahirkan';
}
if($r->leave_type == 'unpaid'){
$leave_type = 'Cuti Tidak Dibayar';
}
// =====================================
// STATUS
// =====================================
if($r->approval_status == 'approved'){
$status = '
<span class="badge bg-success">
Disetujui
</span>
';
} elseif($r->approval_status == 'rejected'){
$status = '
<span class="badge bg-danger">
Ditolak
</span>
';
} else {
$status = '
<span class="badge bg-warning text-dark">
Pending
</span>
';
}
$data[] = [
$no++,
$r->fingerprint_user_id,
$r->full_name,
$leave_type,
$r->start_date,
$r->end_date,
$r->total_days . ' Hari',
$status,
'
<button class="btn btn-warning btn-sm btn-edit"
data-id="'.$r->id.'">
Edit
</button>
<button class="btn btn-danger btn-sm btn-delete"
data-id="'.$r->id.'">
Delete
</button>
'
];
}
echo json_encode([
"data" => $data
]);
}
// ==========================================
// DETAIL
// ==========================================
public function detail($id)
{
$data = $this->db
->get_where(
'k_leave_requests',
['id'=>$id]
)
->row();
echo json_encode($data);
}
// ==========================================
// SAVE
// ==========================================
public function save()
{
try{
$start_date = $this->input->post('start_date');
$end_date = $this->input->post('end_date');
$total_days = (
strtotime($end_date)
-
strtotime($start_date)
) / 86400 + 1;
$data = [
'employee_id' => $this->input->post('employee_id'),
'leave_type' => $this->input->post('leave_type'),
'start_date' => $start_date,
'end_date' => $end_date,
'total_days' => $total_days,
'reason' => $this->input->post('reason'),
'approval_status' => $this->input->post('approval_status')
];
$this->db->insert(
'k_leave_requests',
$data
);
echo json_encode([
'status' => true,
'message'=> 'Leave request berhasil ditambahkan'
]);
} catch(Exception $e){
echo json_encode([
'status' => false,
'message'=> $e->getMessage()
]);
}
}
// ==========================================
// UPDATE
// ==========================================
public function update()
{
try{
$id = $this->input->post('id');
$start_date = $this->input->post('start_date');
$end_date = $this->input->post('end_date');
$total_days = (
strtotime($end_date)
-
strtotime($start_date)
) / 86400 + 1;
$data = [
'employee_id' => $this->input->post('employee_id'),
'leave_type' => $this->input->post('leave_type'),
'start_date' => $start_date,
'end_date' => $end_date,
'total_days' => $total_days,
'reason' => $this->input->post('reason'),
'approval_status' => $this->input->post('approval_status')
];
$this->db->where('id',$id);
$this->db->update(
'k_leave_requests',
$data
);
echo json_encode([
'status' => true,
'message'=> 'Leave request berhasil diupdate'
]);
} catch(Exception $e){
echo json_encode([
'status' => false,
'message'=> $e->getMessage()
]);
}
}
// ==========================================
// DELETE
// ==========================================
public function delete($id)
{
$this->db->delete(
'k_leave_requests',
['id'=>$id]
);
echo json_encode([
'status' => true,
'message'=> 'Leave request berhasil dihapus'
]);
}
}
+145
View File
@@ -0,0 +1,145 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class Lokasiasset extends CI_Controller {
public function __construct()
{
parent::__construct();
if (!$this->session->userdata('logged_in')) {
redirect('auth');
}
}
public function index()
{
$data = ["active_menu" => "lokasiasset"];
$this->load->view('partials/header', $data);
$this->load->view('lokasi_asset/layout');
$this->load->view('partials/footer');
}
// =========================
// GET DATA (DATATABLE)
// =========================
public function get_data()
{
$data = $this->db->get('lokasi_asset')->result();
$result = [];
$no = 1;
foreach ($data as $row) {
$result[] = [
$no++,
$row->nama,
'
<button class="btn btn-sm btn-secondary btn-editgudang" data-id="'.$row->id.'">Edit</button>
<button class="btn btn-sm btn-danger btn-delete" data-id="'.$row->id.'">Delete</button>
'
];
}
echo json_encode(["data"=>$result]);
}
// =========================
// DETAIL
// =========================
public function detail($id)
{
echo json_encode(
$this->db->get_where('lokasi_asset',['id'=>$id])->row()
);
}
// =========================
// SAVE
// =========================
public function save()
{
$nama = $this->input->post('nama');
if(!$nama){
echo json_encode(['status'=>false,'message'=>'Nama wajib diisi']);
return;
}
$this->db->insert('lokasi_asset', [
'nama' => $nama
]);
log_activity(
'kode_barang',
'create',
'Tambah gudang '.$nama,
'success'
);
echo json_encode(['status'=>true,'message'=>'Gudang berhasil ditambahkan']);
}
// =========================
// UPDATE
// =========================
public function update()
{
$id = $this->input->post('id');
$nama = $this->input->post('nama');
if(!$nama){
echo json_encode(['status'=>false,'message'=>'Nama wajib diisi']);
return;
}
$this->db->where('id',$id)->update('lokasi_asset', [
'nama' => $nama
]);
log_activity(
'kode_barang',
'update',
'Update gudang '.$nama,
'success'
);
echo json_encode(['status'=>true,'message'=>'Gudang berhasil diupdate']);
}
// =========================
// DELETE
// =========================
public function delete($id)
{
// optional: cek relasi ke items
$cek = $this->db->get_where('assets',['lokasi_asset_id'=>$id])->num_rows();
if($cek > 0){
echo json_encode([
'status'=>false,
'message'=>'Gudang tidak bisa dihapus karena masih dipakai item'
]);
return;
}
$this->db->delete('lokasi_asset',['id'=>$id]);
log_activity(
'kode_barang',
'delete',
'Delete gudang '.$nama,
'success'
);
echo json_encode(['status'=>true,'message'=>'Gudang berhasil dihapus']);
}
public function list()
{
echo json_encode(
$this->db->get('lokasi_asset')->result()
);
}
}
+170
View File
@@ -0,0 +1,170 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class Neraca 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;
}
// =========================
// GET DATA NERACA
// =========================
public function get_data()
{
$tanggal_dari = $this->input->get('tanggal_dari');
$tanggal_sampai = $this->input->get('tanggal_sampai');
$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;
}
}
echo json_encode([
'activa' => $activa,
'pasiva' => $pasiva,
'total_activa' => $total_activa,
'total_pasiva' => $total_pasiva,
'laba' => $laba,
'selisih' => $total_activa - $total_pasiva
]);
}
}
+112
View File
@@ -0,0 +1,112 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class Neracasaldo 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');
}
// =========================
// 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('
accounts.kode_akun,
accounts.nama_akun,
accounts.tipe,
accounts.posisi,
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');
$this->db->group_by('accounts.id');
$this->db->order_by('accounts.kode_akun','ASC');
$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;
}
// ================= 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);
}
}
echo json_encode($rows);
}
}
+598
View File
@@ -0,0 +1,598 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class Payroll extends CI_Controller {
public function __construct()
{
parent::__construct();
$this->load->database();
date_default_timezone_set('Asia/Jakarta');
}
// =====================================================
// INDEX
// =====================================================
public function index()
{
$data = [
'active_menu' => 'payroll'
];
$this->load->view('partials/header',$data);
$this->load->view('employees/payroll');
$this->load->view('partials/footer');
}
// =====================================================
// GET DATA
// =====================================================
public function get_data()
{
$month = $this->input->post('month');
$this->db->select("
p.*,
e.full_name,
pp.period_name,
pp.start_date,
pp.end_date
");
$this->db->from('k_payrolls p');
$this->db->join(
'k_employees e',
'e.id = p.employee_id'
);
$this->db->join(
'k_payroll_periods pp',
'pp.id = p.payroll_period_id'
);
if($month){
$this->db->where(
'DATE_FORMAT(pp.start_date,"%Y-%m")',
$month
);
}
$this->db->order_by(
'e.full_name',
'ASC'
);
$rows = $this->db->get()->result();
$data = [];
$no = 1;
foreach($rows as $row){
$pendapatan =
(
$row->allowance_amount
+
$row->bonus_amount
+
$row->overtime_amount
);
$potongan =
(
$row->deduction_amount
+
$row->bpjs_kesehatan_amount
+
$row->bpjs_ketenagakerjaan_amount
+
$row->tax_amount
+
$row->attendance_cut_amount
);
$status = '';
if($row->payment_status == 'paid'){
$status = '
<span class="badge bg-success">
Paid
</span>
';
} else {
$status = '
<span class="badge bg-warning text-dark">
Draft
</span>
';
}
$data[] = [
$no++,
$row->full_name,
'Rp '.number_format(
$row->basic_salary,
0,
',',
'.'
),
'Rp '.number_format(
$pendapatan,
0,
',',
'.'
),
'Rp '.number_format(
$potongan,
0,
',',
'.'
),
'Rp '.number_format(
$row->total_salary,
0,
',',
'.'
),
$status,
'
<button
class="btn btn-sm btn-warning btn-edit"
data-id="'.$row->id.'">
<i class="fa fa-edit"></i>
Edit
</button>
'
];
}
echo json_encode([
'data' => $data
]);
}
// =====================================================
// DETAIL
// =====================================================
public function detail($id)
{
$this->db->select("
p.*,
e.full_name
");
$this->db->from('k_payrolls p');
$this->db->join(
'k_employees e',
'e.id = p.employee_id'
);
$this->db->where(
'p.id',
$id
);
$payroll = $this->db->get()->row();
if(!$payroll){
echo json_encode([
'status' => false,
'message' => 'Payroll tidak ditemukan'
]);
return;
}
$items = $this->db
->where('payroll_id',$id)
->order_by('id','ASC')
->get('k_payroll_items')
->result();
echo json_encode([
'status' => true,
'payroll' => $payroll,
'employee' => [
'full_name' => $payroll->full_name
],
'items' => $items
]);
}
// =====================================================
// SAVE ITEMS
// =====================================================
public function save_items()
{
$payrollId = $this->input->post('payroll_id');
$items = $this->input->post('items');
$payroll = $this->db
->where('id',$payrollId)
->get('k_payrolls')
->row();
if(!$payroll){
echo json_encode([
'status' => false,
'message' => 'Payroll tidak ditemukan'
]);
return;
}
// =========================================
// DELETE OLD ITEMS
// =========================================
$this->db
->where('payroll_id',$payrollId)
->delete('k_payroll_items');
$allowance = 0;
$bonus = 0;
$overtime = 0;
$deduction = 0;
$bpjs = 0;
$tax = 0;
if($items){
foreach($items as $item){
$amount = (float)$item['amount'];
$insert = [
'payroll_id' => $payrollId,
'item_type' => $item['item_type'],
'item_name' => $item['item_name'],
'amount' => $amount
];
$this->db->insert(
'k_payroll_items',
$insert
);
switch($item['item_type']){
case 'allowance':
$allowance += $amount;
break;
case 'bonus':
$bonus += $amount;
break;
case 'overtime':
$overtime += $amount;
break;
case 'deduction':
$deduction += $amount;
break;
case 'bpjs':
$bpjs += $amount;
break;
case 'tax':
$tax += $amount;
break;
}
}
}
// =========================================
// TOTAL
// =========================================
$totalSalary =
(
$payroll->basic_salary
+
$allowance
+
$bonus
+
$overtime
)
-
(
$deduction
+
$bpjs
+
$tax
+
$payroll->attendance_cut_amount
);
// =========================================
// UPDATE PAYROLL
// =========================================
$this->db
->where('id',$payrollId)
->update(
'k_payrolls',
[
'allowance_amount' => $allowance,
'bonus_amount' => $bonus,
'overtime_amount' => $overtime,
'deduction_amount' => $deduction,
'bpjs_kesehatan_amount' => $bpjs,
'tax_amount' => $tax,
'total_salary' => $totalSalary
]
);
echo json_encode([
'status' => true,
'message' => 'Payroll berhasil disimpan'
]);
}
// =====================================================
// GENERATE PAYROLL
// =====================================================
public function generate_payroll()
{
$month = $this->input->post('month');
if(!$month){
echo json_encode([
'status' => false,
'message' => 'Bulan wajib dipilih'
]);
return;
}
$startDate = date(
'Y-m-01',
strtotime($month)
);
$endDate = date(
'Y-m-t',
strtotime($month)
);
// =========================================
// PERIOD
// =========================================
$period = $this->db
->where('start_date',$startDate)
->where('end_date',$endDate)
->get('k_payroll_periods')
->row();
if(!$period){
$this->db->insert(
'k_payroll_periods',
[
'period_name' => date(
'F Y',
strtotime($startDate)
),
'start_date' => $startDate,
'end_date' => $endDate,
'status' => 'draft',
'generated_at' => date('Y-m-d H:i:s')
]
);
$periodId = $this->db->insert_id();
} else {
$periodId = $period->id;
}
// =========================================
// EMPLOYEES
// =========================================
$employees = $this->db
->where('is_active',1)
->get('k_employees')
->result();
foreach($employees as $employee){
// =====================================
// ATTENDANCE
// =====================================
$attendance = $this->db
->select("
COUNT(*) as total_work_days,
SUM(attendance_status='present')
as total_present,
SUM(attendance_status='alpha')
as total_alpha,
SUM(overtime_hours)
as total_overtime_hours
")
->where('employee_id',$employee->id)
->where('attendance_date >=',$startDate)
->where('attendance_date <=',$endDate)
->get('k_attendances')
->row();
$dailySalary =
$employee->basic_salary / 30;
$alphaCut =
(
$attendance->total_alpha
* $dailySalary
);
$overtimeRate = 25000;
$overtimeAmount =
(
$attendance->total_overtime_hours
* $overtimeRate
);
$totalSalary =
(
$employee->basic_salary
+ $overtimeAmount
)
- $alphaCut;
$exist = $this->db
->where(
'payroll_period_id',
$periodId
)
->where(
'employee_id',
$employee->id
)
->get('k_payrolls')
->row();
$data = [
'payroll_period_id' => $periodId,
'employee_id' => $employee->id,
'basic_salary' => $employee->basic_salary,
'total_work_days' => (
$attendance->total_work_days ?? 0
),
'total_present' => (
$attendance->total_present ?? 0
),
'total_alpha' => (
$attendance->total_alpha ?? 0
),
'total_overtime_hours' => (
$attendance->total_overtime_hours ?? 0
),
'attendance_cut_amount' => $alphaCut,
'overtime_amount' => $overtimeAmount,
'total_salary' => $totalSalary
];
if($exist){
$this->db
->where('id',$exist->id)
->update(
'k_payrolls',
$data
);
} else {
$this->db->insert(
'k_payrolls',
$data
);
$payrollId = $this->db->insert_id();
// =================================
// AUTO ITEM OVERTIME
// =================================
if($overtimeAmount > 0){
$this->db->insert(
'k_payroll_items',
[
'payroll_id' => $payrollId,
'item_type' => 'overtime',
'item_name' => 'Lembur',
'amount' => $overtimeAmount
]
);
}
// =================================
// AUTO ITEM ALPHA
// =================================
if($alphaCut > 0){
$this->db->insert(
'k_payroll_items',
[
'payroll_id' => $payrollId,
'item_type' => 'deduction',
'item_name' => 'Potongan Alpha',
'amount' => $alphaCut
]
);
}
}
}
echo json_encode([
'status' => true,
'message' => 'Payroll berhasil digenerate'
]);
}
}
+142
View File
@@ -0,0 +1,142 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class Pesan extends CI_Controller {
public function __construct()
{
parent::__construct();
$this->load->database();
// Cek apakah user sudah login
if (!$this->session->userdata('logged_in')) {
redirect('login');
}
}
public function index()
{
if (!check_permission('pesan', 'can_view')) {
show_error('Tidak memiliki izin untuk melihat detail.', 403);
}
$data = [
"active_menu" => "pesan"
];
$this->load->view('partials/header', $data);
$this->load->view('pesan/layout');
$this->load->view('partials/footer');
}
// =========================================================
// DATATABLE SERVERSIDE
// =========================================================
public function get_data()
{
if (!check_permission('pesan', 'can_view')) {
show_error('Tidak memiliki izin untuk melihat detail.', 403);
}
$table = 'notifikasi';
$column_order = array(null, 'created_at', 'type', 'nomor_whatsapp', 'email', 'pesan', 'status');
$column_search = array('pesan', 'nomor_whatsapp', 'email', 'status', 'type');
$order = array('created_at' => 'DESC');
$this->db->from($table);
$search_value = !empty($_POST['search']['value']) ? $_POST['search']['value'] : '';
// SEARCH
if ($search_value != '') {
$this->db->group_start();
foreach ($column_search as $i => $col_name) {
if ($i === 0) {
$this->db->like($col_name, $search_value);
} else {
$this->db->or_like($col_name, $search_value);
}
}
$this->db->group_end();
}
// ORDER
if (isset($_POST['order'])) {
$col_index = $_POST['order'][0]['column'];
$dir = $_POST['order'][0]['dir'];
if (isset($column_order[$col_index])) {
$this->db->order_by($column_order[$col_index], $dir);
}
} else {
$this->db->order_by(key($order), $order[key($order)]);
}
// PAGINATION
if ($_POST['length'] != -1) {
$this->db->limit($_POST['length'], $_POST['start']);
}
$query = $this->db->get();
$data = [];
$no = $_POST['start'];
foreach ($query->result() as $row) {
$no++;
// Badge for 'status'
$statusBadge = '';
switch ($row->status) {
case 'terkirim': $statusBadge = '<span class="badge bg-success">Terkirim</span>'; break;
case 'proses': $statusBadge = '<span class="badge bg-info">Proses</span>'; break;
case 'pending': $statusBadge = '<span class="badge bg-warning">Pending</span>'; break;
case 'dibaca': $statusBadge = '<span class="badge bg-primary">Dibaca</span>'; break;
default: $statusBadge = '<span class="badge bg-secondary">' . htmlspecialchars($row->status) . '</span>'; break;
}
// Type display
$typeDisplay = '';
switch ($row->type) {
case 'wa_official': $typeDisplay = 'WhatsApp Official'; break;
case 'wa_unofficial': $typeDisplay = 'WhatsApp Unofficial'; break;
case 'email': $typeDisplay = 'Email'; break;
default: $typeDisplay = htmlspecialchars($row->type); break;
}
$penerima = !empty($row->nomor_whatsapp) ? htmlspecialchars($row->nomor_whatsapp) : htmlspecialchars($row->email);
$data[] = [
$no,
date('d-m-Y H:i:s', strtotime($row->created_at)),
$typeDisplay,
$penerima,
'<div class="truncate-text">' . htmlspecialchars($row->pesan) . '</div>',
$statusBadge
];
}
// COUNT TOTAL RECORDS
$this->db->from($table);
if ($search_value != '') {
$this->db->group_start();
foreach ($column_search as $i => $col_name) {
if ($i === 0) {
$this->db->like($col_name, $search_value);
} else {
$this->db->or_like($col_name, $search_value);
}
}
$this->db->group_end();
}
$recordsFiltered = $this->db->count_all_results();
$output = [
"draw" => intval($_POST['draw']),
"recordsTotal" => $this->db->count_all($table),
"recordsFiltered" => $recordsFiltered,
"data" => $data
];
echo json_encode($output);
}
}
+50
View File
@@ -0,0 +1,50 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class Qr extends CI_Controller {
public function __construct()
{
parent::__construct();
// Load library QR Code manual
require_once APPPATH . 'libraries/phpqrcode/qrlib.php';
// Cek apakah user sudah login
if (!$this->session->userdata('logged_in')) {
redirect('login');
}
}
// Tampilkan QR (dengan margin kecil)
public function show($text = "contoh")
{
header("Content-Type: image/png");
// Param: text, outfile, ECC, size, margin
QRcode::png($text, null, QR_ECLEVEL_H, 8, 1);
}
// Download QR Code
public function download($text = "contoh")
{
$fileName = "QR_" . time() . ".png";
$tempDir = FCPATH . "downloads/";
if (!is_dir($tempDir)) {
mkdir($tempDir, 0777, true);
}
$filePath = $tempDir . $fileName;
// Generate QR ke file (margin diperkecil)
QRcode::png($text, $filePath, QR_ECLEVEL_H, 8, 1);
// Force download
header('Content-Type: image/png');
header('Content-Disposition: attachment; filename="'.$fileName.'"');
readfile($filePath);
// Hapus setelah download
unlink($filePath);
}
}
+644
View File
@@ -0,0 +1,644 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class Scheduler extends CI_Controller {
public function __construct()
{
parent::__construct();
$this->load->database();
date_default_timezone_set('Asia/Jakarta');
}
// =====================================================
// PROCESS ATTENDANCE LOG
// =====================================================
public function process_attendance()
{
echo "PROCESS ATTENDANCE START\n";
// =============================================
// GET PENDING LOG
// =============================================
$logs = $this->db
->where('process_status','pending')
->order_by('check_time','ASC')
->limit(1000)
->get('k_attendance_logs')
->result();
foreach($logs as $log){
try{
// =====================================
// GET EMPLOYEE
// =====================================
$employee = $this->db
->where(
'fingerprint_user_id',
$log->user_id
)
->where('is_active',1)
->get('k_employees')
->row();
if(!$employee){
$this->updateLogStatus(
$log->id,
'failed'
);
echo "FAILED EMPLOYEE : {$log->user_id}\n";
continue;
}
// =====================================
// UPDATE EMPLOYEE ID
// =====================================
$this->db
->where('id',$log->id)
->update(
'k_attendance_logs',
[
'employee_id' => $employee->id
]
);
$logDateTime = date(
'Y-m-d H:i:s',
strtotime($log->check_time)
);
// =====================================
// FIND SCHEDULE
// SUPPORT SHIFT MALAM
// =====================================
$this->db->from(
'k_employee_shift_schedules'
);
$this->db->where(
'employee_id',
$employee->id
);
$this->db->where(
'status',
'published'
);
// =====================================
// RANGE SHIFT
// =====================================
$this->db->where("
'{$logDateTime}' BETWEEN
DATE_SUB(start_datetime, INTERVAL 3 HOUR)
AND
DATE_ADD(end_datetime, INTERVAL 6 HOUR)
", null, false);
$this->db->order_by(
'start_datetime',
'DESC'
);
$schedule = $this->db->get()->row();
// =====================================
// SCHEDULE NOT FOUND
// =====================================
if(!$schedule){
$this->updateLogStatus(
$log->id,
'failed'
);
echo "FAILED SCHEDULE : {$logDateTime}\n";
continue;
}
// =====================================
// GET SHIFT
// =====================================
$shift = $this->db
->where('id',$schedule->shift_id)
->get('k_shifts')
->row();
if(!$shift){
$this->updateLogStatus(
$log->id,
'failed'
);
echo "FAILED SHIFT : {$schedule->shift_id}\n";
continue;
}
// =====================================
// ATTENDANCE DATE
// =====================================
$attendance_date = date(
'Y-m-d',
strtotime($schedule->start_datetime)
);
// =====================================
// GET ATTENDANCE
// =====================================
$attendance = $this->db
->where(
'employee_id',
$employee->id
)
->where(
'attendance_date',
$attendance_date
)
->get('k_attendances')
->row();
// =====================================
// CHECK LATE
// =====================================
$late_minutes = 0;
$maxCheckin = strtotime(
$schedule->start_datetime
) + (
$shift->late_tolerance_minutes * 60
);
if(
strtotime($logDateTime)
>
$maxCheckin
){
$late_minutes = floor(
(
strtotime($logDateTime)
-
strtotime($schedule->start_datetime)
) / 60
);
if($late_minutes < 0){
$late_minutes = 0;
}
}
// =====================================
// INSERT CHECKIN
// =====================================
if(!$attendance){
$this->db->insert(
'k_attendances',
[
'employee_id' => $employee->id,
'attendance_date' => $attendance_date,
'checkin_time' => $logDateTime,
'late_minutes' => $late_minutes,
'attendance_status' => (
$late_minutes > 0
? 'late'
: 'present'
),
'shift_id' => $shift->id,
'attendance_log_in_id' => $log->id,
'created_at' => date('Y-m-d H:i:s')
]
);
echo "CHECKIN : {$employee->full_name}\n";
} else {
// =================================
// SUDAH ADA CHECKOUT
// =================================
if(!empty($attendance->checkout_time)){
$this->updateLogStatus(
$log->id,
'processed'
);
echo "SKIP LOG (SUDAH CHECKOUT)\n";
continue;
}
// =================================
// CEK SELISIH JAM
// =================================
$diffMinutes = floor(
(
strtotime($logDateTime)
-
strtotime($attendance->checkin_time)
) / 60
);
// =================================
// MINIMAL 2 JAM
// BARU DIANGGAP CHECKOUT
// =================================
if($diffMinutes < 120){
$this->updateLogStatus(
$log->id,
'processed'
);
echo "SKIP DUPLICATE LOG : {$employee->full_name}\n";
continue;
}
// =================================
// WORK HOURS
// =================================
$workHours = (
strtotime($logDateTime)
-
strtotime($attendance->checkin_time)
) / 3600;
if($workHours < 0){
$workHours = 0;
}
// =================================
// EARLY LEAVE
// =================================
$early_leave_minutes = 0;
if(
strtotime($logDateTime)
<
strtotime($schedule->end_datetime)
){
$early_leave_minutes = floor(
(
strtotime($schedule->end_datetime)
-
strtotime($logDateTime)
) / 60
);
if($early_leave_minutes < 0){
$early_leave_minutes = 0;
}
}
// =================================
// OVERTIME
// =================================
$overtime_hours = 0;
if(
strtotime($logDateTime)
>
strtotime($schedule->end_datetime)
){
$overtime_hours = round(
(
strtotime($logDateTime)
-
strtotime($schedule->end_datetime)
) / 3600,
2
);
if($overtime_hours < 0){
$overtime_hours = 0;
}
}
// =================================
// UPDATE CHECKOUT
// =================================
$this->db
->where(
'id',
$attendance->id
)
->update(
'k_attendances',
[
'checkout_time' => $logDateTime,
'attendance_log_out_id' => $log->id,
'work_hours' => round($workHours,2),
'early_leave_minutes' => $early_leave_minutes,
'overtime_hours' => $overtime_hours
]
);
echo "CHECKOUT : {$employee->full_name}\n";
}
// =====================================
// UPDATE LOG STATUS
// =====================================
$this->updateLogStatus(
$log->id,
'processed'
);
echo "SUCCESS LOG ID : {$log->id}\n";
} catch(Exception $e){
$this->updateLogStatus(
$log->id,
'failed'
);
echo "ERROR : " . $e->getMessage() . "\n";
}
}
echo "PROCESS ATTENDANCE DONE\n";
}
// =====================================================
// AUTO CLOSE ATTENDANCE
// =====================================================
public function auto_close_attendance()
{
echo "AUTO CLOSE START\n";
// =============================================
// AMBIL ATTENDANCE BELUM CHECKOUT
// =============================================
$attendances = $this->db
->where('checkout_time IS NULL', null, false)
->get('k_attendances')
->result();
foreach($attendances as $attendance){
// =========================================
// GET SCHEDULE
// =========================================
$schedule = $this->db
->where(
'employee_id',
$attendance->employee_id
)
->where(
'shift_id',
$attendance->shift_id
)
->where(
'DATE(start_datetime)',
$attendance->attendance_date
)
->get('k_employee_shift_schedules')
->row();
if(!$schedule){
continue;
}
// =========================================
// AUTO CLOSE
// END SHIFT + 4 JAM
// =========================================
$autoCloseTime = date(
'Y-m-d H:i:s',
strtotime(
$schedule->end_datetime . ' +4 hours'
)
);
// =========================================
// BELUM WAKTUNYA
// =========================================
if(
strtotime(date('Y-m-d H:i:s'))
<
strtotime($autoCloseTime)
){
continue;
}
// =========================================
// WORK HOURS
// =========================================
$workHours = (
strtotime($schedule->end_datetime)
-
strtotime($attendance->checkin_time)
) / 3600;
if($workHours < 0){
$workHours = 0;
}
// =========================================
// AUTO CHECKOUT
// =========================================
$this->db
->where('id',$attendance->id)
->update(
'k_attendances',
[
'checkout_time' => $schedule->end_datetime,
'work_hours' => round($workHours,2),
'notes' => 'Auto checkout by system'
]
);
echo "AUTO CLOSE EMPLOYEE : {$attendance->employee_id}\n";
}
echo "AUTO CLOSE DONE\n";
}
// =====================================================
// GENERATE DAILY ATTENDANCE
// =====================================================
public function generate_daily_attendance()
{
echo "GENERATE DAILY START\n";
// =============================================
// GENERATE HARI KEMARIN
// =============================================
$date = date(
'Y-m-d',
strtotime('-1 day')
);
// =============================================
// GET ALL SCHEDULE
// =============================================
$schedules = $this->db
->where('schedule_date', $date)
->where('status','published')
->get('k_employee_shift_schedules')
->result();
foreach($schedules as $schedule){
// =========================================
// CHECK ATTENDANCE EXIST
// =========================================
$attendance = $this->db
->where(
'employee_id',
$schedule->employee_id
)
->where(
'attendance_date',
$date
)
->get('k_attendances')
->row();
if($attendance){
continue;
}
// =========================================
// CHECK HOLIDAY
// =========================================
$holiday = $this->db
->where('holiday_date',$date)
->get('k_holidays')
->row();
if($holiday){
$this->db->insert(
'k_attendances',
[
'employee_id' => $schedule->employee_id,
'attendance_date' => $date,
'attendance_status' => 'holiday',
'is_holiday' => 1,
'shift_id' => $schedule->shift_id,
'notes' => $holiday->holiday_name
]
);
continue;
}
// =========================================
// CHECK LEAVE
// =========================================
$leave = $this->db
->where(
'employee_id',
$schedule->employee_id
)
->where(
'approval_status',
'approved'
)
->where('start_date <=',$date)
->where('end_date >=',$date)
->get('k_leave_requests')
->row();
if($leave){
$this->db->insert(
'k_attendances',
[
'employee_id' => $schedule->employee_id,
'attendance_date' => $date,
'attendance_status' => $leave->leave_type,
'shift_id' => $schedule->shift_id,
'notes' => $leave->reason
]
);
continue;
}
// =========================================
// DEFAULT ALPHA
// =========================================
$this->db->insert(
'k_attendances',
[
'employee_id' => $schedule->employee_id,
'attendance_date' => $date,
'attendance_status' => 'alpha',
'shift_id' => $schedule->shift_id,
'notes' => 'Tidak hadir'
]
);
echo "GENERATE EMPLOYEE : {$schedule->employee_id}\n";
}
echo "GENERATE DAILY DONE\n";
}
// =====================================================
// UPDATE LOG STATUS
// =====================================================
private function updateLogStatus(
$id,
$status
){
$this->db
->where('id',$id)
->update(
'k_attendance_logs',
[
'process_status' => $status
]
);
}
}
+336
View File
@@ -0,0 +1,336 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class Setting extends CI_Controller {
public function __construct()
{
parent::__construct();
// Cek apakah user sudah login
if (!$this->session->userdata('logged_in')) {
redirect('login');
}
if (!check_permission('setting', 'can_view')) {
show_error('Anda tidak memiliki izin untuk mengakses halaman ini.', 403);
}
}
// ==========================
// MAIN PAGE
// ==========================
public function index()
{
if (!check_permission('setting', 'can_view')) {
show_error('Tidak memiliki izin untuk melihat detail.', 403);
}
// Ambil config id=1
$cfg = $this->db->get_where("config", ["id" => 1])->row();
// Jika tidak ada data config
$config = $cfg ?: $this->defaultConfig();
// Ambil template pesan
$template_pesan = $this->db
->order_by("id", "ASC")
->get("template_pesan")
->result();
$data = [
"active_menu" => "pengaturan",
"config" => $config,
"template_pesan" => $template_pesan
];
$this->load->view('partials/header', $data);
$this->load->view('setting/layout', $data);
$this->load->view('partials/footer');
}
// ===============================
// DEFAULT CONFIG
// ===============================
private function defaultConfig()
{
if (!check_permission('setting', 'can_view')) {
show_error('Tidak memiliki izin untuk melihat detail.', 403);
}
return (object)[
"nama_lembaga" => "",
"pimpinan_lembaga" => "",
"card_background" => "",
"card_orientation" => "landscape",
"wa_mode" => "official",
"wa_phone" => "",
"wa_phone_id" => "",
"wa_waba_id" => "",
"wa_access_token" => "",
"wa_endpoint" => "",
"wa_api_key" => ""
];
}
// ===============================
// 1. SIMPAN SETTING UMUM
// ===============================
public function save_umum()
{
if (!check_permission('setting', 'can_view')) {
show_error('Tidak memiliki izin untuk melihat detail.', 403);
}
$update = [
"nama_lembaga" => $this->input->post("nama_lembaga"),
"pimpinan_lembaga" => $this->input->post("pimpinan_lembaga"),
"updated_at" => date("Y-m-d H:i:s")
];
$this->db->where("id", 1)->update("config", $update);
redirect("setting");
}
// ===============================
// 2. SIMPAN CARD + UPLOAD BG
// ===============================
public function save_card()
{
if (!check_permission('setting', 'can_view')) {
show_error('Tidak memiliki izin untuk melihat detail.', 403);
}
$orientation = $this->input->post("card_orientation");
$bg = null;
// Jika ada upload file
if (!empty($_FILES["card_background"]["name"])) {
$config['upload_path'] = './uploads/card/';
$config['allowed_types'] = 'jpg|jpeg|png';
$config['file_name'] = 'background_' . time();
if (!is_dir('./uploads/card/')) {
mkdir('./uploads/card/', 0777, true);
}
$this->load->library('upload', $config);
if ($this->upload->do_upload('card_background')) {
$file = $this->upload->data();
$bg = "uploads/card/" . $file['file_name'];
}
}
// Data update
$data = [
"card_orientation" => $orientation,
"updated_at" => date("Y-m-d H:i:s")
];
// Jika ada gambar baru
if ($bg !== null) {
$data["card_background"] = $bg;
}
$this->db->where("id", 1)->update("config", $data);
redirect("setting");
}
// ===============================
// 3. SIMPAN SETTING WHATSAPP
// ===============================
public function save_whatsapp()
{
if (!check_permission('setting', 'can_view')) {
show_error('Tidak memiliki izin untuk melihat detail.', 403);
}
$mode = $this->input->post("wa_mode");
$data = [
"wa_mode" => $mode,
"updated_at" => date("Y-m-d H:i:s")
];
if ($mode == "official") {
$data["wa_phone"] = $this->input->post("wa_phone");
$data["wa_phone_id"] = $this->input->post("wa_phone_id");
$data["wa_waba_id"] = $this->input->post("wa_waba_id");
$data["wa_access_token"] = $this->input->post("wa_access_token");
// Kosongkan unofficial
$data["wa_endpoint"] = "";
$data["wa_api_key"] = "";
} else {
$data["wa_endpoint"] = $this->input->post("wa_endpoint");
$data["wa_api_key"] = $this->input->post("wa_api_key");
// Kosongkan official
$data["wa_phone"] = "";
$data["wa_phone_id"] = "";
$data["wa_waba_id"] = "";
$data["wa_access_token"] = "";
}
$this->db->where("id", 1)->update("config", $data);
redirect("setting");
}
// ===============================
// 4. UPDATE TEMPLATE PESAN
// ===============================
public function update_template_pesan()
{
if (!check_permission('setting', 'can_view')) {
show_error('Tidak memiliki izin untuk melihat detail.', 403);
}
$data = [
"nama_template" => $this->input->post("nama_template"),
"isi_template" => $this->input->post("isi_template")
];
$id = $this->input->post("id");
$this->db->where("id", $id)->update("template_pesan", $data);
redirect("setting");
}
public function cek_whatsapp_status()
{
if (!check_permission('setting', 'can_view')) {
show_error('Tidak memiliki izin untuk melihat detail.', 403);
}
$config = $this->db->get_where("config", ["id" => 1])->row();
if (!$config) {
echo json_encode([
"status" => "error",
"img" => base_url("uploads/whatsapp/disconnect.png"),
"msg" => "Config tidak ditemukan"
]);
return;
}
// Fungsi bantu call API
function callCurl($url, $fields)
{
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => '',
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 0,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_POSTFIELDS => $fields,
CURLOPT_HTTPHEADER => array( 'Content-Type: application/x-www-form-urlencoded' ),
]);
$response = curl_exec($curl);
curl_close($curl);
return $response;
}
// 1. CEK STATUS
$response = callCurl(
$config->wa_endpoint . "getState",
"apiKey=" . $config->wa_api_key
);
$data = json_decode($response, true);
if (!$data || !isset($data['results']['state'])) {
echo json_encode([
"status" => "error",
"img" => base_url('uploads/whatsapp/disconnect.png'),
"msg" => "Gagal membaca respon server WA",
"data" => $data,
"url_key" => $config->wa_endpoint . ' ' . $config->wa_api_key
]);
return;
}
$state = $data['results']['state'];
// ==========================================================
// 1. CONNECTED → tampilkan gambar connected
// ==========================================================
if ($state == "CONNECTED") {
echo json_encode([
"status" => "CONNECTED",
"img" => base_url('uploads/whatsapp/connect.png')
]);
return;
}
// ==========================================================
// 2. SERVICE_OFF → tampilkan disconnect + mulai service
// ==========================================================
if ($state == "SERVICE_OFF") {
// Mulai service
callCurl(
$config->wa_endpoint . "serviceStart",
"apiKey=" . $config->wa_api_key
);
echo json_encode([
"status" => "SERVICE_OFF",
"img" => base_url('uploads/whatsapp/disconnect.png')
]);
return;
}
// ==========================================================
// 3. SERVICE_SCAN → ambil QR
// ==========================================================
if ($state == "SERVICE_SCAN") {
$responseQR = callCurl(
$config->wa_endpoint . "getQR",
"apiKey=" . $config->wa_api_key
);
$qrData = json_decode($responseQR, true);
if (!isset($qrData['results']['qrString'])) {
echo json_encode([
"status" => "SERVICE_SCAN",
"img" => base_url('uploads/whatsapp/map_loader.gif')
]);
return;
}
echo json_encode([
"status" => "SERVICE_SCAN",
"img" => $qrData['results']['qrString']
]);
return;
}
// ==========================================================
// 4. STATUS LAIN → tampilkan loader
// ==========================================================
echo json_encode([
"status" => "UNKNOWN",
"img" => base_url('uploads/whatsapp/map_loader.gif')
]);
}
}
+147
View File
@@ -0,0 +1,147 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class Shifts extends CI_Controller {
public function __construct()
{
parent::__construct();
$this->load->database();
}
// ==========================================
// PAGE
// ==========================================
public function index()
{
$data = [
"active_menu" => "shift"
];
$this->load->view('partials/header', $data);
$this->load->view('employees/shift');
$this->load->view('partials/footer');
}
// =====================================================
// DATATABLE
// =====================================================
public function get_data()
{
$list = $this->db
->order_by('id','DESC')
->get('k_shifts')
->result();
$data = [];
$no = 1;
foreach($list as $r){
$night = $r->is_night_shift == 1
? '<span class="badge bg-dark">Night Shift</span>'
: '<span class="badge bg-success">Normal</span>';
$data[] = [
$no++,
$r->shift_code,
$r->shift_name,
$r->checkin_time,
$r->checkout_time,
$r->late_tolerance_minutes . ' Menit',
$r->work_hours . ' Jam',
$night,
'
<button class="btn btn-warning btn-sm btn-edit"
data-id="'.$r->id.'">
Edit
</button>
<button class="btn btn-danger btn-sm btn-delete"
data-id="'.$r->id.'">
Delete
</button>
'
];
}
echo json_encode([
"data" => $data
]);
}
// =====================================================
// DETAIL
// =====================================================
public function detail($id)
{
$data = $this->db
->get_where('k_shifts',['id'=>$id])
->row();
echo json_encode($data);
}
// =====================================================
// SAVE
// =====================================================
public function save()
{
$data = [
'shift_code' => $this->input->post('shift_code'),
'shift_name' => $this->input->post('shift_name'),
'checkin_time' => $this->input->post('checkin_time'),
'checkout_time' => $this->input->post('checkout_time'),
'late_tolerance_minutes' => $this->input->post('late_tolerance_minutes'),
'work_hours' => $this->input->post('work_hours'),
'is_night_shift' => $this->input->post('is_night_shift')
];
$this->db->insert('k_shifts',$data);
echo json_encode([
'status' => true,
'message'=> 'Shift berhasil ditambahkan'
]);
}
// =====================================================
// UPDATE
// =====================================================
public function update()
{
$id = $this->input->post('id');
$data = [
'shift_code' => $this->input->post('shift_code'),
'shift_name' => $this->input->post('shift_name'),
'checkin_time' => $this->input->post('checkin_time'),
'checkout_time' => $this->input->post('checkout_time'),
'late_tolerance_minutes' => $this->input->post('late_tolerance_minutes'),
'work_hours' => $this->input->post('work_hours'),
'is_night_shift' => $this->input->post('is_night_shift')
];
$this->db->where('id',$id);
$this->db->update('k_shifts',$data);
echo json_encode([
'status' => true,
'message'=> 'Shift berhasil diupdate'
]);
}
// =====================================================
// DELETE
// =====================================================
public function delete($id)
{
$this->db->delete('k_shifts',['id'=>$id]);
echo json_encode([
'status' => true,
'message'=> 'Shift berhasil dihapus'
]);
}
}
+263
View File
@@ -0,0 +1,263 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class Users extends CI_Controller {
public function __construct()
{
parent::__construct();
$this->load->model('DynamicModel', 'dm');
$this->load->library('form_validation');
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" => "users"];
$this->load->view('partials/header', $data);
$this->load->view('users/layout');
$this->load->view('partials/footer');
}
// =========================
// DATATABLE USERS + ROLES
// =========================
public function get_data()
{
$this->db->select('
users.id,
users.username,
users.nama,
roles.nama_role
');
$this->db->from('users');
$this->db->join('roles', 'roles.id = users.role_id', 'left');
$column_order = ['users.id', 'users.username', 'users.nama', 'roles.nama_role'];
$column_search = ['users.username', 'users.nama', 'roles.nama_role'];
$order = ['users.id' => 'DESC'];
$search_value = $this->input->post('search')['value'] ?? '';
if (!empty($search_value)) {
$this->db->group_start();
foreach ($column_search as $i => $col) {
if ($i === 0) {
$this->db->like($col, $search_value);
} else {
$this->db->or_like($col, $search_value);
}
}
$this->db->group_end();
}
if ($this->input->post('order')) {
$col_index = $this->input->post('order')[0]['column'];
$dir = $this->input->post('order')[0]['dir'];
if (isset($column_order[$col_index])) {
$this->db->order_by($column_order[$col_index], $dir);
}
} else {
$this->db->order_by(key($order), $order[key($order)]);
}
if ($this->input->post('length') != -1) {
$this->db->limit(
$this->input->post('length'),
$this->input->post('start')
);
}
$query = $this->db->get();
$data = $query->result();
$output = [
"draw" => intval($this->input->post('draw')),
"recordsTotal" => $this->db->count_all('users'),
"recordsFiltered" => $this->_count_filtered($search_value),
"data" => []
];
foreach ($data as $row) {
$action = '
<button class="btn btn-sm btn-warning btn-edit" data-id="'.$row->id.'">Edit</button>
<button class="btn btn-sm btn-danger btn-delete" data-id="'.$row->id.'">Hapus</button>
';
$output['data'][] = [
$row->id,
$row->username,
$row->nama,
$row->nama_role,
$action
];
}
echo json_encode($output);
}
private function _count_filtered($search_value)
{
$this->db->from('users');
$this->db->join('roles', 'roles.id = users.role_id', 'left');
if (!empty($search_value)) {
$this->db->group_start();
$this->db->like('users.username', $search_value);
$this->db->or_like('users.nama', $search_value);
$this->db->or_like('roles.nama_role', $search_value);
$this->db->group_end();
}
return $this->db->count_all_results();
}
// =========================
// STORE USER
// =========================
public function store()
{
$this->form_validation->set_rules('username', 'Username', 'required|is_unique[users.username]');
$this->form_validation->set_rules('nama', 'Nama', 'required');
$this->form_validation->set_rules('role_id', 'Role', 'required');
$this->form_validation->set_rules('password', 'Password', 'required|min_length[6]');
if ($this->form_validation->run() == FALSE) {
echo json_encode([
'status' => false,
'message' => validation_errors()
]);
return;
}
$data = [
'username' => $this->input->post('username'),
'nama' => $this->input->post('nama'),
'role_id' => $this->input->post('role_id'),
'password' => password_hash($this->input->post('password'), PASSWORD_DEFAULT)
];
$this->dm->setTable('users')->insert($data);
log_activity(
'users',
'create',
'Menambahkan user: ' . $data['username'],
'success'
);
echo json_encode([
'status' => true,
'message' => 'User berhasil ditambahkan'
]);
}
// =========================
// EDIT USER
// =========================
public function edit($id)
{
$user = $this->db->get_where('users', ['id' => $id])->row();
if ($user) {
echo json_encode([
'status' => true,
'data' => $user
]);
} else {
echo json_encode([
'status' => false,
'message' => 'User tidak ditemukan'
]);
}
}
// =========================
// UPDATE USER
// =========================
public function update()
{
$id = $this->input->post('id');
$this->form_validation->set_rules('username', 'Username', 'required');
$this->form_validation->set_rules('nama', 'Nama', 'required');
$this->form_validation->set_rules('role_id', 'Role', 'required');
if (!empty($this->input->post('password'))) {
$this->form_validation->set_rules('password', 'Password', 'min_length[6]');
}
if ($this->form_validation->run() == FALSE) {
echo json_encode([
'status' => false,
'message' => validation_errors()
]);
return;
}
$data = [
'username' => $this->input->post('username'),
'nama' => $this->input->post('nama'),
'role_id' => $this->input->post('role_id')
];
if (!empty($this->input->post('password'))) {
$data['password'] = password_hash($this->input->post('password'), PASSWORD_DEFAULT);
}
$this->dm->setTable('users')->update($id, $data);
log_activity(
'users',
'update',
'Update user ID: ' . $id . ' username: ' . $this->input->post('username'),
'success'
);
echo json_encode([
'status' => true,
'message' => 'User berhasil diupdate'
]);
}
// =========================
// DELETE USER
// =========================
public function delete($id)
{
$this->dm->setTable('users')->delete($id);
log_activity(
'users',
'delete',
'Hapus user ID: ' . $id,
'success'
);
echo json_encode([
'status' => true,
'message' => 'User berhasil dihapus'
]);
}
// =========================
// GET ROLES
// =========================
public function get_role()
{
$roles = $this->db->get('roles')->result();
echo json_encode($roles);
}
}
+145
View File
@@ -0,0 +1,145 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class Warehouses extends CI_Controller {
public function __construct()
{
parent::__construct();
if (!$this->session->userdata('logged_in')) {
redirect('auth');
}
}
public function index()
{
$data = ["active_menu" => "warehouses"];
$this->load->view('partials/header', $data);
$this->load->view('warehouses/layout');
$this->load->view('partials/footer');
}
// =========================
// GET DATA (DATATABLE)
// =========================
public function get_data()
{
$data = $this->db->get('warehouses')->result();
$result = [];
$no = 1;
foreach ($data as $row) {
$result[] = [
$no++,
$row->nama,
'
<button class="btn btn-sm btn-secondary btn-editgudang" data-id="'.$row->id.'">Edit</button>
<button class="btn btn-sm btn-danger btn-delete" data-id="'.$row->id.'">Delete</button>
'
];
}
echo json_encode(["data"=>$result]);
}
// =========================
// DETAIL
// =========================
public function detail($id)
{
echo json_encode(
$this->db->get_where('warehouses',['id'=>$id])->row()
);
}
// =========================
// SAVE
// =========================
public function save()
{
$nama = $this->input->post('nama');
if(!$nama){
echo json_encode(['status'=>false,'message'=>'Nama wajib diisi']);
return;
}
$this->db->insert('warehouses', [
'nama' => $nama
]);
log_activity(
'kode_barang',
'create',
'Tambah gudang '.$nama,
'success'
);
echo json_encode(['status'=>true,'message'=>'Gudang berhasil ditambahkan']);
}
// =========================
// UPDATE
// =========================
public function update()
{
$id = $this->input->post('id');
$nama = $this->input->post('nama');
if(!$nama){
echo json_encode(['status'=>false,'message'=>'Nama wajib diisi']);
return;
}
$this->db->where('id',$id)->update('warehouses', [
'nama' => $nama
]);
log_activity(
'kode_barang',
'update',
'Update gudang '.$nama,
'success'
);
echo json_encode(['status'=>true,'message'=>'Gudang berhasil diupdate']);
}
// =========================
// DELETE
// =========================
public function delete($id)
{
// optional: cek relasi ke items
$cek = $this->db->get_where('items',['warehouse_id'=>$id])->num_rows();
if($cek > 0){
echo json_encode([
'status'=>false,
'message'=>'Gudang tidak bisa dihapus karena masih dipakai item'
]);
return;
}
$this->db->delete('warehouses',['id'=>$id]);
log_activity(
'kode_barang',
'delete',
'Delete gudang '.$nama,
'success'
);
echo json_encode(['status'=>true,'message'=>'Gudang berhasil dihapus']);
}
public function list()
{
echo json_encode(
$this->db->get('warehouses')->result()
);
}
}
+25
View File
@@ -0,0 +1,25 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class Welcome extends CI_Controller {
/**
* Index Page for this controller.
*
* Maps to the following URL
* http://example.com/index.php/welcome
* - or -
* http://example.com/index.php/welcome/index
* - or -
* Since this controller is set as the default controller in
* config/routes.php, it's displayed at http://example.com/
*
* So any other public methods not prefixed with an underscore will
* map to /index.php/welcome/<method_name>
* @see https://codeigniter.com/userguide3/general/urls.html
*/
public function index()
{
$this->load->view('login');
}
}
+11
View File
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<title>403 Forbidden</title>
</head>
<body>
<p>Directory access is forbidden.</p>
</body>
</html>
+11
View File
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<title>403 Forbidden</title>
</head>
<body>
<p>Directory access is forbidden.</p>
</body>
</html>
+26
View File
@@ -0,0 +1,26 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
function log_activity($module, $action, $desc, $status = 'success')
{
$CI =& get_instance();
$ip = $CI->input->ip_address();
if (!$ip || $ip == '0.0.0.0') {
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'] ??
$_SERVER['REMOTE_ADDR'] ??
'UNKNOWN';
}
$CI->db->insert('activity_logs', [
'user_id' => $CI->session->userdata('user_id') ?? 0,
'module' => strtolower($module),
'action' => strtolower($action),
'description' => $desc,
'status' => strtolower($status),
'ip_address' => $ip,
'created_at' => date('Y-m-d H:i:s')
]);
}
+11
View File
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<title>403 Forbidden</title>
</head>
<body>
<p>Directory access is forbidden.</p>
</body>
</html>
+34
View File
@@ -0,0 +1,34 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
if (!function_exists('tanggal_indo')) {
function tanggal_indo($tanggal, $format = 'd F Y')
{
if (empty($tanggal) || $tanggal == '0000-00-00') {
return '-';
}
$bulan = [
1 => 'Januari',
'Februari',
'Maret',
'April',
'Mei',
'Juni',
'Juli',
'Agustus',
'September',
'Oktober',
'November',
'Desember'
];
$timestamp = strtotime($tanggal);
$hari = date('d', $timestamp);
$bulan_index = (int)date('m', $timestamp);
$tahun = date('Y', $timestamp);
return $hari . ' ' . $bulan[$bulan_index] . ' ' . $tahun;
}
}
+30
View File
@@ -0,0 +1,30 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
/**
* Cek permission user
* @param string $key => nama fitur (misal: 'siswa', 'dashboard')
* @param string $action => action yang dicek (misal: 'can_view', 'can_export')
* @return bool
*/
function check_permission($key, $action = 'can_view') {
$CI =& get_instance();
$permissions = $CI->session->userdata('permissions');
if (!$permissions || !is_array($permissions)) {
return false;
}
// Pastikan key ada
if (!isset($permissions[$key])) {
return false;
}
// Jika value berupa array (multiple permissions), cek dengan in_array
if (is_array($permissions[$key])) {
return in_array($action, $permissions[$key]);
}
// Jika value berupa string tunggal
return $permissions[$key] === $action;
}
+11
View File
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<title>403 Forbidden</title>
</head>
<body>
<p>Directory access is forbidden.</p>
</body>
</html>
+11
View File
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<title>403 Forbidden</title>
</head>
<body>
<p>Directory access is forbidden.</p>
</body>
</html>
+11
View File
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<title>403 Forbidden</title>
</head>
<body>
<p>Directory access is forbidden.</p>
</body>
</html>
+11
View File
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<title>403 Forbidden</title>
</head>
<body>
<p>Directory access is forbidden.</p>
</body>
</html>
+255
View File
@@ -0,0 +1,255 @@
<?php
// require_once(APPPATH.'libraries/fpdf/fpdf.php');
require_once(APPPATH.'third_party/fpdf/fpdf.php');
class PDF extends FPDF
{
private $maxWidth = 190;
private $nama;
private $alamat;
private $telepon;
private $logo;
public function setHeaderData($nama, $alamat, $telepon, $logo='')
{
$this->nama = $nama;
$this->alamat = $alamat;
$this->telepon = $telepon;
$this->logo = $logo;
}
function Header()
{
// Logo
$this->Image($this->logo, 10, 8, 22, 22); // Path ke logo Anda, x, y, width
$this->SetXY(35, 11);
$this->SetFont('Arial', 'B', 14);
$this->Cell(0, 6, $this->nama, 0, 1, 'L');
$this->SetFont('Arial', 'I', 10);
$this->SetXY(35, 17);
$this->Cell(0, 6, $this->alamat, 0, 1, 'L');
$this->SetXY(35, 23);
$this->Cell(0, 6, 'Phone: '.$this->telepon, 0, 1, 'L');
$this->Ln(14);
// Garis Horizontal
$this->SetLineWidth(0.8); // Ketebalan garis
$this->Line(10, 33, 200, 33); // x1, y1, x2, y2 (x1, y1 = start; x2, y2 = end)
$this->SetLineWidth(0.4);
$this->Line(10, 34, 200, 34);
$this->SetTextColor(255, 140, 0);
$this->SetFont('Arial', 'B', 20);
$this->Cell(0, -56, 'INVOICE', 0, 1, 'R');
$this->Ln(50);
}
function Footer()
{
$this->SetY(-15);
$this->SetFont('Arial', 'I', 8);
$this->Cell(0, 10, 'Page ' . $this->PageNo(), 0, 0, 'C');
}
function CustomerDetails($customerName, $customerAddress, $invoiceNumber, $invoiceDate, $customerNumber, $invoiceLimit)
{
$this->SetFont('Arial', 'B', 11);
$this->Cell(0, 6, 'Bill To:', 0, 1);
$this->SetFont('Arial', '', 11);
$this->Cell(0, 6, $customerName, 0, 1);
$this->MultiCell(0, 7, $customerAddress);
$this->Cell(0, 6, $customerNumber, 0, 1);
$this->Ln(10);
$this->SetFont('Arial', '', 11);
$this->SetXY(140, 37);
$this->Cell(0, 6, "No Invoice : $invoiceNumber", 0, 1);
$this->SetXY(140, 44);
$this->Cell(0, 6, "Invoice Date : $invoiceDate", 0, 1);
$this->SetXY(140, 51);
$this->Cell(0, 6, "Limit Invoice : $invoiceLimit", 0, 1);
$this->Ln(10);
}
function InvoiceTable($header, $data)
{
// 🔥 7 kolom sekarang
$columnWidths = array(8, 22, 30, 70, 15, 20, 25);
$totalWidth = array_sum($columnWidths);
// scaling biar tetap muat
$scalingFactor = $this->maxWidth / $totalWidth;
// header style
$this->SetFillColor(255, 140, 0);
$this->SetFont('Arial', '', 10);
// ================= HEADER =================
for ($i = 0; $i < count($header); $i++) {
$this->Cell($columnWidths[$i] * $scalingFactor, 8, $header[$i], 1, 0, 'C', true);
}
$this->Ln();
// ================= DATA =================
$this->SetFont('Arial', '', 10);
foreach ($data as $row) {
// hitung multiline
$nbDesc = $this->NbLines($columnWidths[2] * $scalingFactor, $row[2]);
$nbKet = $this->NbLines($columnWidths[3] * $scalingFactor, $row[3]);
$maxHeight = 8 * max($nbDesc, $nbKet);
// pagination
if ($this->GetY() + $maxHeight > $this->PageBreakTrigger) {
$this->AddPage();
$this->SetFillColor(255, 140, 0);
$this->SetFont('Arial', '', 10);
for ($i = 0; $i < count($header); $i++) {
$this->Cell($columnWidths[$i] * $scalingFactor, 8, $header[$i], 1, 0, 'C', true);
}
$this->Ln();
$this->SetFont('Arial', '', 10);
}
$x = $this->GetX();
$y = $this->GetY();
// 1. No
$this->Cell($columnWidths[0] * $scalingFactor, $maxHeight, $row[0], 1, 0, 'C');
// 2. Tanggal
$this->Cell($columnWidths[1] * $scalingFactor, $maxHeight, $row[1], 1, 0, 'C');
// 3. Nama Item (multiline)
$this->SetXY($x + ($columnWidths[0] + $columnWidths[1]) * $scalingFactor, $y);
$this->MultiCell(
$columnWidths[2] * $scalingFactor,
$maxHeight / max(1, $nbDesc),
$row[2],
1,
'L'
);
// 4. Keterangan (multiline)
$this->SetXY($x + ($columnWidths[0] + $columnWidths[1] + $columnWidths[2]) * $scalingFactor, $y);
$this->MultiCell(
$columnWidths[3] * $scalingFactor,
$maxHeight / max(1, $nbKet),
$row[3],
1,
'L'
);
// 5. Qty
$this->SetXY($x + ($columnWidths[0] + $columnWidths[1] + $columnWidths[2] + $columnWidths[3]) * $scalingFactor, $y);
$this->Cell($columnWidths[4] * $scalingFactor, $maxHeight, $row[4], 1, 0, 'C');
// 6. Harga
$this->Cell($columnWidths[5] * $scalingFactor, $maxHeight, $row[5], 1, 0, 'R');
// 7. Subtotal
$this->Cell($columnWidths[6] * $scalingFactor, $maxHeight, $row[6], 1, 0, 'R');
$this->Ln($maxHeight);
}
}
// Fungsi tambahan untuk menghitung jumlah baris Multicell
function NbLines($w, $txt)
{
// Hitung jumlah baris dalam MultiCell
$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 += $cw[$c];
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;
}
function Total($subtotal,$lainnya,$totalAmount,$terbilang,$keteranganlain)
{
$this->Ln(1);
$this->SetFont('Arial', '', 11);
$this->Cell($this->maxWidth - 25, 9, 'Sub Total', 1, 0, 'R');
$this->Cell(25, 9, $subtotal, 1, 1, 'R');
$this->Cell($this->maxWidth - 25, 9, $keteranganlain, 1, 0, 'R');
$this->Cell(25, 9, $lainnya, 1, 1, 'R');
$this->SetFont('Arial', 'B', 11);
$this->Cell($this->maxWidth - 25, 9, 'Total', 1, 0, 'R');
$this->Cell(25, 9, $totalAmount, 1, 1, 'R');
$this->Ln(1);
$this->SetFont('Arial', 'I', 11);
$this->Cell(0, 9, 'Terbilang : ', 0, 1);
$this->SetFont('Arial', 'I', 11);
$this->MultiCell(190, 9, $terbilang, 1, 'L');
}
function Keterangan($Keterangan)
{
$this->Ln(10);
$this->SetFont('Arial', '', 11);
$this->SetFillColor(255, 140, 0);
$this->MultiCell(100, 6, $Keterangan, 0, 'L', true);
}
function Pembuat($Pembuat)
{
$this->Ln(-25);
$this->SetFont('Arial', '', 11);
$this->Cell(300, 6, 'Mengetahui,', 0, 1, 'C');
$this->Ln(18);
$this->SetFont('Arial', 'B', 11);
$this->Cell(300, 6, '( '.$Pembuat.' )', 0, 1, 'C');
$this->SetFont('Arial', 'I', 11);
$this->Cell(300, 6, 'financial department', 0, 1, 'C');
}
}
?>
+11
View File
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<title>403 Forbidden</title>
</head>
<body>
<p>Directory access is forbidden.</p>
</body>
</html>
+38
View File
@@ -0,0 +1,38 @@
* 1.0.0 build 2010031920
- first public release
- help in readme, install
- cleanup ans separation of QRtools and QRspec
- now TCPDF binding requires minimal changes in TCPDF, having most of job
done in QRtools tcpdfBarcodeArray
- nicer QRtools::timeBenchmark output
- license and copyright notices in files
- indent cleanup - from tab to 4spc, keep it that way please :)
- sf project, repository, wiki
- simple code generator in index.php
* 1.1.0 build 2010032113
- added merge tool wich generate merged version of code
located in phpqrcode.php
- splited qrconst.php from qrlib.php
* 1.1.1 build 2010032405
- patch by Rick Seymour allowing saving PNG and displaying it at the same time
- added version info in VERSION file
- modified merge tool to include version info into generated file
- fixed e-mail in almost all head comments
* 1.1.2 build 2010032722
- full integration with TCPDF thanks to Nicola Asuni, it's author
- fixed bug with alphanumeric encoding detection
* 1.1.3 build 2010081807
- short opening tags replaced with standard ones
* 1.1.4 build 2010100721
- added missing static keyword QRinput::check (found by Luke Brookhart, Onjax LLC)
+67
View File
@@ -0,0 +1,67 @@
== REQUIREMENTS ==
* PHP5
* PHP GD2 extension with JPEG and PNG support
== INSTALLATION ==
If you want to recreate cache by yourself make sure cache directory is
writable and you have permisions to write into it. Also make sure you are
able to read files in it if you have cache option enabled
== CONFIGURATION ==
Feel free to modify config constants in qrconfig.php file. Read about it in
provided comments and project wiki page (links in README file)
== QUICK START ==
Notice: probably you should'nt use all of this in same script :)
<?phpb
//include only that one, rest required files will be included from it
include "qrlib.php"
//write code into file, Error corection lecer is lowest, L (one form: L,M,Q,H)
//each code square will be 4x4 pixels (4x zoom)
//code will have 2 code squares white boundary around
QRcode::png('PHP QR Code :)', 'test.png', 'L', 4, 2);
//same as above but outputs file directly into browser (with appr. header etc.)
//all other settings are default
//WARNING! it should be FIRST and ONLY output generated by script, otherwise
//rest of output will land inside PNG binary, breaking it for sure
QRcode::png('PHP QR Code :)');
//show benchmark
QRtools::timeBenchmark();
//rebuild cache
QRtools::buildCache();
//code generated in text mode - as a binary table
//then displayed out as HTML using Unicode block building chars :)
$tab = $qr->encode('PHP QR Code :)');
QRspec::debug($tab, true);
== TCPDF INTEGRATION ==
Inside bindings/tcpdf you will find slightly modified 2dbarcodes.php.
Instal phpqrcode liblaty inside tcpdf folder, then overwrite (or merge)
2dbarcodes.php
Then use similar as example #50 from TCPDF examples:
<?php
$style = array(
'border' => true,
'padding' => 4,
'fgcolor' => array(0,0,0),
'bgcolor' => false, //array(255,255,255)
);
//code name: QR, specify error correction level after semicolon (L,M,Q,H)
$pdf->write2DBarcode('PHP QR Code :)', 'QR,L', '', '', 30, 30, $style, 'N');
+165
View File
@@ -0,0 +1,165 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.
+45
View File
@@ -0,0 +1,45 @@
This is PHP implementation of QR Code 2-D barcode generator. It is pure-php
LGPL-licensed implementation based on C libqrencode by Kentaro Fukuchi.
== LICENSING ==
Copyright (C) 2010 by Dominik Dzienia
This library is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free
Software Foundation; either version 3 of the License, or any later version.
This library is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU Lesser General Public License (LICENSE file)
for more details.
You should have received a copy of the GNU Lesser General Public License along
with this library; if not, write to the Free Software Foundation, Inc., 51
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
== INSTALATION AND USAGE ==
* INSTALL file
* http://sourceforge.net/apps/mediawiki/phpqrcode/index.php?title=Main_Page
== CONTACT ==
Fell free to contact me via e-mail (deltalab at poczta dot fm) or using
folowing project pages:
* http://sourceforge.net/projects/phpqrcode/
* http://phpqrcode.sourceforge.net/
== ACKNOWLEDGMENTS ==
Based on C libqrencode library (ver. 3.1.1)
Copyright (C) 2006-2010 by Kentaro Fukuchi
http://megaui.net/fukuchi/works/qrencode/index.en.html
QR Code is registered trademarks of DENSO WAVE INCORPORATED in JAPAN and other
countries.
Reed-Solomon code encoder is written by Phil Karn, KA9Q.
Copyright (C) 2002, 2003, 2004, 2006 Phil Karn, KA9Q
+2
View File
@@ -0,0 +1,2 @@
1.1.4
2010100721
File diff suppressed because it is too large Load Diff
+2
View File
@@ -0,0 +1,2 @@
Á À E9³u`³"PÅ„CÛ牗T!0$
E•ɲQ™Ém½úhÛ¾9{kI" 9Ln)Ap¤åÖ¾Ë>ß^‡Õz³mënÅ–;ü´mßn†ú¦Ë
Binary file not shown.

After

Width:  |  Height:  |  Size: 126 B

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 202 B

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 205 B

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 216 B

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 210 B

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 213 B

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 219 B

+1
View File
@@ -0,0 +1 @@
xÚí™A E]sëIX´;¸Ün6€È`q”êêW6ñ奚`Œ%A/3!¢°‚¢Š!g–ÈÌ¡’1N) éE¢Ï|;®—>6â¸Þ97$ëÄôëc]kköwé1Öü[·m­CÍœcÊRºÄê¹>¦èµ¾šE,•hʼnp„#áxFyWÏÇVWGçòÕ3¼Õ+шþàË“úSŽâ}Äž#áG8b^c^cÏÀŽp„c&3YQ"ñŽ÷çÌvµù›…ñàÎþþ¼–¹kÞ9ŠÜ‡÷}”¹³ï×ú ¢Ä¿QäÿL—/ÝÔÀÏ
Binary file not shown.

After

Width:  |  Height:  |  Size: 211 B

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 211 B

+2
View File
@@ -0,0 +1,2 @@
xÚí™A
ƒ0E]çÖ…,2;sƒä&ÉÍšh¥ÛêO¡ôÝÈàã1&09OIv@DDÒ Ì&§Ù‰KXÈÕFv•<Ádqò9Ö<%h•¹ Yïs !(d¥²ës;~||b(ÏøYůg#µ`œK ±S¼Åô¹Ä¶˜ùsàidßLg:Ó™Îtþ/gmª™ƒkÅMâ3³{­4rTÈQýÿe¥·s·>ó<Ó™Ît¦3éÌ;ïH¼#Ñ™Ît¦3ÍYœ+og©hù¶óµÙ½¬lnðûF>Øi^»#awm;gè~pÛgìNs{6z’‘»ãºïÞäp¾Ê'
Binary file not shown.

After

Width:  |  Height:  |  Size: 228 B

+3
View File
@@ -0,0 +1,3 @@
xÚíšA
Ä E»öÖ.ĚNo 7Ń›Ť¶iiRÚN2‹áW%đxÁ@ÚÚśę'­
u6×ę.ť*S;}«ŇĂ ĎT účĚztąď%ç,ŇĹÚâÎ}ç;“âç)ąźâÝZÚîLĺčą÷¬Pçç$Ż×÷ĎqËgśLÂôdJ‡;Üáw¸Ăý.]z#źľ«[Íť˝ďOg‚­Ćô"ĐË áBíî¦}Ç}‡;Üáw¸Ăî#1GbŽ„;Üáw¸Ăý_ÝC+w˘@Dfî÷ďç™uťř2™ĹÚÉNţű9R7|pWßkďű®ż“ßßkşöżşú»ĽÎÓ
Binary file not shown.

After

Width:  |  Height:  |  Size: 225 B

+1
View File
@@ -0,0 +1 @@
xÚÍ’Í À F{vë&  à&°Y+?Z1öÐSŸ'y!¢ŸÌÁa815&£•Û´ŽÙHå£Ùžc³•l«ÏFÆè1º#é6 fÊÖü©§6Äø•O7ˆ¨†C¦«›ðÖžÏ8gI®ÏöfB¦ÃÄÿæ\DÔ»(
Binary file not shown.

After

Width:  |  Height:  |  Size: 144 B

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 225 B

+1
View File
@@ -0,0 +1 @@
xÚíšA E]sëIX´;¹Ün6Up“в™ÿ]Ù˜þ< i-eWö‹¶˜)×äÅ•¼ÉÂ…H\jvqÙHL\6–šÝÐ…rI›¢LܹÜÕ%ÅÓ@´þ±V—vÆÂúý¤(ÏP4|ÎXnÒgÉß¼~]D¾ÉÕ×u1Us S\À°€,ÿÅ2Þ¢N§Ã?D›KºüF-:“eJ]p_À°€,˜a0Ã`†ÁÝ °`†Á ƒw,` X´]˜ˆ™‚¹‹˜°5 ‰®Y4{屿ñ2íûåvçJs†±Ûí9±˜í)õu±Û¹êÏØ,«]¸“‹Ù^_§7$ƒ_Í
Binary file not shown.

After

Width:  |  Height:  |  Size: 235 B

+3
View File
@@ -0,0 +1,3 @@
xÚíšA
„0 E]{ë.]{{{³©Z¥BepÆÞwe@VERZ3»Á"*2o€4¦y‰)i#dÒbdFÒ…´ŒI"ú‘—4ž½W­IíuŠÓ45ßx«.Z­SÙ{ÁŸ¯8åËÿk={o.±qÊÙ£[œÍ:帒q»õƒy
)t#á„N8ádCj-OOG}¼:/Ÿ:sz!Å)^<ùe½·S·uâ{ 'œp 'ú=ú=ú=¾'œp 'œp¢ß£ß£ßãN8á„Óÿ9©ªˆôpQQõ]HÔpz¾ØGœ^æ½Qº˜I|¾ß³u;9™ÎïÕëd;“X~$ËÙÑÉt¶ÊÛédy

Some files were not shown because too many files have changed in this diff Show More